Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PATCH] Fix jpeg2k and dnxhr 444 MXF and MOV muxing (PR #20711)
@ 2025-10-16  0:21 Baptiste Coudurier via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: Baptiste Coudurier via ffmpeg-devel @ 2025-10-16  0:21 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Baptiste Coudurier

PR #20711 opened by Baptiste Coudurier (bcoudurier)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20711
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/20711.patch


From 8bc654b2c0f79213195140f372c83095b0e2b123 Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Tue, 3 Jun 2025 17:50:15 -0700
Subject: [PATCH 1/7] lavf/movenc: improve AVdh atom generation for DNxHD/DNxHR

---
 libavformat/movenc.c                       | 86 ++++++++++++++++------
 tests/ref/vsynth/vsynth1-dnxhd-1080i       |  2 +-
 tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit |  2 +-
 tests/ref/vsynth/vsynth1-dnxhd-1080i-colr  |  2 +-
 tests/ref/vsynth/vsynth2-dnxhd-1080i       |  2 +-
 tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit |  2 +-
 tests/ref/vsynth/vsynth2-dnxhd-1080i-colr  |  2 +-
 tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit |  2 +-
 tests/ref/vsynth/vsynth3-dnxhd-1080i-colr  |  2 +-
 9 files changed, 70 insertions(+), 32 deletions(-)

diff --git a/libavformat/movenc.c b/libavformat/movenc.c
index 067d38b14b..f2aa141fb7 100644
--- a/libavformat/movenc.c
+++ b/libavformat/movenc.c
@@ -1679,17 +1679,18 @@ static int mov_write_apvc_tag(AVFormatContext *s, AVIOContext *pb, MOVTrack *tra
 }
 
 /* also used by all avid codecs (dv, imx, meridien) and their variants */
+/* https://community.avid.com/forums/t/136517.aspx */
 static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
 {
     int interlaced;
     int cid;
     int display_width = track->par->width;
+    const uint8_t *extradata;
 
     if (track->extradata[track->last_stsd_index] && track->extradata_size[track->last_stsd_index] > 0x29) {
         if (ff_dnxhd_parse_header_prefix(track->extradata[track->last_stsd_index]) != 0) {
             /* looks like a DNxHD bit stream */
-            interlaced = (track->extradata[track->last_stsd_index][5] & 2);
-            cid = AV_RB32(track->extradata[track->last_stsd_index] + 0x28);
+            extradata = track->extradata[track->last_stsd_index];
         } else {
             av_log(NULL, AV_LOG_WARNING, "Could not locate DNxHD bit stream in vos_data\n");
             return 0;
@@ -1699,60 +1700,97 @@ static int mov_write_avid_tag(AVIOContext *pb, MOVTrack *track)
         return 0;
     }
 
+    cid = AV_RB32(extradata + 0x28);
+
     avio_wb32(pb, 24); /* size */
     ffio_wfourcc(pb, "ACLR");
     ffio_wfourcc(pb, "ACLR");
     ffio_wfourcc(pb, "0001");
+    // 1: CCIR (supercolors will be dropped, 16 will be displayed as black)
+    // 2: FullRange (0 will be displayed as black, 16 will be displayed as dark grey)
     if (track->par->color_range == AVCOL_RANGE_MPEG || /* Legal range (16-235) */
         track->par->color_range == AVCOL_RANGE_UNSPECIFIED) {
-        avio_wb32(pb, 1); /* Corresponds to 709 in official encoder */
-    } else { /* Full range (0-255) */
-        avio_wb32(pb, 2); /* Corresponds to RGB in official encoder */
+        avio_wb32(pb, 1);
+    } else {
+        avio_wb32(pb, 2);
     }
-    avio_wb32(pb, 0); /* unknown */
+    avio_wb32(pb, 0); /* reserved */
 
     if (track->tag == MKTAG('A','V','d','h')) {
+        int alp = extradata[0x07] & 1;
+        int pma = (extradata[0x07] >> 2) & 1;
+        int sbd = (extradata[0x21] >> 5) & 3;
+        int ssc = (extradata[0x2C] >> 5) & 3;
+        int clv = (extradata[0x2C] >> 1) & 3;
+        int clf = extradata[0x2C] & 1;
+
         avio_wb32(pb, 32);
         ffio_wfourcc(pb, "ADHR");
         ffio_wfourcc(pb, "0001");
-        avio_wb32(pb, cid);
-        avio_wb32(pb, 0); /* unknown */
-        avio_wb32(pb, 1); /* unknown */
-        avio_wb32(pb, 0); /* unknown */
-        avio_wb32(pb, 0); /* unknown */
+        avio_wb32(pb, cid); // Compression ID
+        // 0: 4:2:2 Sub Sampling
+        // 1: 4:2:0 Sub Sampling
+        // 2: 4:4:4 Sub Sampling
+        avio_wb32(pb, ssc); // Sub Sampling Control
+        // 1:  8-bits per sample
+        // 2: 10-bits per sample
+        // 3: 12-bits per sample
+        avio_wb32(pb, sbd); // Sample Bit Depth
+        // 0:  Bitstream is encoded using the YCBCR format rules and tables
+        // 1:  Bitstream is encoded using the RGB format rules and tables – only Compression IDs 1256, 1270
+        avio_wb16(pb, clf); // Color Format
+        // 0: ITU-R BT.709
+        // 1: ITU-R BT.2020
+        // 2: ITU-R BT.2020 C
+        // 3: Out-of-band
+        avio_wb16(pb, clv); // Color Volume
+        // 0: Alpha channel not present
+        // 1: Alpha channel present
+        avio_wb16(pb, alp); // Alpha Present
+        // 0: Alpha has not been applied to video channels
+        // 1: Alpha has been applied to the video channels prior to encoding
+        avio_wb16(pb, pma); // Pre-Multiplied Alpha
         return 0;
     }
 
+    interlaced = extradata[5] & 2;
+
     avio_wb32(pb, 24); /* size */
     ffio_wfourcc(pb, "APRG");
     ffio_wfourcc(pb, "APRG");
     ffio_wfourcc(pb, "0001");
-    avio_wb32(pb, 1); /* unknown */
-    avio_wb32(pb, 0); /* unknown */
+    // 1 for progressive or 2 for interlaced
+    if (interlaced)
+        avio_wb32(pb, 2);
+    else
+        avio_wb32(pb, 1);
+    avio_wb32(pb, 0); /* reserved */
 
     avio_wb32(pb, 120); /* size */
     ffio_wfourcc(pb, "ARES");
     ffio_wfourcc(pb, "ARES");
     ffio_wfourcc(pb, "0001");
-    avio_wb32(pb, cid); /* dnxhd cid, some id ? */
+    avio_wb32(pb, cid); /* cid */
     if (   track->par->sample_aspect_ratio.num > 0
         && track->par->sample_aspect_ratio.den > 0)
         display_width = display_width * track->par->sample_aspect_ratio.num / track->par->sample_aspect_ratio.den;
-    avio_wb32(pb, display_width);
-    /* values below are based on samples created with quicktime and avid codecs */
+    avio_wb32(pb, display_width); // field width
     if (interlaced) {
-        avio_wb32(pb, track->par->height / 2);
-        avio_wb32(pb, 2); /* unknown */
-        avio_wb32(pb, 0); /* unknown */
-        avio_wb32(pb, 4); /* unknown */
+        avio_wb32(pb, track->par->height / 2); // field height
+        avio_wb32(pb, 2); // num fields
+        avio_wb32(pb, 0); // num black lines (must be 0)
+        // 4: HD1080i
+        // 5: HD1080P
+        // 6: HD720P
+        avio_wb32(pb, 4); // video format
     } else {
         avio_wb32(pb, track->par->height);
-        avio_wb32(pb, 1); /* unknown */
-        avio_wb32(pb, 0); /* unknown */
+        avio_wb32(pb, 1); // num fields
+        avio_wb32(pb, 0);
         if (track->par->height == 1080)
-            avio_wb32(pb, 5); /* unknown */
+            avio_wb32(pb, 5);
         else
-            avio_wb32(pb, 6); /* unknown */
+            avio_wb32(pb, 6);
     }
     /* padding */
     ffio_fill(pb, 0, 10 * 8);
diff --git a/tests/ref/vsynth/vsynth1-dnxhd-1080i b/tests/ref/vsynth/vsynth1-dnxhd-1080i
index 0e7844b0ee..ce08c85156 100644
--- a/tests/ref/vsynth/vsynth1-dnxhd-1080i
+++ b/tests/ref/vsynth/vsynth1-dnxhd-1080i
@@ -1,4 +1,4 @@
-af5cbe239839f6282a68f1a106ed3a77 *tests/data/fate/vsynth1-dnxhd-1080i.mov
+ac2ec2b1e8daef96715c7032db3447de *tests/data/fate/vsynth1-dnxhd-1080i.mov
 3031911 tests/data/fate/vsynth1-dnxhd-1080i.mov
 fed9ed2a5179c9df0ef58772b025e303 *tests/data/fate/vsynth1-dnxhd-1080i.out.rawvideo
 stddev:    6.18 PSNR: 32.31 MAXDIFF:   64 bytes:  7603200/   760320
diff --git a/tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit b/tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit
index c3f3fda085..8dc3e50fd5 100644
--- a/tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit
+++ b/tests/ref/vsynth/vsynth1-dnxhd-1080i-10bit
@@ -1,4 +1,4 @@
-1a8261120bcc764a7bbdd198febff4c7 *tests/data/fate/vsynth1-dnxhd-1080i-10bit.mov
+4b7ccdc290bd06cfcaf29baad55081fa *tests/data/fate/vsynth1-dnxhd-1080i-10bit.mov
 4588391 tests/data/fate/vsynth1-dnxhd-1080i-10bit.mov
 31032fcb7e6af79daaac02288254c6d6 *tests/data/fate/vsynth1-dnxhd-1080i-10bit.out.rawvideo
 stddev:    5.69 PSNR: 33.02 MAXDIFF:   55 bytes:  7603200/   760320
diff --git a/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr
index 35cd719b63..a07f6b375c 100644
--- a/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr
+++ b/tests/ref/vsynth/vsynth1-dnxhd-1080i-colr
@@ -1,4 +1,4 @@
-5571f4ff9e29d352a7e373a14a9ed2ed *tests/data/fate/vsynth1-dnxhd-1080i-colr.mov
+3bf2c5863bf8a46365e5ead657d01796 *tests/data/fate/vsynth1-dnxhd-1080i-colr.mov
 3031929 tests/data/fate/vsynth1-dnxhd-1080i-colr.mov
 6f2d5429ffc4529a76acfeb28b560542 *tests/data/fate/vsynth1-dnxhd-1080i-colr.out.rawvideo
 stddev:    5.65 PSNR: 33.09 MAXDIFF:   55 bytes:  7603200/   760320
diff --git a/tests/ref/vsynth/vsynth2-dnxhd-1080i b/tests/ref/vsynth/vsynth2-dnxhd-1080i
index 0668a44f9a..619d51a33e 100644
--- a/tests/ref/vsynth/vsynth2-dnxhd-1080i
+++ b/tests/ref/vsynth/vsynth2-dnxhd-1080i
@@ -1,4 +1,4 @@
-0067903558c99e3abed402ed65297735 *tests/data/fate/vsynth2-dnxhd-1080i.mov
+16784dcb2a944d681ee8125744598a00 *tests/data/fate/vsynth2-dnxhd-1080i.mov
 3031911 tests/data/fate/vsynth2-dnxhd-1080i.mov
 e941d2587cfeccddc450da7f41f7f911 *tests/data/fate/vsynth2-dnxhd-1080i.out.rawvideo
 stddev:    1.50 PSNR: 44.56 MAXDIFF:   31 bytes:  7603200/   760320
diff --git a/tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit b/tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit
index 7689e2c22e..0ec33c68e8 100644
--- a/tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit
+++ b/tests/ref/vsynth/vsynth2-dnxhd-1080i-10bit
@@ -1,4 +1,4 @@
-16e86953a697e1e7f9d80903ff4fef0c *tests/data/fate/vsynth2-dnxhd-1080i-10bit.mov
+f72fd4fb2f8969ae51809a049bacc036 *tests/data/fate/vsynth2-dnxhd-1080i-10bit.mov
 4588391 tests/data/fate/vsynth2-dnxhd-1080i-10bit.mov
 e4ca9be476869afb94962d945f90bdf6 *tests/data/fate/vsynth2-dnxhd-1080i-10bit.out.rawvideo
 stddev:    1.57 PSNR: 44.18 MAXDIFF:   33 bytes:  7603200/   760320
diff --git a/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr
index f5ac604716..3029a6f72a 100644
--- a/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr
+++ b/tests/ref/vsynth/vsynth2-dnxhd-1080i-colr
@@ -1,4 +1,4 @@
-eee674d012c850c1d2bb5e816b668cdf *tests/data/fate/vsynth2-dnxhd-1080i-colr.mov
+64957f9ac88f32c175f22106fe60d291 *tests/data/fate/vsynth2-dnxhd-1080i-colr.mov
 3031929 tests/data/fate/vsynth2-dnxhd-1080i-colr.mov
 ec40a8014b819d02951b2f06bee7b514 *tests/data/fate/vsynth2-dnxhd-1080i-colr.out.rawvideo
 stddev:    1.54 PSNR: 44.33 MAXDIFF:   33 bytes:  7603200/   760320
diff --git a/tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit b/tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit
index 2a040e12db..51e46f7450 100644
--- a/tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit
+++ b/tests/ref/vsynth/vsynth3-dnxhd-1080i-10bit
@@ -1,4 +1,4 @@
-4e6185e273297061def8e0b7fabff71b *tests/data/fate/vsynth3-dnxhd-1080i-10bit.mov
+ec15292ebe59baa20dea7a9c8fd64237 *tests/data/fate/vsynth3-dnxhd-1080i-10bit.mov
 4588391 tests/data/fate/vsynth3-dnxhd-1080i-10bit.mov
 c192f36ef8687e56c72a3dc416c7e191 *tests/data/fate/vsynth3-dnxhd-1080i-10bit.out.rawvideo
 stddev:    6.92 PSNR: 31.32 MAXDIFF:   50 bytes:    86700/     8670
diff --git a/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr b/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr
index 08aa30d8c4..920e86ccec 100644
--- a/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr
+++ b/tests/ref/vsynth/vsynth3-dnxhd-1080i-colr
@@ -1,4 +1,4 @@
-92a2f67cf77abf3428fe2d4f53ba2027 *tests/data/fate/vsynth3-dnxhd-1080i-colr.mov
+32cf64f3043053d3003388ab12f53cbe *tests/data/fate/vsynth3-dnxhd-1080i-colr.mov
 3031929 tests/data/fate/vsynth3-dnxhd-1080i-colr.mov
 f907fd2d48bedbc5283fbfc3fb9f61a0 *tests/data/fate/vsynth3-dnxhd-1080i-colr.out.rawvideo
 stddev:    6.92 PSNR: 31.32 MAXDIFF:   50 bytes:    86700/     8670
-- 
2.49.1


From d986e9525424104dbafb2c68702532b07b417a07 Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Wed, 4 Jun 2025 10:43:04 -0700
Subject: [PATCH 2/7] lavf/mxfenc: require pixel format to be set for video
 streams

---
 libavformat/mxfenc.c | 56 +++++++++++++++++---------------------------
 1 file changed, 21 insertions(+), 35 deletions(-)

diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index b14a480ba3..ed315be140 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -1493,11 +1493,6 @@ static int mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st)
     int64_t pos;
     const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format);
 
-    if (!pix_desc) {
-        av_log(s, AV_LOG_ERROR, "Pixel format not set - not writing JPEG2000SubDescriptor\n");
-        return AVERROR(EINVAL);
-    }
-
     /* JPEG2000 subdescriptor key */
     avio_write(pb, mxf_jpeg2000_subdescriptor_key, 16);
     klv_encode_ber4_length(pb, 0);
@@ -1610,7 +1605,6 @@ static int mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st)
 {
     int is_rgb, pos;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codecpar->format);
-    av_assert0(desc);
     is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
 
     pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key);
@@ -2692,11 +2686,6 @@ static int mxf_parse_jpeg2000_frame(AVFormatContext *s, AVStream *st, AVPacket *
     GetByteContext g;
     uint32_t j2k_ncomponents;
 
-    if (!pix_desc) {
-        av_log(s, AV_LOG_ERROR, "Pixel format not set\n");
-        return AVERROR(EINVAL);
-    }
-
     if (mxf->header_written)
         return 1;
 
@@ -2898,20 +2887,18 @@ static enum AVChromaLocation choose_chroma_location(AVFormatContext *s, AVStream
     if (par->chroma_location != AVCHROMA_LOC_UNSPECIFIED)
         return par->chroma_location;
 
-    if (pix_desc) {
-        if (pix_desc->log2_chroma_h == 0) {
-            return AVCHROMA_LOC_TOPLEFT;
-        } else if (pix_desc->log2_chroma_w == 1 && pix_desc->log2_chroma_h == 1) {
-            if (par->field_order == AV_FIELD_UNKNOWN || par->field_order == AV_FIELD_PROGRESSIVE) {
-                switch (par->codec_id) {
-                case AV_CODEC_ID_MJPEG:
-                case AV_CODEC_ID_MPEG1VIDEO: return AVCHROMA_LOC_CENTER;
-                }
+    if (pix_desc->log2_chroma_h == 0) {
+        return AVCHROMA_LOC_TOPLEFT;
+    } else if (pix_desc->log2_chroma_w == 1 && pix_desc->log2_chroma_h == 1) {
+        if (par->field_order == AV_FIELD_UNKNOWN || par->field_order == AV_FIELD_PROGRESSIVE) {
+            switch (par->codec_id) {
+            case AV_CODEC_ID_MJPEG:
+            case AV_CODEC_ID_MPEG1VIDEO: return AVCHROMA_LOC_CENTER;
             }
-            if (par->field_order == AV_FIELD_UNKNOWN || par->field_order != AV_FIELD_PROGRESSIVE) {
-                switch (par->codec_id) {
-                case AV_CODEC_ID_MPEG2VIDEO: return AVCHROMA_LOC_LEFT;
-                }
+        }
+        if (par->field_order == AV_FIELD_UNKNOWN || par->field_order != AV_FIELD_PROGRESSIVE) {
+            switch (par->codec_id) {
+            case AV_CODEC_ID_MPEG2VIDEO: return AVCHROMA_LOC_LEFT;
             }
         }
     }
@@ -2950,27 +2937,26 @@ static int mxf_init(AVFormatContext *s)
         if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
             const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format);
             AVRational tbc = (AVRational){ 0, 0 };
+
+            if (!pix_desc) {
+                av_log(s, AV_LOG_ERROR, "video stream require the codec pixel format to be set\n");
+                return -1;
+            }
+
             if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0)
                 tbc = av_inv_q(st->avg_frame_rate);
             else if (st->r_frame_rate.num > 0 && st->r_frame_rate.den > 0)
                 tbc = av_inv_q(st->r_frame_rate);
 
-            // Default component depth to 8
-            sc->component_depth = 8;
-            sc->h_chroma_sub_sample = 2;
-            sc->v_chroma_sub_sample = 2;
-            sc->color_siting = 0xFF;
-
             if (st->codecpar->sample_aspect_ratio.num && st->codecpar->sample_aspect_ratio.den) {
                 sc->aspect_ratio = av_mul_q(st->codecpar->sample_aspect_ratio,
                                             av_make_q(st->codecpar->width, st->codecpar->height));
             }
 
-            if (pix_desc) {
-                sc->component_depth     = pix_desc->comp[0].depth;
-                sc->h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w;
-                sc->v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h;
-            }
+            sc->component_depth     = pix_desc->comp[0].depth;
+            sc->h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w;
+            sc->v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h;
+
             switch (choose_chroma_location(s, st)) {
             case AVCHROMA_LOC_TOPLEFT: sc->color_siting = 0; break;
             case AVCHROMA_LOC_LEFT:    sc->color_siting = 6; break;
-- 
2.49.1


From 6a26192e536e5eb49b269ce4be496968b693342c Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Tue, 3 Jun 2025 23:11:43 -0700
Subject: [PATCH 3/7] lavf/mxfenc: remove cdci pixel format specific values
 from stream context

---
 libavformat/mxfenc.c | 64 +++++++++++++++++++-------------------------
 1 file changed, 28 insertions(+), 36 deletions(-)

diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index ed315be140..30d8cc1c27 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -106,11 +106,7 @@ typedef struct MXFStreamContext {
     int order;               ///< interleaving order if dts are equal
     int interlaced;          ///< whether picture is interlaced
     int field_dominance;     ///< tff=1, bff=2
-    int component_depth;
-    int color_siting;
     int signal_standard;
-    int h_chroma_sub_sample;
-    int v_chroma_sub_sample;
     int temporal_reordering;
     AVRational aspect_ratio; ///< display aspect ratio
     int closed_gop;          ///< gop is closed, used in mpeg-2 frame parsing
@@ -180,6 +176,8 @@ static int mxf_write_cdci_desc(AVFormatContext *s, AVStream *st);
 static int mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st);
 static int mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st);
 
+static enum AVChromaLocation choose_chroma_location(AVFormatContext *s, AVStream *st);
+
 static const MXFContainerEssenceEntry mxf_essence_container_uls[] = {
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 },
       { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
@@ -1207,6 +1205,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
 {
     MXFStreamContext *sc = st->priv_data;
     AVIOContext *pb = s->pb;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format);
     int stored_width = st->codecpar->width;
     int stored_height = st->codecpar->height;
     int display_width;
@@ -1309,21 +1308,37 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
     }
 
     if (key != mxf_rgba_descriptor_key) {
+        int component_depth     = pix_desc->comp[0].depth;
+        int h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w;
+        int v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h;
+        int color_siting;
+
         // component depth
         mxf_write_local_tag(s, 4, 0x3301);
-        avio_wb32(pb, sc->component_depth);
+        avio_wb32(pb, component_depth);
 
         // horizontal subsampling
         mxf_write_local_tag(s, 4, 0x3302);
-        avio_wb32(pb, sc->h_chroma_sub_sample);
+        avio_wb32(pb, h_chroma_sub_sample);
 
         // vertical subsampling
         mxf_write_local_tag(s, 4, 0x3308);
-        avio_wb32(pb, sc->v_chroma_sub_sample);
+        avio_wb32(pb, v_chroma_sub_sample);
+
+        switch (choose_chroma_location(s, st)) {
+        case AVCHROMA_LOC_TOPLEFT: color_siting = 0; break;
+        case AVCHROMA_LOC_LEFT:    color_siting = 6; break;
+        case AVCHROMA_LOC_TOP:     color_siting = 1; break;
+        case AVCHROMA_LOC_CENTER:  color_siting = 3; break;
+        default:                   color_siting = 0xff;
+        }
+
+        if (IS_D10(s))
+            color_siting = 0; // color siting is specified to be 0 in d-10 specs
 
         // color siting
         mxf_write_local_tag(s, 1, 0x3303);
-        avio_w8(pb, sc->color_siting);
+        avio_w8(pb, color_siting);
 
         // Padding Bits
         mxf_write_local_tag(s, 2, 0x3307);
@@ -1331,12 +1346,12 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
 
         if (st->codecpar->color_range != AVCOL_RANGE_UNSPECIFIED) {
             int black = 0,
-                white = (1<<sc->component_depth) - 1,
-                color = (1<<sc->component_depth);
+                white = (1<<component_depth) - 1,
+                color = (1<<component_depth);
             if (st->codecpar->color_range == AVCOL_RANGE_MPEG) {
-                black = 1   << (sc->component_depth - 4);
-                white = 235 << (sc->component_depth - 8);
-                color = (14 << (sc->component_depth - 4)) + 1;
+                black = 1   << (component_depth - 4);
+                white = 235 << (component_depth - 8);
+                color = (14 << (component_depth - 4)) + 1;
             }
             mxf_write_local_tag(s, 4, 0x3304);
             avio_wb32(pb, black);
@@ -2354,15 +2369,6 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt
     if (i == FF_ARRAY_ELEMS(mxf_dnxhd_codec_uls))
         return 0;
 
-    sc->component_depth = 0;
-    switch (pkt->data[0x21] >> 5) {
-    case 1: sc->component_depth = 8; break;
-    case 2: sc->component_depth = 10; break;
-    case 3: sc->component_depth = 12; break;
-    }
-    if (!sc->component_depth)
-        return 0;
-
     if (cid >= 1270) { // RI raster
         av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den,
                   st->codecpar->width, st->codecpar->height,
@@ -2536,7 +2542,6 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
                       sc->aspect_ratio.num, sc->aspect_ratio.den, 1024*1024);
             intra_only = (sps->constraint_set_flags >> 3) & 1;
             sc->interlaced = !sps->frame_mbs_only_flag;
-            sc->component_depth = sps->bit_depth_luma;
 
             buf = nal_end;
             break;
@@ -2581,7 +2586,6 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
     for (i = 0; i < FF_ARRAY_ELEMS(mxf_h264_codec_uls); i++) {
         if (frame_size == mxf_h264_codec_uls[i].frame_size && sc->interlaced == mxf_h264_codec_uls[i].interlaced) {
             codec_ul = &mxf_h264_codec_uls[i].uid;
-            sc->component_depth = 10; // AVC Intra is always 10 Bit
             sc->aspect_ratio = (AVRational){ 16, 9 }; // 16:9 is mandatory for broadcast HD
             st->codecpar->profile = mxf_h264_codec_uls[i].profile;
             sc->avc_intra = 1;
@@ -2953,17 +2957,6 @@ static int mxf_init(AVFormatContext *s)
                                             av_make_q(st->codecpar->width, st->codecpar->height));
             }
 
-            sc->component_depth     = pix_desc->comp[0].depth;
-            sc->h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w;
-            sc->v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h;
-
-            switch (choose_chroma_location(s, st)) {
-            case AVCHROMA_LOC_TOPLEFT: sc->color_siting = 0; break;
-            case AVCHROMA_LOC_LEFT:    sc->color_siting = 6; break;
-            case AVCHROMA_LOC_TOP:     sc->color_siting = 1; break;
-            case AVCHROMA_LOC_CENTER:  sc->color_siting = 3; break;
-            }
-
             mxf->content_package_rate = ff_mxf_get_content_package_rate(tbc);
             mxf->time_base = tbc;
             avpriv_set_pts_info(st, 64, mxf->time_base.num, mxf->time_base.den);
@@ -3006,7 +2999,6 @@ static int mxf_init(AVFormatContext *s)
                 sc->container_ul = &mxf_d10_container_uls[ul_index];
                 sc->index = INDEX_D10_VIDEO;
                 sc->signal_standard = 1;
-                sc->color_siting = 0;
                 sc->frame_size = (int64_t)sc->video_bit_rate *
                     mxf->time_base.num / (8*mxf->time_base.den);
             }
-- 
2.49.1


From 792e479ec258fe027faa4b61a15b0fa7dc5aea2f Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Wed, 4 Jun 2025 11:24:41 -0700
Subject: [PATCH 4/7] lavf/mxfenc: correctly add jpeg2000 subdescriptor
 reference in cdci, factorize

---
 libavformat/mxfenc.c | 50 +++++++++++++++++++++++---------------------
 1 file changed, 26 insertions(+), 24 deletions(-)

diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index 30d8cc1c27..b935361599 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -117,9 +117,9 @@ typedef struct MXFStreamContext {
     int max_gop;             ///< maximum gop size, used by mpeg-2 descriptor
     int b_picture_count;     ///< maximum number of consecutive b pictures, used in mpeg-2 descriptor
     int low_delay;           ///< low delay, used in mpeg-2 descriptor
-    int avc_intra;
     int micro_version;       ///< format micro_version, used in ffv1 descriptor
     j2k_info_t j2k_info;
+    enum MXFMetadataSetType sub_descriptor;
 } MXFStreamContext;
 
 typedef struct MXFContainerEssenceEntry {
@@ -417,8 +417,9 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 0x4406, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x0C,0x00,0x00,0x00}}, /* User Comments */
     { 0x5001, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x09,0x01,0x00,0x00}}, /* Name */
     { 0x5003, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x02,0x01,0x02,0x0A,0x01,0x00,0x00}}, /* Value */
-    // mxf_avc_subdescriptor_local_tags
+    // sub descriptor used AVC, JPEG2000 and FFV1
     { 0x8100, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00}}, /* SubDescriptors */
+    // mxf_avc_subdescriptor_local_tags
     { 0x8200, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x06,0x01,0x0E,0x00,0x00}}, /* AVC Decoding Delay */
     { 0x8201, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x06,0x01,0x0A,0x00,0x00}}, /* AVC Profile */
     { 0x8202, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x06,0x01,0x0D,0x00,0x00}}, /* AVC Level */
@@ -432,7 +433,6 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 0xDFDA, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x05,0x00,0x00,0x00}}, /* FFV1 Version */
     { 0xDFDB, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x0E,0x04,0x01,0x06,0x0C,0x01,0x00,0x00,0x00}}, /* FFV1 Initialization Metadata */
     // ff_mxf_jpeg2000_local_tags
-    { 0x8400, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x09,0x06,0x01,0x01,0x04,0x06,0x10,0x00,0x00}}, /* Sub Descriptors / Opt Ordered array of strong references to sub descriptor sets */
     { 0x8401, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x01,0x00,0x00,0x00}}, /* Rsiz: An enumerated value that defines the decoder capabilities */
     { 0x8402, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x02,0x00,0x00,0x00}}, /* Xsiz: Width of the reference grid */
     { 0x8403, {0x06,0x0e,0x2b,0x34,0x01,0x01,0x01,0x0a,0x04,0x01,0x06,0x03,0x03,0x00,0x00,0x00}}, /* Ysiz: Height of the reference grid */
@@ -582,10 +582,11 @@ static void mxf_write_primer_pack(AVFormatContext *s)
     AVIOContext *pb = s->pb;
     int local_tag_number = MXF_NUM_TAGS, i;
     int will_have_avc_tags = 0, will_have_mastering_tags = 0, will_have_ffv1_tags = 0, will_have_jpeg2000_tags = 0;
+    int will_have_sub_descriptor = 0;
 
     for (i = 0; i < s->nb_streams; i++) {
         MXFStreamContext *sc = s->streams[i]->priv_data;
-        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264 && !sc->avc_intra) {
+        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264 && sc->sub_descriptor) {
             will_have_avc_tags = 1;
         }
         if (av_packet_side_data_get(s->streams[i]->codecpar->coded_side_data,
@@ -599,6 +600,9 @@ static void mxf_write_primer_pack(AVFormatContext *s)
         if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_JPEG2000){
             will_have_jpeg2000_tags = 1;
         }
+        if (sc->sub_descriptor) {
+            will_have_sub_descriptor = 1;
+        }
     }
 
     if (!mxf->store_user_comments) {
@@ -607,7 +611,7 @@ static void mxf_write_primer_pack(AVFormatContext *s)
         mxf_mark_tag_unused(mxf, 0x5003);
     }
 
-    if (!will_have_avc_tags && !will_have_ffv1_tags) {
+    if (!will_have_sub_descriptor) {
         mxf_mark_tag_unused(mxf, 0x8100);
     }
 
@@ -631,7 +635,6 @@ static void mxf_write_primer_pack(AVFormatContext *s)
     }
 
     if (!will_have_jpeg2000_tags) {
-        mxf_mark_tag_unused(mxf, 0x8400);
         mxf_mark_tag_unused(mxf, 0x8401);
         mxf_mark_tag_unused(mxf, 0x8402);
         mxf_mark_tag_unused(mxf, 0x8403);
@@ -1451,18 +1454,10 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
         avio_w8(pb, sc->field_dominance);
     }
 
-    if (st->codecpar->codec_id == AV_CODEC_ID_H264 && !sc->avc_intra) {
-        // write avc sub descriptor ref
+    if (sc->sub_descriptor) {
         mxf_write_local_tag(s, 8 + 16, 0x8100);
         mxf_write_refs_count(pb, 1);
-        mxf_write_uuid(pb, AVCSubDescriptor, 0);
-    }
-
-    if (st->codecpar->codec_id == AV_CODEC_ID_FFV1) {
-        // write ffv1 sub descriptor ref
-        mxf_write_local_tag(s, 8 + 16, 0x8100);
-        mxf_write_refs_count(pb, 1);
-        mxf_write_uuid(pb, FFV1SubDescriptor, 0);
+        mxf_write_uuid(pb, sc->sub_descriptor, 0);
     }
 
     return pos;
@@ -1561,22 +1556,22 @@ static int mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st)
 
 static int mxf_write_cdci_desc(AVFormatContext *s, AVStream *st)
 {
+    MXFStreamContext *sc = st->priv_data;
     int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key);
     mxf_update_klv_size(s->pb, pos);
 
-    if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
-        mxf_write_avc_subdesc(s, st);
-    }
-    if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) {
-         return mxf_write_jpeg2000_subdesc(s, st);
+    switch (sc->sub_descriptor) {
+    case AVCSubDescriptor: mxf_write_avc_subdesc(s, st); break;
+    case JPEG2000SubDescriptor: mxf_write_jpeg2000_subdesc(s, st); break;
     }
+
     return 0;
 }
 
 static int mxf_write_h264_desc(AVFormatContext *s, AVStream *st)
 {
     MXFStreamContext *sc = st->priv_data;
-    if (sc->avc_intra) {
+    if (!sc->sub_descriptor) {
         mxf_write_mpegvideo_desc(s, st);
     } else {
         int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key);
@@ -2514,7 +2509,7 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
     const UID *codec_ul = NULL;
     uint32_t state = -1;
     int extra_size = 512; // support AVC Intra files without SPS/PPS header
-    int i, frame_size, slice_type, has_sps = 0, intra_only = 0, ret;
+    int i, frame_size, slice_type, has_sps = 0, intra_only = 0, avc_intra = 0, ret;
 
     for (;;) {
         buf = avpriv_find_start_code(buf, buf_end, &state);
@@ -2588,7 +2583,7 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
             codec_ul = &mxf_h264_codec_uls[i].uid;
             sc->aspect_ratio = (AVRational){ 16, 9 }; // 16:9 is mandatory for broadcast HD
             st->codecpar->profile = mxf_h264_codec_uls[i].profile;
-            sc->avc_intra = 1;
+            avc_intra = 1;
             mxf->cbr_index = 1;
             sc->frame_size = pkt->size;
             if (sc->interlaced)
@@ -2609,8 +2604,13 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
         av_log(s, AV_LOG_ERROR, "h264 profile not supported\n");
         return 0;
     }
+
     sc->codec_ul = codec_ul;
 
+    if (!avc_intra) {
+        sc->sub_descriptor = AVCSubDescriptor;
+    }
+
     return 1;
 }
 
@@ -2669,6 +2669,7 @@ static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt)
         av_assert0(v < 2);
     }
     sc->codec_ul = &mxf_ffv1_codec_uls[v];
+    sc->sub_descriptor = FFV1SubDescriptor;
 
     if (st->codecpar->field_order > AV_FIELD_PROGRESSIVE) {
         sc->interlaced = 1;
@@ -2726,6 +2727,7 @@ static int mxf_parse_jpeg2000_frame(AVFormatContext *s, AVStream *st, AVPacket *
     bytestream2_get_bufferu(&g, sc->j2k_info.j2k_comp_desc, 3 * j2k_ncomponents);
 
     sc->frame_size = pkt->size;
+    sc->sub_descriptor = JPEG2000SubDescriptor;
 
     return 1;
 }
-- 
2.49.1


From 0332f44c438d858760b510b2faaadcbe09c22d6d Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Wed, 4 Jun 2025 11:35:31 -0700
Subject: [PATCH 5/7] lavf/mxfenc: fix return value to int64_t

---
 libavformat/mxfenc.c | 5 ++---
 1 file changed, 2 insertions(+), 3 deletions(-)

diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index b935361599..2b37675938 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -1613,11 +1613,10 @@ static int mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st)
 
 static int mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st)
 {
-    int is_rgb, pos;
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codecpar->format);
-    is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
+    int is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
 
-    pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key);
+    int64_t pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key);
     mxf_update_klv_size(s->pb, pos);
     return mxf_write_ffv1_subdesc(s, st);
 }
-- 
2.49.1


From 09cc306bf4de0a90febd800574a0377d489e3ccc Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Wed, 4 Jun 2025 14:09:50 -0700
Subject: [PATCH 6/7] lavf/mxfenc: factorize cdci descriptor functions

---
 libavformat/mxfenc.c | 110 +++++++++++++++++++------------------------
 1 file changed, 48 insertions(+), 62 deletions(-)

diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index 2b37675938..8cd92b980c 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -120,6 +120,7 @@ typedef struct MXFStreamContext {
     int micro_version;       ///< format micro_version, used in ffv1 descriptor
     j2k_info_t j2k_info;
     enum MXFMetadataSetType sub_descriptor;
+    const UID *picture_descriptor_key;
 } MXFStreamContext;
 
 typedef struct MXFContainerEssenceEntry {
@@ -169,20 +170,22 @@ static const struct {
 
 static int mxf_write_wav_desc(AVFormatContext *s, AVStream *st);
 static int mxf_write_aes3_desc(AVFormatContext *s, AVStream *st);
-static int mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st);
-static int mxf_write_h264_desc(AVFormatContext *s, AVStream *st);
-static int mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st);
 static int mxf_write_cdci_desc(AVFormatContext *s, AVStream *st);
 static int mxf_write_generic_sound_desc(AVFormatContext *s, AVStream *st);
 static int mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st);
 
+static void mxf_write_avc_subdesc(AVFormatContext *s, AVStream *st);
+static void mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st);
+
+static int mxf_write_mpegvideo_local_tags(AVFormatContext *s, AVStream *st);
+
 static enum AVChromaLocation choose_chroma_location(AVFormatContext *s, AVStream *st);
 
 static const MXFContainerEssenceEntry mxf_essence_container_uls[] = {
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x02,0x0D,0x01,0x03,0x01,0x02,0x04,0x60,0x01 },
       { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x03,0x04,0x01,0x02,0x02,0x01,0x00,0x00,0x00 },
-      mxf_write_mpegvideo_desc },
+      mxf_write_cdci_desc },
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x0D,0x01,0x03,0x01,0x02,0x06,0x03,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x16,0x01,0x03,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x01,0x04,0x02,0x02,0x01,0x00,0x00,0x00,0x00 },
@@ -220,7 +223,7 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = {
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x0D,0x01,0x03,0x01,0x02,0x10,0x60,0x01 },
       { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x15,0x01,0x05,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x04,0x01,0x02,0x02,0x01,0x00,0x00,0x00 },
-      mxf_write_h264_desc },
+      mxf_write_cdci_desc },
     // S436M ANC
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0a,0x0D,0x01,0x03,0x01,0x02,0x0e,0x00,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0D,0x01,0x03,0x01,0x17,0x01,0x02,0x00 },
@@ -235,7 +238,7 @@ static const MXFContainerEssenceEntry mxf_essence_container_uls[] = {
     { { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x0d,0x01,0x03,0x01,0x02,0x23,0x01,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x01,0x02,0x01,0x01,0x0d,0x01,0x03,0x01,0x15,0x01,0x1d,0x00 },
       { 0x06,0x0E,0x2B,0x34,0x04,0x01,0x01,0x0d,0x04,0x01,0x02,0x02,0x03,0x09,0x00,0x00 },
-      mxf_write_ffv1_desc },
+      mxf_write_cdci_desc },
     { { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
       { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
       { 0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00 },
@@ -1204,7 +1207,7 @@ static inline uint32_t rescale_mastering_luma(AVRational q)
     return av_rescale(q.num, FF_MXF_MASTERING_LUMA_DEN, q.den);
 }
 
-static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID key)
+static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st)
 {
     MXFStreamContext *sc = st->priv_data;
     AVIOContext *pb = s->pb;
@@ -1217,7 +1220,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
     const MXFCodecUL *color_primaries_ul;
     const MXFCodecUL *color_trc_ul;
     const MXFCodecUL *color_space_ul;
-    int64_t pos = mxf_write_generic_desc(s, st, key);
+    int64_t pos = mxf_write_generic_desc(s, st, *sc->picture_descriptor_key);
     const AVPacketSideData *side_data;
 
     color_primaries_ul = mxf_get_codec_ul_by_id(ff_mxf_color_primaries_uls, st->codecpar->color_primaries);
@@ -1310,7 +1313,7 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
         avio_wb32(pb, -((st->codecpar->height - display_height)&1));
     }
 
-    if (key != mxf_rgba_descriptor_key) {
+    if (sc->picture_descriptor_key != &mxf_rgba_descriptor_key) {
         int component_depth     = pix_desc->comp[0].depth;
         int h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w;
         int v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h;
@@ -1460,6 +1463,9 @@ static int64_t mxf_write_cdci_common(AVFormatContext *s, AVStream *st, const UID
         mxf_write_uuid(pb, sc->sub_descriptor, 0);
     }
 
+    if (sc->picture_descriptor_key == &mxf_mpegvideo_descriptor_key)
+        mxf_write_mpegvideo_local_tags(s, st);
+
     return pos;
 }
 
@@ -1557,31 +1563,19 @@ static int mxf_write_jpeg2000_subdesc(AVFormatContext *s, AVStream *st)
 static int mxf_write_cdci_desc(AVFormatContext *s, AVStream *st)
 {
     MXFStreamContext *sc = st->priv_data;
-    int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key);
+    int64_t pos = mxf_write_generic_picture_desc(s, st);
     mxf_update_klv_size(s->pb, pos);
 
     switch (sc->sub_descriptor) {
     case AVCSubDescriptor: mxf_write_avc_subdesc(s, st); break;
     case JPEG2000SubDescriptor: mxf_write_jpeg2000_subdesc(s, st); break;
+    case FFV1SubDescriptor: mxf_write_ffv1_subdesc(s, st); break;
     }
 
     return 0;
 }
 
-static int mxf_write_h264_desc(AVFormatContext *s, AVStream *st)
-{
-    MXFStreamContext *sc = st->priv_data;
-    if (!sc->sub_descriptor) {
-        mxf_write_mpegvideo_desc(s, st);
-    } else {
-        int64_t pos = mxf_write_cdci_common(s, st, mxf_cdci_descriptor_key);
-        mxf_update_klv_size(s->pb, pos);
-        mxf_write_avc_subdesc(s, st);
-    }
-    return 0;
-}
-
-static int mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st)
+static void mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st)
 {
     AVIOContext *pb = s->pb;
     MXFStreamContext *sc = st->priv_data;
@@ -1608,17 +1602,6 @@ static int mxf_write_ffv1_subdesc(AVFormatContext *s, AVStream *st)
     }
 
     mxf_update_klv_size(s->pb, pos);
-    return 0;
-}
-
-static int mxf_write_ffv1_desc(AVFormatContext *s, AVStream *st)
-{
-    const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(st->codecpar->format);
-    int is_rgb = desc->flags & AV_PIX_FMT_FLAG_RGB;
-
-    int64_t pos = mxf_write_cdci_common(s, st, is_rgb ? mxf_rgba_descriptor_key : mxf_cdci_descriptor_key);
-    mxf_update_klv_size(s->pb, pos);
-    return mxf_write_ffv1_subdesc(s, st);
 }
 
 static int mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st)
@@ -1628,42 +1611,38 @@ static int mxf_write_s436m_anc_desc(AVFormatContext *s, AVStream *st)
     return 0;
 }
 
-static int mxf_write_mpegvideo_desc(AVFormatContext *s, AVStream *st)
+static int mxf_write_mpegvideo_local_tags(AVFormatContext *s, AVStream *st)
 {
     AVIOContext *pb = s->pb;
     MXFStreamContext *sc = st->priv_data;
     int profile_and_level = (st->codecpar->profile<<4) | st->codecpar->level;
-    int64_t pos = mxf_write_cdci_common(s, st, mxf_mpegvideo_descriptor_key);
 
-    if (st->codecpar->codec_id != AV_CODEC_ID_H264) {
-        // bit rate
-        mxf_write_local_tag(s, 4, 0x8000);
-        avio_wb32(pb, sc->video_bit_rate);
+    // bit rate
+    mxf_write_local_tag(s, 4, 0x8000);
+    avio_wb32(pb, sc->video_bit_rate);
 
-        // profile and level
-        mxf_write_local_tag(s, 1, 0x8007);
-        if (!st->codecpar->profile)
-            profile_and_level |= 0x80; // escape bit
-        avio_w8(pb, profile_and_level);
+    // profile and level
+    mxf_write_local_tag(s, 1, 0x8007);
+    if (!st->codecpar->profile)
+        profile_and_level |= 0x80; // escape bit
+    avio_w8(pb, profile_and_level);
 
-        // low delay
-        mxf_write_local_tag(s, 1, 0x8003);
-        avio_w8(pb, sc->low_delay);
+    // low delay
+    mxf_write_local_tag(s, 1, 0x8003);
+    avio_w8(pb, sc->low_delay);
 
-        // closed gop
-        mxf_write_local_tag(s, 1, 0x8004);
-        avio_w8(pb, sc->seq_closed_gop);
+    // closed gop
+    mxf_write_local_tag(s, 1, 0x8004);
+    avio_w8(pb, sc->seq_closed_gop);
 
-        // max gop
-        mxf_write_local_tag(s, 2, 0x8006);
-        avio_wb16(pb, sc->max_gop);
+    // max gop
+    mxf_write_local_tag(s, 2, 0x8006);
+    avio_wb16(pb, sc->max_gop);
 
-        // b picture count
-        mxf_write_local_tag(s, 2, 0x8008);
-        avio_wb16(pb, sc->b_picture_count);
-    }
+    // b picture count
+    mxf_write_local_tag(s, 2, 0x8008);
+    avio_wb16(pb, sc->b_picture_count);
 
-    mxf_update_klv_size(pb, pos);
     return 0;
 }
 
@@ -2606,9 +2585,10 @@ static int mxf_parse_h264_frame(AVFormatContext *s, AVStream *st,
 
     sc->codec_ul = codec_ul;
 
-    if (!avc_intra) {
+    if (avc_intra)
+        sc->picture_descriptor_key = &mxf_mpegvideo_descriptor_key;
+     else
         sc->sub_descriptor = AVCSubDescriptor;
-    }
 
     return 1;
 }
@@ -2639,6 +2619,7 @@ static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt)
 {
     MXFContext *mxf = s->priv_data;
     MXFStreamContext *sc = st->priv_data;
+    const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(st->codecpar->format);
     uint8_t state[FFV1_CONTEXT_SIZE];
     RangeCoder c;
     unsigned v;
@@ -2679,6 +2660,9 @@ static int mxf_parse_ffv1_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt)
     av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den,
               sc->aspect_ratio.num, sc->aspect_ratio.den, INT_MAX);
 
+    if (pix_desc->flags & AV_PIX_FMT_FLAG_RGB)
+        sc->picture_descriptor_key = &mxf_rgba_descriptor_key;
+
     return 1;
 }
 
@@ -2828,6 +2812,7 @@ static int mxf_parse_mpeg2_frame(AVFormatContext *s, AVStream *st,
         if (!codec_ul)
             return 0;
         sc->codec_ul = codec_ul;
+        sc->picture_descriptor_key = &mxf_mpegvideo_descriptor_key;
     }
     return 1;
 }
@@ -2968,6 +2953,7 @@ static int mxf_init(AVFormatContext *s)
                 sc->seq_closed_gop = -1; // unknown yet
             }
 
+            sc->picture_descriptor_key = &mxf_cdci_descriptor_key;
             sc->video_bit_rate = st->codecpar->bit_rate;
 
             if (IS_D10(s) ||
-- 
2.49.1


From 2401bddbf00829a51a7964fc856cf52f7b70f1a8 Mon Sep 17 00:00:00 2001
From: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date: Wed, 4 Jun 2025 00:01:44 -0700
Subject: [PATCH 7/7] lavf/mxfenc: fix muxing for DNxHD/DNxHR 444

---
 libavformat/mxfenc.c | 70 ++++++++++++++++++++++++++++++++++----------
 1 file changed, 55 insertions(+), 15 deletions(-)

diff --git a/libavformat/mxfenc.c b/libavformat/mxfenc.c
index 8cd92b980c..12ef521779 100644
--- a/libavformat/mxfenc.c
+++ b/libavformat/mxfenc.c
@@ -121,6 +121,7 @@ typedef struct MXFStreamContext {
     j2k_info_t j2k_info;
     enum MXFMetadataSetType sub_descriptor;
     const UID *picture_descriptor_key;
+    int cid;                 ///< compression id, used by dnxhd
 } MXFStreamContext;
 
 typedef struct MXFContainerEssenceEntry {
@@ -289,6 +290,8 @@ static const uint8_t header_closed_partition_key[] = { 0x06,0x0E,0x2B,0x34,0x02,
 static const uint8_t klv_fill_key[]                = { 0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x03,0x01,0x02,0x10,0x01,0x00,0x00,0x00 };
 static const uint8_t body_partition_key[]          = { 0x06,0x0E,0x2B,0x34,0x02,0x05,0x01,0x01,0x0D,0x01,0x02,0x01,0x01,0x03,0x04,0x00 }; // ClosedComplete
 
+static const UID mxf_rgba_descriptor_key      = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x29,0x00 };
+
 /**
  * partial key for header metadata
  */
@@ -390,6 +393,10 @@ static const MXFLocalTagPair mxf_local_tag_batch[] = {
     { 0x3304, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x03,0x03,0x00,0x00,0x00}}, /* Black Ref level */
     { 0x3305, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x01,0x04,0x01,0x05,0x03,0x04,0x00,0x00,0x00}}, /* White Ref level */
     { 0x3306, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x02,0x04,0x01,0x05,0x03,0x05,0x00,0x00,0x00}}, /* Color Range */
+    // RGBA Picture Essence Descriptor
+    { 0x3401, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x05,0x03,0x06,0x00,0x00,0x00}}, /* Pixel Layout */
+    { 0x3406, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x05,0x03,0x0B,0x00,0x00,0x00}}, /* Component Max Ref */
+    { 0x3407, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x01,0x05,0x03,0x0C,0x00,0x00,0x00}}, /* Component Min Ref */
     // Generic Sound Essence Descriptor
     { 0x3D02, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x04,0x04,0x02,0x03,0x01,0x04,0x00,0x00,0x00}}, /* Locked/Unlocked */
     { 0x3D03, {0x06,0x0E,0x2B,0x34,0x01,0x01,0x01,0x05,0x04,0x02,0x03,0x01,0x01,0x01,0x00,0x00}}, /* Audio sampling rate */
@@ -585,27 +592,31 @@ static void mxf_write_primer_pack(AVFormatContext *s)
     AVIOContext *pb = s->pb;
     int local_tag_number = MXF_NUM_TAGS, i;
     int will_have_avc_tags = 0, will_have_mastering_tags = 0, will_have_ffv1_tags = 0, will_have_jpeg2000_tags = 0;
-    int will_have_sub_descriptor = 0;
+    int will_have_sub_descriptor = 0, will_have_rgba_descriptor = 0;
 
     for (i = 0; i < s->nb_streams; i++) {
-        MXFStreamContext *sc = s->streams[i]->priv_data;
-        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_H264 && sc->sub_descriptor) {
+        AVStream *st = s->streams[i];
+        MXFStreamContext *sc = st->priv_data;
+        if (st->codecpar->codec_id == AV_CODEC_ID_H264 && sc->sub_descriptor) {
             will_have_avc_tags = 1;
         }
-        if (av_packet_side_data_get(s->streams[i]->codecpar->coded_side_data,
-                                    s->streams[i]->codecpar->nb_coded_side_data,
+        if (av_packet_side_data_get(st->codecpar->coded_side_data,
+                                    st->codecpar->nb_coded_side_data,
                                     AV_PKT_DATA_MASTERING_DISPLAY_METADATA)) {
             will_have_mastering_tags = 1;
         }
-        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_FFV1) {
+        if (st->codecpar->codec_id == AV_CODEC_ID_FFV1) {
             will_have_ffv1_tags = 1;
         }
-        if (s->streams[i]->codecpar->codec_id == AV_CODEC_ID_JPEG2000){
+        if (st->codecpar->codec_id == AV_CODEC_ID_JPEG2000) {
             will_have_jpeg2000_tags = 1;
         }
         if (sc->sub_descriptor) {
             will_have_sub_descriptor = 1;
         }
+        if (sc->picture_descriptor_key == &mxf_rgba_descriptor_key) {
+            will_have_rgba_descriptor = 1;
+        }
     }
 
     if (!mxf->store_user_comments) {
@@ -618,6 +629,12 @@ static void mxf_write_primer_pack(AVFormatContext *s)
         mxf_mark_tag_unused(mxf, 0x8100);
     }
 
+    if (!will_have_rgba_descriptor) {
+        mxf_mark_tag_unused(mxf, 0x3401);
+        mxf_mark_tag_unused(mxf, 0x3406);
+        mxf_mark_tag_unused(mxf, 0x3407);
+    }
+
     if (!will_have_avc_tags) {
         mxf_mark_tag_unused(mxf, 0x8200);
         mxf_mark_tag_unused(mxf, 0x8201);
@@ -1190,7 +1207,6 @@ static const UID mxf_mpegvideo_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,
 static const UID mxf_wav_descriptor_key       = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x48,0x00 };
 static const UID mxf_aes3_descriptor_key      = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x47,0x00 };
 static const UID mxf_cdci_descriptor_key      = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x28,0x00 };
-static const UID mxf_rgba_descriptor_key      = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x29,0x00 };
 static const UID mxf_generic_sound_descriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0D,0x01,0x01,0x01,0x01,0x01,0x42,0x00 };
 
 static const UID mxf_avc_subdescriptor_key = { 0x06,0x0E,0x2B,0x34,0x02,0x53,0x01,0x01,0x0d,0x01,0x01,0x01,0x01,0x01,0x6E,0x00 };
@@ -1222,6 +1238,7 @@ static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st)
     const MXFCodecUL *color_space_ul;
     int64_t pos = mxf_write_generic_desc(s, st, *sc->picture_descriptor_key);
     const AVPacketSideData *side_data;
+    int component_depth = pix_desc->comp[0].depth;
 
     color_primaries_ul = mxf_get_codec_ul_by_id(ff_mxf_color_primaries_uls, st->codecpar->color_primaries);
     color_trc_ul       = mxf_get_codec_ul_by_id(ff_mxf_color_trc_uls, st->codecpar->color_trc);
@@ -1236,6 +1253,10 @@ static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st)
     display_width = stored_width;
 
     switch (st->codecpar->codec_id) {
+    case AV_CODEC_ID_DNXHD:
+        if (sc->cid < 1270) // DNxHD
+            break;
+        // fall for DNxHR RI rasters
     case AV_CODEC_ID_MPEG2VIDEO:
     case AV_CODEC_ID_H264:
         //Based on 16x16 macroblocks
@@ -1313,8 +1334,25 @@ static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st)
         avio_wb32(pb, -((st->codecpar->height - display_height)&1));
     }
 
-    if (sc->picture_descriptor_key != &mxf_rgba_descriptor_key) {
-        int component_depth     = pix_desc->comp[0].depth;
+    if (sc->picture_descriptor_key == &mxf_rgba_descriptor_key) {
+        uint8_t rgb_layout[16] = {
+            'R', component_depth,
+            'G', component_depth,
+            'B', component_depth,
+        };
+
+        // pixel layout
+        mxf_write_local_tag(s, 16, 0x3401);
+        avio_write(pb, rgb_layout, 16);
+
+        // component max ref
+        mxf_write_local_tag(s, 4, 0x3406);
+        avio_wb32(pb, (1<<component_depth) - 1);
+
+        // component min ref
+        mxf_write_local_tag(s, 4, 0x3407);
+        avio_wb32(pb, 0);
+    } else {
         int h_chroma_sub_sample = 1 << pix_desc->log2_chroma_w;
         int v_chroma_sub_sample = 1 << pix_desc->log2_chroma_h;
         int color_siting;
@@ -1393,7 +1431,6 @@ static int64_t mxf_write_generic_picture_desc(AVFormatContext *s, AVStream *st)
         f1 *= 2;
     }
 
-
     mxf_write_local_tag(s, 16, 0x320D);
     avio_wb32(pb, 2);
     avio_wb32(pb, 4);
@@ -2323,7 +2360,7 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt
 {
     MXFContext *mxf = s->priv_data;
     MXFStreamContext *sc = st->priv_data;
-    int i, cid;
+    int i;
 
     if (mxf->header_written)
         return 1;
@@ -2331,9 +2368,9 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt
     if (pkt->size < 43)
         return 0;
 
-    cid = AV_RB32(pkt->data + 0x28);
+    sc->cid = AV_RB32(pkt->data + 0x28);
     for (i = 0; i < FF_ARRAY_ELEMS(mxf_dnxhd_codec_uls); i++) {
-        if (cid == mxf_dnxhd_codec_uls[i].cid) {
+        if (sc->cid == mxf_dnxhd_codec_uls[i].cid) {
             sc->codec_ul = &mxf_dnxhd_codec_uls[i].codec_ul;
             sc->interlaced = mxf_dnxhd_codec_uls[i].interlaced;
             break;
@@ -2342,7 +2379,7 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt
     if (i == FF_ARRAY_ELEMS(mxf_dnxhd_codec_uls))
         return 0;
 
-    if (cid >= 1270) { // RI raster
+    if (sc->cid >= 1270) { // RI raster
         av_reduce(&sc->aspect_ratio.num, &sc->aspect_ratio.den,
                   st->codecpar->width, st->codecpar->height,
                   INT_MAX);
@@ -2352,6 +2389,9 @@ static int mxf_parse_dnxhd_frame(AVFormatContext *s, AVStream *st, AVPacket *pkt
 
     sc->frame_size = pkt->size;
 
+    if (sc->cid == 1270 || sc->cid == 1256)
+        sc->picture_descriptor_key = &mxf_rgba_descriptor_key;
+
     return 1;
 }
 
-- 
2.49.1

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2025-10-16  0:22 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2025-10-16  0:21 [FFmpeg-devel] [PATCH] Fix jpeg2k and dnxhr 444 MXF and MOV muxing (PR #20711) Baptiste Coudurier via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git