PR #25528 opened by JuliusBairaktaris URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25528 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25528.patch
`av_amf_display_mastering_meta_to_hdrmeta()` converts the mastering display primaries, white point and luminances with a truncating cast of the `av_q2d()` product. Values that are exact in AMF's units therefore come out one lower: a red y of 0.292 (14600/50000) is written as 14599, and a white point x of 0.3127 as 15634. A zero denominator also turns into an out-of-range float-to-int cast. This patch converts with `av_rescale()`, which rounds to nearest, and treats a zero or negative value as 0. Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029: P010 frames carrying BT.2020/D65 primaries and 1000/0.005 cd/m² mastering luminance, encoded through the API with `hevc_amf`, give a mastering display SEI with exactly those values (14599 and 15634 before). I found this while looking at #24227. The AV1 part of that issue is a separate AMF runtime bug: the AV1 encoder copies these values into the AV1 metadata OBU without converting units. I reported it as GPUOpen-LibrariesAndSDKs/AMF#633. Written with Claude Opus 5.5 in Claude Code; `Assisted-by` trailer on the commit. From abccb172d82db143e28047a767b1c44c2ada7a6c Mon Sep 17 00:00:00 2001 From: Julius Bairaktaris <[email protected]> Date: Tue, 27 Oct 2026 15:51:09 +0100 Subject: [PATCH] avutil/hwcontext_amf: round the mastering display metadata av_amf_display_mastering_meta_to_hdrmeta() converts the primaries, white point and luminances with a truncating cast of the av_q2d() product, so values that are exact in AMF's units come out one lower, e.g. a red y of 0.292 (14600/50000) is written as 14599, and a white point x of 0.3127 as 15634. A zero denominator also turns into an out-of-range cast. Convert with av_rescale(), which rounds to nearest, and treat a zero or negative value as 0. Tested on Windows 11, RX 9070 XT, driver 32.0.32015.7029: frames carrying BT.2020/D65 primaries and 1000/0.005 cd/m2 luminance give an HEVC mastering display SEI with exactly those values from hevc_amf (14599 and 15634 before). Assisted-by: Claude Opus 5.5 Signed-off-by: Julius Bairaktaris <[email protected]> --- libavutil/hwcontext_amf.c | 40 +++++++++++++++++++-------------------- 1 file changed, 20 insertions(+), 20 deletions(-) diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c index 7f47df5b59d..c62cae7157a 100644 --- a/libavutil/hwcontext_amf.c +++ b/libavutil/hwcontext_amf.c @@ -21,6 +21,7 @@ #include "pixfmt.h" #include "pixdesc.h" #include "imgutils.h" +#include "mathematics.h" #include "hwcontext.h" #include "hwcontext_amf.h" #include "hwcontext_internal.h" @@ -183,37 +184,36 @@ enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM av_amf_get_color_profile(enum AVColo } } +static amf_uint32 amf_q_to_units(AVRational q, int den, amf_uint32 max) +{ + if (q.num <= 0 || q.den <= 0) + return 0; + return FFMIN(av_rescale(q.num, den, q.den), max); +} + int av_amf_display_mastering_meta_to_hdrmeta(const AVMasteringDisplayMetadata *display_meta, AMFHDRMetadata *hdrmeta) { if (!display_meta || !hdrmeta) return AVERROR(EINVAL); if (display_meta->has_luminance) { - const unsigned int luma_den = 10000; + const int luma_den = 10000; hdrmeta->maxMasteringLuminance = - (amf_uint32)(luma_den * av_q2d(display_meta->max_luminance)); + amf_q_to_units(display_meta->max_luminance, luma_den, UINT32_MAX); hdrmeta->minMasteringLuminance = - FFMIN((amf_uint32)(luma_den * av_q2d(display_meta->min_luminance)), hdrmeta->maxMasteringLuminance); + amf_q_to_units(display_meta->min_luminance, luma_den, hdrmeta->maxMasteringLuminance); } if (display_meta->has_primaries) { - const unsigned int chroma_den = 50000; - hdrmeta->redPrimary[0] = - FFMIN((amf_uint16)(chroma_den * av_q2d(display_meta->display_primaries[0][0])), chroma_den); - hdrmeta->redPrimary[1] = - FFMIN((amf_uint16)(chroma_den * av_q2d(display_meta->display_primaries[0][1])), chroma_den); - hdrmeta->greenPrimary[0] = - FFMIN((amf_uint16)(chroma_den * av_q2d(display_meta->display_primaries[1][0])), chroma_den); - hdrmeta->greenPrimary[1] = - FFMIN((amf_uint16)(chroma_den * av_q2d(display_meta->display_primaries[1][1])), chroma_den); - hdrmeta->bluePrimary[0] = - FFMIN((amf_uint16)(chroma_den * av_q2d(display_meta->display_primaries[2][0])), chroma_den); - hdrmeta->bluePrimary[1] = - FFMIN((amf_uint16)(chroma_den * av_q2d(display_meta->display_primaries[2][1])), chroma_den); - hdrmeta->whitePoint[0] = - FFMIN((amf_uint16)(chroma_den * av_q2d(display_meta->white_point[0])), chroma_den); - hdrmeta->whitePoint[1] = - FFMIN((amf_uint16)(chroma_den * av_q2d(display_meta->white_point[1])), chroma_den); + const int chroma_den = 50000; + hdrmeta->redPrimary[0] = amf_q_to_units(display_meta->display_primaries[0][0], chroma_den, chroma_den); + hdrmeta->redPrimary[1] = amf_q_to_units(display_meta->display_primaries[0][1], chroma_den, chroma_den); + hdrmeta->greenPrimary[0] = amf_q_to_units(display_meta->display_primaries[1][0], chroma_den, chroma_den); + hdrmeta->greenPrimary[1] = amf_q_to_units(display_meta->display_primaries[1][1], chroma_den, chroma_den); + hdrmeta->bluePrimary[0] = amf_q_to_units(display_meta->display_primaries[2][0], chroma_den, chroma_den); + hdrmeta->bluePrimary[1] = amf_q_to_units(display_meta->display_primaries[2][1], chroma_den, chroma_den); + hdrmeta->whitePoint[0] = amf_q_to_units(display_meta->white_point[0], chroma_den, chroma_den); + hdrmeta->whitePoint[1] = amf_q_to_units(display_meta->white_point[1], chroma_den, chroma_den); } return 0; -- 2.54.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
