PR #24408 opened by reardonia URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24408 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/24408.patch
# Summary of changes Two patches. 1/2 adds a BT.2020 non-constant luminance YUV->RGB macro to libavutil/colorspace.h, which carried only BT.601 and BT.709. It is unused on its own. 2/2 makes pgssubdec honor avctx->colorspace when the caller sets it to a matrix this decoder can apply, falling back to the existing frame height heuristic otherwise. A PGS palette carries no colorimetry of its own; it is authored to match the video stream it accompanies, so only the caller can know it. The height heuristic cannot select BT.2020, so it is always wrong for an HDR title, whose graphics palette is BT.2020 per BD-ROM Part 3. The error is invisible on white and grey text because both matrices map neutral chroma to neutral RGB, which is why it has gone unnoticed; saturated colors come out wrong and are then clipped. No ABI change: AVCodecContext already carries the field. Behavior is unchanged for every current caller because nothing sets it yet. Downstream consumer: Kodi carries this patch and sets avctx->colorspace from the video stream in https://github.com/xbmc/xbmc/pull/29056 <!-- 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 f0818587dc74870b852c5c16cd59e2847e627835 Mon Sep 17 00:00:00 2001 From: reardonia <[email protected]> Date: Mon, 7 Sep 2026 13:26:19 -0400 Subject: [PATCH 1/2] avutil/colorspace: add BT.2020 non-constant luminance YUV->RGB macro colorspace.h carries YUV->RGB macros for CCIR (BT.601) and BT.709 only. Add the BT.2020 non-constant luminance variant, derived from ITU-R BT.2020-2 Table 4 with Kr = 0.2627 and Kb = 0.0593. Unused by itself; the next patch in this series uses it to decode BT.2020 PGS subtitle palettes. Assisted-by: Claude Opus 5 <[email protected]> Signed-off-by: reardonia <[email protected]> --- libavutil/colorspace.h | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/libavutil/colorspace.h b/libavutil/colorspace.h index ef6f6107d6..2cd5777780 100644 --- a/libavutil/colorspace.h +++ b/libavutil/colorspace.h @@ -51,6 +51,18 @@ b_add = ONE_HALF + FIX(1.8556 * 255.0 / 224.0) * cb; \ } +// Derived from ITU-R BT.2020-2 Table 4, non-constant luminance. +// Kr = 0.2627, Kb = 0.0593. +#define YUV_TO_RGB1_CCIR_BT2020_NCL(cb1, cr1) \ + { \ + cb = (cb1) - 128; \ + cr = (cr1) - 128; \ + r_add = ONE_HALF + FIX(1.474600 * 255.0 / 224.0) * cr; \ + g_add = ONE_HALF - FIX(0.164553 * 255.0 / 224.0) * cb - \ + FIX(0.571353 * 255.0 / 224.0) * cr; \ + b_add = ONE_HALF + FIX(1.881400 * 255.0 / 224.0) * cb; \ + } + // To be used for the BT709 variant as well #define YUV_TO_RGB2_CCIR(r, g, b, y1)\ {\ -- 2.52.0 >From d691847628882cf3cb4815074fc4365be1a03f1a Mon Sep 17 00:00:00 2001 From: reardonia <[email protected]> Date: Mon, 7 Sep 2026 13:26:19 -0400 Subject: [PATCH 2/2] avcodec/pgssubdec: use the caller's colorspace for the palette A PGS palette carries no colorimetry of its own. It is authored to match the video stream it accompanies, so only the caller can know what it is, but this decoder picks its YCbCr matrix from the frame height alone: BT.601 at or below 576 lines, BT.709 above. That guess cannot be right for an HDR title, whose graphics palette is BT.2020 per BD-ROM Part 3. No frame height selects BT.2020, so the matrix is always wrong there. The error is invisible on the white and grey text that dominates subtitles, because both matrices map neutral chroma to neutral RGB, which is why it has gone unnoticed. Saturated colors come out wrong and are then clipped, and the clip is not recoverable by any downstream consumer. Honor avctx->colorspace when the caller sets it to a matrix this decoder can apply, and fall back to the existing height heuristic otherwise. No ABI change is needed; AVCodecContext already carries the field. Behavior is unchanged for every current caller because nothing sets it yet. Assisted-by: Claude Opus 5 <[email protected]> Signed-off-by: reardonia <[email protected]> --- libavcodec/pgssubdec.c | 32 ++++++++++++++++++++++++++++---- 1 file changed, 28 insertions(+), 4 deletions(-) diff --git a/libavcodec/pgssubdec.c b/libavcodec/pgssubdec.c index 3a4b60f419..65d929b6bd 100644 --- a/libavcodec/pgssubdec.c +++ b/libavcodec/pgssubdec.c @@ -336,6 +336,7 @@ static int parse_palette_segment(AVCodecContext *avctx, int y, cb, cr, alpha; int r, g, b, r_add, g_add, b_add; int id; + enum AVColorSpace csp = avctx->colorspace; id = bytestream_get_byte(&buf); palette = find_palette(id, &ctx->palettes); @@ -351,6 +352,23 @@ static int parse_palette_segment(AVCodecContext *avctx, /* Skip palette version */ buf += 1; + /* A PGS palette carries no colorimetry of its own; it is authored to + * match the associated video stream, so only the caller can know it. + * A matrix with no conversion here, such as BT.2020 constant luminance + * or SMPTE 240M, keeps the frame-height guess rather than being decoded + * with a different matrix. */ + switch (csp) { + case AVCOL_SPC_BT709: + case AVCOL_SPC_BT470BG: + case AVCOL_SPC_SMPTE170M: + case AVCOL_SPC_BT2020_NCL: + break; + default: + csp = (avctx->height <= 0 || avctx->height > 576) ? AVCOL_SPC_BT709 + : AVCOL_SPC_SMPTE170M; + break; + } + while (buf < buf_end) { color_id = bytestream_get_byte(&buf); y = bytestream_get_byte(&buf); @@ -358,11 +376,17 @@ static int parse_palette_segment(AVCodecContext *avctx, cb = bytestream_get_byte(&buf); alpha = bytestream_get_byte(&buf); - /* Default to BT.709 colorspace. In case of <= 576 height use BT.601 */ - if (avctx->height <= 0 || avctx->height > 576) { - YUV_TO_RGB1_CCIR_BT709(cb, cr); - } else { + switch (csp) { + case AVCOL_SPC_BT2020_NCL: + YUV_TO_RGB1_CCIR_BT2020_NCL(cb, cr); + break; + case AVCOL_SPC_BT470BG: + case AVCOL_SPC_SMPTE170M: YUV_TO_RGB1_CCIR(cb, cr); + break; + default: + YUV_TO_RGB1_CCIR_BT709(cb, cr); + break; } YUV_TO_RGB2_CCIR(r, g, b, y); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
