Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PR] avfilter/vf_vpp_amf: Extend AMF Color Converter HDR capabilities (PR #21811)
@ 2026-02-20 12:04 DmitriiGershenkop via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: DmitriiGershenkop via ffmpeg-devel @ 2026-02-20 12:04 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: DmitriiGershenkop

PR #21811 opened by DmitriiGershenkop
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21811
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/21811.patch


>From c6f64052a7768bfd1007c33723f0b4cdd061beea Mon Sep 17 00:00:00 2001
From: Dmitrii Gershenkop <bortengineer@protonmail.com>
Date: Thu, 19 Feb 2026 14:29:45 +0100
Subject: [PATCH] avfilter/vf_vpp_amf: Extend AMF Color Converter HDR
 capabilities

---
 Changelog                   |   1 +
 libavcodec/amfdec.c         |  65 +-------------
 libavcodec/amfenc.c         |  91 +------------------
 libavcodec/amfenc.h         |   2 -
 libavcodec/amfenc_av1.c     |   4 +-
 libavcodec/amfenc_h264.c    |   3 +-
 libavcodec/amfenc_hevc.c    |   3 +-
 libavfilter/version.h       |   2 +-
 libavfilter/vf_amf_common.c |  89 ++++++++++++++++--
 libavfilter/vf_amf_common.h |  15 +++-
 libavfilter/vf_sr_amf.c     |   9 +-
 libavfilter/vf_vpp_amf.c    | 160 ++++++++++++++++++++++++++-------
 libavutil/hwcontext_amf.c   | 175 +++++++++++++++++++++++++++++++++---
 libavutil/hwcontext_amf.h   |   8 ++
 14 files changed, 413 insertions(+), 214 deletions(-)

diff --git a/Changelog b/Changelog
index a9d68b369e..966ddf5f65 100644
--- a/Changelog
+++ b/Changelog
@@ -20,6 +20,7 @@ version <next>:
 - JPEG-XS raw bitstream muxer and demuxer
 - IAMF Projection mode Ambisonic Audio Elements muxing and demuxing
 - Add vf_mestimate_d3d12 filter
+- Extend AMF Color Converter (vf_vpp_amf) HDR capabilities
 
 
 version 8.0:
diff --git a/libavcodec/amfdec.c b/libavcodec/amfdec.c
index c178d5d912..7c58a96bb0 100644
--- a/libavcodec/amfdec.c
+++ b/libavcodec/amfdec.c
@@ -21,8 +21,6 @@
 #include "amfdec.h"
 #include "codec_internal.h"
 #include "hwconfig.h"
-#include "libavutil/imgutils.h"
-#include "libavutil/mem.h"
 #include "libavutil/time.h"
 #include "decode.h"
 #include "decode_bsf.h"
@@ -125,31 +123,7 @@ static int amf_init_decoder(AVCodecContext *avctx)
     } else if (avctx->color_range != AVCOL_RANGE_UNSPECIFIED) {
         AMF_ASSIGN_PROPERTY_BOOL(res, ctx->decoder, AMF_VIDEO_DECODER_FULL_RANGE_COLOR, 0);
     }
-    color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN;
-    switch (avctx->colorspace) {
-    case AVCOL_SPC_SMPTE170M:
-        if (avctx->color_range == AVCOL_RANGE_JPEG) {
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601;
-        } else {
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601;
-        }
-        break;
-    case AVCOL_SPC_BT709:
-        if (avctx->color_range == AVCOL_RANGE_JPEG) {
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709;
-        } else {
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709;
-        }
-        break;
-    case AVCOL_SPC_BT2020_NCL:
-    case AVCOL_SPC_BT2020_CL:
-        if (avctx->color_range == AVCOL_RANGE_JPEG) {
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020;
-        } else {
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020;
-        }
-        break;
-    }
+    color_profile = av_amf_get_color_profile(avctx->color_range, avctx->colorspace);
     if (color_profile != AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN)
         AMF_ASSIGN_PROPERTY_INT64(res, ctx->decoder, AMF_VIDEO_DECODER_COLOR_PROFILE, color_profile);
     if (avctx->color_trc != AVCOL_TRC_UNSPECIFIED)
@@ -435,41 +409,10 @@ static int amf_amfsurface_to_avframe(AVCodecContext *avctx, AMFSurface* surface,
             AMFHDRMetadata * hdrmeta = (AMFHDRMetadata*)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer);
             if (ret != AMF_OK)
                 return ret;
-            if (hdrmeta != NULL) {
-                AVMasteringDisplayMetadata *mastering = av_mastering_display_metadata_create_side_data(frame);
-                const int chroma_den = 50000;
-                const int luma_den = 10000;
 
-                if (!mastering)
-                    return AVERROR(ENOMEM);
-
-                mastering->display_primaries[0][0] = av_make_q(hdrmeta->redPrimary[0], chroma_den);
-                mastering->display_primaries[0][1] = av_make_q(hdrmeta->redPrimary[1], chroma_den);
-
-                mastering->display_primaries[1][0] = av_make_q(hdrmeta->greenPrimary[0], chroma_den);
-                mastering->display_primaries[1][1] = av_make_q(hdrmeta->greenPrimary[1], chroma_den);
-
-                mastering->display_primaries[2][0] = av_make_q(hdrmeta->bluePrimary[0], chroma_den);
-                mastering->display_primaries[2][1] = av_make_q(hdrmeta->bluePrimary[1], chroma_den);
-
-                mastering->white_point[0] = av_make_q(hdrmeta->whitePoint[0], chroma_den);
-                mastering->white_point[1] = av_make_q(hdrmeta->whitePoint[1], chroma_den);
-
-                mastering->max_luminance = av_make_q(hdrmeta->maxMasteringLuminance, luma_den);
-                mastering->min_luminance = av_make_q(hdrmeta->maxMasteringLuminance, luma_den);
-
-                mastering->has_luminance = 1;
-                mastering->has_primaries = 1;
-                if (hdrmeta->maxContentLightLevel) {
-                   AVContentLightMetadata *light = av_content_light_metadata_create_side_data(frame);
-
-                    if (!light)
-                        return AVERROR(ENOMEM);
-
-                    light->MaxCLL  = hdrmeta->maxContentLightLevel;
-                    light->MaxFALL = hdrmeta->maxFrameAverageLightLevel;
-                }
-            }
+            ret = av_amf_attach_hdr_metadata(frame, hdrmeta);
+            if (ret < 0)
+                return ret;
         }
     }
     return 0;
diff --git a/libavcodec/amfenc.c b/libavcodec/amfenc.c
index 329ce29005..50045b3ca3 100644
--- a/libavcodec/amfenc.c
+++ b/libavcodec/amfenc.c
@@ -17,13 +17,11 @@
  */
 
 #include "config.h"
-#include "config_components.h"
 
 #include "libavutil/avassert.h"
 #include "libavutil/imgutils.h"
 #include "libavutil/hwcontext.h"
 #include "libavutil/hwcontext_amf.h"
-#include "libavutil/hwcontext_amf_internal.h"
 #if CONFIG_D3D11VA
 #include "libavutil/hwcontext_d3d11va.h"
 #endif
@@ -37,62 +35,10 @@
 
 #include "amfenc.h"
 #include "encode.h"
-#include "internal.h"
-#include "libavutil/mastering_display_metadata.h"
 
 #define AMF_AV_FRAME_REF    L"av_frame_ref"
 #define PTS_PROP            L"PtsProp"
 
-static int amf_save_hdr_metadata(AVCodecContext *avctx, const AVFrame *frame, AMFHDRMetadata *hdrmeta)
-{
-    AVFrameSideData            *sd_display;
-    AVFrameSideData            *sd_light;
-    AVMasteringDisplayMetadata *display_meta;
-    AVContentLightMetadata     *light_meta;
-
-    sd_display = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
-    if (sd_display) {
-        display_meta = (AVMasteringDisplayMetadata *)sd_display->data;
-        if (display_meta->has_luminance) {
-            const unsigned int luma_den = 10000;
-            hdrmeta->maxMasteringLuminance =
-                (amf_uint32)(luma_den * av_q2d(display_meta->max_luminance));
-            hdrmeta->minMasteringLuminance =
-                FFMIN((amf_uint32)(luma_den * av_q2d(display_meta->min_luminance)), 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);
-        }
-
-        sd_light = av_frame_get_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
-        if (sd_light) {
-            light_meta = (AVContentLightMetadata *)sd_light->data;
-            if (light_meta) {
-                hdrmeta->maxContentLightLevel = (amf_uint16)light_meta->MaxCLL;
-                hdrmeta->maxFrameAverageLightLevel = (amf_uint16)light_meta->MaxFALL;
-            }
-        }
-        return 0;
-    }
-    return 1;
-}
-
 #if CONFIG_D3D11VA
 #include <d3d11.h>
 #endif
@@ -479,7 +425,7 @@ static int amf_submit_frame(AVCodecContext *avctx, AVFrame    *frame, AMFSurface
         res = amf_device_ctx->context->pVtbl->AllocBuffer(amf_device_ctx->context, AMF_MEMORY_HOST, sizeof(AMFHDRMetadata), &hdrmeta_buffer);
         if (res == AMF_OK) {
             AMFHDRMetadata * hdrmeta = (AMFHDRMetadata*)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer);
-            if (amf_save_hdr_metadata(avctx, frame, hdrmeta) == 0) {
+            if (av_amf_extract_hdr_metadata(frame, hdrmeta) == 0) {
                 switch (avctx->codec->id) {
                 case AV_CODEC_ID_H264:
                     AMF_ASSIGN_PROPERTY_INTERFACE(res, ctx->encoder, AMF_VIDEO_ENCODER_INPUT_HDR_METADATA, hdrmeta_buffer); break;
@@ -733,41 +679,6 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt)
     return ret;
 }
 
-int ff_amf_get_color_profile(AVCodecContext *avctx)
-{
-    amf_int64 color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN;
-    if (avctx->color_range == AVCOL_RANGE_JPEG) {
-        /// Color Space for Full (JPEG) Range
-        switch (avctx->colorspace) {
-        case AVCOL_SPC_SMPTE170M:
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601;
-            break;
-        case AVCOL_SPC_BT709:
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709;
-            break;
-        case AVCOL_SPC_BT2020_NCL:
-        case AVCOL_SPC_BT2020_CL:
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020;
-            break;
-        }
-    } else {
-        /// Color Space for Limited (MPEG) range
-        switch (avctx->colorspace) {
-        case AVCOL_SPC_SMPTE170M:
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601;
-            break;
-        case AVCOL_SPC_BT709:
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709;
-            break;
-        case AVCOL_SPC_BT2020_NCL:
-        case AVCOL_SPC_BT2020_CL:
-            color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020;
-            break;
-        }
-    }
-    return color_profile;
-}
-
 const AVCodecHWConfigInternal *const ff_amfenc_hw_configs[] = {
 #if CONFIG_D3D11VA
     HW_CONFIG_ENCODER_FRAMES(D3D11, D3D11VA),
diff --git a/libavcodec/amfenc.h b/libavcodec/amfenc.h
index 8f5eb5a9ef..52a78b206a 100644
--- a/libavcodec/amfenc.h
+++ b/libavcodec/amfenc.h
@@ -161,8 +161,6 @@ int ff_amf_receive_packet(AVCodecContext *avctx, AVPacket *avpkt);
 */
 extern const enum AVPixelFormat ff_amf_pix_fmts[];
 
-int ff_amf_get_color_profile(AVCodecContext *avctx);
-
 /**
 * Error handling helper
 */
diff --git a/libavcodec/amfenc_av1.c b/libavcodec/amfenc_av1.c
index 70642a6b54..b57c76de27 100644
--- a/libavcodec/amfenc_av1.c
+++ b/libavcodec/amfenc_av1.c
@@ -15,7 +15,7 @@
  * License along with FFmpeg; if not, write to the Free Software
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
-
+#include "libavutil/hwcontext_amf.h"
 #include "libavutil/internal.h"
 #include "libavutil/intreadwrite.h"
 #include "libavutil/mem.h"
@@ -258,7 +258,7 @@ static av_cold int amf_encode_init_av1(AVCodecContext* avctx)
     AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_COLOR_BIT_DEPTH, bit_depth);
 
     // Color profile
-    color_profile = ff_amf_get_color_profile(avctx);
+    color_profile = av_amf_get_color_profile(avctx->color_range, avctx->colorspace);
     AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_AV1_OUTPUT_COLOR_PROFILE, color_profile);
 
     // Color Range
diff --git a/libavcodec/amfenc_h264.c b/libavcodec/amfenc_h264.c
index c39a65aead..b92b9af875 100644
--- a/libavcodec/amfenc_h264.c
+++ b/libavcodec/amfenc_h264.c
@@ -17,6 +17,7 @@
  */
 
 
+#include "libavutil/hwcontext_amf.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
@@ -270,7 +271,7 @@ static av_cold int amf_encode_init_h264(AVCodecContext *avctx)
         AMF_ASSIGN_PROPERTY_RATIO(res, ctx->encoder, AMF_VIDEO_ENCODER_ASPECT_RATIO, ratio);
     }
 
-    color_profile = ff_amf_get_color_profile(avctx);
+    color_profile = av_amf_get_color_profile(avctx->color_range, avctx->colorspace);
     AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_OUTPUT_COLOR_PROFILE, color_profile);
 
     /// Color Range (Support for older Drivers)
diff --git a/libavcodec/amfenc_hevc.c b/libavcodec/amfenc_hevc.c
index fe1d948c92..01d6ea9b3d 100644
--- a/libavcodec/amfenc_hevc.c
+++ b/libavcodec/amfenc_hevc.c
@@ -16,6 +16,7 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "libavutil/hwcontext_amf.h"
 #include "libavutil/internal.h"
 #include "libavutil/mem.h"
 #include "libavutil/opt.h"
@@ -252,7 +253,7 @@ static av_cold int amf_encode_init_hevc(AVCodecContext *avctx)
     AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_COLOR_BIT_DEPTH, bit_depth);
 
     // Color profile
-    color_profile = ff_amf_get_color_profile(avctx);
+    color_profile = av_amf_get_color_profile(avctx->color_range, avctx->colorspace);
     AMF_ASSIGN_PROPERTY_INT64(res, ctx->encoder, AMF_VIDEO_ENCODER_HEVC_OUTPUT_COLOR_PROFILE, color_profile);
 
     // Color Range (Support for older Drivers)
diff --git a/libavfilter/version.h b/libavfilter/version.h
index 64cd692ab6..4239736a2d 100644
--- a/libavfilter/version.h
+++ b/libavfilter/version.h
@@ -32,7 +32,7 @@
 #include "version_major.h"
 
 #define LIBAVFILTER_VERSION_MINOR  12
-#define LIBAVFILTER_VERSION_MICRO 100
+#define LIBAVFILTER_VERSION_MICRO 101
 
 
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
diff --git a/libavfilter/vf_amf_common.c b/libavfilter/vf_amf_common.c
index b458de5e5c..a3f43ff0df 100644
--- a/libavfilter/vf_amf_common.c
+++ b/libavfilter/vf_amf_common.c
@@ -22,12 +22,11 @@
 #include "avfilter.h"
 #include "avfilter_internal.h"
 #include "formats.h"
-#include "libavutil/mem.h"
 #include "libavutil/imgutils.h"
 
+#include "AMF/components/VideoDecoderUVD.h"
 #include "libavutil/hwcontext_amf.h"
 #include "libavutil/hwcontext_amf_internal.h"
-#include "AMF/components/ColorSpace.h"
 #include "scale_eval.h"
 
 #if CONFIG_DXVA2
@@ -65,6 +64,12 @@ void amf_filter_uninit(AVFilterContext *avctx)
         ctx->component = NULL;
     }
 
+    if (ctx->master_display)
+        av_freep(&ctx->master_display);
+
+    if (ctx->light_meta)
+        av_freep(&ctx->light_meta);
+
     av_buffer_unref(&ctx->amf_device_ref);
     av_buffer_unref(&ctx->hwdevice_ref);
     av_buffer_unref(&ctx->hwframes_in_ref);
@@ -137,19 +142,19 @@ int amf_filter_filter_frame(AVFilterLink *inlink, AVFrame *in)
     }
 
     out_color_range = AVCOL_RANGE_UNSPECIFIED;
-    if (ctx->color_range == AMF_COLOR_RANGE_FULL)
+    if (ctx->out_color_range == AMF_COLOR_RANGE_FULL)
         out_color_range = AVCOL_RANGE_JPEG;
-    else if (ctx->color_range == AMF_COLOR_RANGE_STUDIO)
+    else if (ctx->out_color_range == AMF_COLOR_RANGE_STUDIO)
         out_color_range = AVCOL_RANGE_MPEG;
 
-    if (ctx->color_range != AMF_COLOR_RANGE_UNDEFINED)
+    if (ctx->out_color_range != AMF_COLOR_RANGE_UNDEFINED)
         out->color_range = out_color_range;
 
-    if (ctx->primaries != AMF_COLOR_PRIMARIES_UNDEFINED)
-        out->color_primaries = ctx->primaries;
+    if (ctx->out_primaries != AMF_COLOR_PRIMARIES_UNDEFINED)
+        out->color_primaries = ctx->out_primaries;
 
-    if (ctx->trc != AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED)
-        out->color_trc = ctx->trc;
+    if (ctx->out_trc != AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED)
+        out->color_trc = ctx->out_trc;
 
 
     if (ret < 0)
@@ -444,6 +449,7 @@ AVFrame *amf_amfsurface_to_avframe(AVFilterContext *avctx, AMFSurface* pSurface)
 
 int amf_avframe_to_amfsurface(AVFilterContext *avctx, const AVFrame *frame, AMFSurface** ppSurface)
 {
+    AMFVariantStruct var = { 0 };
     AMFFilterContext *ctx = avctx->priv;
     AMFSurface *surface;
     AMF_RESULT  res;
@@ -493,6 +499,71 @@ int amf_avframe_to_amfsurface(AVFilterContext *avctx, const AVFrame *frame, AMFS
         break;
     }
 
+    // If AMFSurface comes from other AMF components, it may have various
+    // properties already set. These properties can be used by other AMF
+    // components to perform their tasks. In the context of the AMF video
+    // filter, that other component could be an AMFVideoConverter. By default,
+    // AMFVideoConverter will use HDR related properties assigned to a surface
+    // by an AMFDecoder. If frames (surfaces) originated from any other source,
+    // i.e. from hevcdec, assign those properties from avframe; do not
+    // overwrite these properties if they already have a value.
+    res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC, &var);
+
+    if (res == AMF_NOT_FOUND && frame->color_trc != AVCOL_TRC_UNSPECIFIED)
+        // Note: as of now(Feb 2026), most AV and AMF enums are interchangeable.
+        // TBD: can enums change their values in the future?
+        // For better future-proofing it's better to have dedicated
+        // enum mapping functions.
+        AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_DECODER_COLOR_TRANSFER_CHARACTERISTIC, frame->color_trc);
+
+    res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_COLOR_PRIMARIES, &var);
+    if (res == AMF_NOT_FOUND && frame->color_primaries != AVCOL_PRI_UNSPECIFIED)
+        AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_DECODER_COLOR_PRIMARIES, frame->color_primaries);
+
+    res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_COLOR_RANGE, &var);
+    if (res == AMF_NOT_FOUND && frame->color_range != AVCOL_RANGE_UNSPECIFIED)
+        AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_DECODER_COLOR_RANGE, frame->color_range);
+
+    // Color range for older drivers
+    if (frame->color_range == AVCOL_RANGE_JPEG) {
+        AMF_ASSIGN_PROPERTY_BOOL(res, surface, AMF_VIDEO_DECODER_FULL_RANGE_COLOR, 1);
+    } else if (frame->color_range != AVCOL_RANGE_UNSPECIFIED)
+        AMF_ASSIGN_PROPERTY_BOOL(res, surface, AMF_VIDEO_DECODER_FULL_RANGE_COLOR, 0);
+
+    // Color profile for newer drivers
+    res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_COLOR_PROFILE, &var);
+    if (res == AMF_NOT_FOUND && frame->color_range != AVCOL_RANGE_UNSPECIFIED && frame->colorspace != AVCOL_SPC_UNSPECIFIED) {
+        amf_int64 color_profile = color_profile = av_amf_get_color_profile(frame->color_range, frame->colorspace);
+
+        if (color_profile != AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN)
+            AMF_ASSIGN_PROPERTY_INT64(res, surface, AMF_VIDEO_DECODER_COLOR_PROFILE, color_profile);
+    }
+
+    if (ctx->in_trc == AMF_COLOR_TRANSFER_CHARACTERISTIC_SMPTE2084 && (ctx->master_display || ctx->light_meta)) {
+        AMFBuffer *hdrmeta_buffer = NULL;
+        res = ctx->amf_device_ctx->context->pVtbl->AllocBuffer(ctx->amf_device_ctx->context, AMF_MEMORY_HOST, sizeof(AMFHDRMetadata), &hdrmeta_buffer);
+        if (res == AMF_OK) {
+            AMFHDRMetadata *hdrmeta = (AMFHDRMetadata*)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer);
+
+            av_amf_display_mastering_meta_to_hdrmeta(ctx->master_display, hdrmeta);
+            av_amf_light_metadata_to_hdrmeta(ctx->light_meta, hdrmeta);
+            AMF_ASSIGN_PROPERTY_INTERFACE(res, surface, AMF_VIDEO_DECODER_HDR_METADATA, hdrmeta_buffer);
+        }
+    } else if (frame->color_trc == AVCOL_TRC_SMPTE2084) {
+        res = surface->pVtbl->GetProperty(surface, AMF_VIDEO_DECODER_HDR_METADATA, &var);
+        if (res == AMF_NOT_FOUND) {
+            AMFBuffer *hdrmeta_buffer = NULL;
+            res = ctx->amf_device_ctx->context->pVtbl->AllocBuffer(ctx->amf_device_ctx->context, AMF_MEMORY_HOST, sizeof(AMFHDRMetadata), &hdrmeta_buffer);
+            if (res == AMF_OK) {
+                AMFHDRMetadata *hdrmeta = (AMFHDRMetadata*)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer);
+
+                if (av_amf_extract_hdr_metadata(frame, hdrmeta) == 0)
+                    AMF_ASSIGN_PROPERTY_INTERFACE(res, surface, AMF_VIDEO_DECODER_HDR_METADATA, hdrmeta_buffer);
+                hdrmeta_buffer->pVtbl->Release(hdrmeta_buffer);
+            }
+        }
+    }
+
     if (frame->crop_left || frame->crop_right || frame->crop_top || frame->crop_bottom) {
         size_t crop_x = frame->crop_left;
         size_t crop_y = frame->crop_top;
diff --git a/libavfilter/vf_amf_common.h b/libavfilter/vf_amf_common.h
index d0a0214978..209ee966d8 100644
--- a/libavfilter/vf_amf_common.h
+++ b/libavfilter/vf_amf_common.h
@@ -24,6 +24,7 @@
 #include "AMF/core/Surface.h"
 #include "AMF/components/Component.h"
 #include "libavutil/hwcontext_amf.h"
+#include "libavutil/mastering_display_metadata.h"
 
 typedef struct AMFFilterContext {
     const AVClass *class;
@@ -31,14 +32,22 @@ typedef struct AMFFilterContext {
     int width, height;
     enum AVPixelFormat format;
     int scale_type;
+    int in_color_range;
+    int in_primaries;
+    int in_trc;
     int color_profile;
-    int color_range;
-    int primaries;
-    int trc;
+    int out_color_range;
+    int out_primaries;
+    int out_trc;
     int fill;
     int fill_color;
     int keep_ratio;
 
+    char *disp_master;
+    char *max_cll;
+    AVMasteringDisplayMetadata *master_display;
+    AVContentLightMetadata     *light_meta;
+
     // HQScaler properties
     int algorithm;
     float sharpness;
diff --git a/libavfilter/vf_sr_amf.c b/libavfilter/vf_sr_amf.c
index 2179e81d2c..8f3ae7ed80 100644
--- a/libavfilter/vf_sr_amf.c
+++ b/libavfilter/vf_sr_amf.c
@@ -117,10 +117,13 @@ static int amf_filter_config_output(AVFilterLink *outlink)
     AMF_ASSIGN_PROPERTY_BOOL(res, ctx->component, AMF_HQ_SCALER_FILL, ctx->fill);
     AMF_ASSIGN_PROPERTY_BOOL(res, ctx->component, AMF_HQ_SCALER_KEEP_ASPECT_RATIO, ctx->keep_ratio);
     // Setup default options to skip color conversion
+    ctx->in_color_range = AMF_COLOR_RANGE_UNDEFINED;
+    ctx->in_primaries = AMF_COLOR_PRIMARIES_UNDEFINED;
+    ctx->in_trc = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED;
     ctx->color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN;
-    ctx->color_range = AMF_COLOR_RANGE_UNDEFINED;
-    ctx->primaries = AMF_COLOR_PRIMARIES_UNDEFINED;
-    ctx->trc = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED;
+    ctx->out_color_range = AMF_COLOR_RANGE_UNDEFINED;
+    ctx->out_primaries = AMF_COLOR_PRIMARIES_UNDEFINED;
+    ctx->out_trc = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED;
 
     res = ctx->component->pVtbl->Init(ctx->component, av_av_to_amf_format(in_format), inlink->w, inlink->h);
     AMF_RETURN_IF_FALSE(avctx, res == AMF_OK, AVERROR_UNKNOWN, "AMFHQScaler-Init() failed with error %d\n", res);
diff --git a/libavfilter/vf_vpp_amf.c b/libavfilter/vf_vpp_amf.c
index 78815551be..5baaa503c4 100644
--- a/libavfilter/vf_vpp_amf.c
+++ b/libavfilter/vf_vpp_amf.c
@@ -21,14 +21,9 @@
  * VPP video filter with AMF hardware acceleration
  */
 
-#include <stdio.h>
-#include <string.h>
-
-#include "libavutil/avassert.h"
-#include "libavutil/imgutils.h"
+#include "libavutil/mem.h"
 #include "libavutil/opt.h"
-#include "libavutil/pixdesc.h"
-#include "libavutil/time.h"
+#include "libavutil/internal.h"
 
 #include "libavutil/hwcontext.h"
 #include "libavutil/hwcontext_amf.h"
@@ -38,8 +33,6 @@
 #include "vf_amf_common.h"
 
 #include "avfilter.h"
-#include "formats.h"
-#include "video.h"
 #include "scale_eval.h"
 #include "avfilter_internal.h"
 
@@ -85,19 +78,26 @@ static int amf_filter_query_formats(AVFilterContext *avctx)
 
 static int amf_filter_config_output(AVFilterLink *outlink)
 {
-    AVFilterContext *avctx = outlink->src;
-    AVFilterLink   *inlink = avctx->inputs[0];
-    AMFFilterContext  *ctx = avctx->priv;
+    AVFilterContext   *avctx = outlink->src;
+    AVFilterLink      *inlink = avctx->inputs[0];
     AVHWFramesContext *hwframes_out = NULL;
+    AMFFilterContext  *ctx = avctx->priv;
+    AMFBuffer         *hdrmeta_buffer = NULL;
+    AMFHDRMetadata    *hdrmeta = NULL;
     AMFSize out_size;
-    int err;
+    size_t size = 0;
+    int ret;
     AMF_RESULT res;
     enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM amf_color_profile;
     enum AVPixelFormat in_format;
+    const int chroma_den = 50000;
+    const int luma_den = 10000;
+    const int total_max_cll_args = 2;
+    const int total_disp_meta_args = 10;
 
-    err = amf_init_filter_config(outlink, &in_format);
-    if (err < 0)
-        return err;
+    ret = amf_init_filter_config(outlink, &in_format);
+    if (ret < 0)
+        return ret;
     // FIXME: add checks whether we have HW context
     hwframes_out = (AVHWFramesContext*)ctx->hwframes_out_ref->data;
     res = ctx->amf_device_ctx->factory->pVtbl->CreateComponent(ctx->amf_device_ctx->factory, ctx->amf_device_ctx->context, AMFVideoConverter, &ctx->component);
@@ -118,21 +118,21 @@ static int amf_filter_config_output(AVFilterLink *outlink)
 
     switch(ctx->color_profile) {
     case AMF_VIDEO_CONVERTER_COLOR_PROFILE_601:
-        if (ctx->color_range == AMF_COLOR_RANGE_FULL) {
+        if (ctx->out_color_range == AMF_COLOR_RANGE_FULL) {
             amf_color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601;
         } else {
             amf_color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_601;
         }
         break;
     case AMF_VIDEO_CONVERTER_COLOR_PROFILE_709:
-        if (ctx->color_range == AMF_COLOR_RANGE_FULL) {
+        if (ctx->out_color_range == AMF_COLOR_RANGE_FULL) {
             amf_color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709;
         } else {
             amf_color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709;
         }
         break;
     case AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020:
-        if (ctx->color_range == AMF_COLOR_RANGE_FULL) {
+        if (ctx->out_color_range == AMF_COLOR_RANGE_FULL) {
             amf_color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020;
         } else {
             amf_color_profile = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020;
@@ -143,20 +143,110 @@ static int amf_filter_config_output(AVFilterLink *outlink)
         break;
     }
 
+    if (ctx->in_color_range != AMF_COLOR_RANGE_UNDEFINED) {
+        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_INPUT_COLOR_RANGE, ctx->in_color_range);
+    }
+
+    if (ctx->in_primaries != AMF_COLOR_PRIMARIES_UNDEFINED) {
+        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_INPUT_COLOR_PRIMARIES, ctx->in_primaries);
+    }
+
+    if (ctx->in_trc != AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED) {
+        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_INPUT_TRANSFER_CHARACTERISTIC, ctx->in_trc);
+    }
+
+    if (ctx->disp_master) {
+        ctx->master_display = av_mastering_display_metadata_alloc();
+        if (!ctx->master_display)
+            return AVERROR(ENOMEM);
+
+        ret = sscanf_s(ctx->disp_master,
+            "G(%hu,%hu)B(%hu,%hu)R(%hu,%hu)WP(%hu,%hu)L(%u,%u)",
+            (uint16_t*)&ctx->master_display->display_primaries[1][0].num,
+            (uint16_t*)&ctx->master_display->display_primaries[1][1].num,
+            (uint16_t*)&ctx->master_display->display_primaries[2][0].num,
+            (uint16_t*)&ctx->master_display->display_primaries[2][1].num,
+            (uint16_t*)&ctx->master_display->display_primaries[0][0].num,
+            (uint16_t*)&ctx->master_display->display_primaries[0][1].num,
+            (uint16_t*)&ctx->master_display->white_point[0].num,
+            (uint16_t*)&ctx->master_display->white_point[1].num,
+            (unsigned*)&ctx->master_display->max_luminance.num,
+            (unsigned*)&ctx->master_display->min_luminance.num
+        );
+
+        if (ret != total_disp_meta_args) {
+            av_freep(&ctx->master_display);
+            av_log(avctx, AV_LOG_ERROR, "failed to parse mastering_display option\n");
+            return AVERROR(EINVAL);
+        }
+
+        ctx->master_display->display_primaries[1][0].den = chroma_den;
+        ctx->master_display->display_primaries[1][1].den = chroma_den;
+        ctx->master_display->display_primaries[2][0].den = chroma_den;
+        ctx->master_display->display_primaries[2][1].den = chroma_den;
+        ctx->master_display->display_primaries[0][0].den = chroma_den;
+        ctx->master_display->display_primaries[0][1].den = chroma_den;
+        ctx->master_display->white_point[0].den = chroma_den;
+        ctx->master_display->white_point[1].den = chroma_den;
+        ctx->master_display->max_luminance.den = luma_den;
+        ctx->master_display->min_luminance.den = luma_den;
+
+        ctx->master_display->has_primaries = 1;
+        ctx->master_display->has_luminance = 1;
+    }
+
+
+    if (ctx->max_cll) {
+        ctx->light_meta = av_content_light_metadata_alloc(&size);
+        if (!ctx->light_meta)
+            return AVERROR(ENOMEM);
+
+        ret = sscanf_s(ctx->max_cll,
+            "%hu,%hu",
+            (uint16_t*)&ctx->light_meta->MaxCLL,
+            (uint16_t*)&ctx->light_meta->MaxFALL
+        );
+
+        if (ret != total_max_cll_args) {
+            av_freep(ctx->light_meta);
+            ctx->light_meta = NULL;
+            av_log(avctx, AV_LOG_ERROR, "failed to parse max_cll option\n");
+            return AVERROR(EINVAL);
+        }
+    }
+
+    if (ctx->light_meta || ctx->master_display) {
+        if (ctx->in_trc == AVCOL_TRC_SMPTEST2084) {
+            res = ctx->amf_device_ctx->context->pVtbl->AllocBuffer(ctx->amf_device_ctx->context, AMF_MEMORY_HOST, sizeof(AMFHDRMetadata), &hdrmeta_buffer);
+            if (res == AMF_OK) {
+                hdrmeta = (AMFHDRMetadata*)hdrmeta_buffer->pVtbl->GetNative(hdrmeta_buffer);
+
+                av_amf_display_mastering_meta_to_hdrmeta(ctx->master_display, hdrmeta);
+                av_amf_light_metadata_to_hdrmeta(ctx->light_meta, hdrmeta);
+
+                AMF_ASSIGN_PROPERTY_INTERFACE(res, ctx->component, AMF_VIDEO_CONVERTER_INPUT_HDR_METADATA, hdrmeta_buffer);
+
+                hdrmeta_buffer->pVtbl->Release(hdrmeta_buffer);
+            }
+        } else {
+            av_log(avctx, AV_LOG_WARNING, "master_display/max_cll options are applicable only if in_trc is set to SMPTE2084\n");
+        }
+    }
+
     if (amf_color_profile != AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN) {
         AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_COLOR_PROFILE, amf_color_profile);
     }
 
-    if (ctx->color_range != AMF_COLOR_RANGE_UNDEFINED) {
-        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_OUTPUT_COLOR_RANGE, ctx->color_range);
+    if (ctx->out_color_range != AMF_COLOR_RANGE_UNDEFINED) {
+        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_OUTPUT_COLOR_RANGE, ctx->out_color_range);
     }
 
-    if (ctx->primaries != AMF_COLOR_PRIMARIES_UNDEFINED) {
-        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_OUTPUT_COLOR_PRIMARIES, ctx->primaries);
+    if (ctx->out_primaries != AMF_COLOR_PRIMARIES_UNDEFINED) {
+        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_OUTPUT_COLOR_PRIMARIES, ctx->out_primaries);
     }
 
-    if (ctx->trc != AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED) {
-        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_OUTPUT_TRANSFER_CHARACTERISTIC, ctx->trc);
+    if (ctx->out_trc != AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED) {
+        AMF_ASSIGN_PROPERTY_INT64(res, ctx->component, AMF_VIDEO_CONVERTER_OUTPUT_TRANSFER_CHARACTERISTIC, ctx->out_trc);
     }
 
     res = ctx->component->pVtbl->Init(ctx->component, av_av_to_amf_format(in_format), inlink->w, inlink->h);
@@ -181,11 +271,13 @@ static const AVOption vpp_amf_options[] = {
     { "bt709",          "BT.709",           0,  AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_CONVERTER_COLOR_PROFILE_709 },  0, 0, FLAGS, "color_profile" },
     { "bt2020",         "BT.2020",          0,  AV_OPT_TYPE_CONST, { .i64 = AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020 },  0, 0, FLAGS, "color_profile" },
 
-    { "color_range",    "Color range",          OFFSET(color_range),      AV_OPT_TYPE_INT,   { .i64 = AMF_COLOR_RANGE_UNDEFINED }, AMF_COLOR_RANGE_UNDEFINED, AMF_COLOR_RANGE_FULL, FLAGS, "color_range" },
-    { "studio",         "Studio",                   0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_RANGE_STUDIO }, 0, 0, FLAGS, "color_range" },
-    { "full",           "Full",                     0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_RANGE_FULL }, 0, 0, FLAGS, "color_range" },
+    { "in_color_range",  "Input color range",        OFFSET(in_color_range),  AV_OPT_TYPE_INT, { .i64 = AMF_COLOR_RANGE_UNDEFINED }, AMF_COLOR_RANGE_UNDEFINED, AMF_COLOR_RANGE_FULL, FLAGS, "color_range" },
+    { "out_color_range", "Output color range",       OFFSET(out_color_range), AV_OPT_TYPE_INT, { .i64 = AMF_COLOR_RANGE_UNDEFINED }, AMF_COLOR_RANGE_UNDEFINED, AMF_COLOR_RANGE_FULL, FLAGS, "color_range" },
+    { "studio",          "Studio",                   0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_RANGE_STUDIO }, 0, 0, FLAGS, "color_range" },
+    { "full",            "Full",                     0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_RANGE_FULL }, 0, 0, FLAGS, "color_range" },
 
-    { "primaries",      "Output color primaries",   OFFSET(primaries),  AV_OPT_TYPE_INT,   { .i64 = AMF_COLOR_PRIMARIES_UNDEFINED }, AMF_COLOR_PRIMARIES_UNDEFINED, AMF_COLOR_PRIMARIES_JEDEC_P22, FLAGS, "primaries" },
+    { "in_primaries",   "Input color primaries",    OFFSET(in_primaries),  AV_OPT_TYPE_INT, { .i64 = AMF_COLOR_PRIMARIES_UNDEFINED }, AMF_COLOR_PRIMARIES_UNDEFINED, AMF_COLOR_PRIMARIES_JEDEC_P22, FLAGS, "primaries" },
+    { "out_primaries",  "Output color primaries",   OFFSET(out_primaries), AV_OPT_TYPE_INT, { .i64 = AMF_COLOR_PRIMARIES_UNDEFINED }, AMF_COLOR_PRIMARIES_UNDEFINED, AMF_COLOR_PRIMARIES_JEDEC_P22, FLAGS, "primaries" },
     { "bt709",          "BT.709",                   0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_PRIMARIES_BT709 }, 0, 0, FLAGS, "primaries" },
     { "bt470m",         "BT.470M",                  0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_PRIMARIES_BT470M }, 0, 0, FLAGS, "primaries" },
     { "bt470bg",        "BT.470BG",                 0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_PRIMARIES_BT470BG }, 0, 0, FLAGS, "primaries" },
@@ -198,7 +290,8 @@ static const AVOption vpp_amf_options[] = {
     { "smpte432",       "SMPTE432",                 0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_PRIMARIES_SMPTE432 }, 0, 0, FLAGS, "primaries" },
     { "jedec-p22",      "JEDEC_P22",                0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_PRIMARIES_JEDEC_P22 }, 0, 0, FLAGS, "primaries" },
 
-    { "trc",            "Output transfer characteristics",  OFFSET(trc),  AV_OPT_TYPE_INT,   { .i64 = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED }, AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, AMF_COLOR_TRANSFER_CHARACTERISTIC_ARIB_STD_B67, FLAGS, "trc" },
+    { "in_trc",         "Input transfer characteristics",   OFFSET(in_trc),  AV_OPT_TYPE_INT, { .i64 = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED }, AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, AMF_COLOR_TRANSFER_CHARACTERISTIC_ARIB_STD_B67, FLAGS, "trc" },
+    { "out_trc",        "Output transfer characteristics",  OFFSET(out_trc), AV_OPT_TYPE_INT, { .i64 = AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED }, AMF_COLOR_TRANSFER_CHARACTERISTIC_UNDEFINED, AMF_COLOR_TRANSFER_CHARACTERISTIC_ARIB_STD_B67, FLAGS, "trc" },
     { "bt709",          "BT.709",                   0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_TRANSFER_CHARACTERISTIC_BT709 }, 0, 0, FLAGS, "trc" },
     { "gamma22",        "GAMMA22",                  0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA22 }, 0, 0, FLAGS, "trc" },
     { "gamma28",        "GAMMA28",                  0,  AV_OPT_TYPE_CONST, { .i64 = AMF_COLOR_TRANSFER_CHARACTERISTIC_GAMMA28 }, 0, 0, FLAGS, "trc" },
@@ -223,6 +316,13 @@ static const AVOption vpp_amf_options[] = {
     { "force_divisible_by", "enforce that the output resolution is divisible by a defined integer when force_original_aspect_ratio is used", OFFSET(force_divisible_by), AV_OPT_TYPE_INT, { .i64 = 1}, 1, 256, FLAGS },
     { "reset_sar", "reset SAR to 1 and scale to square pixels if scaling proportionally", OFFSET(reset_sar), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, FLAGS },
 
+    { "master_display",
+      "set SMPTE2084 mastering display color volume info using libx265-style parameter string (G(%hu,%hu)B(%hu,%hu)R(%hu,%hu)WP(%hu,%hu)L(%u,%u)).",
+       OFFSET(disp_master), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS
+    },
+
+    { "max_cll", "set SMPTE2084 Max CLL and Max FALL values using libx265-style parameter string (%hu,%hu)", OFFSET(max_cll), AV_OPT_TYPE_STRING, { .str = NULL }, 0, 0, FLAGS },
+
     { NULL },
 };
 
diff --git a/libavutil/hwcontext_amf.c b/libavutil/hwcontext_amf.c
index 57bf08bc65..480fdb6391 100644
--- a/libavutil/hwcontext_amf.c
+++ b/libavutil/hwcontext_amf.c
@@ -16,12 +16,22 @@
  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
  */
 
+#include "mem.h"
 #include "buffer.h"
-#include "common.h"
+#include "pixfmt.h"
+#include "pixdesc.h"
+#include "imgutils.h"
 #include "hwcontext.h"
 #include "hwcontext_amf.h"
 #include "hwcontext_internal.h"
 #include "hwcontext_amf_internal.h"
+
+#include "libavutil/thread.h"
+#include "libavutil/avassert.h"
+
+#include <AMF/core/Surface.h>
+#include <AMF/core/Trace.h>
+
 #if CONFIG_VULKAN
 #include "hwcontext_vulkan.h"
 #endif
@@ -35,14 +45,6 @@
 #define COBJMACROS
 #include "libavutil/hwcontext_dxva2.h"
 #endif
-#include "mem.h"
-#include "pixdesc.h"
-#include "pixfmt.h"
-#include "imgutils.h"
-#include "thread.h"
-#include "libavutil/avassert.h"
-#include <AMF/core/Surface.h>
-#include <AMF/core/Trace.h>
 #ifdef _WIN32
 #include "compat/w32dlfcn.h"
 #else
@@ -150,6 +152,157 @@ enum AVPixelFormat av_amf_to_av_format(enum AMF_SURFACE_FORMAT fmt)
     return AV_PIX_FMT_NONE;
 }
 
+enum AMF_VIDEO_CONVERTER_COLOR_PROFILE_ENUM av_amf_get_color_profile(enum AVColorRange color_range, enum AVColorSpace color_space)
+{
+    switch (color_space) {
+    case AVCOL_SPC_SMPTE170M:
+        if (color_range == AVCOL_RANGE_JPEG) {
+            return AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_601;
+        } else {
+            return AMF_VIDEO_CONVERTER_COLOR_PROFILE_601;
+        }
+        break;
+    case AVCOL_SPC_BT709:
+        if (color_range == AVCOL_RANGE_JPEG) {
+            return AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_709;
+        } else {
+            return AMF_VIDEO_CONVERTER_COLOR_PROFILE_709;
+        }
+        break;
+    case AVCOL_SPC_BT2020_NCL:
+    case AVCOL_SPC_BT2020_CL:
+        if (color_range == AVCOL_RANGE_JPEG) {
+            return AMF_VIDEO_CONVERTER_COLOR_PROFILE_FULL_2020;
+        } else {
+            return AMF_VIDEO_CONVERTER_COLOR_PROFILE_2020;
+        }
+        break;
+
+    default:
+        return AMF_VIDEO_CONVERTER_COLOR_PROFILE_UNKNOWN;
+    }
+}
+
+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;
+        hdrmeta->maxMasteringLuminance =
+            (amf_uint32)(luma_den * av_q2d(display_meta->max_luminance));
+        hdrmeta->minMasteringLuminance =
+            FFMIN((amf_uint32)(luma_den * av_q2d(display_meta->min_luminance)), 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);
+    }
+
+    return 0;
+}
+
+int av_amf_light_metadata_to_hdrmeta(const AVContentLightMetadata *light_meta, AMFHDRMetadata *hdrmeta)
+{
+    if (!light_meta || !hdrmeta)
+        return AVERROR(EINVAL);
+
+    hdrmeta->maxContentLightLevel = (amf_uint16)light_meta->MaxCLL;
+    hdrmeta->maxFrameAverageLightLevel = (amf_uint16)light_meta->MaxFALL;
+
+    return 0;
+}
+
+int av_amf_extract_hdr_metadata(const AVFrame *frame, AMFHDRMetadata *hdrmeta)
+{
+    AVFrameSideData *sidedata;
+
+    if (!frame || !hdrmeta)
+        return AVERROR(EINVAL);
+
+    sidedata = av_frame_get_side_data(frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
+    if (!sidedata)
+        return AVERROR(ENODATA);
+
+    if (av_amf_display_mastering_meta_to_hdrmeta((AVMasteringDisplayMetadata *)sidedata->data, hdrmeta) != 0)
+        return AVERROR(ENODATA);
+
+    sidedata = av_frame_get_side_data(frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
+    if (sidedata)
+        av_amf_light_metadata_to_hdrmeta((AVContentLightMetadata *)sidedata->data, hdrmeta);
+
+    return 0;
+
+}
+
+int av_amf_attach_hdr_metadata(AVFrame *frame, const AMFHDRMetadata *hdrmeta) {
+    if (!hdrmeta || !frame)
+        return AVERROR(EINVAL);
+
+    AVMasteringDisplayMetadata *mastering =
+        av_mastering_display_metadata_create_side_data(frame);
+    const int chroma_den = 50000;
+    const int luma_den = 10000;
+
+    if (!mastering)
+      return AVERROR(ENOMEM);
+
+    mastering->display_primaries[0][0] =
+        av_make_q(hdrmeta->redPrimary[0], chroma_den);
+    mastering->display_primaries[0][1] =
+        av_make_q(hdrmeta->redPrimary[1], chroma_den);
+
+    mastering->display_primaries[1][0] =
+        av_make_q(hdrmeta->greenPrimary[0], chroma_den);
+    mastering->display_primaries[1][1] =
+        av_make_q(hdrmeta->greenPrimary[1], chroma_den);
+
+    mastering->display_primaries[2][0] =
+        av_make_q(hdrmeta->bluePrimary[0], chroma_den);
+    mastering->display_primaries[2][1] =
+        av_make_q(hdrmeta->bluePrimary[1], chroma_den);
+
+    mastering->white_point[0] = av_make_q(hdrmeta->whitePoint[0], chroma_den);
+    mastering->white_point[1] = av_make_q(hdrmeta->whitePoint[1], chroma_den);
+
+    mastering->max_luminance =
+        av_make_q(hdrmeta->maxMasteringLuminance, luma_den);
+    mastering->min_luminance =
+        av_make_q(hdrmeta->maxMasteringLuminance, luma_den);
+
+    mastering->has_luminance = 1;
+    mastering->has_primaries = 1;
+    if (hdrmeta->maxContentLightLevel) {
+      AVContentLightMetadata *light =
+          av_content_light_metadata_create_side_data(frame);
+
+      if (!light)
+        return AVERROR(ENOMEM);
+
+      light->MaxCLL = hdrmeta->maxContentLightLevel;
+      light->MaxFALL = hdrmeta->maxFrameAverageLightLevel;
+    }
+
+    return 0;
+}
+
 static const enum AVPixelFormat supported_formats[] = {
     AV_PIX_FMT_NV12,
     AV_PIX_FMT_YUV420P,
@@ -433,7 +586,7 @@ static int amf_device_init(AVHWDeviceContext *ctx)
             if (res != AMF_OK && res != AMF_ALREADY_INITIALIZED) {
                 if (res == AMF_NOT_SUPPORTED)
                     av_log(ctx, AV_LOG_ERROR, "AMF via Vulkan is not supported on the given device.\n");
-                 else
+                else
                     av_log(ctx, AV_LOG_ERROR, "AMF failed to initialise on the given Vulkan device: %d.\n", res);
                  return AVERROR(ENOSYS);
             }
@@ -462,7 +615,7 @@ static int amf_load_library(AVAMFDeviceContext* amf_ctx,  void* avcl)
     version_fun = (AMFQueryVersion_Fn)dlsym(amf_ctx->library, AMF_QUERY_VERSION_FUNCTION_NAME);
     AMF_RETURN_IF_FALSE(avcl, version_fun != NULL, AVERROR_UNKNOWN, "DLL %s failed to find function %s\n", AMF_DLL_NAMEA, AMF_QUERY_VERSION_FUNCTION_NAME);
 
-    res = version_fun(&amf_ctx->version);
+    res = version_fun((unsigned long long*)&amf_ctx->version);
     AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_QUERY_VERSION_FUNCTION_NAME, res);
     res = init_fun(AMF_FULL_VERSION, &amf_ctx->factory);
     AMF_RETURN_IF_FALSE(avcl, res == AMF_OK, AVERROR_UNKNOWN, "%s failed with error %d\n", AMF_INIT_FUNCTION_NAME, res);
diff --git a/libavutil/hwcontext_amf.h b/libavutil/hwcontext_amf.h
index 6f2cabc878..918eec97b8 100644
--- a/libavutil/hwcontext_amf.h
+++ b/libavutil/hwcontext_amf.h
@@ -26,6 +26,8 @@
 #include <AMF/core/Context.h>
 #include <AMF/core/Trace.h>
 #include <AMF/core/Debug.h>
+#include <AMF/components/ColorSpace.h>
+#include "libavutil/mastering_display_metadata.h"
 
 /**
  * This struct is allocated as AVHWDeviceContext.hwctx
@@ -47,4 +49,10 @@ typedef struct AVAMFDeviceContext {
 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);
+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);
+int av_amf_attach_hdr_metadata(AVFrame *frame, const AMFHDRMetadata *hdrmeta);
+
 #endif /* AVUTIL_HWCONTEXT_AMF_H */
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-02-20 12:05 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-20 12:04 [FFmpeg-devel] [PR] avfilter/vf_vpp_amf: Extend AMF Color Converter HDR capabilities (PR #21811) DmitriiGershenkop via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git