PR #24540 opened by Niklas Haas (haasn) URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24540 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24540.patch
This, in principle, is how QuickTime expects files to tag the alpha mixing mode. Whether or not implementations will follow this in practice is, of course, another concern - but at least now FFmpeg handles it correctly. >From 8f5bdf225ba6df1270183ef60bd3ea23d5f84a5f Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Wed, 16 Sep 2026 20:05:13 +0200 Subject: [PATCH 1/3] avformat/isom: add defines for QTFF vmhd graphics modes Sponsored-by: nxtedition AB Signed-off-by: Niklas Haas <[email protected]> --- libavformat/isom.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/libavformat/isom.h b/libavformat/isom.h index e02af09aaa..5474d700d6 100644 --- a/libavformat/isom.h +++ b/libavformat/isom.h @@ -439,6 +439,17 @@ void ff_mp4_parse_es_descr(AVIOContext *pb, int *es_id); #define MOV_SAMPLE_DEPENDENCY_YES 0x1 #define MOV_SAMPLE_DEPENDENCY_NO 0x2 +/* https://developer.apple.com/documentation/quicktime-file-format/graphics_modes */ +#define MOV_GRAPHICS_MODE_COPY 0x0 +#define MOV_GRAPHICS_MODE_DITHER_COPY 0x40 +#define MOV_GRAPHICS_MODE_BLEND 0x20 +#define MOV_GRAPHICS_MODE_TRANSPARENT 0x24 +#define MOV_GRAPHICS_MODE_STRAIGHT_ALPHA 0x100 +#define MOV_GRAPHICS_MODE_PREMUL_WHITE_ALPHA 0x101 +#define MOV_GRAPHICS_MODE_PREMUL_BLACK_ALPHA 0x102 +#define MOV_GRAPHICS_MODE_COMPOSITION 0x103 +#define MOV_GRAPHICS_MODE_STRAIGHT_ALPHA_BLEND 0x104 + #define TAG_IS_AVCI(tag) \ ((tag) == MKTAG('a', 'i', '5', 'p') || \ (tag) == MKTAG('a', 'i', '5', 'q') || \ -- 2.52.0 >From d419430291750090e9875ab39a10c351132a494a Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Wed, 16 Sep 2026 19:09:34 +0200 Subject: [PATCH 2/3] avformat/movenc: emit vmhd graphics mode from AVAlphaMode tag Only for MOV files, as MP4 does not define any non-0 graphics modes. Sponsored-by: nxtedition AB Signed-off-by: Niklas Haas <[email protected]> --- libavformat/movenc.c | 20 +++++++++++++++++--- 1 file changed, 17 insertions(+), 3 deletions(-) diff --git a/libavformat/movenc.c b/libavformat/movenc.c index ecc6544bad..78ccc81355 100644 --- a/libavformat/movenc.c +++ b/libavformat/movenc.c @@ -3584,12 +3584,26 @@ static int mov_write_smhd_tag(AVIOContext *pb) return 16; } -static int mov_write_vmhd_tag(AVIOContext *pb) +static uint16_t mov_graphics_mode(MOVTrack *track) +{ + if (track->mode != MODE_MOV) + return 0; /* ISO/IEC 14496-12 doesn't define any other modes */ + + switch (track->par->alpha_mode) { + default: return MOV_GRAPHICS_MODE_COPY; + case AVALPHA_MODE_STRAIGHT: return MOV_GRAPHICS_MODE_STRAIGHT_ALPHA; + case AVALPHA_MODE_PREMULTIPLIED: return MOV_GRAPHICS_MODE_PREMUL_BLACK_ALPHA; + } +} + +static int mov_write_vmhd_tag(AVIOContext *pb, MOVTrack *track) { avio_wb32(pb, 0x14); /* size (always 0x14) */ ffio_wfourcc(pb, "vmhd"); avio_wb32(pb, 0x01); /* version & flags */ - avio_wb64(pb, 0); /* reserved (graphics mode = copy) */ + avio_wb16(pb, mov_graphics_mode(track)); + for (int i = 0; i < 3; i++) + avio_wb16(pb, 0); /* opcolor */ return 0x14; } @@ -3863,7 +3877,7 @@ static int mov_write_minf_tag(AVFormatContext *s, AVIOContext *pb, MOVMuxContext avio_wb32(pb, 0); /* size */ ffio_wfourcc(pb, "minf"); if (track->par->codec_type == AVMEDIA_TYPE_VIDEO) - mov_write_vmhd_tag(pb); + mov_write_vmhd_tag(pb, track); else if (track->par->codec_type == AVMEDIA_TYPE_AUDIO) mov_write_smhd_tag(pb); else if (track->par->codec_type == AVMEDIA_TYPE_SUBTITLE) { -- 2.52.0 >From 39d08a65c92da7e1b04ccc21402a4ead9d59ea2a Mon Sep 17 00:00:00 2001 From: Niklas Haas <[email protected]> Date: Wed, 16 Sep 2026 20:25:27 +0200 Subject: [PATCH 3/3] avformat/mov: read and parse AVAlphaMode from vmhd tag In principle, we could add support for the other graphics modes at a later point in time; but as written, AVAlphaMode only supports the given two modes. Sponsored-by: nxtedition AB Signed-off-by: Niklas Haas <[email protected]> --- libavformat/mov.c | 34 ++++++++++++++++++++++++++++++++++ 1 file changed, 34 insertions(+) diff --git a/libavformat/mov.c b/libavformat/mov.c index 59361cd30d..6047eabc17 100644 --- a/libavformat/mov.c +++ b/libavformat/mov.c @@ -10001,6 +10001,39 @@ fail: return ret; } +static int mov_read_vmhd(MOVContext *c, AVIOContext *pb, MOVAtom atom) +{ + avio_rb32(pb); // version & flags + uint16_t graphics_mode = avio_rb16(pb); + // ignored: opcolor[3] + + if (c->fc->nb_streams < 1) + return 0; + AVStream *st = c->fc->streams[c->fc->nb_streams - 1]; + if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) + return 0; + + switch (graphics_mode) { + case MOV_GRAPHICS_MODE_COPY: + case MOV_GRAPHICS_MODE_DITHER_COPY: + st->codecpar->alpha_mode = AVALPHA_MODE_UNSPECIFIED; + break; + case MOV_GRAPHICS_MODE_STRAIGHT_ALPHA: + st->codecpar->alpha_mode = AVALPHA_MODE_STRAIGHT; + break; + case MOV_GRAPHICS_MODE_PREMUL_BLACK_ALPHA: + st->codecpar->alpha_mode = AVALPHA_MODE_PREMULTIPLIED; + break; + default: + st->codecpar->alpha_mode = AVALPHA_MODE_UNSPECIFIED; + av_log(c->fc, AV_LOG_WARNING, "Unhandled graphics mode: 0x%x\n", + graphics_mode); + break; + } + + return 0; +} + static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('A','C','L','R'), mov_read_aclr }, { MKTAG('A','P','R','G'), mov_read_avid }, @@ -10132,6 +10165,7 @@ static const MOVParseTableEntry mov_default_parse_table[] = { { MKTAG('i','a','c','b'), mov_read_iacb }, #endif { MKTAG('s','r','a','t'), mov_read_srat }, +{ MKTAG('v','m','h','d'), mov_read_vmhd }, { 0, NULL } }; -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
