From patchwork Thu May 17 22:49:26 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Aman Karmani X-Patchwork-Id: 9004 Delivered-To: ffmpegpatchwork@gmail.com Received: by 2002:a02:155:0:0:0:0:0 with SMTP id c82-v6csp4626351jad; Thu, 17 May 2018 15:50:23 -0700 (PDT) X-Google-Smtp-Source: AB8JxZrwBdhe2k2wiLbDyRv+Uzp6Kr7wVxx+FfQbeG6JMpVZM0YoGWrRHsA5ObgWjMo5OyXnMoqU X-Received: by 2002:a1c:8e95:: with SMTP id q143-v6mr2862069wmd.161.1526597423655; Thu, 17 May 2018 15:50:23 -0700 (PDT) ARC-Seal: i=1; a=rsa-sha256; t=1526597423; cv=none; d=google.com; s=arc-20160816; b=XFhwgRrMd9dVosubjBvRCMhza7yocQWGVogH0SGsvq5s9o0C14YPWN/pAXjRXPcMSK CK+Q7rXGApL7CLXW7CoW1G/eXRLe9MKCF+sYSC7bkjPN+r52tz7mC28PQBrnytnG6gZF U3Si1/Siv/7a4Jq+1rzLgvao1wyemqqWlyybcrh6iybAkw7ZpkNfOEy1RBHzA0QctzV7 dOJo1EmS7asvEsK2hhjl7WGgqq7B0BBZyFrzajNJ3D0m69wTlsH811L0NTU/KlLKi+8x dahp/28mSRXPR0gfHw2jH2NLwb/k2R/h1iORM/n3Qhx83oWneYrjaTVQTgJRWXmDOqJI HHqQ== ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=google.com; s=arc-20160816; h=sender:errors-to:content-transfer-encoding:mime-version:cc:reply-to :list-subscribe:list-help:list-post:list-archive:list-unsubscribe :list-id:precedence:subject:references:in-reply-to:message-id:date :to:from:dkim-signature:delivered-to:arc-authentication-results; bh=u3CGYzp+9nd08Wedt82woQ74hvfjdPKuR5s+fL/55mc=; b=tjCvLCEVwNnStaReCxG7qsqY7uWgJCxPGjIh9AfJPqfCxtLYN+8rPcv1a06//MERgP ydaPBmr86xgL1ISpOT9sWOGV/J12oyP9layI2EzLp1taaktT2wRyOh/a7L8Ocyssk0yv UQYj1uCQ3IV/fLDeTnjJcUGcEXvjXUU6Gy3C8iPMqgv3u5e9NfBQqdgpIipa4RWubX8y 4Dv6yL8vBNDeCPu8YrF36YRABbuFlOC/Ca3de6BU4Wrr9k6UEs6oejA5LB2ILXOqrTXq RCV9hjs0aHr6IGx7+a1gpO7pLH2A+/k+78oBOX+s8CkI1+VN4i4p2a4NVRLsPAJ9ydCp ogPw== ARC-Authentication-Results: i=1; mx.google.com; dkim=neutral (body hash did not verify) header.i=@tmm1-net.20150623.gappssmtp.com header.s=20150623 header.b=Gmd3vFJg; 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 3-v6si5382305wrd.90.2018.05.17.15.50.23; Thu, 17 May 2018 15:50:23 -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=@tmm1-net.20150623.gappssmtp.com header.s=20150623 header.b=Gmd3vFJg; 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 7107D68A52C; Fri, 18 May 2018 01:49:25 +0300 (EEST) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-pg0-f68.google.com (mail-pg0-f68.google.com [74.125.83.68]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 6E44068A4B6 for ; Fri, 18 May 2018 01:49:18 +0300 (EEST) Received: by mail-pg0-f68.google.com with SMTP id l2-v6so2434110pgc.7 for ; Thu, 17 May 2018 15:49:57 -0700 (PDT) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=tmm1-net.20150623.gappssmtp.com; s=20150623; h=sender:from:to:cc:subject:date:message-id:in-reply-to:references; bh=kiRUr7jZSubmJhBKiirgaHP/oJQSUOU4PMhYNNcA/AI=; b=Gmd3vFJgCzNOCKB1a5q0Q8myf8Z0MCmDRXdIsePI7n4vqUVix9Oo1duue4qeN3evOK RLtecslGQd+fRZi2UlVaVgzEaYOh+4qgQv3kRJlzs8m3yg4vO91Hn37aUNfICSP6abbX j5Wkgww92w0RnRlZ4wUv+5qDhpgJIvvtAreCTfzZv0oZIbVlY4+L0dg2G54hpgX0FOMd NCKtptlc7wZjNrttxeB/XOgcLS4clHATyhXp7wyAOhN6gdjVVp5UEOcNiTEBzqpqxJVK Dqd3f1TOVcrpk1UVaOGZp53l9Ahva3RxHIO/1TtLSLhveJa2NbUKGY51T6A1EU2il9sc VIvQ== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:sender:from:to:cc:subject:date:message-id :in-reply-to:references; bh=kiRUr7jZSubmJhBKiirgaHP/oJQSUOU4PMhYNNcA/AI=; b=emsqzvpxe/O+xp9OZt3sr7n4rYH66/tXjEIhDju4E2JKJyvbg0rmfjZAeo3W9V5DRU SiXl0B+kfe29lMRAqPiQVnv9AZ8wsMEun6nEx0p9x5PL5+x7TIZRMIUObG9Uf6HNmkig YL0MSHe2zcJcnXCX/fPRv88c2zjDVVPscYvGtXXwnQxYNjR+m7Y6nS20+IdPfRTicoa/ MWtnJX6VuaPLIg6EsabQUAtoc9hgEPHjOkE4zrCz1uzbuk4z6Aj3ASpf5Ctp4eyPhrvP hF/db94AmIe7cbNfIqpeqKs0zT3hISyGj5FsFIUB2KioQHDGN5Xa+4rPKmgCZP2mZQJK TEHg== X-Gm-Message-State: ALKqPwcYgTfoPd5KuSbErHJIDdkExi+OKCDQRMC+3ea0EjNI+pDMjvsb 5JXpp+MylQDIHuwjBX35wMwJwH4P X-Received: by 2002:a62:d8c7:: with SMTP id e190-v6mr6839005pfg.161.1526597395879; Thu, 17 May 2018 15:49:55 -0700 (PDT) Received: from localhost.localdomain (c-69-181-54-242.hsd1.ca.comcast.net. [69.181.54.242]) by smtp.gmail.com with ESMTPSA id v6-v6sm8881588pfm.120.2018.05.17.15.49.54 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Thu, 17 May 2018 15:49:55 -0700 (PDT) From: Aman Gupta To: ffmpeg-devel@ffmpeg.org Date: Thu, 17 May 2018 15:49:26 -0700 Message-Id: <20180517224926.4106-3-ffmpeg@tmm1.net> X-Mailer: git-send-email 2.14.2 In-Reply-To: <20180517224926.4106-1-ffmpeg@tmm1.net> References: <20180517224926.4106-1-ffmpeg@tmm1.net> Subject: [FFmpeg-devel] [PATCH v3 3/3] 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 Cc: michael@niedermayer.cc, cus@passwd.hu, Aman Gupta , jeebjp@gmail.com MIME-Version: 1.0 Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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 Signed-off-by: Aman Gupta --- doc/demuxers.texi | 4 ++ libavformat/mpegts.c | 99 +++++++++++++++++++++++++++++++++-- tests/fate/mpegts.mak | 5 ++ tests/ref/fate/mpegts-probe-pmt-merge | 32 +++++++++++ 4 files changed, 137 insertions(+), 3 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 e83238d733..287d7954a9 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -84,6 +84,7 @@ typedef struct MpegTSSectionFilter { unsigned int end_of_section_reached : 1; SectionCallback *section_cb; void *opaque; + int orig_pcr_pid; /* pmt specific */ } MpegTSSectionFilter; struct MpegTSFilter { @@ -147,6 +148,7 @@ struct MpegTSContext { int scan_all_pmts; int resync_size; + int merge_pmt_versions; /******************************************/ /* private mpegts data */ @@ -172,6 +174,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, @@ -1084,6 +1088,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 */ pes->st = avformat_new_stream(ts->stream, NULL); if (!pes->st) @@ -2000,6 +2006,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 || @@ -2017,6 +2086,7 @@ 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 stream_id = -1; int mp4_descr_count = 0; Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = { { 0 } }; @@ -2049,6 +2119,8 @@ 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); update_av_program_info(ts->stream, h->id, pcr_pid, h->version); + if (!tssf->orig_pcr_pid) + tssf->orig_pcr_pid = pcr_pid; av_log(ts->stream, AV_LOG_TRACE, "pcr_pid=0x%x\n", pcr_pid); @@ -2103,11 +2175,21 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len if (pid == ts->current_pid) goto out; + if (ts->merge_pmt_versions) + stream_id = parse_stream_identifier_desc(p, p_end); + /* 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; @@ -2117,7 +2199,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; @@ -2127,7 +2216,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]