PR #23327 opened by azatnurg URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23327 Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/23327.patch
# Summary of changes The AMF hardware decoder exposes color information and HDR side data on output surfaces, but the decoder previously copied only static avctx values onto output frames. Read primaries, transfer, range, and profile from each surface, keep avctx updated, and fall back to cached values when a field is not signalled on a given frame. >From b176c913a8d0fb9257f950fb52edc70b66385015 Mon Sep 17 00:00:00 2001 From: Azat Nurgaliev <[email protected]> Date: Mon, 1 Jun 2026 18:51:58 +0200 Subject: [PATCH 1/2] libavutil/hwcontext_amf: add AMF-to-AV color conversion helpers Add five functions mapping AMF color enumeration values to their FFmpeg equivalents, providing the inverse of the existing av_amf_get_color_profile() used by encoders and filters: av_amf_to_av_primaries() - AMF_COLOR_PRIMARIES_ENUM -> AVColorPrimaries av_amf_to_av_trc() - AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM -> AVColorTransferCharacteristic av_amf_to_av_range() - AMF_COLOR_RANGE_ENUM -> AVColorRange av_amf_to_av_colorspace() - AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM -> AVColorSpace av_amf_to_av_profile_range() - AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM -> AVColorRange AMF_COLOR_PRIMARIES_* and AMF_COLOR_TRANSFER_CHARACTERISTIC_* follow ITU-T H.273 / ISO 23001-8 code points that match AVColorPrimaries and AVColorTransferCharacteristic directly; unspecified and reserved codes are filtered to AVCOL_PRI_UNSPECIFIED / AVCOL_TRC_UNSPECIFIED. --- doc/APIchanges | 4 ++ libavutil/hwcontext_amf.c | 89 +++++++++++++++++++++++++++++++++++++++ libavutil/hwcontext_amf.h | 38 +++++++++++++++++ libavutil/version.h | 2 +- 4 files changed, 132 insertions(+), 1 deletion(-) diff --git a/doc/APIchanges b/doc/APIchanges index 58a9b483a6..309ebd5b59 100644 --- a/doc/APIchanges +++ b/doc/APIchanges @@ -2,6 +2,10 @@ The last version increases of all libraries were on 2025-03-28 API changes, most recent first: +2026-06-xx - xxxxxxxxxx - lavu 60.33.100 - hwcontext_amf.h + Add av_amf_to_av_primaries(), av_amf_to_av_trc(), av_amf_to_av_range(), + av_amf_to_av_colorspace(), av_amf_to_av_profile_range(). + 2026-06-xx - xxxxxxxxxxx - lavu 60.32.100 - hwcontext_vulkan.h Add AVVulkanDeviceContext.queue_flags. diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c index 754b1c60a2..91288a035b 100644 --- a/libavutil/hwcontext_amf.c +++ b/libavutil/hwcontext_amf.c @@ -183,6 +183,95 @@ enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM av_amf_get_color_profile(enum AVColo } } +static int amf_color_primaries_valid(enum AVColorPrimaries pri) +{ + return pri != AVCOL_PRI_UNSPECIFIED && pri != AVCOL_PRI_RESERVED0 && + pri != AVCOL_PRI_RESERVED && av_color_primaries_name(pri); +} + +static int amf_color_trc_valid(enum AVColorTransferCharacteristic trc) +{ + return trc != AVCOL_TRC_UNSPECIFIED && trc != AVCOL_TRC_RESERVED0 && + trc != AVCOL_TRC_RESERVED && av_color_transfer_name(trc); +} + +enum AVColorPrimaries av_amf_to_av_primaries(enum AMF_COLOR_PRIMARIES_ENUM pri) +{ + enum AVColorPrimaries avpri = (enum AVColorPrimaries)pri; + + switch (pri) { + case AMF_COLOR_PRIMARIES_UNDEFINED: + case AMF_COLOR_PRIMARIES_UNSPECIFIED: + case AMF_COLOR_PRIMARIES_RESERVED: + return AVCOL_PRI_UNSPECIFIED; + default: + if (amf_color_primaries_valid(avpri)) + return avpri; + return AVCOL_PRI_UNSPECIFIED; + } +} + +enum AVColorTransferCharacteristic av_amf_to_av_trc(enum AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM trc) +{ + enum AVColorTransferCharacteristic avtrc = (enum AVColorTransferCharacteristic)trc; + + switch (trc) { + case AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED: + case AMF_COLOR_TRANSFER_CHARACTERISTIC_UNSPECIFIED: + case AMF_COLOR_TRANSFER_CHARACTERISTIC_RESERVED: + return AVCOL_TRC_UNSPECIFIED; + default: + if (amf_color_trc_valid(avtrc)) + return avtrc; + return AVCOL_TRC_UNSPECIFIED; + } +} + +enum AVColorRange av_amf_to_av_range(enum AMF_COLOR_RANGE_ENUM range) +{ + switch (range) { + case AMF_COLOR_RANGE_FULL: + return AVCOL_RANGE_JPEG; + case AMF_COLOR_RANGE_STUDIO: + return AVCOL_RANGE_MPEG; + default: + return AVCOL_RANGE_UNSPECIFIED; + } +} + +enum AVColorSpace av_amf_to_av_colorspace(enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM profile) +{ + switch (profile) { + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_601: + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601: + return AVCOL_SPC_SMPTE170M; + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_709: + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709: + return AVCOL_SPC_BT709; + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020: + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020: + return AVCOL_SPC_BT2020_NCL; + default: + return AVCOL_SPC_UNSPECIFIED; + } +} + +enum AVColorRange av_amf_to_av_profile_range(enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM profile) +{ + switch (profile) { + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_601: + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_709: + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020: + return AVCOL_RANGE_MPEG; + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601: + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709: + case AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020: + return AVCOL_RANGE_JPEG; + default: + return AVCOL_RANGE_UNSPECIFIED; + } +} + int av_amf_display_mastering_meta_to_hdrmeta(const AVMasteringDisplayMetadata *display_meta, AMFHDRMetadata *hdrmeta) { if (!display_meta || !hdrmeta) diff --git a/libavutil/hwcontext_amf.h b/libavutil/hwcontext_amf.h index 918eec97b8..48e8fb67a1 100644 --- a/libavutil/hwcontext_amf.h +++ b/libavutil/hwcontext_amf.h @@ -50,6 +50,44 @@ enum AMF_SURFACE_FORMAT av_av_to_amf_format(enum AVPixelFormat fmt); enum AVPixelFormat av_amf_to_av_format(enum AMF_SURFACE_FORMAT fmt); enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM av_amf_get_color_profile(enum AVColorRange color_range, enum AVColorSpace color_space); + +/** + * Map an AMF color primaries value to the AVColorPrimaries equivalent. + * AMF_COLOR_PRIMARIES_* follow ITU-T H.273 / ISO 23001-8 code points, + * which match AVColorPrimaries directly. Unrecognized or reserved values + * are mapped to AVCOL_PRI_UNSPECIFIED. + */ +enum AVColorPrimaries av_amf_to_av_primaries(enum AMF_COLOR_PRIMARIES_ENUM pri); + +/** + * Map an AMF color transfer characteristic value to the + * AVColorTransferCharacteristic equivalent. + * AMF_COLOR_TRANSFER_CHARACTERISTIC_* follow ITU-T H.273 / ISO 23001-8 + * code points, which match AVColorTransferCharacteristic directly. + * Unrecognized or reserved values are mapped to AVCOL_TRC_UNSPECIFIED. + */ +enum AVColorTransferCharacteristic av_amf_to_av_trc(enum AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM trc); + +/** + * Map an AMF color range value to the AVColorRange equivalent. + * Returns AVCOL_RANGE_UNSPECIFIED for AMF_COLOR_RANGE_UNDEFINED or any + * other unrecognized value. + */ +enum AVColorRange av_amf_to_av_range(enum AMF_COLOR_RANGE_ENUM range); + +/** + * Map an AMF video converter color profile to the AVColorSpace equivalent. + * Returns AVCOL_SPC_UNSPECIFIED for unrecognized profiles. + */ +enum AVColorSpace av_amf_to_av_colorspace(enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM profile); + +/** + * Map an AMF video converter color profile to the AVColorRange equivalent. + * Full-range profiles map to AVCOL_RANGE_JPEG; studio-range profiles map + * to AVCOL_RANGE_MPEG. Returns AVCOL_RANGE_UNSPECIFIED for unrecognized + * profiles. + */ +enum AVColorRange av_amf_to_av_profile_range(enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM profile); int av_amf_display_mastering_meta_to_hdrmeta(const AVMasteringDisplayMetadata *display_meta, AMFHDRMetadata *hdrmeta); int av_amf_light_metadata_to_hdrmeta(const AVContentLightMetadata *light_meta, AMFHDRMetadata *hdrmeta); int av_amf_extract_hdr_metadata(const AVFrame *frame, AMFHDRMetadata *hdrmeta); diff --git a/libavutil/version.h b/libavutil/version.h index e7aeab9995..42f2d41052 100644 --- a/libavutil/version.h +++ b/libavutil/version.h @@ -79,7 +79,7 @@ */ #define LIBAVUTIL_VERSION_MAJOR 60 -#define LIBAVUTIL_VERSION_MINOR 32 +#define LIBAVUTIL_VERSION_MINOR 33 #define LIBAVUTIL_VERSION_MICRO 100 #define LIBAVUTIL_VERSION_INT AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \ -- 2.52.0 >From dff8132ee58f3e4692cca7b092ae5ac4962b7690 Mon Sep 17 00:00:00 2001 From: Azat Nurgaliev <[email protected]> Date: Wed, 3 Jun 2026 12:45:05 +0200 Subject: [PATCH 2/2] avcodec/amfdec: propagate color and HDR metadata from AMF surfaces The AMF hardware decoder exposes color information and HDR side data on output surfaces, but the decoder previously copied only static avctx values onto output frames. Read primaries, transfer, range, and profile from each surface, keep avctx updated, and fall back to cached values when a field is not signalled on a given frame. On resolution change, re-create the hw frames context instead of trying to re-init an already initialized pool. Attach HDR10 mastering/display metadata when PQ transfer is detected. Release the AMF HDR metadata buffer after attaching side data. --- libavcodec/amfdec.c | 142 +++++++++++++++++++++++++++++++++++++------- 1 file changed, 122 insertions(+), 20 deletions(-) diff --git a/libavcodec/amfdec.c b/libavcodec/amfdec.c index 38fae3f4e7..f24e446010 100644 --- a/libavcodec/amfdec.c +++ b/libavcodec/amfdec.c @@ -24,7 +24,6 @@ #include "libavutil/time.h" #include "decode.h" #include "decode_bsf.h" -#include "libavutil/mastering_display_metadata.h" #if CONFIG_D3D11VA #include "libavutil/hwcontext_d3d11va.h" @@ -233,13 +232,27 @@ static int amf_init_frames_context(AVCodecContext *avctx, int sw_format, int new ret = av_hwframe_ctx_init(avctx->hw_frames_ctx); if (ret < 0) { - av_log(NULL, AV_LOG_ERROR, "Error initializing a AMF frame pool\n"); + av_log(avctx, AV_LOG_ERROR, "Error initializing a AMF frame pool\n"); av_buffer_unref(&avctx->hw_frames_ctx); return ret; } return 0; } +static int amf_reinit_frames_context(AVCodecContext *avctx, int sw_format, + int new_width, int new_height) +{ + if (!avctx->hw_device_ctx) + return 0; + + av_buffer_unref(&avctx->hw_frames_ctx); + avctx->hw_frames_ctx = av_hwframe_ctx_alloc(avctx->hw_device_ctx); + if (!avctx->hw_frames_ctx) + return AVERROR(ENOMEM); + + return amf_init_frames_context(avctx, sw_format, new_width, new_height); +} + static int amf_decode_init(AVCodecContext *avctx) { AMFDecoderContext *ctx = avctx->priv_data; @@ -321,6 +334,76 @@ fail: return ret; } +static void amf_read_surface_color(AVCodecContext *avctx, AMFSurface *surface, + AVFrame *frame) +{ + AMFVariantStruct var = {0}; + AMF_RESULT res; + enum AVColorPrimaries primaries = AVCOL_PRI_UNSPECIFIED; + enum AVColorTransferCharacteristic trc = AVCOL_TRC_UNSPECIFIED; + enum AVColorSpace colorspace = AVCOL_SPC_UNSPECIFIED; + enum AVColorRange range = AVCOL_RANGE_UNSPECIFIED; + + res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_COLOR_PRIMARIES, &var); + if (res == AMF_OK) + primaries = av_amf_to_av_primaries((enum AMF_COLOR_PRIMARIES_ENUM)var.int64Value); + AMFVariantClear(&var); + + res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC, &var); + if (res == AMF_OK) + trc = av_amf_to_av_trc((enum AMF_COLOR_TRANSFER_CHARACTERISTIC_ENUM)var.int64Value); + AMFVariantClear(&var); + + res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_COLOR_RANGE, &var); + if (res == AMF_OK) + range = av_amf_to_av_range((enum AMF_COLOR_RANGE_ENUM)var.int64Value); + AMFVariantClear(&var); + + res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_COLOR_PROFILE, &var); + if (res == AMF_OK) { + enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM profile = + (enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM)var.int64Value; + enum AVColorSpace profile_space = av_amf_to_av_colorspace(profile); + enum AVColorRange profile_range = av_amf_to_av_profile_range(profile); + + if (colorspace == AVCOL_SPC_UNSPECIFIED) + colorspace = profile_space; + if (range == AVCOL_RANGE_UNSPECIFIED) + range = profile_range; + } + AMFVariantClear(&var); + + /* Only trust the standalone full-range flag when the stream actually + * carries a color description; otherwise it would invent a range for + * content that signalled none. */ + if (range == AVCOL_RANGE_UNSPECIFIED && + (primaries != AVCOL_PRI_UNSPECIFIED || trc != AVCOL_TRC_UNSPECIFIED || + colorspace != AVCOL_SPC_UNSPECIFIED)) { + res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_FULL_RANGE_COLOR, &var); + if (res == AMF_OK) + range = var.boolValue ? AVCOL_RANGE_JPEG : AVCOL_RANGE_MPEG; + AMFVariantClear(&var); + } + + if (primaries == AVCOL_PRI_UNSPECIFIED && trc == AVCOL_TRC_UNSPECIFIED && + colorspace == AVCOL_SPC_UNSPECIFIED) + range = AVCOL_RANGE_UNSPECIFIED; + + if (primaries != AVCOL_PRI_UNSPECIFIED) + avctx->color_primaries = primaries; + if (trc != AVCOL_TRC_UNSPECIFIED) + avctx->color_trc = trc; + if (colorspace != AVCOL_SPC_UNSPECIFIED) + avctx->colorspace = colorspace; + if (range != AVCOL_RANGE_UNSPECIFIED) + avctx->color_range = range; + + frame->color_primaries = primaries != AVCOL_PRI_UNSPECIFIED ? primaries : avctx->color_primaries; + frame->color_trc = trc != AVCOL_TRC_UNSPECIFIED ? trc : avctx->color_trc; + frame->colorspace = colorspace != AVCOL_SPC_UNSPECIFIED ? colorspace : avctx->colorspace; + frame->color_range = range != AVCOL_RANGE_UNSPECIFIED ? range : avctx->color_range; +} + static AMF_RESULT amf_get_property_buffer(AMFData *object, const wchar_t *name, AMFBuffer **val) { AMF_RESULT res; @@ -342,6 +425,37 @@ static AMF_RESULT amf_get_property_buffer(AMFData *object, const wchar_t *name, return res; } +static int amf_attach_hdr_metadata(AVCodecContext *avctx, AMFSurface *surface, AVFrame *frame) +{ + AMFBuffer *hdrmeta_buffer = NULL; + AMFHDRMetadata *hdrmeta; + AMF_RESULT res; + int ret; + + if (frame->color_trc != AVCOL_TRC_SMPTE2084) + return 0; + + res = amf_get_property_buffer((AMFData *)surface, AMF_VIDEO_DECODER_HDR_METADATA, + &hdrmeta_buffer); + if (!hdrmeta_buffer) + return 0; + + if (res != AMF_OK) { + hdrmeta_buffer->pVtbl->Release(hdrmeta_buffer); + return AVERROR(EINVAL); + } + + hdrmeta = (AMFHDRMetadata *)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer); + if (!hdrmeta) { + hdrmeta_buffer->pVtbl->Release(hdrmeta_buffer); + return AVERROR(EINVAL); + } + + ret = av_amf_attach_hdr_metadata(frame, hdrmeta); + hdrmeta_buffer->pVtbl->Release(hdrmeta_buffer); + return ret; +} + static int amf_amfsurface_to_avframe(AVCodecContext *avctx, AMFSurface* surface, AVFrame *frame) { AMFVariantStruct var = {0}; @@ -407,24 +521,12 @@ static int amf_amfsurface_to_avframe(AVCodecContext *avctx, AMFSurface* surface, if (frame->duration < 0) frame->duration = 0; - frame->color_range = avctx->color_range; - frame->colorspace = avctx->colorspace; - frame->color_trc = avctx->color_trc; - frame->color_primaries = avctx->color_primaries; + amf_read_surface_color(avctx, surface, frame); - if (frame->color_trc == AVCOL_TRC_SMPTE2084) { - AMFBuffer * hdrmeta_buffer = NULL; - ret = amf_get_property_buffer((AMFData *)surface, AMF_VIDEO_DECODER_HDR_METADATA, &hdrmeta_buffer); - if (hdrmeta_buffer != NULL) { - AMFHDRMetadata * hdrmeta = (AMFHDRMetadata*)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer); - if (ret != AMF_OK) - return ret; + ret = amf_attach_hdr_metadata(avctx, surface, frame); + if (ret < 0) + return ret; - ret = av_amf_attach_hdr_metadata(frame, hdrmeta); - if (ret < 0) - return ret; - } - } return 0; } @@ -617,10 +719,10 @@ static int amf_decode_frame(AVCodecContext *avctx, struct AVFrame *frame) if (res != AMF_OK) { return AVERROR(EINVAL); } - int ret = amf_init_frames_context(avctx, av_amf_to_av_format(format_var.int64Value), avctx->coded_width, avctx->coded_height); + int ret = amf_reinit_frames_context(avctx, av_amf_to_av_format(format_var.int64Value), avctx->coded_width, avctx->coded_height); if (ret < 0) return ret; - }else + } else return AVERROR_EOF; } else { av_log(avctx, AV_LOG_ERROR, "Unknown result from QueryOutput %d\n", res); -- 2.52.0 _______________________________________________ ffmpeg-devel mailing list -- [email protected] To unsubscribe send an email to [email protected]
