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]

Reply via email to