mirror of
https://github.com/FFmpeg/FFmpeg.git
synced 2024-12-23 12:43:46 +02:00
avformat/mpegts: add merge_pmt_versions option
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 position within the PMT. 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 in which the PMT changes repeatedly and ES streams move to different pids three times during playback: 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] detected PMT change (program=1, version=3/6, pcr_pid=0xf98/0xfb7) [mpegts] re-using existing video stream 0 (pid=0xf98) for new pid=0xfb7 [mpegts] re-using existing audio stream 1 (pid=0xf99) for new pid=0xfb8 [mpegts] re-using existing audio stream 2 (pid=0xf9a) for new pid=0xfb9 [mpegts] detected PMT change (program=1, version=6/3, pcr_pid=0xfb7/0xf98) [mpegts] detected PMT change (program=1, version=3/4, pcr_pid=0xf98/0xf9b) [mpegts] re-using existing video stream 0 (pid=0xf98) for new pid=0xf9b [mpegts] re-using existing audio stream 1 (pid=0xf99) for new pid=0xf9c [mpegts] re-using existing audio stream 2 (pid=0xf9a) for new pid=0xf9d [mpegts] detected PMT change (program=1, version=4/5, pcr_pid=0xf9b/0xfa9) [mpegts] re-using existing video stream 0 (pid=0xf98) for new pid=0xfa9 [mpegts] re-using existing audio stream 1 (pid=0xf99) for new pid=0xfaa [mpegts] re-using existing audio stream 2 (pid=0xf9a) for new pid=0xfab [mpegts] detected PMT change (program=1, version=5/6, pcr_pid=0xfa9/0xfb7) Signed-off-by: Aman Gupta <aman@tmm1.net>
This commit is contained in:
parent
24579bf537
commit
16b4f97b72
@ -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
|
||||
|
@ -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;
|
||||
|
@ -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)
|
||||
|
32
tests/ref/fate/mpegts-probe-pmt-merge
Normal file
32
tests/ref/fate/mpegts-probe-pmt-merge
Normal file
@ -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]
|
Loading…
Reference in New Issue
Block a user