X-Git-Url: https://git.sesse.net/?a=blobdiff_plain;f=libavformat%2Fmpegts.c;h=edf6b5701df482111f008249373222dd000de176;hb=7b2a9aaa0b784244f6112dbb4b756ca6c4b374d1;hp=18df1afd9dfcde1c299abab2e1379c066acb3b1a;hpb=e24d768a76b14a1cbea0ec622c89573f7d06fdee;p=ffmpeg diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index 18df1afd9df..edf6b5701df 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, @@ -244,6 +247,7 @@ typedef struct PESContext { uint8_t header[MAX_PES_HEADER_SIZE]; AVBufferRef *buffer; SLConfigDescr sl; + int merged_st; } PESContext; extern AVInputFormat ff_mpegts_demuxer; @@ -333,12 +337,23 @@ static void set_pmt_found(MpegTSContext *ts, unsigned int programid) p->pmt_found = 1; } -static void set_pcr_pid(AVFormatContext *s, unsigned int programid, unsigned int pid) +static void update_av_program_info(AVFormatContext *s, unsigned int programid, + unsigned int pid, int version) { int i; for (i = 0; i < s->nb_programs; i++) { - if (s->programs[i]->id == programid) { - s->programs[i]->pcr_pid = pid; + AVProgram *program = s->programs[i]; + if (program->id == programid) { + int old_pcr_pid = program->pcr_pid, + old_version = program->pmt_version; + program->pcr_pid = pid; + program->pmt_version = version; + + if (old_version != -1 && old_version != version) { + av_log(s, AV_LOG_VERBOSE, + "detected PMT change (program=%d, version=%d/%d, pcr_pid=0x%x/0x%x)\n", + programid, old_version, version, old_pcr_pid, pid); + } break; } } @@ -450,6 +465,7 @@ static void write_section_data(MpegTSContext *ts, MpegTSFilter *tss1, offset += tss->section_h_size; tss->section_h_size = -1; } else { + tss->section_h_size = -1; tss->end_of_section_reached = 0; break; } @@ -536,8 +552,8 @@ static void mpegts_close_filter(MpegTSContext *ts, MpegTSFilter *filter) PESContext *pes = filter->u.pes_filter.opaque; av_buffer_unref(&pes->buffer); /* referenced private data will be freed later in - * avformat_close_input */ - if (!((PESContext *)filter->u.pes_filter.opaque)->st) { + * avformat_close_input (pes->st->priv_data == pes) */ + if (!pes->st || pes->merged_st) { av_freep(&filter->u.pes_filter.opaque); } } @@ -1075,6 +1091,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) @@ -1965,7 +1983,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type int service_type = ((component_type & service_type_mask) >> 3); if (service_type == 0x02 /* 0b010 */) { st->disposition |= AV_DISPOSITION_DESCRIPTIONS; - av_log(ts->stream, AV_LOG_DEBUG, "New track disposition for id %u: %u\n", st->id, st->disposition); + av_log(ts ? ts->stream : fc, AV_LOG_DEBUG, "New track disposition for id %u: %u\n", st->id, st->disposition); } } } @@ -1979,7 +1997,7 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type int service_type = ((component_type & service_type_mask) >> 3); if (service_type == 0x02 /* 0b010 */) { st->disposition |= AV_DISPOSITION_DESCRIPTIONS; - av_log(ts->stream, AV_LOG_DEBUG, "New track disposition for id %u: %u\n", st->id, st->disposition); + av_log(ts ? ts->stream : fc, AV_LOG_DEBUG, "New track disposition for id %u: %u\n", st->id, st->disposition); } } } @@ -1991,6 +2009,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_identifier, 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_identifier != -1) { /* match based on "stream identifier descriptor" if present */ + if (st->stream_identifier == stream_identifier+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 || @@ -2008,6 +2092,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_identifier = -1; int mp4_descr_count = 0; Mp4Descr mp4_descr[MAX_MP4_DESCR_COUNT] = { { 0 } }; @@ -2041,7 +2126,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len return; pcr_pid &= 0x1fff; add_pid_to_pmt(ts, h->id, pcr_pid); - set_pcr_pid(ts->stream, h->id, pcr_pid); + update_av_program_info(ts->stream, h->id, pcr_pid, h->version); av_log(ts->stream, AV_LOG_TRACE, "pcr_pid=0x%x\n", pcr_pid); @@ -2083,7 +2168,7 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len set_pmt_found(ts, h->id); - for (;;) { + for (i = 0; ; i++) { st = 0; pes = NULL; stream_type = get8(&p, p_end); @@ -2096,35 +2181,67 @@ 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_identifier = 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_identifier, i); + if (st) { + pes->st = st; + pes->stream_type = stream_type; + pes->merged_st = 1; + } + } 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; + pes->st->program_num = h->id; + pes->st->pmt_version = h->version; + pes->st->pmt_stream_idx = i; } st = pes->st; } else if (is_pes_stream(stream_type, prog_reg_desc)) { 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_identifier, i); + if (st) { + pes->st = st; + pes->stream_type = stream_type; + pes->merged_st = 1; + } + } + if (pes && !pes->st) { st = avformat_new_stream(pes->stream, NULL); if (!st) goto out; st->id = pes->pid; + st->program_num = h->id; + st->pmt_version = h->version; + st->pmt_stream_idx = i; } } else { 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_identifier, i); + } + if (!st) { st = avformat_new_stream(ts->stream, NULL); if (!st) goto out; st->id = pid; + st->program_num = h->id; + st->pmt_version = h->version; + st->pmt_stream_idx = i; st->codecpar->codec_type = AVMEDIA_TYPE_DATA; if (stream_type == 0x86 && prog_reg_desc == AV_RL32("CUEI")) { mpegts_find_stream_type(st, stream_type, SCTE_types);