PR #23123 opened by Kacper Michajłow (kasper93) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23123 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23123.patch
From f3220848172f0c20896bbbe81f88d12e8006046b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 16 May 2026 19:46:53 +0200 Subject: [PATCH 1/2] avformat: add an Dolby Vision stream group MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Signed-off-by: Kacper Michajłow <[email protected]> --- doc/APIchanges | 4 ++++ libavformat/avformat.c | 5 +++++ libavformat/avformat.h | 21 +++++++++++++++++++++ libavformat/matroskadec.c | 4 ++++ libavformat/options.c | 25 +++++++++++++++++++++++++ libavformat/version.h | 2 +- 6 files changed, 60 insertions(+), 1 deletion(-) diff --git a/doc/APIchanges b/doc/APIchanges index 28e7b7b31c..8ed27c47bd 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,10 @@ The last version increases of all libraries were on 2025-03-28 API changes, most recent first: +2026-05-16 - xxxxxxxxxx - lavf 62.17.100 - avformat.h + Add AV_STREAM_GROUP_PARAMS_DOLBY_VISION + Add AVStreamGroupDolbyVision + 2026-05-16 - xxxxxxxxxxx - lavf 62.16.100 - avformat.h Add AVFMT_FIXED_FRAMESIZE. diff --git a/libavformat/avformat.c b/libavformat/avformat.c index 3bc79a3592..02c5a52182 100644 --- a/libavformat/avformat.c +++ b/libavformat/avformat.c @@ -106,6 +106,10 @@ void ff_free_stream_group(AVStreamGroup **pstg) av_opt_free(stg->params.lcevc); av_freep(&stg->params.lcevc); break; + case AV_STREAM_GROUP_PARAMS_DOLBY_VISION: + av_opt_free(stg->params.dovi); + av_freep(&stg->params.dovi); + break; default: break; } @@ -264,6 +268,7 @@ const char *avformat_stream_group_name(enum AVStreamGroupParamsType type) case AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION: return "IAMF Mix Presentation"; case AV_STREAM_GROUP_PARAMS_TILE_GRID: return "Tile Grid"; case AV_STREAM_GROUP_PARAMS_LCEVC: return "LCEVC (Split video and enhancement)"; + case AV_STREAM_GROUP_PARAMS_DOLBY_VISION: return "Dolby Vision (Split base and enhancement layer)"; } return NULL; } diff --git a/libavformat/avformat.h b/libavformat/avformat.h index d6740fcf5d..95e1043b51 100644 --- a/libavformat/avformat.h +++ b/libavformat/avformat.h @@ -1087,12 +1087,32 @@ typedef struct AVStreamGroupLCEVC { int height; } AVStreamGroupLCEVC; +/** + * AVStreamGroupDolbyVision is meant to define the relation between a Dolby + * Vision base layer video stream and a separate enhancement layer video + * stream (Profile 7 dual-layer encoding). + * + * The group is expected to contain exactly two streams: the base layer and + * the enhancement layer, both video streams. The base layer carries the + * AVDOVIDecoderConfigurationRecord side data. The enhancement layer is + * identified by @ref el_index. + */ +typedef struct AVStreamGroupDolbyVision { + const AVClass *av_class; + + /** + * Index of the Dolby Vision enhancement layer stream in AVStreamGroup. + */ + unsigned int el_index; +} AVStreamGroupDolbyVision; + enum AVStreamGroupParamsType { AV_STREAM_GROUP_PARAMS_NONE, AV_STREAM_GROUP_PARAMS_IAMF_AUDIO_ELEMENT, AV_STREAM_GROUP_PARAMS_IAMF_MIX_PRESENTATION, AV_STREAM_GROUP_PARAMS_TILE_GRID, AV_STREAM_GROUP_PARAMS_LCEVC, + AV_STREAM_GROUP_PARAMS_DOLBY_VISION, }; struct AVIAMFAudioElement; @@ -1135,6 +1155,7 @@ typedef struct AVStreamGroup { struct AVIAMFMixPresentation *iamf_mix_presentation; struct AVStreamGroupTileGrid *tile_grid; struct AVStreamGroupLCEVC *lcevc; + struct AVStreamGroupDolbyVision *dovi; } params; /** diff --git a/libavformat/matroskadec.c b/libavformat/matroskadec.c index bc584abc75..5eccaea3ed 100644 --- a/libavformat/matroskadec.c +++ b/libavformat/matroskadec.c @@ -3465,6 +3465,10 @@ static int matroska_read_header(AVFormatContext *s) matroska_convert_tags(s); + res = mkv_parse_dovi_streams(s); + if (res < 0) + return res; + return 0; } diff --git a/libavformat/options.c b/libavformat/options.c index 9365a16e1d..15ecfa9ac6 100644 --- a/libavformat/options.c +++ b/libavformat/options.c @@ -366,6 +366,19 @@ static const AVClass lcevc_class = { .option = lcevc_options, }; +#define OFFSET(x) offsetof(AVStreamGroupDolbyVision, x) +static const AVOption dovi_options[] = { + { "el_index", "Index of the Dolby Vision enhancement layer stream within the group", OFFSET(el_index), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, FLAGS }, + { NULL }, +}; +#undef OFFSET + +static const AVClass dovi_class = { + .class_name = "AVStreamGroupDolbyVision", + .version = LIBAVUTIL_VERSION_INT, + .option = dovi_options, +}; + static void *stream_group_child_next(void *obj, void *prev) { AVStreamGroup *stg = obj; @@ -379,6 +392,8 @@ static void *stream_group_child_next(void *obj, void *prev) return stg->params.tile_grid; case AV_STREAM_GROUP_PARAMS_LCEVC: return stg->params.lcevc; + case AV_STREAM_GROUP_PARAMS_DOLBY_VISION: + return stg->params.dovi; default: break; } @@ -409,6 +424,9 @@ static const AVClass *stream_group_child_iterate(void **opaque) case AV_STREAM_GROUP_PARAMS_LCEVC: ret = &lcevc_class; break; + case AV_STREAM_GROUP_PARAMS_DOLBY_VISION: + ret = &dovi_class; + break; default: break; } @@ -485,6 +503,13 @@ AVStreamGroup *avformat_stream_group_create(AVFormatContext *s, stg->params.lcevc->av_class = &lcevc_class; av_opt_set_defaults(stg->params.lcevc); break; + case AV_STREAM_GROUP_PARAMS_DOLBY_VISION: + stg->params.dovi = av_mallocz(sizeof(*stg->params.dovi)); + if (!stg->params.dovi) + goto fail; + stg->params.dovi->av_class = &dovi_class; + av_opt_set_defaults(stg->params.dovi); + break; default: goto fail; } diff --git a/libavformat/version.h b/libavformat/version.h index 9e1f484db4..2a28a3bf40 100644 --- a/libavformat/version.h +++ b/libavformat/version.h @@ -31,7 +31,7 @@ #include "version_major.h" -#define LIBAVFORMAT_VERSION_MINOR 16 +#define LIBAVFORMAT_VERSION_MINOR 17 #define LIBAVFORMAT_VERSION_MICRO 100 #define LIBAVFORMAT_VERSION_INT AV_VERSION_INT(LIBAVFORMAT_VERSION_MAJOR, \ -- 2.52.0 From c575ab1a2b34bb3b716716403e236b7b71e8a6ad Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Kacper=20Michaj=C5=82ow?= <[email protected]> Date: Sat, 16 May 2026 19:52:06 +0200 Subject: [PATCH 2/2] avformat/mov: create Dolby Vision stream group --- libavformat/isom.h | 1 + libavformat/mov.c | 81 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 82 insertions(+) diff --git a/libavformat/isom.h b/libavformat/isom.h index d7e138585a..543e1cd470 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -435,6 +435,7 @@ void ff_mp4_parse_es_descr(AVIOContext *pb, int *es_id); #define MOV_SAMPLE_DEPENDENCY_NO 0x2 #define MOV_TREF_FLAG_ENHANCEMENT 0x1 +#define MOV_TREF_FLAG_DOVI_EL 0x2 #define TAG_IS_AVCI(tag) \ ((tag) == MKTAG('a', 'i', '5', 'p') || \ diff --git a/libavformat/mov.c b/libavformat/mov.c index 0d982e5a79..ed52968ba9 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -2571,6 +2571,30 @@ static int mov_read_sbas(MOVContext* c, AVIOContext* pb, MOVAtom atom) return 0; } +static int mov_read_vdep(MOVContext* c, AVIOContext* pb, MOVAtom atom) +{ + AVStream* st; + MOVStreamContext* sc; + + if (c->fc->nb_streams < 1) + return 0; + + /* Dolby Vision Profile 7 places a single vdep tref on the EL track + * pointing to the BL track. Anything beyond the first reference is + * out of spec and ignored. */ + if (atom.size > 4) { + av_log(c->fc, AV_LOG_WARNING, + "More than one vdep reference is not supported.\n"); + } + + st = c->fc->streams[c->fc->nb_streams - 1]; + sc = st->priv_data; + sc->tref_id = avio_rb32(pb); + sc->tref_flags |= MOV_TREF_FLAG_DOVI_EL; + + return 0; +} + /** * An strf atom is a BITMAPINFOHEADER struct. This struct is 40 bytes itself, * but can have extradata appended at the end after the 40 bytes belonging @@ -9560,6 +9584,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('p','a','s','p'), mov_read_pasp }, { MKTAG('c','l','a','p'), mov_read_clap }, { MKTAG('s','b','a','s'), mov_read_sbas }, +{ MKTAG('v','d','e','p'), mov_read_vdep }, { MKTAG('s','i','d','x'), mov_read_sidx }, { MKTAG('s','t','b','l'), mov_read_default }, { MKTAG('s','t','c','o'), mov_read_stco }, @@ -10868,6 +10893,57 @@ static int mov_parse_lcevc_streams(AVFormatContext *s) return 0; } +static int mov_parse_dovi_streams(AVFormatContext *s) +{ + int err; + + if (s->nb_streams <= 1) + return 0; + + for (int i = 0; i < s->nb_streams; i++) { + AVStreamGroup *stg; + AVStream *st = s->streams[i]; + AVStream *st_base; + MOVStreamContext *sc = st->priv_data; + int j = 0; + + /* Find a Dolby Vision Profile 7 enhancement-layer stream. */ + if (st->codecpar->codec_id != AV_CODEC_ID_HEVC || + !(sc->tref_flags & MOV_TREF_FLAG_DOVI_EL)) + continue; + + stg = avformat_stream_group_create(s, AV_STREAM_GROUP_PARAMS_DOLBY_VISION, NULL); + if (!stg) + return AVERROR(ENOMEM); + + stg->id = st->id; + + while ((st_base = mov_find_reference_track(s, st, j))) { + err = avformat_stream_group_add_stream(stg, st_base); + if (err < 0) + return err; + + j = st_base->index + 1; + } + if (!j) { + int loglevel = (s->error_recognition & AV_EF_EXPLODE) ? AV_LOG_ERROR : AV_LOG_WARNING; + av_log(s, loglevel, "Failed to find base layer for Dolby Vision EL stream\n"); + ff_remove_stream_group(s, stg); + if (s->error_recognition & AV_EF_EXPLODE) + return AVERROR_INVALIDDATA; + continue; + } + + err = avformat_stream_group_add_stream(stg, st); + if (err < 0) + return err; + + stg->params.dovi->el_index = stg->nb_streams - 1; + } + + return 0; +} + static void fix_stream_ids(AVFormatContext *s) { int highest_id = 0, lowest_iamf_id = INT_MAX; @@ -10989,6 +11065,11 @@ static int mov_read_header(AVFormatContext *s) if (err < 0) return err; + /* Create Dolby Vision Profile 7 stream groups. */ + err = mov_parse_dovi_streams(s); + if (err < 0) + return err; + for (i = 0; i < s->nb_streams; i++) { AVStream *st = s->streams[i]; FFStream *const sti = ffstream(st); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
