diff mbox series

[FFmpeg-devel] libavcodec/qsvenc: Adding support for HDR metadata to hevc qsv encode

Message ID 20201202131826.10558-1-omondifredrick@gmail.com
State New
Headers show
Series [FFmpeg-devel] libavcodec/qsvenc: Adding support for HDR metadata to hevc qsv encode
Related show

Checks

Context Check Description
andriy/x86_make success Make finished
andriy/x86_make_fate success Make fate finished
andriy/PPC64_make success Make finished
andriy/PPC64_make_fate success Make fate finished

Commit Message

Fredrick Odhiambo Dec. 2, 2020, 1:18 p.m. UTC
This patch allows passing of HDR10 metadata through qsv_params for encoding with hevc_qsv similar to how HDR10 parameters are passed to x265-params in libx265 encoding. The HDR10 metadata parameters passed are master-display, max-cll, color primary, color matrix, transfer characteristics and color range.

-qsv_params master-display=R(35399,14599)G(8500,39850)B(6550,2300)WP(15634,16450)L(10000000,50):max-cll=1000,400:colorprim=bt2020:colormatrix=bt2020c:transfer=smpte2084:range=full -y hdr_vid_output.mp4

Signed-off-by: Fredrick Odhiambo <fredrick.odhiambo@intel.com>
---
 libavcodec/qsvenc.c | 425 ++++++++++++++++++++++++++++++++++++++++++++
 libavcodec/qsvenc.h | 110 +++++++++++-
 2 files changed, 534 insertions(+), 1 deletion(-)

Comments

Xiang, Haihao Dec. 3, 2020, 8:27 a.m. UTC | #1
On Wed, 2020-12-02 at 13:18 +0000, Fredrick Odhiambo wrote:
> This patch allows passing of HDR10 metadata through qsv_params for encoding
> with hevc_qsv similar to how HDR10 parameters are passed to x265-params in
> libx265 encoding. The HDR10 metadata parameters passed are master-display,
> max-cll, color primary, color matrix, transfer characteristics and color
> range.
> 
> -qsv_params master-
> display=R(35399,14599)G(8500,39850)B(6550,2300)WP(15634,16450)L(10000000,50):m
> ax-
> cll=1000,400:colorprim=bt2020:colormatrix=bt2020c:transfer=smpte2084:range=ful
> l -y hdr_vid_output.mp4

May we get the HDR parameters from input frame via
AV_FRAME_DATA_MASTERING_DISPLAY_METADATA ? 

avcodec has already provided options color_primaries / color_trc / colorspace /
color_range to set the corresponding parameters in avctx. 

> 
> Signed-off-by: Fredrick Odhiambo <fredrick.odhiambo@intel.com>
> ---
>  libavcodec/qsvenc.c | 425 ++++++++++++++++++++++++++++++++++++++++++++
>  libavcodec/qsvenc.h | 110 +++++++++++-
>  2 files changed, 534 insertions(+), 1 deletion(-)
> 
> diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
> index 2bd2a56227..29faa59497 100644
> --- a/libavcodec/qsvenc.c
> +++ b/libavcodec/qsvenc.c
> @@ -1102,6 +1102,7 @@ int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext
> *q)
>      int iopattern = 0;
>      int opaque_alloc = 0;
>      int ret;
> +    AVContentLightMetadata light_meta;
>  
>      q->param.AsyncDepth = q->async_depth;
>  
> @@ -1203,6 +1204,92 @@ int ff_qsv_enc_init(AVCodecContext *avctx,
> QSVEncContext *q)
>              return ret;
>      }
>  
> +    if (q->qsv_opts) {
> +        AVDictionaryEntry *en = NULL;
> +        AVMasteringDisplayMetadata hdr_meta;
> +        memset(&hdr_meta, 0, sizeof(AVMasteringDisplayMetadata));
> +        memset(&light_meta, 0, sizeof(AVContentLightMetadata));
> +        if (!avctx) {
> +            return ff_qsv_print_error(avctx, MFX_ERR_INVALID_VIDEO_PARAM,
> +                                  "AVCodecContext no set");
> +        }

Don't check avctx against NULL after the pointer has been dereferenced. 

> +
> +        while ((en = av_dict_get(q->qsv_opts, "", en,
> AV_DICT_IGNORE_SUFFIX))) {
> +            int parse_ret = ff_qsv_validate_params(en->key, en->value);
> +            switch (parse_ret) {
> +                case QSV_PARAM_VALID:
> +                       if (!strcmp(en->key, "master-display")) {

Please use the same indentation style.

> +                        ff_qsv_extract_display_values(en->value, &hdr_meta);
> +                        break;
> +                    }
> +                    if (!strcmp(en->key, "range")) {
> +                        avctx->color_range = ff_qsv_extract_range_value(en-
> >value);
> +                        break;
> +                    }
> +                    if (!strcmp(en->key, "max-cll")) {
> +                         ff_qsv_get_content_light_level(en->value,
> &light_meta);
> +                         break;
> +                    }
> +                    if (!strcmp(en->key, "colorprim")) {
> +                        avctx->color_primaries =
> av_color_primaries_from_name(en->value);
> +                        break;
> +                    }
> +                    if (!strcmp(en->key, "colormatrix")) {
> +                        avctx->colorspace = av_color_space_from_name(en-
> >value);
> +                        break;
> +                    }
> +                    if (!strcmp(en->key, "transfer")) {
> +                        avctx->color_trc = av_color_transfer_from_name(en-
> >value);
> +                        break;
> +                    }
> +                case QSV_PARAM_BAD_VALUE:
> +                    av_log(q, AV_LOG_ERROR,
> +                        "Invalid value for %s: %s.\n", en->key, en->value);
> +                    return ff_qsv_print_error(avctx,
> MFX_ERR_INVALID_VIDEO_PARAM,
> +                                  "Invalid value");
> +                case QSV_PARAM_BAD_NAME:
> +                    av_log(q, AV_LOG_ERROR,
> +                        "Invalid value for %s: %s.\n", en->key, en->value);
> +                    return ff_qsv_print_error(avctx,
> MFX_ERR_INVALID_VIDEO_PARAM,
> +                                  "Invalid parameter name");
> +            }
> +        }
> +#if QSV_HAVE_HDR_METADATA
> +        if (hdr_meta.has_primaries) {
> +            q->master_display_volume_data.Header.BufferId =
> MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME;
> +            q->master_display_volume_data.Header.BufferSz = sizeof(q-
> >master_display_volume_data);
> +            q->master_display_volume_data.InsertPayloadToggle =
> MFX_PAYLOAD_IDR;
> +            q->master_display_volume_data.DisplayPrimariesX[0] =
> (int)av_q2d(hdr_meta.display_primaries[0][0]);
> +            q->master_display_volume_data.DisplayPrimariesX[1] =
> (int)av_q2d(hdr_meta.display_primaries[0][1]);
> +            q->master_display_volume_data.DisplayPrimariesX[2] =
> (int)av_q2d(hdr_meta.display_primaries[1][0]);
> +            q->master_display_volume_data.DisplayPrimariesY[0] =
> (int)av_q2d(hdr_meta.display_primaries[1][1]);
> +            q->master_display_volume_data.DisplayPrimariesY[1] =
> (int)av_q2d(hdr_meta.display_primaries[2][0]);
> +            q->master_display_volume_data.DisplayPrimariesY[2] =
> (int)av_q2d(hdr_meta.display_primaries[2][1]);

DisplayPrimariesX[0] and DisplayPrimariesY[0] in MSDK is R however
display_primaries[0][0] and display_primaries[0][1] is R in FFmpeg, so the above
assignments are wrong. 


> +            q->master_display_volume_data.WhitePointX =
> (int)av_q2d(hdr_meta.white_point[0]);
> +            q->master_display_volume_data.WhitePointY =
> (int)av_q2d(hdr_meta.white_point[1]);
> +            q->master_display_volume_data.MaxDisplayMasteringLuminance =
> (int)av_q2d(hdr_meta.max_luminance);
> +            q->master_display_volume_data.MinDisplayMasteringLuminance =
> (int)av_q2d(hdr_meta.min_luminance);
> +
> +            q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer
> *)&q->master_display_volume_data;
> +            q->param.ExtParam[q->param.NumExtParam++] = (mfxExtBuffer*)&q-
> >master_display_volume_data;
> +
> +#if QSV_HAVE_VIDEO_SIGNAL_INFO
> +            q->video_signal_data.Header.BufferId =
> MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
> +            q->video_signal_data.Header.BufferSz = sizeof(q-
> >video_signal_data);
> +            q->video_signal_data.VideoFormat =
> VIDEO_SIGNAL_FORMAT_UNSPECIFIED;
> +            q->video_signal_data.VideoFullRange =
> ff_avcol_range_to_mfx_col_range(avctx->color_range);
> +            q->video_signal_data.ColourDescriptionPresent =
> VIDEO_SIGNAL_COLOR_DESCRIPTION_PRESENT;
> +            q->video_signal_data.ColourPrimaries = avctx->color_primaries;
> +            q->video_signal_data.TransferCharacteristics = avctx->color_trc;
> +            q->video_signal_data.MatrixCoefficients = avctx->colorspace;
> +
> +            q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer
> *)&q->video_signal_data;
> +            q->param.ExtParam[q->param.NumExtParam++] = (mfxExtBuffer*)&q-
> >video_signal_data;
> +#endif
> +        }
> +#endif
> +    }
> +
>      ret = MFXVideoENCODE_Init(q->session, &q->param);
>      if (ret < 0)
>          return ff_qsv_print_error(avctx, ret,
> @@ -1661,3 +1748,341 @@ const AVCodecHWConfigInternal *const
> ff_qsv_enc_hw_configs[] = {
>      HW_CONFIG_ENCODER_DEVICE(P010, QSV),
>      NULL,
>  };
> +
> +int ff_qsv_extract_values(char *input_value, int *output_values)
> +{
> +    char *p;
> +    int i;
> +    int res[2];
> +    char input_array[QSV_PARAMS_BUFFER_LENGTH];
> +    strcpy(input_array, input_value);
> +    for (i = 1, p = strtok(input_array, ","); p != NULL; p = strtok(NULL,
> ","), i++) {
> +        for (int j = 0; j < strlen(p); j++) {
> +            if (!isdigit(p[j])) {
> +                return AVERROR(EINVAL);
> +            }
> +        }
> +        res[i-1] = (int) strtol(p, (char **)NULL, 10);
> +    }
> +    output_values[0] = res[0];
> +    output_values[1] = res[1];
> +
> +    return 0;
> +}
> +
> +int ff_qsv_validate_cll(char * value)
> +{
> +    int n = strlen(value);
> +    int res[LIGHT_LEVEL_DATA_LENGTH];
> +    const int maximum_cll = 1000;
> +    const int minimum_cll = 400;
> +    if (!ff_qsv_extract_values(value, res)) {
> +        if (n > QSV_PARAMS_BUFFER_LENGTH) {
> +            return AVERROR(EINVAL);
> +        }
> +    }
> +    if (res[1] >= minimum_cll && res[0] <= maximum_cll) {
> +        return 0;
> +    }
> +
> +    return AVERROR(EINVAL);
> +}
> +
> +void ff_build_sub_string(char *input_string, char *output, int start_index)
> +{
> +    int n = strlen(input_string);
> +    for (int i = start_index; i < n; i++) {
> +        if (input_string[i] != '(' && input_string[i] != ')' &&
> input_string[i] != ' ' &&
> +            input_string[i] != 'P') {
> +            strncat(output, &input_string[i], 1);
> +        }
> +        if (input_string[i] ==')') {
> +            break;
> +        }
> +    }
> +}
> +
> +int ff_qsv_validate_display(char *value)
> +{
> +    int n = strlen(value);
> +    int ret = 0;
> +    char *red_values = NULL;
> +    char *blue_values = NULL;
> +    char *green_values = NULL;
> +    char *white_point_values = NULL;
> +    char *light_info_values = NULL;
> +    int xy_red_values[RGB_CHANNEL_LENGTH] ;
> +    int xy_green_values[RGB_CHANNEL_LENGTH];
> +    int xy_blue_values[RGB_CHANNEL_LENGTH];
> +    int xy_white_point_values[WHITE_POINT_DATA_LENGTH];
> +    int luminousity_values[LIGHT_LEVEL_DATA_LENGTH];
> +
> +    if (!(red_values = (char*)av_calloc(n + 1, sizeof(red_values))))
> +        return AVERROR(ENOMEM);
> +    if (!(green_values = (char*)av_calloc(n + 1, sizeof(green_values))))
> +        return AVERROR(ENOMEM);
> +    if (!(blue_values = (char*)av_calloc(n + 1, sizeof(blue_values))))
> +        return AVERROR(ENOMEM);
> +    if (!(white_point_values = (char*)av_calloc(n + 1,
> sizeof(white_point_values))))
> +        return AVERROR(ENOMEM);
> +    if (!(light_info_values = (char*)av_calloc(n + 1,
> sizeof(light_info_values))))
> +        return AVERROR(ENOMEM);
> +
> +    for (int i = 0; i < n; i++) {
> +        char current_char = value[i];
> +        switch (current_char) {
> +            case 'R':
> +                ff_build_sub_string(value, red_values, i+1);
> +            break;
> +            case 'G':
> +                ff_build_sub_string(value, green_values, i+1);
> +            break;
> +            case 'B':
> +                ff_build_sub_string(value, blue_values, i+1);
> +            break;
> +            case 'W':
> +                ff_build_sub_string(value, white_point_values, i+1);
> +            break;
> +            case 'L':
> +                ff_build_sub_string(value, light_info_values, i+1);
> +            break;
> +            default:
> +            break;
> +        }
> +    }
> +
> +    if (!ff_qsv_extract_values(red_values, xy_red_values) &&
> +        !ff_qsv_validate_display_color_range(xy_red_values) &&
> +        !ff_qsv_extract_values(green_values, xy_green_values) &&
> +        !ff_qsv_validate_display_color_range(xy_green_values) &&
> +        !ff_qsv_extract_values(blue_values, xy_blue_values) &&
> +        !ff_qsv_validate_display_color_range(xy_blue_values) &&
> +        !ff_qsv_extract_values(white_point_values, xy_white_point_values) &&
> +        !ff_qsv_validate_display_color_range(xy_white_point_values) &&
> +        !ff_qsv_extract_values(light_info_values, luminousity_values) &&
> +        !ff_qsv_validate_display_luminousity_range(luminousity_values)) {
> +            goto end;
> +    } else {
> +        ret = AVERROR(EINVAL);
> +    }
> +
> +end:
> +    av_free(red_values);
> +    av_free(green_values);
> +    av_free(blue_values);
> +    av_free(white_point_values);
> +    av_free(light_info_values);
> +
> +    return ret;
> +}
> +
> +int ff_qsv_validate_params(char *key, char *value)
> +{
> +    int key_is_valid = 0;
> +    int value_is_valid = 0;
> +    int key_index;
> +    int n = sizeof(qsv_key_names)/sizeof(qsv_key_names[0]);
> +    for (int i = 0;i < n; i++) {
> +        if (!strcmp(key, qsv_key_names[i])) {
> +            key_is_valid = 1;
> +            key_index = i;
> +        }
> +    }
> +    if (key_is_valid) {
> +        switch (key_index) {
> +            case 0:
> +                for (int i = 0; i <
> (sizeof(qsv_color_range_names)/sizeof(qsv_color_range_names[0])); i++) {
> +                    if (!strcasecmp(value, qsv_color_range_names[i])) {

strcasecmp is defined in strings.h which is not included in this file, so it
will break the compiling.

> +                        value_is_valid = 1;
> +                        break;
> +                    }
> +                }
> +                break;
> +            case 1:
> +                for(int i = 0; i <
> (sizeof(qsv_colorprim_names)/sizeof(qsv_colorprim_names[0])); i++) {
> +                    if (!strcmp(value, qsv_colorprim_names[i])) {
> +                        value_is_valid = 1;
> +                        break;
> +                    }
> +                }
> +                break;
> +            case 2:
> +                for(int i = 0; i <
> (sizeof(qsv_transfer_names)/sizeof(qsv_transfer_names[0])); i++) {
> +                    if (!strcmp(value, qsv_transfer_names[i])) {
> +                        value_is_valid = 1;
> +                        break;
> +                    }
> +                }
> +                break;
> +            case 3:
> +                for(int i = 0; i <
> (sizeof(qsv_colmatrix_names)/sizeof(qsv_colmatrix_names[0])); i++) {
> +                    if (!strcmp(value, qsv_colmatrix_names[i])) {
> +                        value_is_valid = 1;
> +                        break;
> +                    }
> +                }
> +                break;
> +            case 4:
> +                value_is_valid = (!ff_qsv_validate_display(value)) ? 1 : 0;
> +                break;
> +            case 5:
> +                value_is_valid = (!ff_qsv_validate_cll(value)) ? 1 : 0;
> +                break;
> +            default:
> +                break;
> +        }
> +    }
> +    if (!key_is_valid) {
> +        return QSV_PARAM_BAD_NAME;
> +    }
> +    if (!value_is_valid) {
> +        return QSV_PARAM_BAD_VALUE;
> +    }
> +    return QSV_PARAM_VALID;
> +}
> +
> +int ff_qsv_validate_display_color_range(int *input_colors)
> +{
> +    const int display_color_minimum = 0;
> +    const int display_color_maximum = 50000;
> +    if (( input_colors[0] >= display_color_minimum && input_colors[0] <=
> display_color_maximum) &&
> +        input_colors[1] >= display_color_minimum && input_colors[1] <=
> display_color_maximum) {
> +            return 0;
> +    }
> +
> +    return AVERROR(EINVAL);
> +}
> +
> +int ff_qsv_validate_display_luminousity_range(int *input_luminousity)
> +{
> +    const int display_primary_luminous_minimum = 50;
> +    const int display_primary_luminous_maximum = 10000000;
> +    if (input_luminousity[0] >= display_primary_luminous_minimum &&
> +        input_luminousity[1] <= display_primary_luminous_maximum) {
> +            return 0;
> +    }
> +
> +    return AVERROR(EINVAL);
> +}
> +
> +int ff_qsv_extract_range_value(char *value)
> +{
> +    if (!strcmp(value, "limited")) {
> +        return AVCOL_RANGE_MPEG;
> +    }
> +    if (!strcmp(value, "full")) {
> +         return AVCOL_RANGE_JPEG;
> +    }
> +
> +    return AVCOL_PRI_UNSPECIFIED;
> +}
> +
> +int ff_qsv_extract_display_values(char *value, AVMasteringDisplayMetadata
> *hdr_meta)
> +{
> +    int n = strlen(value);
> +    int ret = 0;
> +    char *red_values = NULL;
> +    char *blue_values = NULL;
> +    char *green_values = NULL;
> +    char *light_info_values = NULL;
> +    char *white_point_values = NULL;
> +    int hdr_data_red[RGB_CHANNEL_LENGTH];
> +    int hdr_data_green[RGB_CHANNEL_LENGTH];
> +    int hdr_data_blue[RGB_CHANNEL_LENGTH];
> +    int hdr_data_light_level[LIGHT_LEVEL_DATA_LENGTH];
> +    int hdr_data_white_point[WHITE_POINT_DATA_LENGTH];
> +    if (!(red_values = (char*)av_calloc(n + 1, sizeof(red_values))))
> +        return AVERROR(ENOMEM);
> +    if (!(green_values = (char*)av_calloc(n + 1, sizeof(green_values))))
> +        return AVERROR(ENOMEM);
> +    if (!(blue_values = (char*)av_calloc(n + 1, sizeof(blue_values))))
> +        return AVERROR(ENOMEM);
> +    if (!(white_point_values = (char*)av_calloc(n + 1,
> sizeof(white_point_values))))
> +        return AVERROR(ENOMEM);
> +    if (!(light_info_values = (char*)av_calloc(n + 1,
> sizeof(light_info_values))))
> +        return AVERROR(ENOMEM);
> +
> +    for (int i = 0; i < n; i++) {
> +        char current_char = value[i];
> +        switch (current_char) {
> +            case 'R':
> +                ff_build_sub_string(value, red_values, i+1);
> +            break;
> +            case 'G':
> +                ff_build_sub_string(value, green_values, i+1);
> +            break;
> +            case 'B':
> +                ff_build_sub_string(value, blue_values, i+1);
> +            break;
> +            case 'W':
> +                ff_build_sub_string(value, white_point_values, i+1);
> +            break;
> +            case 'L':
> +                ff_build_sub_string(value, light_info_values, i+1);
> +            break;
> +            default:
> +            break;
> +        }
> +    }
> +
> +    if (!ff_qsv_extract_values(red_values, hdr_data_red) &&
> +        !ff_qsv_extract_values(green_values, hdr_data_green) &&
> +        !ff_qsv_extract_values(blue_values, hdr_data_blue) &&
> +        !ff_qsv_extract_values(white_point_values, hdr_data_white_point) &&
> +        !ff_qsv_extract_values(light_info_values, hdr_data_light_level)) {
> +            hdr_meta->display_primaries[0][0] = (AVRational) {
> hdr_data_red[0], 1 };
> +            hdr_meta->display_primaries[0][1] = (AVRational) {
> hdr_data_red[1], 1 };
> +            hdr_meta->display_primaries[1][0] = (AVRational) {
> hdr_data_green[0], 1 };
> +            hdr_meta->display_primaries[1][1] = (AVRational) {
> hdr_data_green[1], 1 };
> +            hdr_meta->display_primaries[2][0] = (AVRational) {
> hdr_data_blue[0], 1 };
> +            hdr_meta->display_primaries[2][1] = (AVRational) {
> hdr_data_blue[1], 1 };
> +            hdr_meta->white_point[0] = (AVRational) {
> hdr_data_white_point[0], 1 };
> +            hdr_meta->white_point[1] = (AVRational) {
> hdr_data_white_point[1], 1 };
> +            hdr_meta->min_luminance = (AVRational) { hdr_data_light_level[0],
> 1 };
> +            hdr_meta->max_luminance =(AVRational) { hdr_data_light_level[1],
> 1 };
> +            hdr_meta->has_primaries = 1;
> +            hdr_meta->has_luminance = 1;
> +
> +            goto end;
> +    }else {
> +        ret = AVERROR(EINVAL);
> +    }
> +
> +end:
> +    av_free(red_values);
> +    av_free(green_values);
> +    av_free(blue_values);
> +    av_free(white_point_values);
> +    av_free(light_info_values);
> +
> +    return ret;
> +}
> +
> +int ff_qsv_get_content_light_level(char *value, AVContentLightMetadata
> *light_meta)
> +{
> +    int n = strlen(value);
> +    int results[2];
> +    if (!ff_qsv_extract_values(value, results)) {
> +        if (n > QSV_PARAMS_BUFFER_LENGTH) {
> +            return AVERROR(EINVAL);
> +        }
> +    }
> +    light_meta->MaxCLL = results[0];
> +    light_meta->MaxFALL = results[1];
> +
> +    return 0;
> +}
> +
> +int ff_avcol_range_to_mfx_col_range(int color_range)
> +{
> +    switch (color_range) {
> +        case AVCOL_RANGE_JPEG:
> +            return MFX_NOMINALRANGE_0_255;
> +            break;
> +        case AVCOL_RANGE_MPEG:
> +            return MFX_NOMINALRANGE_16_235;
> +            break;
> +        default:
> +            return MFX_NOMINALRANGE_UNKNOWN;
> +    }
> +}
> diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h
> index 6d305f87dd..b663e2fa05 100644
> --- a/libavcodec/qsvenc.h
> +++ b/libavcodec/qsvenc.h
> @@ -25,11 +25,18 @@
>  
>  #include <stdint.h>
>  #include <sys/types.h>
> +#include <string.h>
> +#include <stdlib.h>
> +#include <ctype.h>
>  
>  #include <mfx/mfxvideo.h>
>  
>  #include "libavutil/avutil.h"
>  #include "libavutil/fifo.h"
> +#include "libavutil/mastering_display_metadata.h"
> +#include "libavutil/rational.h"
> +#include "libavutil/parseutils.h"
> +#include "libavutil/pixdesc.h"
>  
>  #include "avcodec.h"
>  #include "hwconfig.h"
> @@ -38,6 +45,8 @@
>  #define QSV_HAVE_CO2 QSV_VERSION_ATLEAST(1, 6)
>  #define QSV_HAVE_CO3 QSV_VERSION_ATLEAST(1, 11)
>  #define QSV_HAVE_CO_VPS  QSV_VERSION_ATLEAST(1, 17)
> +#define QSV_HAVE_HDR_METADATA QSV_VERSION_ATLEAST(1, 26)
> +#define QSV_HAVE_VIDEO_SIGNAL_INFO QSV_VERSION_ATLEAST(1, 3

lack of ')' ?  It will break the compiling. 

>  
>  #define QSV_HAVE_EXT_HEVC_TILES QSV_VERSION_ATLEAST(1, 13)
>  #define QSV_HAVE_EXT_VP9_PARAM QSV_VERSION_ATLEAST(1, 26)
> @@ -74,6 +83,16 @@
>  #define MFX_LOOKAHEAD_DS_4x 0
>  #endif
>  
> +#define QSV_PARAM_BAD_NAME (-1)
> +#define QSV_PARAM_BAD_VALUE (-2)
> +#define QSV_PARAMS_BUFFER_LENGTH 100
> +#define QSV_PARAM_VALID (0)
> +#define VIDEO_SIGNAL_COLOR_DESCRIPTION_PRESENT (1)
> +#define VIDEO_SIGNAL_FORMAT_UNSPECIFIED (5)
> +#define RGB_CHANNEL_LENGTH (2)
> +#define LIGHT_LEVEL_DATA_LENGTH (2)
> +#define WHITE_POINT_DATA_LENGTH (2)
> +
>  #define QSV_COMMON_OPTS \
>  { "async_depth", "Maximum processing parallelism", OFFSET(qsv.async_depth),
> AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 1, INT_MAX, VE
> },                          \
>  { "avbr_accuracy",    "Accuracy of the AVBR
> ratecontrol",    OFFSET(qsv.avbr_accuracy),    AV_OPT_TYPE_INT, { .i64 = 0 },
> 0, INT_MAX, VE },                             \
> @@ -97,6 +116,19 @@
>  { "b_strategy",     "Strategy to choose between I/P/B-frames",
> OFFSET(qsv.b_strategy),    AV_OPT_TYPE_INT, { .i64 = -1 }, -1,          1, VE
> },                         \
>  { "forced_idr",     "Forcing I frames as IDR
> frames",         OFFSET(qsv.forced_idr),     AV_OPT_TYPE_BOOL,{ .i64 =
> 0  },  0,          1, VE },                         \
>  { "low_power", "enable low power mode(experimental: many limitations by mfx
> version, BRC modes, etc.)", OFFSET(qsv.low_power), AV_OPT_TYPE_BOOL, { .i64 =
> 0}, 0, 1, VE},\
> +{ "qsv_params", "set the qsv configuration using a :-separated list of
> key=value parameters", OFFSET(qsv.qsv_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE
> },\
> +
> +/* String keys and values accepted by qsv_param_keys*/
> +static const char * const qsv_color_range_names[] = { "limited", "full", 0 };
> +static const char * const qsv_colorprim_names[] = { "reserved", "bt709",
> "unknown",
> +                                                    "reserved", "bt470m",
> "bt470bg", "smpte170m", "smpte240m", "film", "bt2020", "smpte428", "smpte431",
> "smpte432", 0 };
> +static const char * const qsv_transfer_names[] = { "reserved", "bt709",
> "unknown", "reserved", "bt470m", "bt470bg", "smpte170m", "smpte240m",
> "linear", "log100",
> +                                                    "log316", "iec61966-2-4", 
> "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12",
> +                                                    "smpte2084", "smpte428",
> "arib-std-b67", 0 };
> +static const char * const qsv_colmatrix_names[] = { "gbr", "bt709",
> "unknown", "", "fcc", "bt470bg", "smpte170m", "smpte240m",
> +                                                      "ycgco", "bt2020nc",
> "bt2020c", "smpte2085", "chroma-derived-nc", "chroma-derived-c", "ictcp", 0 };
> +static const char * const qsv_key_names[] = { "range", "colorprim",
> "transfer", "colormatrix", "master-display", "max-cll" };
> +extern const AVCodecHWConfigInternal *const ff_qsv_enc_hw_configs[];
>  
>  extern const AVCodecHWConfigInternal *const ff_qsv_enc_hw_configs[];
>  
> @@ -131,6 +163,12 @@ typedef struct QSVEncContext {
>  #if QSV_HAVE_EXT_HEVC_TILES
>      mfxExtHEVCTiles exthevctiles;
>  #endif
> +#if QSV_HAVE_HDR_METADATA
> +    mfxExtMasteringDisplayColourVolume master_display_volume_data;
> +#endif
> +#if QSV_HAVE_VIDEO_SIGNAL_INFO
> +    mfxExtVideoSignalInfo video_signal_data;
> +#endif
>  #if QSV_HAVE_EXT_VP9_PARAM
>      mfxExtVP9Param  extvp9param;
>  #endif
> @@ -139,7 +177,7 @@ typedef struct QSVEncContext {
>      mfxFrameSurface1       **opaque_surfaces;
>      AVBufferRef             *opaque_alloc_buf;
>  
> -    mfxExtBuffer  *extparam_internal[2 + QSV_HAVE_CO2 + QSV_HAVE_CO3 +
> (QSV_HAVE_MF * 2)];
> +    mfxExtBuffer  *extparam_internal[2 + QSV_HAVE_CO2 + QSV_HAVE_CO3 +
> (QSV_HAVE_MF * 2) + QSV_HAVE_HDR_METADATA + QSV_HAVE_VIDEO_SIGNAL_INFO];
>      int         nb_extparam_internal;
>  
>      mfxExtBuffer **extparam;
> @@ -194,6 +232,7 @@ typedef struct QSVEncContext {
>      int gpb;
>  
>      int a53_cc;
> +    AVDictionary *qsv_opts;
>  
>  #if QSV_HAVE_MF
>      int mfmode;
> @@ -210,4 +249,73 @@ int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext
> *q,
>  
>  int ff_qsv_enc_close(AVCodecContext *avctx, QSVEncContext *q);
>  
> +/**
> + * Verify a key that specifies a specific qsv parameter exists and its value
> + * is valid
> + */
> +int ff_qsv_validate_params(char *key, char *value);
> +
> +/**
> + * validate master display values provided as input and check if they are
> within HDR
> + * range
> + */
> +int ff_qsv_validate_display(char *value);
> +
> +/**
> + * Extract the display_color values once verified and store in an
> AVMasteringDisplayMetadata
> + * struct
> + */
> +int ff_qsv_extract_display_values(char *value, AVMasteringDisplayMetadata
> *aVMasteringDisplayMetadata);
> +
> +/**
> + * Verify content light level is within acceptable HDR range
> + */
> +int ff_qsv_validate_cll(char *value);
> +
> +/**
> + * Parse and extract values from a string and store them in an array as
> integers
> + */
> +int ff_qsv_extract_values(char *input_value, int *output_values);
> +
> +/**
> + * validate display's RGB and white point color levels
> + * @param input_colors an array representation of the X and Y values of the
> color.
> + */
> +int ff_qsv_validate_display_color_range(int *input_colors);
> +
> +/**
> + * validate display's luminousity
> + * @param input_luminousity an array representation of the min and max values
> of the display luminousity.
> + */
> +
> +int ff_qsv_validate_display_luminousity_range(int *input_luminousity);
> +
> +/**
> + * Obtain video range value from the acceptable strings
> + * @param value a predefined string defining the video color range as per
> ISO/IEC TR 23091-4.
> + */
> +
> +int ff_qsv_extract_range_value(char *value);
> +
> +/**
> + * Obtain source video light level and assign these values to
> AVContentLightMetadata reference
> + * @param value a predefined string defining the light level.
> + * @param light_meta AVContentLightMetadata reference.
> + */
> + int ff_qsv_get_content_light_level(char *value, AVContentLightMetadata
> *light_meta);
> +
> +/**
> + * convert AVCodecContext color range value to equivalent mfx color range
> + * @param AVCodecContext color range
> + */
> +int ff_avcol_range_to_mfx_col_range(int color_range);
> +
> +/**
> +* Extract a substring from a string from a specified index
> + * @param start_index, where the target substring offset begins
> + * @param input_string, pointer to the first character of the original long
> string
> + * @param ouput_string,  pointer to the first character of the string
> variable that will hold the result substring
> + */
> +void ff_build_sub_string(char *input_string, char *ouput_string, int
> start_index);
> +
>  #endif /* AVCODEC_QSVENC_H */
diff mbox series

Patch

diff --git a/libavcodec/qsvenc.c b/libavcodec/qsvenc.c
index 2bd2a56227..29faa59497 100644
--- a/libavcodec/qsvenc.c
+++ b/libavcodec/qsvenc.c
@@ -1102,6 +1102,7 @@  int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q)
     int iopattern = 0;
     int opaque_alloc = 0;
     int ret;
+    AVContentLightMetadata light_meta;
 
     q->param.AsyncDepth = q->async_depth;
 
@@ -1203,6 +1204,92 @@  int ff_qsv_enc_init(AVCodecContext *avctx, QSVEncContext *q)
             return ret;
     }
 
+    if (q->qsv_opts) {
+        AVDictionaryEntry *en = NULL;
+        AVMasteringDisplayMetadata hdr_meta;
+        memset(&hdr_meta, 0, sizeof(AVMasteringDisplayMetadata));
+        memset(&light_meta, 0, sizeof(AVContentLightMetadata));
+        if (!avctx) {
+            return ff_qsv_print_error(avctx, MFX_ERR_INVALID_VIDEO_PARAM,
+                                  "AVCodecContext no set");
+        }
+
+        while ((en = av_dict_get(q->qsv_opts, "", en, AV_DICT_IGNORE_SUFFIX))) {
+            int parse_ret = ff_qsv_validate_params(en->key, en->value);
+            switch (parse_ret) {
+                case QSV_PARAM_VALID:
+                       if (!strcmp(en->key, "master-display")) {
+                        ff_qsv_extract_display_values(en->value, &hdr_meta);
+                        break;
+                    }
+                    if (!strcmp(en->key, "range")) {
+                        avctx->color_range = ff_qsv_extract_range_value(en->value);
+                        break;
+                    }
+                    if (!strcmp(en->key, "max-cll")) {
+                         ff_qsv_get_content_light_level(en->value, &light_meta);
+                         break;
+                    }
+                    if (!strcmp(en->key, "colorprim")) {
+                        avctx->color_primaries = av_color_primaries_from_name(en->value);
+                        break;
+                    }
+                    if (!strcmp(en->key, "colormatrix")) {
+                        avctx->colorspace = av_color_space_from_name(en->value);
+                        break;
+                    }
+                    if (!strcmp(en->key, "transfer")) {
+                        avctx->color_trc = av_color_transfer_from_name(en->value);
+                        break;
+                    }
+                case QSV_PARAM_BAD_VALUE:
+                    av_log(q, AV_LOG_ERROR,
+                        "Invalid value for %s: %s.\n", en->key, en->value);
+                    return ff_qsv_print_error(avctx, MFX_ERR_INVALID_VIDEO_PARAM,
+                                  "Invalid value");
+                case QSV_PARAM_BAD_NAME:
+                    av_log(q, AV_LOG_ERROR,
+                        "Invalid value for %s: %s.\n", en->key, en->value);
+                    return ff_qsv_print_error(avctx, MFX_ERR_INVALID_VIDEO_PARAM,
+                                  "Invalid parameter name");
+            }
+        }
+#if QSV_HAVE_HDR_METADATA
+        if (hdr_meta.has_primaries) {
+            q->master_display_volume_data.Header.BufferId = MFX_EXTBUFF_MASTERING_DISPLAY_COLOUR_VOLUME;
+            q->master_display_volume_data.Header.BufferSz = sizeof(q->master_display_volume_data);
+            q->master_display_volume_data.InsertPayloadToggle = MFX_PAYLOAD_IDR;
+            q->master_display_volume_data.DisplayPrimariesX[0] = (int)av_q2d(hdr_meta.display_primaries[0][0]);
+            q->master_display_volume_data.DisplayPrimariesX[1] = (int)av_q2d(hdr_meta.display_primaries[0][1]);
+            q->master_display_volume_data.DisplayPrimariesX[2] = (int)av_q2d(hdr_meta.display_primaries[1][0]);
+            q->master_display_volume_data.DisplayPrimariesY[0] = (int)av_q2d(hdr_meta.display_primaries[1][1]);
+            q->master_display_volume_data.DisplayPrimariesY[1] = (int)av_q2d(hdr_meta.display_primaries[2][0]);
+            q->master_display_volume_data.DisplayPrimariesY[2] = (int)av_q2d(hdr_meta.display_primaries[2][1]);
+            q->master_display_volume_data.WhitePointX = (int)av_q2d(hdr_meta.white_point[0]);
+            q->master_display_volume_data.WhitePointY = (int)av_q2d(hdr_meta.white_point[1]);
+            q->master_display_volume_data.MaxDisplayMasteringLuminance = (int)av_q2d(hdr_meta.max_luminance);
+            q->master_display_volume_data.MinDisplayMasteringLuminance = (int)av_q2d(hdr_meta.min_luminance);
+
+            q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->master_display_volume_data;
+            q->param.ExtParam[q->param.NumExtParam++] = (mfxExtBuffer*)&q->master_display_volume_data;
+
+#if QSV_HAVE_VIDEO_SIGNAL_INFO
+            q->video_signal_data.Header.BufferId = MFX_EXTBUFF_VIDEO_SIGNAL_INFO;
+            q->video_signal_data.Header.BufferSz = sizeof(q->video_signal_data);
+            q->video_signal_data.VideoFormat = VIDEO_SIGNAL_FORMAT_UNSPECIFIED;
+            q->video_signal_data.VideoFullRange = ff_avcol_range_to_mfx_col_range(avctx->color_range);
+            q->video_signal_data.ColourDescriptionPresent = VIDEO_SIGNAL_COLOR_DESCRIPTION_PRESENT;
+            q->video_signal_data.ColourPrimaries = avctx->color_primaries;
+            q->video_signal_data.TransferCharacteristics = avctx->color_trc;
+            q->video_signal_data.MatrixCoefficients = avctx->colorspace;
+
+            q->extparam_internal[q->nb_extparam_internal++] = (mfxExtBuffer *)&q->video_signal_data;
+            q->param.ExtParam[q->param.NumExtParam++] = (mfxExtBuffer*)&q->video_signal_data;
+#endif
+        }
+#endif
+    }
+
     ret = MFXVideoENCODE_Init(q->session, &q->param);
     if (ret < 0)
         return ff_qsv_print_error(avctx, ret,
@@ -1661,3 +1748,341 @@  const AVCodecHWConfigInternal *const ff_qsv_enc_hw_configs[] = {
     HW_CONFIG_ENCODER_DEVICE(P010, QSV),
     NULL,
 };
+
+int ff_qsv_extract_values(char *input_value, int *output_values)
+{
+    char *p;
+    int i;
+    int res[2];
+    char input_array[QSV_PARAMS_BUFFER_LENGTH];
+    strcpy(input_array, input_value);
+    for (i = 1, p = strtok(input_array, ","); p != NULL; p = strtok(NULL, ","), i++) {
+        for (int j = 0; j < strlen(p); j++) {
+            if (!isdigit(p[j])) {
+                return AVERROR(EINVAL);
+            }
+        }
+        res[i-1] = (int) strtol(p, (char **)NULL, 10);
+    }
+    output_values[0] = res[0];
+    output_values[1] = res[1];
+
+    return 0;
+}
+
+int ff_qsv_validate_cll(char * value)
+{
+    int n = strlen(value);
+    int res[LIGHT_LEVEL_DATA_LENGTH];
+    const int maximum_cll = 1000;
+    const int minimum_cll = 400;
+    if (!ff_qsv_extract_values(value, res)) {
+        if (n > QSV_PARAMS_BUFFER_LENGTH) {
+            return AVERROR(EINVAL);
+        }
+    }
+    if (res[1] >= minimum_cll && res[0] <= maximum_cll) {
+        return 0;
+    }
+
+    return AVERROR(EINVAL);
+}
+
+void ff_build_sub_string(char *input_string, char *output, int start_index)
+{
+    int n = strlen(input_string);
+    for (int i = start_index; i < n; i++) {
+        if (input_string[i] != '(' && input_string[i] != ')' && input_string[i] != ' ' &&
+            input_string[i] != 'P') {
+            strncat(output, &input_string[i], 1);
+        }
+        if (input_string[i] ==')') {
+            break;
+        }
+    }
+}
+
+int ff_qsv_validate_display(char *value)
+{
+    int n = strlen(value);
+    int ret = 0;
+    char *red_values = NULL;
+    char *blue_values = NULL;
+    char *green_values = NULL;
+    char *white_point_values = NULL;
+    char *light_info_values = NULL;
+    int xy_red_values[RGB_CHANNEL_LENGTH] ;
+    int xy_green_values[RGB_CHANNEL_LENGTH];
+    int xy_blue_values[RGB_CHANNEL_LENGTH];
+    int xy_white_point_values[WHITE_POINT_DATA_LENGTH];
+    int luminousity_values[LIGHT_LEVEL_DATA_LENGTH];
+
+    if (!(red_values = (char*)av_calloc(n + 1, sizeof(red_values))))
+        return AVERROR(ENOMEM);
+    if (!(green_values = (char*)av_calloc(n + 1, sizeof(green_values))))
+        return AVERROR(ENOMEM);
+    if (!(blue_values = (char*)av_calloc(n + 1, sizeof(blue_values))))
+        return AVERROR(ENOMEM);
+    if (!(white_point_values = (char*)av_calloc(n + 1, sizeof(white_point_values))))
+        return AVERROR(ENOMEM);
+    if (!(light_info_values = (char*)av_calloc(n + 1, sizeof(light_info_values))))
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < n; i++) {
+        char current_char = value[i];
+        switch (current_char) {
+            case 'R':
+                ff_build_sub_string(value, red_values, i+1);
+            break;
+            case 'G':
+                ff_build_sub_string(value, green_values, i+1);
+            break;
+            case 'B':
+                ff_build_sub_string(value, blue_values, i+1);
+            break;
+            case 'W':
+                ff_build_sub_string(value, white_point_values, i+1);
+            break;
+            case 'L':
+                ff_build_sub_string(value, light_info_values, i+1);
+            break;
+            default:
+            break;
+        }
+    }
+
+    if (!ff_qsv_extract_values(red_values, xy_red_values) &&
+        !ff_qsv_validate_display_color_range(xy_red_values) &&
+        !ff_qsv_extract_values(green_values, xy_green_values) &&
+        !ff_qsv_validate_display_color_range(xy_green_values) &&
+        !ff_qsv_extract_values(blue_values, xy_blue_values) &&
+        !ff_qsv_validate_display_color_range(xy_blue_values) &&
+        !ff_qsv_extract_values(white_point_values, xy_white_point_values) &&
+        !ff_qsv_validate_display_color_range(xy_white_point_values) &&
+        !ff_qsv_extract_values(light_info_values, luminousity_values) &&
+        !ff_qsv_validate_display_luminousity_range(luminousity_values)) {
+            goto end;
+    } else {
+        ret = AVERROR(EINVAL);
+    }
+
+end:
+    av_free(red_values);
+    av_free(green_values);
+    av_free(blue_values);
+    av_free(white_point_values);
+    av_free(light_info_values);
+
+    return ret;
+}
+
+int ff_qsv_validate_params(char *key, char *value)
+{
+    int key_is_valid = 0;
+    int value_is_valid = 0;
+    int key_index;
+    int n = sizeof(qsv_key_names)/sizeof(qsv_key_names[0]);
+    for (int i = 0;i < n; i++) {
+        if (!strcmp(key, qsv_key_names[i])) {
+            key_is_valid = 1;
+            key_index = i;
+        }
+    }
+    if (key_is_valid) {
+        switch (key_index) {
+            case 0:
+                for (int i = 0; i < (sizeof(qsv_color_range_names)/sizeof(qsv_color_range_names[0])); i++) {
+                    if (!strcasecmp(value, qsv_color_range_names[i])) {
+                        value_is_valid = 1;
+                        break;
+                    }
+                }
+                break;
+            case 1:
+                for(int i = 0; i < (sizeof(qsv_colorprim_names)/sizeof(qsv_colorprim_names[0])); i++) {
+                    if (!strcmp(value, qsv_colorprim_names[i])) {
+                        value_is_valid = 1;
+                        break;
+                    }
+                }
+                break;
+            case 2:
+                for(int i = 0; i < (sizeof(qsv_transfer_names)/sizeof(qsv_transfer_names[0])); i++) {
+                    if (!strcmp(value, qsv_transfer_names[i])) {
+                        value_is_valid = 1;
+                        break;
+                    }
+                }
+                break;
+            case 3:
+                for(int i = 0; i < (sizeof(qsv_colmatrix_names)/sizeof(qsv_colmatrix_names[0])); i++) {
+                    if (!strcmp(value, qsv_colmatrix_names[i])) {
+                        value_is_valid = 1;
+                        break;
+                    }
+                }
+                break;
+            case 4:
+                value_is_valid = (!ff_qsv_validate_display(value)) ? 1 : 0;
+                break;
+            case 5:
+                value_is_valid = (!ff_qsv_validate_cll(value)) ? 1 : 0;
+                break;
+            default:
+                break;
+        }
+    }
+    if (!key_is_valid) {
+        return QSV_PARAM_BAD_NAME;
+    }
+    if (!value_is_valid) {
+        return QSV_PARAM_BAD_VALUE;
+    }
+    return QSV_PARAM_VALID;
+}
+
+int ff_qsv_validate_display_color_range(int *input_colors)
+{
+    const int display_color_minimum = 0;
+    const int display_color_maximum = 50000;
+    if (( input_colors[0] >= display_color_minimum && input_colors[0] <= display_color_maximum) &&
+        input_colors[1] >= display_color_minimum && input_colors[1] <= display_color_maximum) {
+            return 0;
+    }
+
+    return AVERROR(EINVAL);
+}
+
+int ff_qsv_validate_display_luminousity_range(int *input_luminousity)
+{
+    const int display_primary_luminous_minimum = 50;
+    const int display_primary_luminous_maximum = 10000000;
+    if (input_luminousity[0] >= display_primary_luminous_minimum &&
+        input_luminousity[1] <= display_primary_luminous_maximum) {
+            return 0;
+    }
+
+    return AVERROR(EINVAL);
+}
+
+int ff_qsv_extract_range_value(char *value)
+{
+    if (!strcmp(value, "limited")) {
+        return AVCOL_RANGE_MPEG;
+    }
+    if (!strcmp(value, "full")) {
+         return AVCOL_RANGE_JPEG;
+    }
+
+    return AVCOL_PRI_UNSPECIFIED;
+}
+
+int ff_qsv_extract_display_values(char *value, AVMasteringDisplayMetadata *hdr_meta)
+{
+    int n = strlen(value);
+    int ret = 0;
+    char *red_values = NULL;
+    char *blue_values = NULL;
+    char *green_values = NULL;
+    char *light_info_values = NULL;
+    char *white_point_values = NULL;
+    int hdr_data_red[RGB_CHANNEL_LENGTH];
+    int hdr_data_green[RGB_CHANNEL_LENGTH];
+    int hdr_data_blue[RGB_CHANNEL_LENGTH];
+    int hdr_data_light_level[LIGHT_LEVEL_DATA_LENGTH];
+    int hdr_data_white_point[WHITE_POINT_DATA_LENGTH];
+    if (!(red_values = (char*)av_calloc(n + 1, sizeof(red_values))))
+        return AVERROR(ENOMEM);
+    if (!(green_values = (char*)av_calloc(n + 1, sizeof(green_values))))
+        return AVERROR(ENOMEM);
+    if (!(blue_values = (char*)av_calloc(n + 1, sizeof(blue_values))))
+        return AVERROR(ENOMEM);
+    if (!(white_point_values = (char*)av_calloc(n + 1, sizeof(white_point_values))))
+        return AVERROR(ENOMEM);
+    if (!(light_info_values = (char*)av_calloc(n + 1, sizeof(light_info_values))))
+        return AVERROR(ENOMEM);
+
+    for (int i = 0; i < n; i++) {
+        char current_char = value[i];
+        switch (current_char) {
+            case 'R':
+                ff_build_sub_string(value, red_values, i+1);
+            break;
+            case 'G':
+                ff_build_sub_string(value, green_values, i+1);
+            break;
+            case 'B':
+                ff_build_sub_string(value, blue_values, i+1);
+            break;
+            case 'W':
+                ff_build_sub_string(value, white_point_values, i+1);
+            break;
+            case 'L':
+                ff_build_sub_string(value, light_info_values, i+1);
+            break;
+            default:
+            break;
+        }
+    }
+
+    if (!ff_qsv_extract_values(red_values, hdr_data_red) &&
+        !ff_qsv_extract_values(green_values, hdr_data_green) &&
+        !ff_qsv_extract_values(blue_values, hdr_data_blue) &&
+        !ff_qsv_extract_values(white_point_values, hdr_data_white_point) &&
+        !ff_qsv_extract_values(light_info_values, hdr_data_light_level)) {
+            hdr_meta->display_primaries[0][0] = (AVRational) { hdr_data_red[0], 1 };
+            hdr_meta->display_primaries[0][1] = (AVRational) { hdr_data_red[1], 1 };
+            hdr_meta->display_primaries[1][0] = (AVRational) { hdr_data_green[0], 1 };
+            hdr_meta->display_primaries[1][1] = (AVRational) { hdr_data_green[1], 1 };
+            hdr_meta->display_primaries[2][0] = (AVRational) { hdr_data_blue[0], 1 };
+            hdr_meta->display_primaries[2][1] = (AVRational) { hdr_data_blue[1], 1 };
+            hdr_meta->white_point[0] = (AVRational) { hdr_data_white_point[0], 1 };
+            hdr_meta->white_point[1] = (AVRational) { hdr_data_white_point[1], 1 };
+            hdr_meta->min_luminance = (AVRational) { hdr_data_light_level[0], 1 };
+            hdr_meta->max_luminance =(AVRational) { hdr_data_light_level[1], 1 };
+            hdr_meta->has_primaries = 1;
+            hdr_meta->has_luminance = 1;
+
+            goto end;
+    }else {
+        ret = AVERROR(EINVAL);
+    }
+
+end:
+    av_free(red_values);
+    av_free(green_values);
+    av_free(blue_values);
+    av_free(white_point_values);
+    av_free(light_info_values);
+
+    return ret;
+}
+
+int ff_qsv_get_content_light_level(char *value, AVContentLightMetadata *light_meta)
+{
+    int n = strlen(value);
+    int results[2];
+    if (!ff_qsv_extract_values(value, results)) {
+        if (n > QSV_PARAMS_BUFFER_LENGTH) {
+            return AVERROR(EINVAL);
+        }
+    }
+    light_meta->MaxCLL = results[0];
+    light_meta->MaxFALL = results[1];
+
+    return 0;
+}
+
+int ff_avcol_range_to_mfx_col_range(int color_range)
+{
+    switch (color_range) {
+        case AVCOL_RANGE_JPEG:
+            return MFX_NOMINALRANGE_0_255;
+            break;
+        case AVCOL_RANGE_MPEG:
+            return MFX_NOMINALRANGE_16_235;
+            break;
+        default:
+            return MFX_NOMINALRANGE_UNKNOWN;
+    }
+}
diff --git a/libavcodec/qsvenc.h b/libavcodec/qsvenc.h
index 6d305f87dd..b663e2fa05 100644
--- a/libavcodec/qsvenc.h
+++ b/libavcodec/qsvenc.h
@@ -25,11 +25,18 @@ 
 
 #include <stdint.h>
 #include <sys/types.h>
+#include <string.h>
+#include <stdlib.h>
+#include <ctype.h>
 
 #include <mfx/mfxvideo.h>
 
 #include "libavutil/avutil.h"
 #include "libavutil/fifo.h"
+#include "libavutil/mastering_display_metadata.h"
+#include "libavutil/rational.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/pixdesc.h"
 
 #include "avcodec.h"
 #include "hwconfig.h"
@@ -38,6 +45,8 @@ 
 #define QSV_HAVE_CO2 QSV_VERSION_ATLEAST(1, 6)
 #define QSV_HAVE_CO3 QSV_VERSION_ATLEAST(1, 11)
 #define QSV_HAVE_CO_VPS  QSV_VERSION_ATLEAST(1, 17)
+#define QSV_HAVE_HDR_METADATA QSV_VERSION_ATLEAST(1, 26)
+#define QSV_HAVE_VIDEO_SIGNAL_INFO QSV_VERSION_ATLEAST(1, 3
 
 #define QSV_HAVE_EXT_HEVC_TILES QSV_VERSION_ATLEAST(1, 13)
 #define QSV_HAVE_EXT_VP9_PARAM QSV_VERSION_ATLEAST(1, 26)
@@ -74,6 +83,16 @@ 
 #define MFX_LOOKAHEAD_DS_4x 0
 #endif
 
+#define QSV_PARAM_BAD_NAME (-1)
+#define QSV_PARAM_BAD_VALUE (-2)
+#define QSV_PARAMS_BUFFER_LENGTH 100
+#define QSV_PARAM_VALID (0)
+#define VIDEO_SIGNAL_COLOR_DESCRIPTION_PRESENT (1)
+#define VIDEO_SIGNAL_FORMAT_UNSPECIFIED (5)
+#define RGB_CHANNEL_LENGTH (2)
+#define LIGHT_LEVEL_DATA_LENGTH (2)
+#define WHITE_POINT_DATA_LENGTH (2)
+
 #define QSV_COMMON_OPTS \
 { "async_depth", "Maximum processing parallelism", OFFSET(qsv.async_depth), AV_OPT_TYPE_INT, { .i64 = ASYNC_DEPTH_DEFAULT }, 1, INT_MAX, VE },                          \
 { "avbr_accuracy",    "Accuracy of the AVBR ratecontrol",    OFFSET(qsv.avbr_accuracy),    AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, VE },                             \
@@ -97,6 +116,19 @@ 
 { "b_strategy",     "Strategy to choose between I/P/B-frames", OFFSET(qsv.b_strategy),    AV_OPT_TYPE_INT, { .i64 = -1 }, -1,          1, VE },                         \
 { "forced_idr",     "Forcing I frames as IDR frames",         OFFSET(qsv.forced_idr),     AV_OPT_TYPE_BOOL,{ .i64 = 0  },  0,          1, VE },                         \
 { "low_power", "enable low power mode(experimental: many limitations by mfx version, BRC modes, etc.)", OFFSET(qsv.low_power), AV_OPT_TYPE_BOOL, { .i64 = 0}, 0, 1, VE},\
+{ "qsv_params", "set the qsv configuration using a :-separated list of key=value parameters", OFFSET(qsv.qsv_opts), AV_OPT_TYPE_DICT, { 0 }, 0, 0, VE },\
+
+/* String keys and values accepted by qsv_param_keys*/
+static const char * const qsv_color_range_names[] = { "limited", "full", 0 };
+static const char * const qsv_colorprim_names[] = { "reserved", "bt709", "unknown",
+                                                    "reserved", "bt470m", "bt470bg", "smpte170m", "smpte240m", "film", "bt2020", "smpte428", "smpte431", "smpte432", 0 };
+static const char * const qsv_transfer_names[] = { "reserved", "bt709", "unknown", "reserved", "bt470m", "bt470bg", "smpte170m", "smpte240m", "linear", "log100",
+                                                    "log316", "iec61966-2-4", "bt1361e", "iec61966-2-1", "bt2020-10", "bt2020-12",
+                                                    "smpte2084", "smpte428", "arib-std-b67", 0 };
+static const char * const qsv_colmatrix_names[] = { "gbr", "bt709", "unknown", "", "fcc", "bt470bg", "smpte170m", "smpte240m",
+                                                      "ycgco", "bt2020nc", "bt2020c", "smpte2085", "chroma-derived-nc", "chroma-derived-c", "ictcp", 0 };
+static const char * const qsv_key_names[] = { "range", "colorprim", "transfer", "colormatrix", "master-display", "max-cll" };
+extern const AVCodecHWConfigInternal *const ff_qsv_enc_hw_configs[];
 
 extern const AVCodecHWConfigInternal *const ff_qsv_enc_hw_configs[];
 
@@ -131,6 +163,12 @@  typedef struct QSVEncContext {
 #if QSV_HAVE_EXT_HEVC_TILES
     mfxExtHEVCTiles exthevctiles;
 #endif
+#if QSV_HAVE_HDR_METADATA
+    mfxExtMasteringDisplayColourVolume master_display_volume_data;
+#endif
+#if QSV_HAVE_VIDEO_SIGNAL_INFO
+    mfxExtVideoSignalInfo video_signal_data;
+#endif
 #if QSV_HAVE_EXT_VP9_PARAM
     mfxExtVP9Param  extvp9param;
 #endif
@@ -139,7 +177,7 @@  typedef struct QSVEncContext {
     mfxFrameSurface1       **opaque_surfaces;
     AVBufferRef             *opaque_alloc_buf;
 
-    mfxExtBuffer  *extparam_internal[2 + QSV_HAVE_CO2 + QSV_HAVE_CO3 + (QSV_HAVE_MF * 2)];
+    mfxExtBuffer  *extparam_internal[2 + QSV_HAVE_CO2 + QSV_HAVE_CO3 + (QSV_HAVE_MF * 2) + QSV_HAVE_HDR_METADATA + QSV_HAVE_VIDEO_SIGNAL_INFO];
     int         nb_extparam_internal;
 
     mfxExtBuffer **extparam;
@@ -194,6 +232,7 @@  typedef struct QSVEncContext {
     int gpb;
 
     int a53_cc;
+    AVDictionary *qsv_opts;
 
 #if QSV_HAVE_MF
     int mfmode;
@@ -210,4 +249,73 @@  int ff_qsv_encode(AVCodecContext *avctx, QSVEncContext *q,
 
 int ff_qsv_enc_close(AVCodecContext *avctx, QSVEncContext *q);
 
+/**
+ * Verify a key that specifies a specific qsv parameter exists and its value
+ * is valid
+ */
+int ff_qsv_validate_params(char *key, char *value);
+
+/**
+ * validate master display values provided as input and check if they are within HDR
+ * range
+ */
+int ff_qsv_validate_display(char *value);
+
+/**
+ * Extract the display_color values once verified and store in an AVMasteringDisplayMetadata
+ * struct
+ */
+int ff_qsv_extract_display_values(char *value, AVMasteringDisplayMetadata *aVMasteringDisplayMetadata);
+
+/**
+ * Verify content light level is within acceptable HDR range
+ */
+int ff_qsv_validate_cll(char *value);
+
+/**
+ * Parse and extract values from a string and store them in an array as integers
+ */
+int ff_qsv_extract_values(char *input_value, int *output_values);
+
+/**
+ * validate display's RGB and white point color levels
+ * @param input_colors an array representation of the X and Y values of the color.
+ */
+int ff_qsv_validate_display_color_range(int *input_colors);
+
+/**
+ * validate display's luminousity
+ * @param input_luminousity an array representation of the min and max values of the display luminousity.
+ */
+
+int ff_qsv_validate_display_luminousity_range(int *input_luminousity);
+
+/**
+ * Obtain video range value from the acceptable strings
+ * @param value a predefined string defining the video color range as per ISO/IEC TR 23091-4.
+ */
+
+int ff_qsv_extract_range_value(char *value);
+
+/**
+ * Obtain source video light level and assign these values to AVContentLightMetadata reference
+ * @param value a predefined string defining the light level.
+ * @param light_meta AVContentLightMetadata reference.
+ */
+ int ff_qsv_get_content_light_level(char *value, AVContentLightMetadata *light_meta);
+
+/**
+ * convert AVCodecContext color range value to equivalent mfx color range
+ * @param AVCodecContext color range
+ */
+int ff_avcol_range_to_mfx_col_range(int color_range);
+
+/**
+* Extract a substring from a string from a specified index
+ * @param start_index, where the target substring offset begins
+ * @param input_string, pointer to the first character of the original long string
+ * @param ouput_string,  pointer to the first character of the string variable that will hold the result substring
+ */
+void ff_build_sub_string(char *input_string, char *ouput_string, int start_index);
+
 #endif /* AVCODEC_QSVENC_H */