lavfi/tonemap_vaapi: Add support for HDR to HDR tone mapping
Usage example: ffmpeg -y -hwaccel vaapi -hwaccel_output_format vaapi -i hdr.mp4 \ -vf "tonemap_vaapi=display=7500 3000|34000 16000|13250 34500|15635 16450|500 10000000:extra_hw_frames=64" \ -c:v hevc_vaapi output.mp4 Signed-off-by: Xinpeng Sun <xinpeng.sun@intel.com> Signed-off-by: Haihao Xiang <haihao.xiang@intel.com>
This commit is contained in:
parent
db22804145
commit
35ae44c615
@ -27832,8 +27832,7 @@ The inputs have same memory layout for color channels, the overlay has additiona
|
||||
|
||||
@section tonemap_vaapi
|
||||
|
||||
Perform HDR(High Dynamic Range) to SDR(Standard Dynamic Range) conversion with tone-mapping.
|
||||
It maps the dynamic range of HDR10 content to the SDR content.
|
||||
Perform HDR-to-SDR or HDR-to-HDR tone-mapping.
|
||||
It currently only accepts HDR10 as input.
|
||||
|
||||
It accepts the following parameters:
|
||||
@ -27842,28 +27841,42 @@ It accepts the following parameters:
|
||||
@item format
|
||||
Specify the output pixel format.
|
||||
|
||||
Currently supported formats are:
|
||||
@table @var
|
||||
@item p010
|
||||
@item nv12
|
||||
@end table
|
||||
|
||||
Default is nv12.
|
||||
Default is nv12 for HDR-to-SDR tone-mapping and p010 for HDR-to-HDR
|
||||
tone-mapping.
|
||||
|
||||
@item primaries, p
|
||||
Set the output color primaries.
|
||||
|
||||
Default is bt709.
|
||||
Default is bt709 for HDR-to-SDR tone-mapping and same as input for HDR-to-HDR
|
||||
tone-mapping.
|
||||
|
||||
@item transfer, t
|
||||
Set the output transfer characteristics.
|
||||
|
||||
Default is bt709.
|
||||
Default is bt709 for HDR-to-SDR tone-mapping and same as input for HDR-to-HDR
|
||||
tone-mapping.
|
||||
|
||||
@item matrix, m
|
||||
Set the output colorspace matrix.
|
||||
|
||||
Default is bt709.
|
||||
Default is bt709 for HDR-to-SDR tone-mapping and same as input for HDR-to-HDR
|
||||
tone-mapping.
|
||||
|
||||
@item display
|
||||
Set the output mastering display colour volume. It is given by a '|'-separated
|
||||
list of two values, two values are space separated. It set display primaries
|
||||
x & y in G, B, R order, then white point x & y, the nominal minimum & maximum
|
||||
display luminances.
|
||||
|
||||
HDR-to-HDR tone-mapping will be performed when this option is set.
|
||||
|
||||
@item light
|
||||
Set the output content light level information. It accepts 2 space-separated
|
||||
values, the first input is the maximum light level and the second input is
|
||||
the maximum average light level.
|
||||
|
||||
It is ignored for HDR-to-SDR tone-mapping, and optional for HDR-to-HDR
|
||||
tone-mapping.
|
||||
|
||||
@end table
|
||||
|
||||
@ -27875,6 +27888,11 @@ Convert HDR(HDR10) video to bt2020-transfer-characteristic p010 format
|
||||
@example
|
||||
tonemap_vaapi=format=p010:t=bt2020-10
|
||||
@end example
|
||||
@item
|
||||
Convert HDR video to HDR video
|
||||
@example
|
||||
tonemap_vaapi=display=7500\ 3000|34000\ 16000|13250\ 34500|15635\ 16450|500\ 10000000
|
||||
@end example
|
||||
@end itemize
|
||||
|
||||
@section hstack_vaapi
|
||||
|
@ -39,7 +39,11 @@ typedef struct HDRVAAPIContext {
|
||||
enum AVColorTransferCharacteristic color_transfer;
|
||||
enum AVColorSpace color_matrix;
|
||||
|
||||
char *mastering_display;
|
||||
char *content_light;
|
||||
|
||||
VAHdrMetaDataHDR10 in_metadata;
|
||||
VAHdrMetaDataHDR10 out_metadata;
|
||||
|
||||
AVFrameSideData *src_display;
|
||||
AVFrameSideData *src_light;
|
||||
@ -146,6 +150,87 @@ static int tonemap_vaapi_save_metadata(AVFilterContext *avctx, AVFrame *input_fr
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tonemap_vaapi_update_sidedata(AVFilterContext *avctx, AVFrame *output_frame)
|
||||
{
|
||||
HDRVAAPIContext *ctx = avctx->priv;
|
||||
AVFrameSideData *metadata;
|
||||
AVMasteringDisplayMetadata *hdr_meta;
|
||||
AVFrameSideData *metadata_lt;
|
||||
AVContentLightMetadata *hdr_meta_lt;
|
||||
int i;
|
||||
const int mapping[3] = {1, 2, 0}; //green, blue, red
|
||||
const int chroma_den = 50000;
|
||||
const int luma_den = 10000;
|
||||
|
||||
metadata = av_frame_new_side_data(output_frame,
|
||||
AV_FRAME_DATA_MASTERING_DISPLAY_METADATA,
|
||||
sizeof(AVMasteringDisplayMetadata));
|
||||
if (!metadata)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
hdr_meta = (AVMasteringDisplayMetadata *)metadata->data;
|
||||
|
||||
for (i = 0; i < 3; i++) {
|
||||
const int j = mapping[i];
|
||||
hdr_meta->display_primaries[j][0].num = ctx->out_metadata.display_primaries_x[i];
|
||||
hdr_meta->display_primaries[j][0].den = chroma_den;
|
||||
|
||||
hdr_meta->display_primaries[j][1].num = ctx->out_metadata.display_primaries_y[i];
|
||||
hdr_meta->display_primaries[j][1].den = chroma_den;
|
||||
}
|
||||
|
||||
hdr_meta->white_point[0].num = ctx->out_metadata.white_point_x;
|
||||
hdr_meta->white_point[0].den = chroma_den;
|
||||
|
||||
hdr_meta->white_point[1].num = ctx->out_metadata.white_point_y;
|
||||
hdr_meta->white_point[1].den = chroma_den;
|
||||
hdr_meta->has_primaries = 1;
|
||||
|
||||
hdr_meta->max_luminance.num = ctx->out_metadata.max_display_mastering_luminance;
|
||||
hdr_meta->max_luminance.den = luma_den;
|
||||
|
||||
hdr_meta->min_luminance.num = ctx->out_metadata.min_display_mastering_luminance;
|
||||
hdr_meta->min_luminance.den = luma_den;
|
||||
hdr_meta->has_luminance = 1;
|
||||
|
||||
av_log(avctx, AV_LOG_DEBUG,
|
||||
"Mastering display colour volume(out):\n");
|
||||
av_log(avctx, AV_LOG_DEBUG,
|
||||
"G(%u,%u) B(%u,%u) R(%u,%u) WP(%u,%u)\n",
|
||||
ctx->out_metadata.display_primaries_x[0],
|
||||
ctx->out_metadata.display_primaries_y[0],
|
||||
ctx->out_metadata.display_primaries_x[1],
|
||||
ctx->out_metadata.display_primaries_y[1],
|
||||
ctx->out_metadata.display_primaries_x[2],
|
||||
ctx->out_metadata.display_primaries_y[2],
|
||||
ctx->out_metadata.white_point_x,
|
||||
ctx->out_metadata.white_point_y);
|
||||
av_log(avctx, AV_LOG_DEBUG,
|
||||
"max_display_mastering_luminance=%u, min_display_mastering_luminance=%u\n",
|
||||
ctx->out_metadata.max_display_mastering_luminance,
|
||||
ctx->out_metadata.min_display_mastering_luminance);
|
||||
|
||||
metadata_lt = av_frame_new_side_data(output_frame,
|
||||
AV_FRAME_DATA_CONTENT_LIGHT_LEVEL,
|
||||
sizeof(AVContentLightMetadata));
|
||||
if (!metadata_lt)
|
||||
return AVERROR(ENOMEM);
|
||||
|
||||
hdr_meta_lt = (AVContentLightMetadata *)metadata_lt->data;
|
||||
|
||||
hdr_meta_lt->MaxCLL = FFMIN(ctx->out_metadata.max_content_light_level, 65535);
|
||||
hdr_meta_lt->MaxFALL = FFMIN(ctx->out_metadata.max_pic_average_light_level, 65535);
|
||||
|
||||
av_log(avctx, AV_LOG_DEBUG,
|
||||
"Content light level information(out):\n");
|
||||
av_log(avctx, AV_LOG_DEBUG,
|
||||
"MaxCLL(%u) MaxFALL(%u)\n",
|
||||
ctx->out_metadata.max_content_light_level,
|
||||
ctx->out_metadata.max_pic_average_light_level);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int tonemap_vaapi_set_filter_params(AVFilterContext *avctx, AVFrame *input_frame)
|
||||
{
|
||||
VAAPIVPPContext *vpp_ctx = avctx->priv;
|
||||
@ -208,15 +293,26 @@ static int tonemap_vaapi_build_filter_params(AVFilterContext *avctx)
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
for (i = 0; i < num_query_caps; i++) {
|
||||
if (VA_TONE_MAPPING_HDR_TO_SDR & hdr_cap[i].caps_flag)
|
||||
break;
|
||||
}
|
||||
|
||||
if (i >= num_query_caps) {
|
||||
av_log(avctx, AV_LOG_ERROR,
|
||||
"VAAPI driver doesn't support HDR to SDR\n");
|
||||
return AVERROR(EINVAL);
|
||||
if (ctx->mastering_display) {
|
||||
for (i = 0; i < num_query_caps; i++) {
|
||||
if (VA_TONE_MAPPING_HDR_TO_HDR & hdr_cap[i].caps_flag)
|
||||
break;
|
||||
}
|
||||
if (i >= num_query_caps) {
|
||||
av_log(avctx, AV_LOG_ERROR,
|
||||
"VAAPI driver doesn't support HDR to HDR\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
} else {
|
||||
for (i = 0; i < num_query_caps; i++) {
|
||||
if (VA_TONE_MAPPING_HDR_TO_SDR & hdr_cap[i].caps_flag)
|
||||
break;
|
||||
}
|
||||
if (i >= num_query_caps) {
|
||||
av_log(avctx, AV_LOG_ERROR,
|
||||
"VAAPI driver doesn't support HDR to SDR\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
hdrtm_param.type = VAProcFilterHighDynamicRangeToneMapping;
|
||||
@ -241,6 +337,8 @@ static int tonemap_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame
|
||||
VAProcPipelineParameterBuffer params;
|
||||
int err;
|
||||
|
||||
VAHdrMetaData out_hdr_metadata;
|
||||
|
||||
av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
|
||||
av_get_pix_fmt_name(input_frame->format),
|
||||
input_frame->width, input_frame->height, input_frame->pts);
|
||||
@ -278,10 +376,15 @@ static int tonemap_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
/* Use BT709 by default for HDR to SDR output frame */
|
||||
output_frame->color_primaries = AVCOL_PRI_BT709;
|
||||
output_frame->color_trc = AVCOL_TRC_BT709;
|
||||
output_frame->colorspace = AVCOL_SPC_BT709;
|
||||
av_frame_remove_side_data(output_frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
|
||||
av_frame_remove_side_data(output_frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
|
||||
|
||||
if (!ctx->mastering_display) {
|
||||
/* Use BT709 by default for HDR to SDR output frame */
|
||||
output_frame->color_primaries = AVCOL_PRI_BT709;
|
||||
output_frame->color_trc = AVCOL_TRC_BT709;
|
||||
output_frame->colorspace = AVCOL_SPC_BT709;
|
||||
}
|
||||
|
||||
if (ctx->color_primaries != AVCOL_PRI_UNSPECIFIED)
|
||||
output_frame->color_primaries = ctx->color_primaries;
|
||||
@ -292,11 +395,24 @@ static int tonemap_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame
|
||||
if (ctx->color_matrix != AVCOL_SPC_UNSPECIFIED)
|
||||
output_frame->colorspace = ctx->color_matrix;
|
||||
|
||||
if (ctx->mastering_display) {
|
||||
err = tonemap_vaapi_update_sidedata(avctx, output_frame);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
}
|
||||
|
||||
err = ff_vaapi_vpp_init_params(avctx, ¶ms,
|
||||
input_frame, output_frame);
|
||||
if (err < 0)
|
||||
goto fail;
|
||||
|
||||
if (ctx->mastering_display) {
|
||||
out_hdr_metadata.metadata_type = VAProcHighDynamicRangeMetadataHDR10;
|
||||
out_hdr_metadata.metadata = &ctx->out_metadata;
|
||||
out_hdr_metadata.metadata_size = sizeof(VAHdrMetaDataHDR10);
|
||||
params.output_hdr_metadata = &out_hdr_metadata;
|
||||
}
|
||||
|
||||
if (vpp_ctx->nb_filter_buffers) {
|
||||
params.filters = &vpp_ctx->filter_buffers[0];
|
||||
params.num_filters = vpp_ctx->nb_filter_buffers;
|
||||
@ -312,9 +428,6 @@ static int tonemap_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame
|
||||
av_get_pix_fmt_name(output_frame->format),
|
||||
output_frame->width, output_frame->height, output_frame->pts);
|
||||
|
||||
av_frame_remove_side_data(output_frame, AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
|
||||
av_frame_remove_side_data(output_frame, AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
|
||||
|
||||
return ff_filter_frame(outlink, output_frame);
|
||||
|
||||
fail:
|
||||
@ -335,8 +448,13 @@ static av_cold int tonemap_vaapi_init(AVFilterContext *avctx)
|
||||
if (ctx->output_format_string) {
|
||||
vpp_ctx->output_format = av_get_pix_fmt(ctx->output_format_string);
|
||||
} else {
|
||||
vpp_ctx->output_format = AV_PIX_FMT_NV12;
|
||||
av_log(avctx, AV_LOG_VERBOSE, "Output format not set, use default format NV12 for HDR to SDR tone mapping.\n");
|
||||
if (ctx->mastering_display) {
|
||||
vpp_ctx->output_format = AV_PIX_FMT_P010;
|
||||
av_log(avctx, AV_LOG_VERBOSE, "Output format not set, use default format P010 for HDR to HDR tone mapping.\n");
|
||||
} else {
|
||||
vpp_ctx->output_format = AV_PIX_FMT_NV12;
|
||||
av_log(avctx, AV_LOG_VERBOSE, "Output format not set, use default format NV12 for HDR to SDR tone mapping.\n");
|
||||
}
|
||||
}
|
||||
|
||||
#define STRING_OPTION(var_name, func_name, default_value) do { \
|
||||
@ -356,6 +474,37 @@ static av_cold int tonemap_vaapi_init(AVFilterContext *avctx)
|
||||
STRING_OPTION(color_transfer, color_transfer, AVCOL_TRC_UNSPECIFIED);
|
||||
STRING_OPTION(color_matrix, color_space, AVCOL_SPC_UNSPECIFIED);
|
||||
|
||||
if (ctx->mastering_display) {
|
||||
if (10 != sscanf(ctx->mastering_display,
|
||||
"%hu %hu|%hu %hu|%hu %hu|%hu %hu|%u %u",
|
||||
&ctx->out_metadata.display_primaries_x[0],
|
||||
&ctx->out_metadata.display_primaries_y[0],
|
||||
&ctx->out_metadata.display_primaries_x[1],
|
||||
&ctx->out_metadata.display_primaries_y[1],
|
||||
&ctx->out_metadata.display_primaries_x[2],
|
||||
&ctx->out_metadata.display_primaries_y[2],
|
||||
&ctx->out_metadata.white_point_x,
|
||||
&ctx->out_metadata.white_point_y,
|
||||
&ctx->out_metadata.min_display_mastering_luminance,
|
||||
&ctx->out_metadata.max_display_mastering_luminance)) {
|
||||
av_log(avctx, AV_LOG_ERROR,
|
||||
"Option mastering-display input invalid\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
|
||||
if (!ctx->content_light) {
|
||||
ctx->out_metadata.max_content_light_level = 0;
|
||||
ctx->out_metadata.max_pic_average_light_level = 0;
|
||||
} else if (2 != sscanf(ctx->content_light,
|
||||
"%hu %hu",
|
||||
&ctx->out_metadata.max_content_light_level,
|
||||
&ctx->out_metadata.max_pic_average_light_level)) {
|
||||
av_log(avctx, AV_LOG_ERROR,
|
||||
"Option content-light input invalid\n");
|
||||
return AVERROR(EINVAL);
|
||||
}
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
@ -381,6 +530,12 @@ static const AVOption tonemap_vaapi_options[] = {
|
||||
{ "t", "Output color transfer characteristics set",
|
||||
OFFSET(color_transfer_string), AV_OPT_TYPE_STRING,
|
||||
{ .str = NULL }, .flags = FLAGS, .unit = "transfer" },
|
||||
{ "display", "set mastering display colour volume",
|
||||
OFFSET(mastering_display), AV_OPT_TYPE_STRING,
|
||||
{ .str = NULL }, .flags = FLAGS },
|
||||
{ "light", "set content light level information",
|
||||
OFFSET(content_light), AV_OPT_TYPE_STRING,
|
||||
{ .str = NULL }, .flags = FLAGS },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user