summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--doc/demuxers.texi4
-rw-r--r--libavformat/mpegts.c99
-rw-r--r--tests/fate/mpegts.mak5
-rw-r--r--tests/ref/fate/mpegts-probe-pmt-merge32
4 files changed, 137 insertions, 3 deletions
diff --git a/doc/demuxers.texi b/doc/demuxers.texi
index 1d2ee5bf37..072918be28 100644
--- a/doc/demuxers.texi
+++ b/doc/demuxers.texi
@@ -555,6 +555,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 b21e55d3a7..0c9847f85b 100644
--- a/libavformat/mpegts.c
+++ b/libavformat/mpegts.c
@@ -148,6 +148,7 @@ struct MpegTSContext {
int scan_all_pmts;
int resync_size;
+ int merge_pmt_versions;
/******************************************/
/* private mpegts data */
@@ -175,6 +176,8 @@ static const AVOption options[] = {
{.i64 = -1}, -1, 1, AV_OPT_FLAG_DECODING_PARAM },
{"skip_unknown_pmt", "skip PMTs for programs not advertised in the PAT", offsetof(MpegTSContext, skip_unknown_pmt), AV_OPT_TYPE_BOOL,
{.i64 = 0}, 0, 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,
@@ -1086,6 +1089,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)
@@ -2002,6 +2007,72 @@ 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 pmt_stream_idx)
+{
+ AVFormatContext *s = ts->stream;
+ int i;
+ AVStream *found = NULL;
+
+ for (i = 0; i < s->nb_streams; i++) {
+ AVStream *st = s->streams[i];
+ if (stream_id != -1) { /* match based on "stream identifier descriptor" if present */
+ if (st->stream_identifier == stream_id+1) {
+ found = st;
+ break;
+ }
+ } else if (st->pmt_stream_idx == pmt_stream_idx) { /* match based on position within the PMT */
+ found = st;
+ break;
+ }
+ }
+
+ if (found) {
+ av_log(ts->stream, AV_LOG_VERBOSE,
+ "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;
+ }
+
+ return -1;
+}
+
static int is_pes_stream(int stream_type, uint32_t prog_reg_desc)
{
return !(stream_type == 0x13 ||
@@ -2019,6 +2090,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 } };
@@ -2107,11 +2179,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, i);
+ 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;
@@ -2124,7 +2206,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, i);
+ 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;
@@ -2137,7 +2226,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, i);
+ }
+ 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]