int skip_changes;
int skip_clear;
+ int skip_unknown_pmt;
int scan_all_pmts;
int resync_size;
+ int merge_pmt_versions;
/******************************************/
/* private mpegts data */
{.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 },
+ {"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,
uint8_t header[MAX_PES_HEADER_SIZE];
AVBufferRef *buffer;
SLConfigDescr sl;
+ int merged_st;
} PESContext;
extern AVInputFormat ff_mpegts_demuxer;
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;
}
}
offset += tss->section_h_size;
tss->section_h_size = -1;
} else {
+ tss->section_h_size = -1;
tss->end_of_section_reached = 0;
break;
}
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);
}
}
static int new_pes_packet(PESContext *pes, AVPacket *pkt)
{
- char *sd;
+ uint8_t *sd;
av_init_packet(pkt);
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)
|| pes->st->codecpar->codec_id == AV_CODEC_ID_DVB_SUBTITLE)
) {
AVProgram *p = NULL;
+ int pcr_found = 0;
while ((p = av_find_program_from_stream(pes->stream, p, pes->st->index))) {
if (p->pcr_pid != -1 && p->discard != AVDISCARD_ALL) {
MpegTSFilter *f = pes->ts->pids[p->pcr_pid];
// and the pcr error to this packet should be no more than 100 ms.
// TODO: we should interpolate the PCR, not just use the last one
int64_t pcr = f->last_pcr / 300;
+ pcr_found = 1;
pes->st->pts_wrap_reference = st->pts_wrap_reference;
pes->st->pts_wrap_behavior = st->pts_wrap_behavior;
if (pes->dts == AV_NOPTS_VALUE || pes->dts < pcr) {
}
}
}
+
+ if (!pcr_found) {
+ av_log(pes->stream, AV_LOG_VERBOSE,
+ "Forcing DTS/PTS to be unset for a "
+ "non-trustworthy PES packet for PID %d as "
+ "PCR hasn't been received yet.\n",
+ pes->pid);
+ pes->dts = pes->pts = AV_NOPTS_VALUE;
+ }
}
}
break;
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);
}
}
}
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);
}
}
}
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 ||
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 } };
if (!ts->scan_all_pmts && ts->skip_changes)
return;
+ if (ts->skip_unknown_pmt && !get_program(ts, h->id))
+ return;
if (!ts->skip_clear)
clear_program(ts, h->id);
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);
set_pmt_found(ts, h->id);
- for (;;) {
+ for (i = 0; ; i++) {
st = 0;
pes = NULL;
stream_type = get8(&p, p_end);
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);