This is an automated email from the git hooks/post-receive script. Git pushed a commit to branch master in repository ffmpeg.
commit 29bc8ec8d15493abf3bcbdea68b3046d150334e5 Author: Kacper Michajłow <[email protected]> AuthorDate: Sun May 17 15:44:42 2026 +0200 Commit: James Almer <[email protected]> CommitDate: Sun May 31 03:43:29 2026 +0000 avformat/mpegts: create Dolby Vision stream group See: https://professionalsupport.dolby.com/s/article/How-to-signal-Dolby-Vision-in-MPEG-2-TS Signed-off-by: Kacper Michajłow <[email protected]> --- libavformat/mpegts.c | 127 ++++++++++++++++++++++++++++++++++++++++++++++++--- libavformat/mpegts.h | 1 + 2 files changed, 122 insertions(+), 6 deletions(-) diff --git a/libavformat/mpegts.c b/libavformat/mpegts.c index 210635c9c9..aa1484fb74 100644 --- a/libavformat/mpegts.c +++ b/libavformat/mpegts.c @@ -120,6 +120,7 @@ struct Stream { struct StreamGroup { enum AVStreamGroupParamsType type; int id; + int dep_pid; /* PID of the linked dependency stream */ unsigned int nb_streams; AVStream *streams[MAX_STREAMS_PER_PROGRAM]; }; @@ -2446,6 +2447,36 @@ int ff_parse_mpeg2_descriptor(AVFormatContext *fc, AVStream *st, int stream_type dependency_pid, dovi->dv_bl_signal_compatibility_id, dovi->dv_md_compression); + + /* A Profile 7 dual-track EL stream points at its base layer via + * the descriptor's dependency_pid. Record a pending group entry, + * it will be resolved once all streams are available. */ + if (dovi->dv_profile == 7 && dovi->el_present_flag && + !dovi->bl_present_flag && dependency_pid >= 0) { + struct Program *p = get_program(ts, prg_id); + struct StreamGroup *stg; + int gi; + + if (!p) + break; + + for (gi = 0; gi < p->nb_stream_groups; gi++) { + stg = &p->stream_groups[gi]; + if (stg->type == AV_STREAM_GROUP_PARAMS_DOLBY_VISION && + stg->nb_streams && stg->streams[0] == st) + break; + } + if (gi == p->nb_stream_groups) { + if (p->nb_stream_groups == MAX_STREAMS_PER_PROGRAM) + return AVERROR(EINVAL); + p->nb_stream_groups++; + stg = &p->stream_groups[gi]; + stg->type = AV_STREAM_GROUP_PARAMS_DOLBY_VISION; + stg->id = st->id; + stg->streams[stg->nb_streams++] = st; + } + stg->dep_pid = dependency_pid; + } } break; case EXTENSION_DESCRIPTOR: /* descriptor extension */ @@ -2541,12 +2572,81 @@ static int is_pes_stream(int stream_type, uint32_t prog_reg_desc) } } -static void create_stream_groups(MpegTSContext *ts, const struct Program *prg) +/* UHD Blu-ray titles don't follow the Dolby Vision MPEG-TS spec [1] when + * signaling a dual-PID Profile 7 stream. The enhancement-layer PID lacks the + * DOVI_video_stream_descriptor, is advertised with stream_type 0x24 (HEVC) + * instead of the spec-mandated 0x06, and uses the HDMV registration descriptor + * instead of "DOVI". EL always has M2TS_VIDEO_EL_PID (0x1015) PID. + * [1] <https://professionalsupport.dolby.com/s/article/How-to-signal-Dolby-Vision-in-MPEG-2-TS> + */ +static void detect_bdmv_dovi_group(MpegTSContext *ts, struct Program *prg, + uint32_t prog_reg_desc) +{ + AVStream *bl_st = NULL, *el_st = NULL; + struct StreamGroup *grp; + + if (prog_reg_desc != AV_RL32("HDMV")) + return; + + if (prg->nb_stream_groups >= MAX_STREAMS_PER_PROGRAM) + return; + + for (int i = 0; i < prg->nb_stream_groups; i++) { + if (prg->stream_groups[i].type == AV_STREAM_GROUP_PARAMS_DOLBY_VISION) + return; + } + + for (int j = 0; j < prg->nb_streams; j++) { + int idx = prg->streams[j].idx; + AVStream *st; + if (idx < 0 || idx >= ts->stream->nb_streams) + continue; + st = ts->stream->streams[idx]; + if (st->codecpar->codec_id != AV_CODEC_ID_HEVC) + continue; + if (st->id == M2TS_VIDEO_PID) + bl_st = st; + else if (st->id == M2TS_VIDEO_EL_PID) + el_st = st; + } + + if (!bl_st || !el_st) + return; + + grp = &prg->stream_groups[prg->nb_stream_groups++]; + grp->type = AV_STREAM_GROUP_PARAMS_DOLBY_VISION; + grp->id = el_st->id; + grp->streams[0] = bl_st; + grp->streams[1] = el_st; + grp->nb_streams = 2; +} + +static void create_stream_groups(MpegTSContext *ts, struct Program *prg) { for (int i = 0; i < prg->nb_stream_groups; i++) { - const struct StreamGroup *grp = &prg->stream_groups[i]; + struct StreamGroup *grp = &prg->stream_groups[i]; AVStreamGroup *stg; int j; + + /* The DOVI descriptor on a Profile 7 EL points at its base layer + * via dependency_pid. Resolve the BL now that all streams of the + * program have been added. */ + if (grp->type == AV_STREAM_GROUP_PARAMS_DOLBY_VISION && grp->nb_streams == 1) { + for (j = 0; j < prg->nb_streams; j++) { + int idx = prg->streams[j].idx; + AVStream *cand; + if (idx < 0 || idx >= ts->stream->nb_streams) + continue; + cand = ts->stream->streams[idx]; + if (cand == grp->streams[0] || cand->id != grp->dep_pid) + continue; + grp->streams[1] = grp->streams[0]; + grp->streams[0] = cand; + grp->nb_streams = 2; + break; + } + } + if (grp->nb_streams < 2) continue; for (j = 0; j < ts->stream->nb_stream_groups; j++) { @@ -2560,7 +2660,6 @@ static void create_stream_groups(MpegTSContext *ts, const struct Program *prg) 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]); @@ -2568,8 +2667,22 @@ static void create_stream_groups(MpegTSContext *ts, const struct Program *prg) 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; + switch (grp->type) { + case AV_STREAM_GROUP_PARAMS_LCEVC: + if (grp->streams[j]->codecpar->codec_id == AV_CODEC_ID_LCEVC) + stg->params.lcevc->lcevc_index = stg->nb_streams - 1; + break; + case AV_STREAM_GROUP_PARAMS_DOLBY_VISION: + if (j == 0) { + stg->params.layered_video->width = grp->streams[j]->codecpar->width; + stg->params.layered_video->height = grp->streams[j]->codecpar->height; + } + if (j == grp->nb_streams - 1) + stg->params.layered_video->el_index = stg->nb_streams - 1; + break; + default: + av_unreachable("Invalid group type!"); + } } } } @@ -2791,8 +2904,10 @@ static void pmt_cb(MpegTSFilter *filter, const uint8_t *section, int section_len mpegts_open_pcr_filter(ts, pcr_pid); out: - if (prg) + if (prg) { + detect_bdmv_dovi_group(ts, prg, prog_reg_desc); create_stream_groups(ts, prg); + } for (i = 0; i < mp4_descr_count; i++) av_free(mp4_descr[i].dec_config_descr); diff --git a/libavformat/mpegts.h b/libavformat/mpegts.h index 13f958429b..58ef24848b 100644 --- a/libavformat/mpegts.h +++ b/libavformat/mpegts.h @@ -73,6 +73,7 @@ #define M2TS_PMT_PID 0x0100 #define M2TS_PCR_PID 0x1001 #define M2TS_VIDEO_PID 0x1011 +#define M2TS_VIDEO_EL_PID 0x1015 #define M2TS_AUDIO_START_PID 0x1100 #define M2TS_PGSSUB_START_PID 0x1200 #define M2TS_TEXTSUB_PID 0x1800 _______________________________________________ ffmpeg-cvslog mailing list -- [email protected] To unsubscribe send an email to [email protected]
