From patchwork Wed Dec 2 13:18:26 2020 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Fredrick Odhiambo X-Patchwork-Id: 24321 Return-Path: X-Original-To: patchwork@ffaux-bg.ffmpeg.org Delivered-To: patchwork@ffaux-bg.ffmpeg.org Received: from ffbox0-bg.mplayerhq.hu (ffbox0-bg.ffmpeg.org [79.124.17.100]) by ffaux.localdomain (Postfix) with ESMTP id EF3AB44B013 for ; Wed, 2 Dec 2020 15:27:02 +0200 (EET) Received: from [127.0.1.1] (localhost [127.0.0.1]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTP id B84D8689B62; Wed, 2 Dec 2020 15:27:02 +0200 (EET) X-Original-To: ffmpeg-devel@ffmpeg.org Delivered-To: ffmpeg-devel@ffmpeg.org Received: from mail-wr1-f65.google.com (mail-wr1-f65.google.com [209.85.221.65]) by ffbox0-bg.mplayerhq.hu (Postfix) with ESMTPS id 8C825689248 for ; Wed, 2 Dec 2020 15:26:56 +0200 (EET) Received: by mail-wr1-f65.google.com with SMTP id o1so3950165wrx.7 for ; Wed, 02 Dec 2020 05:26:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=qcjwsERa4sMfG8lPJ2G8C/fiuWLxsyyQa8QvnuxjB34=; b=Bwv8yOLp5IQ8pfua4JNDkHDJel4IlR9yaHma2bFALDPQJPqMXy5zni5vx48+Wx8agj 0o2pnwOw7cAjUvETk/luFC4RTQntFwQKHsTkc4lRv6eRiAZTV3yN+BZY4idMEIcnKeyp t/Wiha3Um63Bf2vidQ0lPJYt+PJ5FsnaWYQJfCG+NNdteTh79y6htA9ujpokVVBNwwJD mZ3s7YpxBCCgrTt6HLB1aDTLKkJRiC7yRs0fZpAJsTGEFSJqcbOdXG71RNBmG6L5xade BToIn1DLciTS1C4Y1KK8n4EAjkvrk28rdPwKxvoNc05LiTWJMFhNPiDmxGeu2FRJtTqp UADg== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:mime-version :content-transfer-encoding; bh=qcjwsERa4sMfG8lPJ2G8C/fiuWLxsyyQa8QvnuxjB34=; b=kfFuvYMUpfpT+Joj5lz99bAy3RtcuVOSrYOGXDK/csEiH/EmZ+X+ZhMAT3ewySg6Hb ZsVJ6KOn4cMb6HpbJcSClRfU431CE0oZLDE/z5alY4l46Kh5v1gn0liDtsqWLEnhkECk tktV1XPPeZtA8uLmRYL6kiw2fxKAcnPHfSvaI4exC5/XBlX5qeR7u19wUqq6w4yjB0p7 vK9Zv4JP2d0LD7SzQHXRkuQo5sErA+d6QQwYf88DBHdEZqnTl9Y4oOsGJhpD3Skpg7m/ aa2mNCLXWT3cKk0tIg97BvNAdBPB/Xczw3T0QRxDhUGUxK+0PsL63ILPK73N4zAOBdch 9qig== X-Gm-Message-State: AOAM532HVtPjI9t4CCnlX/1Mw3zO6Z3Dnl0dOQr7JkPp72kz68VsfF6N +Z8adb4GO2BGcy3vMUj3RGQmik2Wo8oXq18p X-Google-Smtp-Source: ABdhPJwX2ltHleZWap6UcXqJTJFvU2BbhfsJbVB5fPP6il6TvBKNbpKfOknsGVXjlwnST4MdYOwtQw== X-Received: by 2002:adf:e611:: with SMTP id p17mr3356526wrm.180.1606915128119; Wed, 02 Dec 2020 05:18:48 -0800 (PST) Received: from localhost.localdomain (cpc81878-swin19-2-0-cust138.3-1.cable.virginm.net. [86.7.196.139]) by smtp.googlemail.com with ESMTPSA id r21sm2169732wrc.16.2020.12.02.05.18.47 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Wed, 02 Dec 2020 05:18:47 -0800 (PST) From: Fredrick Odhiambo To: ffmpeg-devel@ffmpeg.org Date: Wed, 2 Dec 2020 13:18:26 +0000 Message-Id: <20201202131826.10558-1-omondifredrick@gmail.com> X-Mailer: git-send-email 2.22.0 MIME-Version: 1.0 Subject: [FFmpeg-devel] [PATCH] libavcodec/qsvenc: Adding support for HDR metadata to hevc qsv encode X-BeenThere: ffmpeg-devel@ffmpeg.org X-Mailman-Version: 2.1.20 Precedence: list List-Id: FFmpeg development discussions and patches List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Reply-To: FFmpeg development discussions and patches Cc: Fredrick Odhiambo , Fredrick Odhiambo Errors-To: ffmpeg-devel-bounces@ffmpeg.org Sender: "ffmpeg-devel" 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 --- 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"); + } + + 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 #include +#include +#include +#include #include #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 */