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]
