2 * This file is part of Libav.
4 * Libav 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 * Libav 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 Libav; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
22 #include <va/va_vpp.h>
24 #include "libavutil/avassert.h"
25 #include "libavutil/common.h"
26 #include "libavutil/hwcontext.h"
27 #include "libavutil/hwcontext_vaapi.h"
28 #include "libavutil/mem.h"
29 #include "libavutil/opt.h"
30 #include "libavutil/pixdesc.h"
37 #define MAX_REFERENCES 8
39 typedef struct DeintVAAPIContext {
42 AVVAAPIDeviceContext *hwctx;
43 AVBufferRef *device_ref;
51 VAContextID va_context;
53 AVBufferRef *input_frames_ref;
54 AVHWFramesContext *input_frames;
59 VAProcFilterCapDeinterlacing
60 deint_caps[VAProcDeinterlacingCount];
62 VAProcPipelineCaps pipeline_caps;
66 AVFrame *frame_queue[MAX_REFERENCES];
67 int extra_delay_for_timestamps;
69 VABufferID filter_buffer;
72 static const char *deint_vaapi_mode_name(int mode)
75 #define D(name) case VAProcDeinterlacing ## name: return #name
86 static int deint_vaapi_query_formats(AVFilterContext *avctx)
88 enum AVPixelFormat pix_fmts[] = {
89 AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE,
92 ff_formats_ref(ff_make_format_list(pix_fmts),
93 &avctx->inputs[0]->out_formats);
94 ff_formats_ref(ff_make_format_list(pix_fmts),
95 &avctx->outputs[0]->in_formats);
100 static int deint_vaapi_pipeline_uninit(AVFilterContext *avctx)
102 DeintVAAPIContext *ctx = avctx->priv;
105 for (i = 0; i < ctx->queue_count; i++)
106 av_frame_free(&ctx->frame_queue[i]);
107 ctx->queue_count = 0;
109 if (ctx->filter_buffer != VA_INVALID_ID) {
110 vaDestroyBuffer(ctx->hwctx->display, ctx->filter_buffer);
111 ctx->filter_buffer = VA_INVALID_ID;
114 if (ctx->va_context != VA_INVALID_ID) {
115 vaDestroyContext(ctx->hwctx->display, ctx->va_context);
116 ctx->va_context = VA_INVALID_ID;
119 if (ctx->va_config != VA_INVALID_ID) {
120 vaDestroyConfig(ctx->hwctx->display, ctx->va_config);
121 ctx->va_config = VA_INVALID_ID;
124 av_buffer_unref(&ctx->device_ref);
130 static int deint_vaapi_config_input(AVFilterLink *inlink)
132 AVFilterContext *avctx = inlink->dst;
133 DeintVAAPIContext *ctx = avctx->priv;
135 deint_vaapi_pipeline_uninit(avctx);
137 if (!inlink->hw_frames_ctx) {
138 av_log(avctx, AV_LOG_ERROR, "A hardware frames reference is "
139 "required to associate the processing device.\n");
140 return AVERROR(EINVAL);
143 ctx->input_frames_ref = av_buffer_ref(inlink->hw_frames_ctx);
144 ctx->input_frames = (AVHWFramesContext*)ctx->input_frames_ref->data;
149 static int deint_vaapi_build_filter_params(AVFilterContext *avctx)
151 DeintVAAPIContext *ctx = avctx->priv;
153 VAProcFilterParameterBufferDeinterlacing params;
156 ctx->nb_deint_caps = VAProcDeinterlacingCount;
157 vas = vaQueryVideoProcFilterCaps(ctx->hwctx->display,
159 VAProcFilterDeinterlacing,
161 &ctx->nb_deint_caps);
162 if (vas != VA_STATUS_SUCCESS) {
163 av_log(avctx, AV_LOG_ERROR, "Failed to query deinterlacing "
164 "caps: %d (%s).\n", vas, vaErrorStr(vas));
168 if (ctx->mode == VAProcDeinterlacingNone) {
169 for (i = 0; i < ctx->nb_deint_caps; i++) {
170 if (ctx->deint_caps[i].type > ctx->mode)
171 ctx->mode = ctx->deint_caps[i].type;
173 av_log(avctx, AV_LOG_VERBOSE, "Picking %d (%s) as default "
174 "deinterlacing mode.\n", ctx->mode,
175 deint_vaapi_mode_name(ctx->mode));
177 for (i = 0; i < ctx->nb_deint_caps; i++) {
178 if (ctx->deint_caps[i].type == ctx->mode)
181 if (i >= ctx->nb_deint_caps) {
182 av_log(avctx, AV_LOG_ERROR, "Deinterlacing mode %d (%s) is "
183 "not supported.\n", ctx->mode,
184 deint_vaapi_mode_name(ctx->mode));
188 params.type = VAProcFilterDeinterlacing;
189 params.algorithm = ctx->mode;
192 av_assert0(ctx->filter_buffer == VA_INVALID_ID);
193 vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
194 VAProcFilterParameterBufferType,
195 sizeof(params), 1, ¶ms,
196 &ctx->filter_buffer);
197 if (vas != VA_STATUS_SUCCESS) {
198 av_log(avctx, AV_LOG_ERROR, "Failed to create deinterlace "
199 "parameter buffer: %d (%s).\n", vas, vaErrorStr(vas));
203 vas = vaQueryVideoProcPipelineCaps(ctx->hwctx->display,
205 &ctx->filter_buffer, 1,
206 &ctx->pipeline_caps);
207 if (vas != VA_STATUS_SUCCESS) {
208 av_log(avctx, AV_LOG_ERROR, "Failed to query pipeline "
209 "caps: %d (%s).\n", vas, vaErrorStr(vas));
213 ctx->extra_delay_for_timestamps = ctx->field_rate == 2 &&
214 ctx->pipeline_caps.num_backward_references == 0;
216 ctx->queue_depth = ctx->pipeline_caps.num_backward_references +
217 ctx->pipeline_caps.num_forward_references +
218 ctx->extra_delay_for_timestamps + 1;
219 if (ctx->queue_depth > MAX_REFERENCES) {
220 av_log(avctx, AV_LOG_ERROR, "Pipeline requires too many "
221 "references (%u forward, %u back).\n",
222 ctx->pipeline_caps.num_forward_references,
223 ctx->pipeline_caps.num_backward_references);
224 return AVERROR(ENOSYS);
230 static int deint_vaapi_config_output(AVFilterLink *outlink)
232 AVFilterContext *avctx = outlink->src;
233 AVFilterLink *inlink = avctx->inputs[0];
234 DeintVAAPIContext *ctx = avctx->priv;
235 AVVAAPIHWConfig *hwconfig = NULL;
236 AVHWFramesConstraints *constraints = NULL;
237 AVHWFramesContext *output_frames;
238 AVVAAPIFramesContext *va_frames;
242 deint_vaapi_pipeline_uninit(avctx);
244 av_assert0(ctx->input_frames);
245 ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref);
246 ctx->hwctx = ((AVHWDeviceContext*)ctx->device_ref->data)->hwctx;
248 ctx->output_width = ctx->input_frames->width;
249 ctx->output_height = ctx->input_frames->height;
251 av_assert0(ctx->va_config == VA_INVALID_ID);
252 vas = vaCreateConfig(ctx->hwctx->display, VAProfileNone,
253 VAEntrypointVideoProc, 0, 0, &ctx->va_config);
254 if (vas != VA_STATUS_SUCCESS) {
255 av_log(avctx, AV_LOG_ERROR, "Failed to create processing pipeline "
256 "config: %d (%s).\n", vas, vaErrorStr(vas));
261 hwconfig = av_hwdevice_hwconfig_alloc(ctx->device_ref);
263 err = AVERROR(ENOMEM);
266 hwconfig->config_id = ctx->va_config;
268 constraints = av_hwdevice_get_hwframe_constraints(ctx->device_ref,
271 err = AVERROR(ENOMEM);
275 if (ctx->output_width < constraints->min_width ||
276 ctx->output_height < constraints->min_height ||
277 ctx->output_width > constraints->max_width ||
278 ctx->output_height > constraints->max_height) {
279 av_log(avctx, AV_LOG_ERROR, "Hardware does not support "
280 "deinterlacing to size %dx%d "
281 "(constraints: width %d-%d height %d-%d).\n",
282 ctx->output_width, ctx->output_height,
283 constraints->min_width, constraints->max_width,
284 constraints->min_height, constraints->max_height);
285 err = AVERROR(EINVAL);
289 outlink->hw_frames_ctx = av_hwframe_ctx_alloc(ctx->device_ref);
290 if (!outlink->hw_frames_ctx) {
291 av_log(avctx, AV_LOG_ERROR, "Failed to create HW frame context "
293 err = AVERROR(ENOMEM);
297 output_frames = (AVHWFramesContext*)outlink->hw_frames_ctx->data;
299 output_frames->format = AV_PIX_FMT_VAAPI;
300 output_frames->sw_format = ctx->input_frames->sw_format;
301 output_frames->width = ctx->output_width;
302 output_frames->height = ctx->output_height;
304 output_frames->initial_pool_size = 4;
306 err = ff_filter_init_hw_frames(avctx, outlink, 10);
310 err = av_hwframe_ctx_init(outlink->hw_frames_ctx);
312 av_log(avctx, AV_LOG_ERROR, "Failed to initialise VAAPI frame "
313 "context for output: %d\n", err);
317 va_frames = output_frames->hwctx;
319 av_assert0(ctx->va_context == VA_INVALID_ID);
320 vas = vaCreateContext(ctx->hwctx->display, ctx->va_config,
321 ctx->output_width, ctx->output_height, 0,
322 va_frames->surface_ids, va_frames->nb_surfaces,
324 if (vas != VA_STATUS_SUCCESS) {
325 av_log(avctx, AV_LOG_ERROR, "Failed to create processing pipeline "
326 "context: %d (%s).\n", vas, vaErrorStr(vas));
331 err = deint_vaapi_build_filter_params(avctx);
335 outlink->w = inlink->w;
336 outlink->h = inlink->h;
338 outlink->time_base = av_mul_q(inlink->time_base,
339 (AVRational) { 1, ctx->field_rate });
340 outlink->frame_rate = av_mul_q(inlink->frame_rate,
341 (AVRational) { ctx->field_rate, 1 });
344 av_hwframe_constraints_free(&constraints);
348 av_buffer_unref(&outlink->hw_frames_ctx);
350 av_hwframe_constraints_free(&constraints);
354 static int vaapi_proc_colour_standard(enum AVColorSpace av_cs)
357 #define CS(av, va) case AVCOL_SPC_ ## av: return VAProcColorStandard ## va;
359 CS(BT470BG, BT470BG);
360 CS(SMPTE170M, SMPTE170M);
361 CS(SMPTE240M, SMPTE240M);
364 return VAProcColorStandardNone;
368 static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
370 AVFilterContext *avctx = inlink->dst;
371 AVFilterLink *outlink = avctx->outputs[0];
372 DeintVAAPIContext *ctx = avctx->priv;
373 AVFrame *output_frame = NULL;
374 VASurfaceID input_surface, output_surface;
375 VASurfaceID backward_references[MAX_REFERENCES];
376 VASurfaceID forward_references[MAX_REFERENCES];
377 VAProcPipelineParameterBuffer params;
378 VAProcFilterParameterBufferDeinterlacing *filter_params;
379 VARectangle input_region;
380 VABufferID params_id;
382 void *filter_params_addr = NULL;
383 int err, i, field, current_frame_index;
385 av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
386 av_get_pix_fmt_name(input_frame->format),
387 input_frame->width, input_frame->height, input_frame->pts);
389 if (ctx->queue_count < ctx->queue_depth) {
390 ctx->frame_queue[ctx->queue_count++] = input_frame;
391 if (ctx->queue_count < ctx->queue_depth) {
392 // Need more reference surfaces before we can continue.
396 av_frame_free(&ctx->frame_queue[0]);
397 for (i = 0; i + 1 < ctx->queue_count; i++)
398 ctx->frame_queue[i] = ctx->frame_queue[i + 1];
399 ctx->frame_queue[i] = input_frame;
402 current_frame_index = ctx->pipeline_caps.num_forward_references;
404 input_frame = ctx->frame_queue[current_frame_index];
405 input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];
406 for (i = 0; i < ctx->pipeline_caps.num_forward_references; i++)
407 forward_references[i] = (VASurfaceID)(uintptr_t)
408 ctx->frame_queue[current_frame_index - i - 1]->data[3];
409 for (i = 0; i < ctx->pipeline_caps.num_backward_references; i++)
410 backward_references[i] = (VASurfaceID)(uintptr_t)
411 ctx->frame_queue[current_frame_index + i + 1]->data[3];
413 av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for "
414 "deinterlace input.\n", input_surface);
415 av_log(avctx, AV_LOG_DEBUG, "Backward references:");
416 for (i = 0; i < ctx->pipeline_caps.num_backward_references; i++)
417 av_log(avctx, AV_LOG_DEBUG, " %#x", backward_references[i]);
418 av_log(avctx, AV_LOG_DEBUG, "\n");
419 av_log(avctx, AV_LOG_DEBUG, "Forward references:");
420 for (i = 0; i < ctx->pipeline_caps.num_forward_references; i++)
421 av_log(avctx, AV_LOG_DEBUG, " %#x", forward_references[i]);
422 av_log(avctx, AV_LOG_DEBUG, "\n");
424 for (field = 0; field < ctx->field_rate; field++) {
425 output_frame = ff_get_video_buffer(outlink, ctx->output_width,
428 err = AVERROR(ENOMEM);
432 output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
433 av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for "
434 "deinterlace output.\n", output_surface);
436 memset(¶ms, 0, sizeof(params));
438 input_region = (VARectangle) {
441 .width = input_frame->width,
442 .height = input_frame->height,
445 params.surface = input_surface;
446 params.surface_region = &input_region;
447 params.surface_color_standard =
448 vaapi_proc_colour_standard(input_frame->colorspace);
450 params.output_region = NULL;
451 params.output_background_color = 0xff000000;
452 params.output_color_standard = params.surface_color_standard;
454 params.pipeline_flags = 0;
455 params.filter_flags = VA_FRAME_PICTURE;
457 if (!ctx->auto_enable || input_frame->interlaced_frame) {
458 vas = vaMapBuffer(ctx->hwctx->display, ctx->filter_buffer,
459 &filter_params_addr);
460 if (vas != VA_STATUS_SUCCESS) {
461 av_log(avctx, AV_LOG_ERROR, "Failed to map filter parameter "
462 "buffer: %d (%s).\n", vas, vaErrorStr(vas));
466 filter_params = filter_params_addr;
467 filter_params->flags = 0;
468 if (input_frame->top_field_first) {
469 filter_params->flags |= field ? VA_DEINTERLACING_BOTTOM_FIELD : 0;
471 filter_params->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST;
472 filter_params->flags |= field ? 0 : VA_DEINTERLACING_BOTTOM_FIELD;
474 filter_params_addr = NULL;
475 vas = vaUnmapBuffer(ctx->hwctx->display, ctx->filter_buffer);
476 if (vas != VA_STATUS_SUCCESS)
477 av_log(avctx, AV_LOG_ERROR, "Failed to unmap filter parameter "
478 "buffer: %d (%s).\n", vas, vaErrorStr(vas));
480 params.filters = &ctx->filter_buffer;
481 params.num_filters = 1;
483 params.forward_references = forward_references;
484 params.num_forward_references =
485 ctx->pipeline_caps.num_forward_references;
486 params.backward_references = backward_references;
487 params.num_backward_references =
488 ctx->pipeline_caps.num_backward_references;
491 params.filters = NULL;
492 params.num_filters = 0;
495 vas = vaBeginPicture(ctx->hwctx->display,
496 ctx->va_context, output_surface);
497 if (vas != VA_STATUS_SUCCESS) {
498 av_log(avctx, AV_LOG_ERROR, "Failed to attach new picture: "
499 "%d (%s).\n", vas, vaErrorStr(vas));
504 vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
505 VAProcPipelineParameterBufferType,
506 sizeof(params), 1, ¶ms, ¶ms_id);
507 if (vas != VA_STATUS_SUCCESS) {
508 av_log(avctx, AV_LOG_ERROR, "Failed to create parameter buffer: "
509 "%d (%s).\n", vas, vaErrorStr(vas));
511 goto fail_after_begin;
513 av_log(avctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n",
516 vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context,
518 if (vas != VA_STATUS_SUCCESS) {
519 av_log(avctx, AV_LOG_ERROR, "Failed to render parameter buffer: "
520 "%d (%s).\n", vas, vaErrorStr(vas));
522 goto fail_after_begin;
525 vas = vaEndPicture(ctx->hwctx->display, ctx->va_context);
526 if (vas != VA_STATUS_SUCCESS) {
527 av_log(avctx, AV_LOG_ERROR, "Failed to start picture processing: "
528 "%d (%s).\n", vas, vaErrorStr(vas));
530 goto fail_after_render;
533 if (HAVE_VAAPI_1 || ctx->hwctx->driver_quirks &
534 AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) {
535 vas = vaDestroyBuffer(ctx->hwctx->display, params_id);
536 if (vas != VA_STATUS_SUCCESS) {
537 av_log(avctx, AV_LOG_ERROR, "Failed to free parameter buffer: "
538 "%d (%s).\n", vas, vaErrorStr(vas));
543 err = av_frame_copy_props(output_frame, input_frame);
547 if (ctx->field_rate == 2) {
549 output_frame->pts = 2 * input_frame->pts;
551 output_frame->pts = input_frame->pts +
552 ctx->frame_queue[current_frame_index + 1]->pts;
554 output_frame->interlaced_frame = 0;
556 av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
557 av_get_pix_fmt_name(output_frame->format),
558 output_frame->width, output_frame->height, output_frame->pts);
560 err = ff_filter_frame(outlink, output_frame);
568 vaRenderPicture(ctx->hwctx->display, ctx->va_context, ¶ms_id, 1);
570 vaEndPicture(ctx->hwctx->display, ctx->va_context);
572 if (filter_params_addr)
573 vaUnmapBuffer(ctx->hwctx->display, ctx->filter_buffer);
574 av_frame_free(&output_frame);
578 static av_cold int deint_vaapi_init(AVFilterContext *avctx)
580 DeintVAAPIContext *ctx = avctx->priv;
582 ctx->va_config = VA_INVALID_ID;
583 ctx->va_context = VA_INVALID_ID;
584 ctx->filter_buffer = VA_INVALID_ID;
590 static av_cold void deint_vaapi_uninit(AVFilterContext *avctx)
592 DeintVAAPIContext *ctx = avctx->priv;
595 deint_vaapi_pipeline_uninit(avctx);
597 av_buffer_unref(&ctx->input_frames_ref);
598 av_buffer_unref(&ctx->device_ref);
601 #define OFFSET(x) offsetof(DeintVAAPIContext, x)
602 #define FLAGS (AV_OPT_FLAG_VIDEO_PARAM)
603 static const AVOption deint_vaapi_options[] = {
604 { "mode", "Deinterlacing mode",
605 OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = VAProcDeinterlacingNone },
606 VAProcDeinterlacingNone, VAProcDeinterlacingCount - 1, FLAGS, "mode" },
607 { "default", "Use the highest-numbered (and therefore possibly most advanced) deinterlacing algorithm",
608 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingNone }, .unit = "mode" },
609 { "bob", "Use the bob deinterlacing algorithm",
610 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingBob }, .unit = "mode" },
611 { "weave", "Use the weave deinterlacing algorithm",
612 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingWeave }, .unit = "mode" },
613 { "motion_adaptive", "Use the motion adaptive deinterlacing algorithm",
614 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionAdaptive }, .unit = "mode" },
615 { "motion_compensated", "Use the motion compensated deinterlacing algorithm",
616 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionCompensated }, .unit = "mode" },
618 { "rate", "Generate output at frame rate or field rate",
619 OFFSET(field_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 2, FLAGS, "rate" },
620 { "frame", "Output at frame rate (one frame of output for each field-pair)",
621 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, .unit = "rate" },
622 { "field", "Output at field rate (one frame of output for each field)",
623 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, .unit = "rate" },
625 { "auto", "Only deinterlace fields, passing frames through unchanged",
626 OFFSET(auto_enable), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
631 static const AVClass deint_vaapi_class = {
632 .class_name = "deinterlace_vaapi",
633 .item_name = av_default_item_name,
634 .option = deint_vaapi_options,
635 .version = LIBAVUTIL_VERSION_INT,
638 static const AVFilterPad deint_vaapi_inputs[] = {
641 .type = AVMEDIA_TYPE_VIDEO,
642 .filter_frame = &deint_vaapi_filter_frame,
643 .config_props = &deint_vaapi_config_input,
648 static const AVFilterPad deint_vaapi_outputs[] = {
651 .type = AVMEDIA_TYPE_VIDEO,
652 .config_props = &deint_vaapi_config_output,
657 AVFilter ff_vf_deinterlace_vaapi = {
658 .name = "deinterlace_vaapi",
659 .description = NULL_IF_CONFIG_SMALL("Deinterlacing of VAAPI surfaces"),
660 .priv_size = sizeof(DeintVAAPIContext),
661 .init = &deint_vaapi_init,
662 .uninit = &deint_vaapi_uninit,
663 .query_formats = &deint_vaapi_query_formats,
664 .inputs = deint_vaapi_inputs,
665 .outputs = deint_vaapi_outputs,
666 .priv_class = &deint_vaapi_class,
667 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,