From patchwork Tue May 15 23:12:10 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Gupta X-Patchwork-Id: 8985 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:a02:155:0:0:0:0:0 with SMTP id c82-v6csp2064428jad; Tue, 15 May 2018 16:12:43 -0700 (PDT) X-Google-Smtp-Source: AB8JxZoQ6KC0AY5HvhelX8nVTH+bherdsSbX5Z0kJw7rifPXejiGiAmra6m1TSdTqlDWJ0Li7ycw X-Received: by 2002:a1c:eb0e:: with SMTP id j14-v6mr8479766wmh.87.1526425962980; Tue, 15 May 2018 16:12:42 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526425962; cv=none; d=google.com; s=arc-20160816; b=uTxWEtp8GODhaFeXAjpvOjr/KCWLI1ucZg9EDMMbgH3ygfkD33ol6YwU23R20Jz9x8 PyQ8HUwsY5DlkpGllqMbWsgYgqy15FUbp/EqJpSztG/bjDU7RStJtsrJ8wwRcvWkbG3v sv5S+Mhp+IlWee0cK+EvLEjjFq0j12uDDUaZYYLF7FB0O1sL1TuK+7qytTJh43vH2gjf oTMRrDTz/d13bDUM/QR9AVMcCZzQyCtY06YMm72Q2SI4XXrvyVy3pGV2CRVgOjYlmIaq qLvzgwiwfyjlICLPtBgNji9tukXMDe+iclIgk6ikihxRUo1THC/waYOl3RFYSCPQGQ7A Fc2A== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:reply-to:list-subscribe :list-help:list-post:list-archive:list-unsubscribe:list-id :precedence:subject:to:message-id:date:from:references:in-reply-to :mime-version:dkim-signature:dkim-signature:delivered-to :arc-authentication-results; bh=Uca5RpP4hqNSmY7RvuadwDYQOY/UhfCHMIT+2AAMoKU=; b=e9qAp02Qd8iWeBtZCzDDv74I5oIDvEBHU3rJq/47UtYpFozeV08vhbO1RsvonpsmTi xzrhYtaosYmIrfbmM6LTLc90PE5f5WfJhGFKFKV7VK+V6DaF3nsrk3kZIl8ZtdfKzFVu hJCCoSmyEMw7vRlHj6FRTGvCgc5nZD35XFtKDswILDDBX45WYALLStKlKRc3eJCAw2Yv INeiKBLGAVCvuTuX/Y1ohJky6Y0EQ9RkBFeTtkviOJNWmiMt9hg6/8lGE598Mev1DGVO JpNsRWr2nVuzBn2yqw8P77MTCvTrHII/VkbdtjYhvD1yZgEh/PRYOLIzjDmTE2EqOdCH acZg== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=gPqZJnJT; dkim=neutral (body hash did not verify) header.i=@tmm1-net.20150623.gappssmtp.com header.s=20150623 header.b=sFuUSA2w; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Return-Path: Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org. [79.124.17.100]) by mx.google.com with ESMTP id y132-v6si1000169wmy.27.2018.05.15.16.12.42; Tue, 15 May 2018 16:12:42 -0700 (PDT) Received-SPF: pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) client-ip=79.124.17.100; Authentication-Results: mx.google.com; dkim=neutral (body hash did not verify) header.i=@gmail.com header.s=20161025 header.b=gPqZJnJT; dkim=neutral (body hash did not verify) header.i=@tmm1-net.20150623.gappssmtp.com header.s=20150623 header.b=sFuUSA2w; spf=pass (google.com: domain of ffmpeg-devel-bounces@ffmpeg.org designates 79.124.17.100 as permitted sender) smtp.mailfrom=ffmpeg-devel-bounces@ffmpeg.org Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id 933DF68972B; Wed, 16 May 2018 02:12:02 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-ot0-f172.google.com (mail-ot0-f172.google.com [74.125.82.172]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id EB7206800B5 for ; Wed, 16 May 2018 02:11:54 +0300 (EEST) Received: by mail-ot0-f172.google.com with SMTP id t1-v6so2226202oth.8 for ; Tue, 15 May 2018 16:12:33 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=mime-version:sender:in-reply-to:references:from:date:message-id :subject:to; bh=KCIu44VpDZzFXSaZWFa8hluxdrFNfVehgbcaQL6rlxA=; b=gPqZJnJTSCKHspVmj+7WkJR7InNVjfu5w8gzABuoE7TDJ4Ye7n3o6nzgG3VBQ2/Z7z 0YkzDs3I98xlGZVP/aS5BwjrrF3/r2QoEQF1C8NG5PZJuFJ5i7AwzGCjkhVmsFGHBL0+ E+GdRMET50C8W1jf2wktgSH5ikaPRm0ia6yTjJqcE4i0zyIFn3oxMoimFy7fKgMOtFvv K43KNcgrjqTNm/esw36crcFxxe5tTadIJltgg0Q+TzTk1CsUnyN98uvPuinLeFlUCLCa 2I+uUzdMAY2r3+VXLXNDFLyNbeztMpZf+VE1EouAyLOZWiEMnWvlOdjlu2KJKvQmgQ8S +FEg== DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tmm1-net.20150623.gappssmtp.com; s=20150623; h=mime-version:sender:in-reply-to:references:from:date:message-id :subject:to; bh=KCIu44VpDZzFXSaZWFa8hluxdrFNfVehgbcaQL6rlxA=; b=sFuUSA2wOXGs+/NCJcWV9LVyZKP5j5Arvp/1Ezj4TUPawC4m7VxkO04ckoCN+KmDkv WcjXM4tj0TPqa91lmV4GK7d4zgpcwxK1il4NAIVsk/lAPohNtRDvTzVP+mLYul6L7jlQ LuNXC5u14LMcinwilCViJ0XSx3PncDBIccXfRytd43k7zhEFJDB4rR4sXhMOQCtYA6vw SNZj7COXnT2k22S+J2nuUtInUJd4VcEVXxZ7FEGHq/8PzYm1cM0Tu74fcWRAVLH7fmqZ I4qKo2BVjoFEgZNinNXnAeAnUIXvnbMcG4kt68kxUHmA//jWzKGPBbvHYDCBr3jyP01Z t0Sg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:mime-version:sender:in-reply-to:references:from :date:message-id:subject:to; bh=KCIu44VpDZzFXSaZWFa8hluxdrFNfVehgbcaQL6rlxA=; b=ccvvTwhsbsPVigbyjLdMk71JNDQkPx0n/1ee2nJaUcD1VNa4ncOGMqw1dRdTgj5HG9 mHTKTmKW6vrbkcqQrOoB1bSatCaFlIUdOBxxy1/LSEpadDpnHs3C7OPATcwhI+ITb9LB cfEn67gMBHELxzKAyku/jf/xBid1ByrdDeJE18UMfI4BaC6LRcddYIggEd85r3tLALvc HdIfyrOQ28t9t31FrtGFBOE6aPcLzXkAGc7VijFFpS8/6NZlSDPQU8z9PTqbGvG9ByDr 1xd5e+TQxPAclEvRq1XLg7YABI3LGuIno9Dhbo57Al+9gxdaAy7IEorix63gTBUuF9di 3fvw== X-Gm-Message-State: ALKqPwcJDfXeqJf7/TcOFH4CbaRqDdgyXbYChUB6cB4ZvCTW5XUV9VV6 2cL4/LlTlcdHua+LBtd02lSiFmvBNNgaaEYzFUr6HRe4 X-Received: by 2002:a9d:2572:: with SMTP id j47-v6mr11706473otd.280.1526425951412; Tue, 15 May 2018 16:12:31 -0700 (PDT) MIME-Version: 1.0 Received: by 10.201.24.198 with HTTP; Tue, 15 May 2018 16:12:10 -0700 (PDT) In-Reply-To: <20180515192344.66526-1-ffmpeg@tmm1.net> References: <20180515121202.GY20131@michaelspb> <20180515192344.66526-1-ffmpeg@tmm1.net> From: Aman Gupta Date: Tue, 15 May 2018 16:12:10 -0700 X-Google-Sender-Auth: GIV-73IQXv-mQNtwbOtjeZrDLbU Message-ID: To: FFmpeg development discussions and patches X-Content-Filtered-By: Mailman/MimeDel 2.1.20 Subject: Re: [FFmpeg-devel] [PATCH v2] avformat/mpegts: add merge_pmt_versions option X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" On Tue, May 15, 2018 at 12:23 PM, Aman Gupta wrote: > From: Aman Gupta > > This new optional flag makes it easier to deal with mpegts > samples where the PMT is updated and elementary streams move > to different PIDs in the middle of playback. > > Previously, new AVStreams were created per PID, and it was up > to the user to figure out which streams had migrated to a new PID > (by iterating over the list of AVProgram and making guesses), and > switch seamlessly to the new AVStream during playback. > > Transcoding or remuxing these streams with ffmpeg on the CLI was > also quite painful, and the user would need to extract each set of > PIDs into a separate file and then stitch them back together. > > With this new option, the mpegts demuxer will automatically detect > PMT changes and feed data from the new PID to the original AVStream > that was created for the orignal PID. For mpegts samples with > stream_identifier_descriptor available, the unique ID is used to merge > PIDs together. If the stream id is not available, the demuxer attempts > to map PIDs based on their order and relation to the PCR pid. > > With this change, I am able to playback and transcode/remux these > two samples which previously caused issues: > > https://tmm1.s3.amazonaws.com/pmt-version-change.ts > https://kuroko.fushizen.eu/videos/pid_switch_sample.ts > > I also have another longer sample which contains multiple PMT > changes, some of which change the ES pids and others which do not: > > https://tmm1.s3.amazonaws.com/multiple-pmt-change.ts > > Demuxing this sample with the new option shows several new log > messages as the PMT changes are handled: > > [mpegts @ 0x7ffe18801200] detected PMT change (version=3/4, > pcr_pid=0xf98/0xf9b) > [mpegts @ 0x7ffe18801200] re-using existing video stream 0 (pid=0xf98) > for new pid=0xf9b > [mpegts @ 0x7ffe18801200] re-using existing audio stream 1 (pid=0xf99) > for new pid=0xf9c > [mpegts @ 0x7ffe18801200] re-using existing audio stream 2 (pid=0xf9a) > for new pid=0xf9d > [mpegts @ 0x7ffe18801200] detected PMT change (version=4/5, > pcr_pid=0xf9b/0xfa9) > [mpegts @ 0x7ffe18801200] re-using existing video stream 0 (pid=0xf98) > for new pid=0xfa9 > [mpegts @ 0x7ffe18801200] re-using existing audio stream 1 (pid=0xf99) > for new pid=0xfaa > [mpegts @ 0x7ffe18801200] re-using existing audio stream 2 (pid=0xf9a) > for new pid=0xfab > One issue I found with this patch: if you seek around aggressively sometimes you may encounter a new ES before a PMT, which causes it not to get merged in as expected. I'm adding the following change locally to fix the issue. pes->st = avformat_new_stream(ts->stream, NULL); This has the side-effect that any pids not advertised in a PMT will be ignored, but I think that's acceptable behavior if you're opting-in to this new feature. Aman > Signed-off-by: Aman Gupta > --- > doc/demuxers.texi | 4 ++ > libavformat/mpegts.c | 106 ++++++++++++++++++++++++++++++ > ++-- > tests/fate/mpegts.mak | 5 ++ > tests/ref/fate/mpegts-probe-pmt-merge | 32 ++++++++++ > 4 files changed, 143 insertions(+), 4 deletions(-) > create mode 100644 tests/ref/fate/mpegts-probe-pmt-merge > > diff --git a/doc/demuxers.texi b/doc/demuxers.texi > index e7c2abce57..2f7d7e0f3a 100644 > --- a/doc/demuxers.texi > +++ b/doc/demuxers.texi > @@ -552,6 +552,10 @@ Show the detected raw packet size, cannot be set by > the user. > Scan and combine all PMTs. The value is an integer with value from -1 > to 1 (-1 means automatic setting, 1 means enabled, 0 means > disabled). Default value is -1. > + > +@item merge_pmt_versions > +Re-use existing streams when a PMT's version is updated and elementary > +streams move to different PIDs. Default value is 0. > @end table > > @section mpjpeg > diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c > index 27b1c30a44..f16eae3235 100644 > --- a/libavformat/mpegts.c > +++ b/libavformat/mpegts.c > @@ -84,6 +84,8 @@ typedef struct MpegTSSectionFilter { > unsigned int end_of_section_reached : 1; > SectionCallback *section_cb; > void *opaque; > + int orig_pcr_pid; /* pmt specific */ > + int last_pcr_pid; > } MpegTSSectionFilter; > > struct MpegTSFilter { > @@ -147,6 +149,7 @@ struct MpegTSContext { > int scan_all_pmts; > > int resync_size; > + int merge_pmt_versions; > > /******************************************/ > /* private mpegts data */ > @@ -172,6 +175,8 @@ static const AVOption options[] = { > {.i64 = 0}, 0, 0, AV_OPT_FLAG_DECODING_PARAM | AV_OPT_FLAG_EXPORT | > AV_OPT_FLAG_READONLY }, > {"scan_all_pmts", "scan and combine all PMTs", > offsetof(MpegTSContext, scan_all_pmts), AV_OPT_TYPE_BOOL, > {.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM }, > + {"merge_pmt_versions", "re-use streams when PMT's version/pids > change", offsetof(MpegTSContext, merge_pmt_versions), AV_OPT_TYPE_BOOL, > + {.i64 = 0}, 0, 1, AV_OPT_FLAG_DECODING_PARAM }, > {"skip_changes", "skip changing / adding streams / programs", > offsetof(MpegTSContext, skip_changes), AV_OPT_TYPE_BOOL, > {.i64 = 0}, 0, 1, 0 }, > {"skip_clear", "skip clearing programs", offsetof(MpegTSContext, > skip_clear), AV_OPT_TYPE_BOOL, > @@ -1073,7 +1078,8 @@ static int mpegts_push_data(MpegTSFilter *filter, > if (ts->skip_changes) > goto skip; > > - pes->st = avformat_new_stream(ts->stream, NULL); > + if (!pes->st) > + pes->st = avformat_new_stream(ts->stream, > NULL); > if (!pes->st) > return AVERROR(ENOMEM); > pes->st->id = pes->pid; > @@ -1983,6 +1989,69 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, > AVStream *st, int stream_type > return 0; > } > > +static AVStream *find_matching_stream(MpegTSContext *ts, int pid, > + int stream_id, > + int pcr_pid, int orig_pcr_pid) > +{ > + AVFormatContext *s = ts->stream; > + int i, orig_pid = orig_pcr_pid + (pid - pcr_pid); > + AVStream *found = NULL; > + > + for (i = 0; i < s->nb_streams; i++) { > + AVStream *st = s->streams[i]; > + if (stream_id != -1) { > + if (st->stream_identifier == stream_id+1) { > + found = st; > + break; > + } > + } else if (pcr_pid != orig_pcr_pid && st->id == orig_pid) { > + found = st; > + break; > + } > + } > + > + if (found) { > + av_log(ts->stream, AV_LOG_DEBUG, "re-using existing %s stream %d > (pid=0x%x) for new pid=0x%x\n", > + av_get_media_type_string(found->codecpar->codec_type), i, > found->id, pid); > + } > + > + return found; > +} > + > +static int parse_stream_identifier_desc(const uint8_t *p, const uint8_t > *p_end) > +{ > + const uint8_t **pp = &p; > + const uint8_t *desc_list_end; > + const uint8_t *desc_end; > + int desc_list_len; > + int desc_len, desc_tag; > + > + desc_list_len = get16(pp, p_end); > + if (desc_list_len < 0) > + return -1; > + desc_list_len &= 0xfff; > + desc_list_end = p + desc_list_len; > + if (desc_list_end > p_end) > + return -1; > + > + while (1) { > + desc_tag = get8(pp, desc_list_end); > + if (desc_tag < 0) > + return -1; > + desc_len = get8(pp, desc_list_end); > + if (desc_len < 0) > + return -1; > + desc_end = *pp + desc_len; > + if (desc_end > desc_list_end) > + return -1; > + > + if (desc_tag == 0x52) { > + return get8(pp, desc_end); > + } > + *pp = desc_end; > + } > +} > + > static int is_pes_stream(int stream_type, uint32_t prog_reg_desc) > { > return !(stream_type == 0x13 || > @@ -2000,6 +2069,8 @@ static void pmt_cb(MpegTSFilter *filter, const > uint8_t *section, int section_len > int program_info_length, pcr_pid, pid, stream_type; > int desc_list_len; > uint32_t prog_reg_desc = 0; /* registration descriptor */ > + int last_ver = tssf->last_ver; > + int stream_id = -1; > > int mp4_descr_count = 0; > Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = { { 0 } }; > @@ -2032,6 +2103,14 @@ static void pmt_cb(MpegTSFilter *filter, const > uint8_t *section, int section_len > pcr_pid &= 0x1fff; > add_pid_to_pmt(ts, h->id, pcr_pid); > set_pcr_pid(ts->stream, h->id, pcr_pid); > + if (!tssf->orig_pcr_pid) > + tssf->orig_pcr_pid = pcr_pid; > + > + if (last_ver != -1 && h->version != last_ver) { > + av_log(ts->stream, AV_LOG_DEBUG, "detected PMT change > (version=%d/%d, pcr_pid=0x%x/0x%x)\n", > + last_ver, h->version, tssf->last_pcr_pid, pcr_pid); > + } > + tssf->last_pcr_pid = pcr_pid; > > av_log(ts->stream, AV_LOG_TRACE, "pcr_pid=0x%x\n", pcr_pid); > > @@ -2083,14 +2162,22 @@ static void pmt_cb(MpegTSFilter *filter, const > uint8_t *section, int section_len > if (pid < 0) > goto out; > pid &= 0x1fff; > + stream_id = parse_stream_identifier_desc(p, p_end); > if (pid == ts->current_pid) > goto out; > > /* now create stream */ > if (ts->pids[pid] && ts->pids[pid]->type == MPEGTS_PES) { > pes = ts->pids[pid]->u.pes_filter.opaque; > + if (ts->merge_pmt_versions && !pes->st) { > + st = find_matching_stream(ts, pid, stream_id, pcr_pid, > tssf->orig_pcr_pid); > + if (st) { > + pes->st = st; > + pes->stream_type = stream_type; > + } > + } > if (!pes->st) { > - pes->st = avformat_new_stream(pes->stream, NULL); > + pes->st = avformat_new_stream(pes->stream, NULL); > if (!pes->st) > goto out; > pes->st->id = pes->pid; > @@ -2100,7 +2187,14 @@ static void pmt_cb(MpegTSFilter *filter, const > uint8_t *section, int section_len > if (ts->pids[pid]) > mpegts_close_filter(ts, ts->pids[pid]); // wrongly added > sdt filter probably > pes = add_pes_stream(ts, pid, pcr_pid); > - if (pes) { > + if (ts->merge_pmt_versions && pes && !pes->st) { > + st = find_matching_stream(ts, pid, stream_id, pcr_pid, > tssf->orig_pcr_pid); > + if (st) { > + pes->st = st; > + pes->stream_type = stream_type; > + } > + } > + if (pes && !pes->st) { > st = avformat_new_stream(pes->stream, NULL); > if (!st) > goto out; > @@ -2110,7 +2204,11 @@ static void pmt_cb(MpegTSFilter *filter, const > uint8_t *section, int section_len > int idx = ff_find_stream_index(ts->stream, pid); > if (idx >= 0) { > st = ts->stream->streams[idx]; > - } else { > + } > + if (ts->merge_pmt_versions && !st) { > + st = find_matching_stream(ts, pid, stream_id, pcr_pid, > tssf->orig_pcr_pid); > + } > + if (!st) { > st = avformat_new_stream(ts->stream, NULL); > if (!st) > goto out; > diff --git a/tests/fate/mpegts.mak b/tests/fate/mpegts.mak > index 2b128492e0..bbcbfc47b2 100644 > --- a/tests/fate/mpegts.mak > +++ b/tests/fate/mpegts.mak > @@ -15,6 +15,11 @@ fate-mpegts-probe-program: SRC = > $(TARGET_SAMPLES)/mpegts/loewe.ts > fate-mpegts-probe-program: CMD = run $(PROBE_CODEC_NAME_COMMAND) > -select_streams p:769:v:0 -i "$(SRC)" > > > +FATE_MPEGTS_PROBE-$(call DEMDEC, MPEGTS) += fate-mpegts-probe-pmt-merge > +fate-mpegts-probe-pmt-merge: SRC = $(TARGET_SAMPLES)/mpegts/pmtchange.ts > +fate-mpegts-probe-pmt-merge: CMD = run $(PROBE_CODEC_NAME_COMMAND) > -merge_pmt_versions 1 -i "$(SRC)" > + > + > FATE_SAMPLES_FFPROBE += $(FATE_MPEGTS_PROBE-yes) > > fate-mpegts: $(FATE_MPEGTS_PROBE-yes) > diff --git a/tests/ref/fate/mpegts-probe-pmt-merge > b/tests/ref/fate/mpegts-probe-pmt-merge > new file mode 100644 > index 0000000000..6e424af5d2 > --- /dev/null > +++ b/tests/ref/fate/mpegts-probe-pmt-merge > @@ -0,0 +1,32 @@ > +[PROGRAM] > +[STREAM] > +codec_name=ac3 > +[/STREAM] > +[STREAM] > +codec_name=ac3 > +[/STREAM] > +[STREAM] > +codec_name=ac3 > +[/STREAM] > +[STREAM] > +codec_name=mpeg2video > +[/STREAM] > +[STREAM] > +codec_name=scte_35 > +[/STREAM] > +[/PROGRAM] > +[STREAM] > +codec_name=ac3 > +[/STREAM] > +[STREAM] > +codec_name=ac3 > +[/STREAM] > +[STREAM] > +codec_name=ac3 > +[/STREAM] > +[STREAM] > +codec_name=mpeg2video > +[/STREAM] > +[STREAM] > +codec_name=scte_35 > +[/STREAM] > -- > 2.14.2 > > diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index f16eae3235..3d8e9da66f 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -1077,6 +1077,8 @@ static int mpegts_push_data(MpegTSFilter *filter, if (!pes->st) { if (ts->skip_changes) goto skip; + if (ts->merge_pmt_versions) + goto skip; /* wait for PMT to merge new stream */ if (!pes->st)