From 5a75d905cb24572d14c4e37ad68e3965bfdacd84 Mon Sep 17 00:00:00 2001 From: James Almer Date: Thu, 26 Mar 2026 14:44:49 -0300 Subject: [PATCH] avformat/mpegts: create stream groups after having parsed the entire PMT Some faulty files have an LCEVC descriptor with a single stream, resulting in a group being created but never fully populated with the current implementation. Signed-off-by: James Almer --- libavformat/mpegts.c | 107 ++++++++++++++++++++++++++++++++----------- 1 file changed, 80 insertions(+), 27 deletions(-) diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index 6f58e52e70..8eb3bae720 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -116,12 +116,22 @@ struct Stream { #define MAX_STREAMS_PER_PROGRAM 128 #define MAX_PIDS_PER_PROGRAM (MAX_STREAMS_PER_PROGRAM + 2) + +struct StreamGroup { + enum AVStreamGroupParamsType type; + int id; + unsigned int nb_streams; + AVStream *streams[MAX_STREAMS_PER_PROGRAM]; +}; + struct Program { unsigned int id; // program id/service id unsigned int nb_pids; unsigned int pids[MAX_PIDS_PER_PROGRAM]; unsigned int nb_streams; struct Stream streams[MAX_STREAMS_PER_PROGRAM]; + unsigned int nb_stream_groups; + struct StreamGroup stream_groups[MAX_STREAMS_PER_PROGRAM]; /** have we found pmt for this program */ int pmt_found; @@ -309,6 +319,8 @@ static void clear_program(struct Program *p) return; p->nb_pids = 0; p->nb_streams = 0; + p->nb_stream_groups = 0; + memset(p->stream_groups, 0, sizeof(p->stream_groups)); p->pmt_found = 0; } @@ -1917,82 +1929,87 @@ static int parse_mpeg2_extension_descriptor(AVFormatContext *fc, AVStream *st, i break; case LCEVC_VIDEO_DESCRIPTOR: { - AVStreamGroup *stg = NULL; + struct Program *p = get_program(ts, prg_id); + struct StreamGroup *stg; int lcevc_stream_tag = get8(pp, desc_end); - int ret, i; + int i; - if (!get_program(ts, prg_id)) + if (!p) return 0; if (st->codecpar->codec_id != AV_CODEC_ID_LCEVC) return AVERROR_INVALIDDATA; - for (i = 0; i < fc->nb_stream_groups; i++) { - stg = fc->stream_groups[i]; + for (i = 0; i < p->nb_stream_groups; i++) { + stg = &p->stream_groups[i]; if (stg->type != AV_STREAM_GROUP_PARAMS_LCEVC) continue; if (stg->id == lcevc_stream_tag) break; } - if (i == fc->nb_stream_groups) - stg = avformat_stream_group_create(fc, AV_STREAM_GROUP_PARAMS_LCEVC, NULL); - if (!stg) - return AVERROR(ENOMEM); + if (i == p->nb_stream_groups) { + if (p->nb_stream_groups == MAX_STREAMS_PER_PROGRAM) + return AVERROR(EINVAL); + p->nb_stream_groups++; + } + stg = &p->stream_groups[i]; stg->id = lcevc_stream_tag; + stg->type = AV_STREAM_GROUP_PARAMS_LCEVC; for (i = 0; i < stg->nb_streams; i++) { if (stg->streams[i]->codecpar->codec_id == AV_CODEC_ID_LCEVC) break; } if (i == stg->nb_streams) { - ret = avformat_stream_group_add_stream(stg, st); - av_assert0(ret != AVERROR(EEXIST)); - if (ret < 0) - return ret; + if (stg->nb_streams == MAX_STREAMS_PER_PROGRAM) + return AVERROR(EINVAL); + stg->streams[stg->nb_streams++] = st; } else stg->streams[i] = st; av_assert0(i < stg->nb_streams); - stg->params.lcevc->lcevc_index = i; } break; case LCEVC_LINKAGE_DESCRIPTOR: { + struct Program *p = get_program(ts, prg_id); int num_lcevc_stream_tags = get8(pp, desc_end); - if (!get_program(ts, prg_id)) + if (!p) return 0; if (st->codecpar->codec_id == AV_CODEC_ID_LCEVC) return AVERROR_INVALIDDATA; for (int i = 0; i < num_lcevc_stream_tags; i++) { - AVStreamGroup *stg = NULL; + struct StreamGroup *stg = NULL; int lcevc_stream_tag = get8(pp, desc_end);; - int ret, j; + int j; - for (j = 0; j < fc->nb_stream_groups; j++) { - stg = fc->stream_groups[j]; + for (j = 0; j < p->nb_stream_groups; j++) { + stg = &p->stream_groups[j]; if (stg->type != AV_STREAM_GROUP_PARAMS_LCEVC) continue; if (stg->id == lcevc_stream_tag) break; } - if (j == fc->nb_stream_groups) - stg = avformat_stream_group_create(fc, AV_STREAM_GROUP_PARAMS_LCEVC, NULL); - if (!stg) - return AVERROR(ENOMEM); + if (j == p->nb_stream_groups) { + if (p->nb_stream_groups == MAX_STREAMS_PER_PROGRAM) + return AVERROR(EINVAL); + p->nb_stream_groups++; + } + stg = &p->stream_groups[j]; stg->id = lcevc_stream_tag; + stg->type = AV_STREAM_GROUP_PARAMS_LCEVC; for (j = 0; j < stg->nb_streams; j++) { if (stg->streams[j]->index == st->index) break; } if (j == stg->nb_streams) { - ret = avformat_stream_group_add_stream(stg, st); - av_assert0(ret != AVERROR(EEXIST)); - if (ret < 0) - return ret; + if (stg->nb_streams == MAX_STREAMS_PER_PROGRAM) + return AVERROR(EINVAL); + stg->streams[stg->nb_streams++] = st; } } } @@ -2529,6 +2546,39 @@ static int is_pes_stream(int stream_type, uint32_t prog_reg_desc) } } +static void create_stream_groups(MpegTSContext *ts, const struct Program *prg) +{ + for (int i = 0; i < prg->nb_stream_groups; i++) { + const struct StreamGroup *grp = &prg->stream_groups[i]; + AVStreamGroup *stg; + int j; + if (grp->nb_streams < 2) + continue; + for (j = 0; j < ts->stream->nb_stream_groups; j++) { + stg = ts->stream->stream_groups[j]; + if (stg->id == grp->id) + break; + } + if (j == ts->stream->nb_stream_groups) + stg = avformat_stream_group_create(ts->stream, grp->type, NULL); + else + continue; + if (!stg) + continue; + av_assert0(grp->type == AV_STREAM_GROUP_PARAMS_LCEVC); + stg->id = grp->id; + for (int j = 0; j < grp->nb_streams; j++) { + int ret = avformat_stream_group_add_stream(stg, grp->streams[j]); + if (ret < 0) { + ff_remove_stream_group(ts->stream, stg); + continue; + } + if (grp->streams[j]->codecpar->codec_id == AV_CODEC_ID_LCEVC) + stg->params.lcevc->lcevc_index = stg->nb_streams - 1; + } + } +} + static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len) { MpegTSContext *ts = filter->u.section_filter.opaque; @@ -2746,6 +2796,9 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len mpegts_open_pcr_filter(ts, pcr_pid); out: + if (prg) + create_stream_groups(ts, prg); + for (i = 0; i < mp4_descr_count; i++) av_free(mp4_descr[i].dec_config_descr); }