2 * This file is part of FFmpeg.
4 * FFmpeg is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * FFmpeg is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with FFmpeg; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 #include "libavutil/avassert.h"
21 #include "libavutil/mem.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/pixdesc.h"
24 #include "libavutil/mastering_display_metadata.h"
29 #include "vaapi_vpp.h"
31 typedef struct HDRVAAPIContext {
32 VAAPIVPPContext vpp_ctx; // must be the first field
34 char *output_format_string;
36 char *color_primaries_string;
37 char *color_transfer_string;
38 char *color_matrix_string;
40 enum AVColorPrimaries color_primaries;
41 enum AVColorTransferCharacteristic color_transfer;
42 enum AVColorSpace color_matrix;
44 VAHdrMetaDataHDR10 in_metadata;
46 AVFrameSideData *src_display;
47 AVFrameSideData *src_light;
50 static int tonemap_vaapi_save_metadata(AVFilterContext *avctx, AVFrame *input_frame)
52 HDRVAAPIContext *ctx = avctx->priv;
53 AVMasteringDisplayMetadata *hdr_meta;
54 AVContentLightMetadata *light_meta;
56 if (input_frame->color_trc != AVCOL_TRC_SMPTE2084) {
57 av_log(avctx, AV_LOG_WARNING, "Only support HDR10 as input for vaapi tone-mapping\n");
60 ctx->src_display = av_frame_get_side_data(input_frame,
61 AV_FRAME_DATA_MASTERING_DISPLAY_METADATA);
62 if (ctx->src_display) {
63 hdr_meta = (AVMasteringDisplayMetadata *)ctx->src_display->data;
65 av_log(avctx, AV_LOG_ERROR, "No mastering display data\n");
66 return AVERROR(EINVAL);
69 if (hdr_meta->has_luminance) {
70 const int luma_den = 10000;
71 ctx->in_metadata.max_display_mastering_luminance =
72 lrint(luma_den * av_q2d(hdr_meta->max_luminance));
73 ctx->in_metadata.min_display_mastering_luminance =
74 FFMIN(lrint(luma_den * av_q2d(hdr_meta->min_luminance)),
75 ctx->in_metadata.max_display_mastering_luminance);
77 av_log(avctx, AV_LOG_DEBUG,
78 "Mastering Display Metadata(in luminance):\n");
79 av_log(avctx, AV_LOG_DEBUG,
80 "min_luminance=%u, max_luminance=%u\n",
81 ctx->in_metadata.min_display_mastering_luminance,
82 ctx->in_metadata.max_display_mastering_luminance);
85 if (hdr_meta->has_primaries) {
87 const int mapping[3] = {1, 2, 0}; //green, blue, red
88 const int chroma_den = 50000;
90 for (i = 0; i < 3; i++) {
91 const int j = mapping[i];
92 ctx->in_metadata.display_primaries_x[i] =
93 FFMIN(lrint(chroma_den *
94 av_q2d(hdr_meta->display_primaries[j][0])),
96 ctx->in_metadata.display_primaries_y[i] =
97 FFMIN(lrint(chroma_den *
98 av_q2d(hdr_meta->display_primaries[j][1])),
102 ctx->in_metadata.white_point_x =
103 FFMIN(lrint(chroma_den * av_q2d(hdr_meta->white_point[0])),
105 ctx->in_metadata.white_point_y =
106 FFMIN(lrint(chroma_den * av_q2d(hdr_meta->white_point[1])),
109 av_log(avctx, AV_LOG_DEBUG,
110 "Mastering Display Metadata(in primaries):\n");
111 av_log(avctx, AV_LOG_DEBUG,
112 "G(%u,%u) B(%u,%u) R(%u,%u) WP(%u,%u)\n",
113 ctx->in_metadata.display_primaries_x[0],
114 ctx->in_metadata.display_primaries_y[0],
115 ctx->in_metadata.display_primaries_x[1],
116 ctx->in_metadata.display_primaries_y[1],
117 ctx->in_metadata.display_primaries_x[2],
118 ctx->in_metadata.display_primaries_y[2],
119 ctx->in_metadata.white_point_x,
120 ctx->in_metadata.white_point_y);
123 av_log(avctx, AV_LOG_ERROR, "No mastering display data from input\n");
124 return AVERROR(EINVAL);
127 ctx->src_light = av_frame_get_side_data(input_frame,
128 AV_FRAME_DATA_CONTENT_LIGHT_LEVEL);
129 if (ctx->src_light) {
130 light_meta = (AVContentLightMetadata *)ctx->src_light->data;
132 av_log(avctx, AV_LOG_ERROR, "No light metadata\n");
133 return AVERROR(EINVAL);
136 ctx->in_metadata.max_content_light_level = light_meta->MaxCLL;
137 ctx->in_metadata.max_pic_average_light_level = light_meta->MaxFALL;
139 av_log(avctx, AV_LOG_DEBUG,
140 "Mastering Content Light Level (in):\n");
141 av_log(avctx, AV_LOG_DEBUG,
142 "MaxCLL(%u) MaxFALL(%u)\n",
143 ctx->in_metadata.max_content_light_level,
144 ctx->in_metadata.max_pic_average_light_level);
146 av_log(avctx, AV_LOG_DEBUG, "No content light level from input\n");
151 static int tonemap_vaapi_set_filter_params(AVFilterContext *avctx, AVFrame *input_frame)
153 VAAPIVPPContext *vpp_ctx = avctx->priv;
154 HDRVAAPIContext *ctx = avctx->priv;
156 VAProcFilterParameterBufferHDRToneMapping *hdrtm_param;
158 vas = vaMapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0],
159 (void**)&hdrtm_param);
160 if (vas != VA_STATUS_SUCCESS) {
161 av_log(avctx, AV_LOG_ERROR, "Failed to map "
162 "buffer (%d): %d (%s).\n",
163 vpp_ctx->filter_buffers[0], vas, vaErrorStr(vas));
167 memcpy(hdrtm_param->data.metadata, &ctx->in_metadata, sizeof(VAHdrMetaDataHDR10));
169 vas = vaUnmapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0]);
170 if (vas != VA_STATUS_SUCCESS) {
171 av_log(avctx, AV_LOG_ERROR, "Failed to unmap output buffers: "
172 "%d (%s).\n", vas, vaErrorStr(vas));
179 static int tonemap_vaapi_build_filter_params(AVFilterContext *avctx)
181 VAAPIVPPContext *vpp_ctx = avctx->priv;
182 HDRVAAPIContext *ctx = avctx->priv;
184 VAProcFilterParameterBufferHDRToneMapping hdrtm_param;
185 VAProcFilterCapHighDynamicRange hdr_cap[VAProcHighDynamicRangeMetadataTypeCount];
189 memset(&hdrtm_param, 0, sizeof(hdrtm_param));
190 memset(&ctx->in_metadata, 0, sizeof(ctx->in_metadata));
192 num_query_caps = VAProcHighDynamicRangeMetadataTypeCount;
193 vas = vaQueryVideoProcFilterCaps(vpp_ctx->hwctx->display,
195 VAProcFilterHighDynamicRangeToneMapping,
196 &hdr_cap, &num_query_caps);
197 if (vas != VA_STATUS_SUCCESS) {
198 av_log(avctx, AV_LOG_ERROR, "Failed to query HDR caps "
199 "context: %d (%s).\n", vas, vaErrorStr(vas));
203 for (i = 0; i < num_query_caps; i++) {
204 if (hdr_cap[i].metadata_type != VAProcHighDynamicRangeMetadataNone)
208 if (i >= num_query_caps) {
209 av_log(avctx, AV_LOG_ERROR, "VAAPI driver doesn't support HDR\n");
210 return AVERROR(EINVAL);
213 for (i = 0; i < num_query_caps; i++) {
214 if (VA_TONE_MAPPING_HDR_TO_SDR & hdr_cap[i].caps_flag)
218 if (i >= num_query_caps) {
219 av_log(avctx, AV_LOG_ERROR,
220 "VAAPI driver doesn't support HDR to SDR\n");
221 return AVERROR(EINVAL);
224 hdrtm_param.type = VAProcFilterHighDynamicRangeToneMapping;
225 hdrtm_param.data.metadata_type = VAProcHighDynamicRangeMetadataHDR10;
226 hdrtm_param.data.metadata = &ctx->in_metadata;
227 hdrtm_param.data.metadata_size = sizeof(VAHdrMetaDataHDR10);
229 return ff_vaapi_vpp_make_param_buffers(avctx,
230 VAProcFilterParameterBufferType,
231 &hdrtm_param, sizeof(hdrtm_param), 1);
234 static int tonemap_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
236 AVFilterContext *avctx = inlink->dst;
237 AVFilterLink *outlink = avctx->outputs[0];
238 VAAPIVPPContext *vpp_ctx = avctx->priv;
239 HDRVAAPIContext *ctx = avctx->priv;
240 AVFrame *output_frame = NULL;
241 VASurfaceID input_surface, output_surface;
243 VAProcPipelineParameterBuffer params;
246 av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
247 av_get_pix_fmt_name(input_frame->format),
248 input_frame->width, input_frame->height, input_frame->pts);
250 if (vpp_ctx->va_context == VA_INVALID_ID){
251 av_frame_free(&input_frame);
252 return AVERROR(EINVAL);
255 err = tonemap_vaapi_save_metadata(avctx, input_frame);
259 err = tonemap_vaapi_set_filter_params(avctx, input_frame);
263 input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];
264 av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for tonemap vpp input.\n",
267 output_frame = ff_get_video_buffer(outlink, vpp_ctx->output_width,
268 vpp_ctx->output_height);
270 err = AVERROR(ENOMEM);
274 output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
275 av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for tonemap vpp output.\n",
277 memset(¶ms, 0, sizeof(params));
279 err = av_frame_copy_props(output_frame, input_frame);
283 if (ctx->color_primaries != AVCOL_PRI_UNSPECIFIED)
284 output_frame->color_primaries = ctx->color_primaries;
286 if (ctx->color_transfer != AVCOL_TRC_UNSPECIFIED)
287 output_frame->color_trc = ctx->color_transfer;
289 output_frame->color_trc = AVCOL_TRC_BT709;
291 if (ctx->color_matrix != AVCOL_SPC_UNSPECIFIED)
292 output_frame->colorspace = ctx->color_matrix;
294 err = ff_vaapi_vpp_init_params(avctx, ¶ms,
295 input_frame, output_frame);
299 err = ff_vaapi_vpp_render_picture(avctx, ¶ms, output_frame);
303 av_frame_free(&input_frame);
305 av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
306 av_get_pix_fmt_name(output_frame->format),
307 output_frame->width, output_frame->height, output_frame->pts);
309 return ff_filter_frame(outlink, output_frame);
312 av_frame_free(&input_frame);
313 av_frame_free(&output_frame);
317 static av_cold int tonemap_vaapi_init(AVFilterContext *avctx)
319 VAAPIVPPContext *vpp_ctx = avctx->priv;
320 HDRVAAPIContext *ctx = avctx->priv;
322 ff_vaapi_vpp_ctx_init(avctx);
323 vpp_ctx->build_filter_params = tonemap_vaapi_build_filter_params;
324 vpp_ctx->pipeline_uninit = ff_vaapi_vpp_pipeline_uninit;
326 if (ctx->output_format_string) {
327 vpp_ctx->output_format = av_get_pix_fmt(ctx->output_format_string);
328 switch (vpp_ctx->output_format) {
329 case AV_PIX_FMT_NV12:
330 case AV_PIX_FMT_P010:
333 av_log(avctx, AV_LOG_ERROR, "Invalid output format.\n");
334 return AVERROR(EINVAL);
337 vpp_ctx->output_format = AV_PIX_FMT_NV12;
338 av_log(avctx, AV_LOG_WARNING, "Output format not set, use default format NV12\n");
341 #define STRING_OPTION(var_name, func_name, default_value) do { \
342 if (ctx->var_name ## _string) { \
343 int var = av_ ## func_name ## _from_name(ctx->var_name ## _string); \
345 av_log(avctx, AV_LOG_ERROR, "Invalid %s.\n", #var_name); \
346 return AVERROR(EINVAL); \
348 ctx->var_name = var; \
350 ctx->var_name = default_value; \
354 STRING_OPTION(color_primaries, color_primaries, AVCOL_PRI_UNSPECIFIED);
355 STRING_OPTION(color_transfer, color_transfer, AVCOL_TRC_UNSPECIFIED);
356 STRING_OPTION(color_matrix, color_space, AVCOL_SPC_UNSPECIFIED);
361 #define OFFSET(x) offsetof(HDRVAAPIContext, x)
362 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_FILTERING_PARAM)
363 static const AVOption tonemap_vaapi_options[] = {
364 { "format", "Output pixel format set", OFFSET(output_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS, "format" },
365 { "matrix", "Output color matrix coefficient set",
366 OFFSET(color_matrix_string), AV_OPT_TYPE_STRING,
367 { .str = NULL }, .flags = FLAGS, "matrix" },
368 { "m", "Output color matrix coefficient set",
369 OFFSET(color_matrix_string), AV_OPT_TYPE_STRING,
370 { .str = NULL }, .flags = FLAGS, "matrix" },
371 { "primaries", "Output color primaries set",
372 OFFSET(color_primaries_string), AV_OPT_TYPE_STRING,
373 { .str = NULL }, .flags = FLAGS, "primaries" },
374 { "p", "Output color primaries set",
375 OFFSET(color_primaries_string), AV_OPT_TYPE_STRING,
376 { .str = NULL }, .flags = FLAGS, "primaries" },
377 { "transfer", "Output color transfer characteristics set",
378 OFFSET(color_transfer_string), AV_OPT_TYPE_STRING,
379 { .str = NULL }, .flags = FLAGS, "transfer" },
380 { "t", "Output color transfer characteristics set",
381 OFFSET(color_transfer_string), AV_OPT_TYPE_STRING,
382 { .str = NULL }, .flags = FLAGS, "transfer" },
387 AVFILTER_DEFINE_CLASS(tonemap_vaapi);
389 static const AVFilterPad tonemap_vaapi_inputs[] = {
392 .type = AVMEDIA_TYPE_VIDEO,
393 .filter_frame = &tonemap_vaapi_filter_frame,
394 .config_props = &ff_vaapi_vpp_config_input,
399 static const AVFilterPad tonemap_vaapi_outputs[] = {
402 .type = AVMEDIA_TYPE_VIDEO,
403 .config_props = &ff_vaapi_vpp_config_output,
408 AVFilter ff_vf_tonemap_vaapi = {
409 .name = "tonemap_vaapi",
410 .description = NULL_IF_CONFIG_SMALL("VAAPI VPP for tone-mapping"),
411 .priv_size = sizeof(HDRVAAPIContext),
412 .init = &tonemap_vaapi_init,
413 .uninit = &ff_vaapi_vpp_ctx_uninit,
414 .query_formats = &ff_vaapi_vpp_query_formats,
415 .inputs = tonemap_vaapi_inputs,
416 .outputs = tonemap_vaapi_outputs,
417 .priv_class = &tonemap_vaapi_class,
418 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,