PR #23849 opened by Zhao Zhili (quink)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23849
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23849.patch

# Summary of changes

The CLF bit marks an RGB stream per SMPTE ST 2019-1, but the header-level
pix_fmt default treated it as YCbCr, so RGB DNxHR 4:4:4 streams were
decoded into a YUV444 buffer.

Default to GBRP when CLF is set. Set picture->format to the post-decode
pix_fmt; without it the frame kept the header-level default even when
the frame-end pass corrected ctx->pix_fmt, leaving callers with a wrong
pixel format.

Fix #23800

<!--
If this PR requires new FATE test samples, attach them to the PR and
list their target paths below (relative to the fate-suite root).

Attached filenames must match the sample's filename:

```fate-samples
# e.g. vorbis/new-sample.ogg
```
-->



>From d30f95608dffbd2db491637ee8cf9eff65850965 Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Fri, 17 Jul 2026 21:56:58 +0800
Subject: [PATCH 1/2] avcodec/dnxhddec: rename luma/chroma plane vars to p0/p12

When the decoder outputs GBRP, dest_y/u/v hold G/B/R plane pointers.
The YUV-specific names are misleading.

No functional changes.
---
 libavcodec/dnxhddec.c | 72 +++++++++++++++++++++----------------------
 1 file changed, 36 insertions(+), 36 deletions(-)

diff --git a/libavcodec/dnxhddec.c b/libavcodec/dnxhddec.c
index 7ec61f9ebd..6a830d0707 100644
--- a/libavcodec/dnxhddec.c
+++ b/libavcodec/dnxhddec.c
@@ -492,9 +492,9 @@ static int dnxhd_decode_macroblock(const DNXHDContext *ctx, 
RowContext *row,
                                    AVFrame *frame, int x, int y)
 {
     int shift1 = ctx->bit_depth >= 10;
-    int dct_linesize_luma   = frame->linesize[0];
-    int dct_linesize_chroma = frame->linesize[1];
-    uint8_t *dest_y, *dest_u, *dest_v;
+    int linesize_p0 = frame->linesize[0];
+    int linesize_p12 = frame->linesize[1];
+    uint8_t *dest_p0, *dest_p1, *dest_p2;
     int dct_y_offset, dct_x_offset;
     int qscale, i, act;
     int interlaced_mb = 0;
@@ -535,55 +535,55 @@ static int dnxhd_decode_macroblock(const DNXHDContext 
*ctx, RowContext *row,
     }
 
     if (frame->flags & AV_FRAME_FLAG_INTERLACED) {
-        dct_linesize_luma   <<= 1;
-        dct_linesize_chroma <<= 1;
+        linesize_p0 <<= 1;
+        linesize_p12 <<= 1;
     }
 
-    dest_y = frame->data[0] + ((y * dct_linesize_luma)   << 4) + (x << (4 + 
shift1));
-    dest_u = frame->data[1] + ((y * dct_linesize_chroma) << 4) + (x << (3 + 
shift1 + ctx->is_444));
-    dest_v = frame->data[2] + ((y * dct_linesize_chroma) << 4) + (x << (3 + 
shift1 + ctx->is_444));
+    dest_p0 = frame->data[0] + ((y * linesize_p0)  << 4) + (x << (4 + shift1));
+    dest_p1 = frame->data[1] + ((y * linesize_p12) << 4) + (x << (3 + shift1 + 
ctx->is_444));
+    dest_p2 = frame->data[2] + ((y * linesize_p12) << 4) + (x << (3 + shift1 + 
ctx->is_444));
 
     if ((frame->flags & AV_FRAME_FLAG_INTERLACED) && ctx->cur_field) {
-        dest_y += frame->linesize[0];
-        dest_u += frame->linesize[1];
-        dest_v += frame->linesize[2];
+        dest_p0 += frame->linesize[0];
+        dest_p1 += frame->linesize[1];
+        dest_p2 += frame->linesize[2];
     }
     if (interlaced_mb) {
-        dct_linesize_luma   <<= 1;
-        dct_linesize_chroma <<= 1;
+        linesize_p0 <<= 1;
+        linesize_p12 <<= 1;
     }
 
-    dct_y_offset = interlaced_mb ? frame->linesize[0] : (dct_linesize_luma << 
3);
+    dct_y_offset = interlaced_mb ? frame->linesize[0] : (linesize_p0 << 3);
     dct_x_offset = 8 << shift1;
     if (!ctx->is_444) {
-        ctx->idsp.idct_put(dest_y,                               
dct_linesize_luma, row->blocks[0]);
-        ctx->idsp.idct_put(dest_y + dct_x_offset,                
dct_linesize_luma, row->blocks[1]);
-        ctx->idsp.idct_put(dest_y + dct_y_offset,                
dct_linesize_luma, row->blocks[4]);
-        ctx->idsp.idct_put(dest_y + dct_y_offset + dct_x_offset, 
dct_linesize_luma, row->blocks[5]);
+        ctx->idsp.idct_put(dest_p0,                               linesize_p0, 
row->blocks[0]);
+        ctx->idsp.idct_put(dest_p0 + dct_x_offset,                linesize_p0, 
row->blocks[1]);
+        ctx->idsp.idct_put(dest_p0 + dct_y_offset,                linesize_p0, 
row->blocks[4]);
+        ctx->idsp.idct_put(dest_p0 + dct_y_offset + dct_x_offset, linesize_p0, 
row->blocks[5]);
 
         if (!(ctx->avctx->flags & AV_CODEC_FLAG_GRAY)) {
-            dct_y_offset = interlaced_mb ? frame->linesize[1] : 
(dct_linesize_chroma << 3);
-            ctx->idsp.idct_put(dest_u,                dct_linesize_chroma, 
row->blocks[2]);
-            ctx->idsp.idct_put(dest_v,                dct_linesize_chroma, 
row->blocks[3]);
-            ctx->idsp.idct_put(dest_u + dct_y_offset, dct_linesize_chroma, 
row->blocks[6]);
-            ctx->idsp.idct_put(dest_v + dct_y_offset, dct_linesize_chroma, 
row->blocks[7]);
+            dct_y_offset = interlaced_mb ? frame->linesize[1] : (linesize_p12 
<< 3);
+            ctx->idsp.idct_put(dest_p1,                linesize_p12, 
row->blocks[2]);
+            ctx->idsp.idct_put(dest_p2,                linesize_p12, 
row->blocks[3]);
+            ctx->idsp.idct_put(dest_p1 + dct_y_offset, linesize_p12, 
row->blocks[6]);
+            ctx->idsp.idct_put(dest_p2 + dct_y_offset, linesize_p12, 
row->blocks[7]);
         }
     } else {
-        ctx->idsp.idct_put(dest_y,                               
dct_linesize_luma, row->blocks[0]);
-        ctx->idsp.idct_put(dest_y + dct_x_offset,                
dct_linesize_luma, row->blocks[1]);
-        ctx->idsp.idct_put(dest_y + dct_y_offset,                
dct_linesize_luma, row->blocks[6]);
-        ctx->idsp.idct_put(dest_y + dct_y_offset + dct_x_offset, 
dct_linesize_luma, row->blocks[7]);
+        ctx->idsp.idct_put(dest_p0,                               linesize_p0, 
row->blocks[0]);
+        ctx->idsp.idct_put(dest_p0 + dct_x_offset,                linesize_p0, 
row->blocks[1]);
+        ctx->idsp.idct_put(dest_p0 + dct_y_offset,                linesize_p0, 
row->blocks[6]);
+        ctx->idsp.idct_put(dest_p0 + dct_y_offset + dct_x_offset, linesize_p0, 
row->blocks[7]);
 
         if (!(ctx->avctx->flags & AV_CODEC_FLAG_GRAY)) {
-            dct_y_offset = interlaced_mb ? frame->linesize[1] : 
(dct_linesize_chroma << 3);
-            ctx->idsp.idct_put(dest_u,                               
dct_linesize_chroma, row->blocks[2]);
-            ctx->idsp.idct_put(dest_u + dct_x_offset,                
dct_linesize_chroma, row->blocks[3]);
-            ctx->idsp.idct_put(dest_u + dct_y_offset,                
dct_linesize_chroma, row->blocks[8]);
-            ctx->idsp.idct_put(dest_u + dct_y_offset + dct_x_offset, 
dct_linesize_chroma, row->blocks[9]);
-            ctx->idsp.idct_put(dest_v,                               
dct_linesize_chroma, row->blocks[4]);
-            ctx->idsp.idct_put(dest_v + dct_x_offset,                
dct_linesize_chroma, row->blocks[5]);
-            ctx->idsp.idct_put(dest_v + dct_y_offset,                
dct_linesize_chroma, row->blocks[10]);
-            ctx->idsp.idct_put(dest_v + dct_y_offset + dct_x_offset, 
dct_linesize_chroma, row->blocks[11]);
+            dct_y_offset = interlaced_mb ? frame->linesize[1] : (linesize_p12 
<< 3);
+            ctx->idsp.idct_put(dest_p1,                               
linesize_p12, row->blocks[2]);
+            ctx->idsp.idct_put(dest_p1 + dct_x_offset,                
linesize_p12, row->blocks[3]);
+            ctx->idsp.idct_put(dest_p1 + dct_y_offset,                
linesize_p12, row->blocks[8]);
+            ctx->idsp.idct_put(dest_p1 + dct_y_offset + dct_x_offset, 
linesize_p12, row->blocks[9]);
+            ctx->idsp.idct_put(dest_p2,                               
linesize_p12, row->blocks[4]);
+            ctx->idsp.idct_put(dest_p2 + dct_x_offset,                
linesize_p12, row->blocks[5]);
+            ctx->idsp.idct_put(dest_p2 + dct_y_offset,                
linesize_p12, row->blocks[10]);
+            ctx->idsp.idct_put(dest_p2 + dct_y_offset + dct_x_offset, 
linesize_p12, row->blocks[11]);
         }
     }
 
-- 
2.52.0


>From 9afb8b0b7c024ab83d9fb02ae1a4f778f75890bc Mon Sep 17 00:00:00 2001
From: Zhao Zhili <[email protected]>
Date: Fri, 17 Jul 2026 22:02:21 +0800
Subject: [PATCH 2/2] avcodec/dnxhddec: fix pixel format for RGB streams

The CLF bit marks an RGB stream per SMPTE ST 2019-1, but the header-level
pix_fmt default treated it as YCbCr, so RGB DNxHR 4:4:4 streams were
decoded into a YUV444 buffer.

Default to GBRP when CLF is set. Set picture->format to the post-decode
pix_fmt; without it the frame kept the header-level default even when
the frame-end pass corrected ctx->pix_fmt, leaving callers with a wrong
pixel format.

Fix #23800
---
 libavcodec/dnxhddec.c | 34 ++++++++++++++++++++++++++--------
 1 file changed, 26 insertions(+), 8 deletions(-)

diff --git a/libavcodec/dnxhddec.c b/libavcodec/dnxhddec.c
index 6a830d0707..441146487e 100644
--- a/libavcodec/dnxhddec.c
+++ b/libavcodec/dnxhddec.c
@@ -253,12 +253,16 @@ static int dnxhd_decode_header(DNXHDContext *ctx, AVFrame 
*frame,
             return AVERROR_INVALIDDATA;
         } else if (bitdepth == 10) {
             ctx->decode_dct_block = dnxhd_decode_dct_block_10_444;
-            ctx->pix_fmt = ctx->act ? AV_PIX_FMT_YUV444P10
-                                    : AV_PIX_FMT_GBRP10;
+            /* CLF signals an RGB stream per SMPTE ST 2019-1, so default to
+             * GBRP. When every macroblock is YCBCR-encoded (ACF=1) the
+             * frame-end pass below overrides this to YUV444.
+             */
+            ctx->pix_fmt = ctx->act ? AV_PIX_FMT_GBRP10
+                                    : AV_PIX_FMT_YUV444P10;
         } else {
             ctx->decode_dct_block = dnxhd_decode_dct_block_12_444;
-            ctx->pix_fmt = ctx->act ? AV_PIX_FMT_YUV444P12
-                                    : AV_PIX_FMT_GBRP12;
+            ctx->pix_fmt = ctx->act ? AV_PIX_FMT_GBRP12
+                                    : AV_PIX_FMT_YUV444P12;
         }
     } else if (bitdepth == 12) {
         ctx->decode_dct_block = dnxhd_decode_dct_block_12;
@@ -569,6 +573,11 @@ static int dnxhd_decode_macroblock(const DNXHDContext 
*ctx, RowContext *row,
             ctx->idsp.idct_put(dest_p2 + dct_y_offset, linesize_p12, 
row->blocks[7]);
         }
     } else {
+        /* RGB coding is R/G/B while GBRP plane order is G/B/R. */
+        if (!act) {
+            FFSWAP(uint8_t *, dest_p0, dest_p2);
+            FFSWAP(uint8_t *, dest_p1, dest_p2);
+        }
         ctx->idsp.idct_put(dest_p0,                               linesize_p0, 
row->blocks[0]);
         ctx->idsp.idct_put(dest_p0 + dct_x_offset,                linesize_p0, 
row->blocks[1]);
         ctx->idsp.idct_put(dest_p0 + dct_y_offset,                linesize_p0, 
row->blocks[6]);
@@ -686,6 +695,13 @@ decode_coding_unit:
         }
         switch (format) {
         case -1:
+            /* row->format stays -1 when no macroblock had ACF=1, i.e. the
+             * entire row is RGB-coded; treat the same as case 0. */
+            av_fallthrough;
+        case 0:
+            ctx->pix_fmt = ctx->bit_depth==10
+                         ? AV_PIX_FMT_GBRP10 : AV_PIX_FMT_GBRP12;
+            break;
         case 2:
             if (!act_warned) {
                 act_warned = 1;
@@ -693,17 +709,19 @@ decode_coding_unit:
                        "Unsupported: variable ACT flag.\n");
             }
             break;
-        case 0:
-            ctx->pix_fmt = ctx->bit_depth==10
-                         ? AV_PIX_FMT_GBRP10 : AV_PIX_FMT_GBRP12;
-            break;
         case 1:
             ctx->pix_fmt = ctx->bit_depth==10
                          ? AV_PIX_FMT_YUV444P10 : AV_PIX_FMT_YUV444P12;
             break;
         }
     }
+    /* The frame-end ACF pass above may have flipped ctx->pix_fmt between
+     * GBRP10/12 and YUV444P10/12 after ff_thread_get_buffer() already
+     * allocated the buffer using the header-level default. This is safe
+     * only because these format pairs share identical plane layouts.
+     */
     avctx->pix_fmt = ctx->pix_fmt;
+    picture->format = ctx->pix_fmt;
     if (ret) {
         av_log(ctx->avctx, AV_LOG_ERROR, "%d lines with errors\n", ret);
         return AVERROR_INVALIDDATA;
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to