@@ -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
@@ -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,
@@ -1083,6 +1087,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)
@@ -1999,6 +2005,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 ||
@@ -2016,6 +2085,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 } };
@@ -2048,6 +2118,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);
@@ -2102,11 +2174,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;
@@ -2119,7 +2201,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;
@@ -2132,7 +2221,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;
@@ -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)
new file mode 100644
@@ -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]