FFmpeg coverage


Directory: ../../../ffmpeg/
File: src/libavfilter/vf_deinterlace_vaapi.c
Date: 2022-12-09 07:38:14
Exec Total Coverage
Lines: 0 184 0.0%
Functions: 0 7 0.0%
Branches: 0 93 0.0%

Line Branch Exec Source
1 /*
2 * This file is part of FFmpeg.
3 *
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.
8 *
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.
13 *
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
17 */
18
19 #include <string.h>
20
21 #include "libavutil/common.h"
22 #include "libavutil/opt.h"
23 #include "libavutil/pixdesc.h"
24
25 #include "avfilter.h"
26 #include "formats.h"
27 #include "internal.h"
28 #include "video.h"
29 #include "vaapi_vpp.h"
30
31 #define MAX_REFERENCES 8
32
33 typedef struct DeintVAAPIContext {
34 VAAPIVPPContext vpp_ctx; // must be the first field
35
36 int mode;
37 int field_rate;
38 int auto_enable;
39
40 VAProcFilterCapDeinterlacing
41 deint_caps[VAProcDeinterlacingCount];
42 int nb_deint_caps;
43 VAProcPipelineCaps pipeline_caps;
44
45 int queue_depth;
46 int queue_count;
47 AVFrame *frame_queue[MAX_REFERENCES];
48 int extra_delay_for_timestamps;
49
50 int eof;
51 int prev_pts;
52 } DeintVAAPIContext;
53
54 static const char *deint_vaapi_mode_name(int mode)
55 {
56 switch (mode) {
57 #define D(name) case VAProcDeinterlacing ## name: return #name
58 D(Bob);
59 D(Weave);
60 D(MotionAdaptive);
61 D(MotionCompensated);
62 #undef D
63 default:
64 return "Invalid";
65 }
66 }
67
68 static void deint_vaapi_pipeline_uninit(AVFilterContext *avctx)
69 {
70 DeintVAAPIContext *ctx = avctx->priv;
71 int i;
72
73 for (i = 0; i < ctx->queue_count; i++)
74 av_frame_free(&ctx->frame_queue[i]);
75 ctx->queue_count = 0;
76
77 ff_vaapi_vpp_pipeline_uninit(avctx);
78 }
79
80 static int deint_vaapi_build_filter_params(AVFilterContext *avctx)
81 {
82 VAAPIVPPContext *vpp_ctx = avctx->priv;
83 DeintVAAPIContext *ctx = avctx->priv;
84 VAStatus vas;
85 VAProcFilterParameterBufferDeinterlacing params;
86 int i;
87
88 ctx->nb_deint_caps = VAProcDeinterlacingCount;
89 vas = vaQueryVideoProcFilterCaps(vpp_ctx->hwctx->display,
90 vpp_ctx->va_context,
91 VAProcFilterDeinterlacing,
92 &ctx->deint_caps,
93 &ctx->nb_deint_caps);
94 if (vas != VA_STATUS_SUCCESS) {
95 av_log(avctx, AV_LOG_ERROR, "Failed to query deinterlacing "
96 "caps: %d (%s).\n", vas, vaErrorStr(vas));
97 return AVERROR(EIO);
98 }
99
100 if (ctx->mode == VAProcDeinterlacingNone) {
101 for (i = 0; i < ctx->nb_deint_caps; i++) {
102 if (ctx->deint_caps[i].type > ctx->mode)
103 ctx->mode = ctx->deint_caps[i].type;
104 }
105 av_log(avctx, AV_LOG_VERBOSE, "Picking %d (%s) as default "
106 "deinterlacing mode.\n", ctx->mode,
107 deint_vaapi_mode_name(ctx->mode));
108 } else {
109 for (i = 0; i < ctx->nb_deint_caps; i++) {
110 if (ctx->deint_caps[i].type == ctx->mode)
111 break;
112 }
113 if (i >= ctx->nb_deint_caps) {
114 av_log(avctx, AV_LOG_ERROR, "Deinterlacing mode %d (%s) is "
115 "not supported.\n", ctx->mode,
116 deint_vaapi_mode_name(ctx->mode));
117 return AVERROR(EINVAL);
118 }
119 }
120
121 params.type = VAProcFilterDeinterlacing;
122 params.algorithm = ctx->mode;
123 params.flags = 0;
124
125 vas = ff_vaapi_vpp_make_param_buffers(avctx,
126 VAProcFilterParameterBufferType,
127 &params,
128 sizeof(params),
129 1);
130 if (vas)
131 return vas;
132
133 vas = vaQueryVideoProcPipelineCaps(vpp_ctx->hwctx->display,
134 vpp_ctx->va_context,
135 &vpp_ctx->filter_buffers[0], 1,
136 &ctx->pipeline_caps);
137 if (vas != VA_STATUS_SUCCESS) {
138 av_log(avctx, AV_LOG_ERROR, "Failed to query pipeline "
139 "caps: %d (%s).\n", vas, vaErrorStr(vas));
140 return AVERROR(EIO);
141 }
142
143 ctx->extra_delay_for_timestamps = ctx->field_rate == 2 &&
144 ctx->pipeline_caps.num_backward_references == 0;
145
146 ctx->queue_depth = ctx->pipeline_caps.num_backward_references +
147 ctx->pipeline_caps.num_forward_references +
148 ctx->extra_delay_for_timestamps + 1;
149 if (ctx->queue_depth > MAX_REFERENCES) {
150 av_log(avctx, AV_LOG_ERROR, "Pipeline requires too many "
151 "references (%u forward, %u back).\n",
152 ctx->pipeline_caps.num_forward_references,
153 ctx->pipeline_caps.num_backward_references);
154 return AVERROR(ENOSYS);
155 }
156
157 return 0;
158 }
159
160 static int deint_vaapi_config_output(AVFilterLink *outlink)
161 {
162 AVFilterLink *inlink = outlink->src->inputs[0];
163 AVFilterContext *avctx = outlink->src;
164 DeintVAAPIContext *ctx = avctx->priv;
165 int err;
166
167 err = ff_vaapi_vpp_config_output(outlink);
168 if (err < 0)
169 return err;
170 outlink->time_base = av_mul_q(inlink->time_base,
171 (AVRational) { 1, ctx->field_rate });
172 outlink->frame_rate = av_mul_q(inlink->frame_rate,
173 (AVRational) { ctx->field_rate, 1 });
174
175 return 0;
176 }
177
178 static int deint_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
179 {
180 AVFilterContext *avctx = inlink->dst;
181 AVFilterLink *outlink = avctx->outputs[0];
182 VAAPIVPPContext *vpp_ctx = avctx->priv;
183 DeintVAAPIContext *ctx = avctx->priv;
184 AVFrame *output_frame = NULL;
185 VASurfaceID input_surface;
186 VASurfaceID backward_references[MAX_REFERENCES];
187 VASurfaceID forward_references[MAX_REFERENCES];
188 VAProcPipelineParameterBuffer params;
189 VAProcFilterParameterBufferDeinterlacing *filter_params;
190 VAStatus vas;
191 void *filter_params_addr = NULL;
192 int err, i, field, current_frame_index;
193
194 // NULL frame is used to flush the queue in field mode
195 if (input_frame)
196 av_log(avctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
197 av_get_pix_fmt_name(input_frame->format),
198 input_frame->width, input_frame->height, input_frame->pts);
199
200 if (ctx->queue_count < ctx->queue_depth) {
201 ctx->frame_queue[ctx->queue_count++] = input_frame;
202 if (ctx->queue_count < ctx->queue_depth) {
203 // Need more reference surfaces before we can continue.
204 return 0;
205 }
206 } else {
207 av_frame_free(&ctx->frame_queue[0]);
208 for (i = 0; i + 1 < ctx->queue_count; i++)
209 ctx->frame_queue[i] = ctx->frame_queue[i + 1];
210 ctx->frame_queue[i] = input_frame;
211 }
212
213 current_frame_index = ctx->pipeline_caps.num_forward_references;
214
215 input_frame = ctx->frame_queue[current_frame_index];
216 if (!input_frame)
217 return 0;
218
219 input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];
220 for (i = 0; i < ctx->pipeline_caps.num_forward_references; i++)
221 forward_references[i] = (VASurfaceID)(uintptr_t)
222 ctx->frame_queue[current_frame_index - i - 1]->data[3];
223 for (i = 0; i < ctx->pipeline_caps.num_backward_references; i++)
224 backward_references[i] = (VASurfaceID)(uintptr_t)
225 ctx->frame_queue[current_frame_index + i + 1]->data[3];
226
227 av_log(avctx, AV_LOG_DEBUG, "Using surface %#x for "
228 "deinterlace input.\n", input_surface);
229 av_log(avctx, AV_LOG_DEBUG, "Backward references:");
230 for (i = 0; i < ctx->pipeline_caps.num_backward_references; i++)
231 av_log(avctx, AV_LOG_DEBUG, " %#x", backward_references[i]);
232 av_log(avctx, AV_LOG_DEBUG, "\n");
233 av_log(avctx, AV_LOG_DEBUG, "Forward references:");
234 for (i = 0; i < ctx->pipeline_caps.num_forward_references; i++)
235 av_log(avctx, AV_LOG_DEBUG, " %#x", forward_references[i]);
236 av_log(avctx, AV_LOG_DEBUG, "\n");
237
238 for (field = 0; field < ctx->field_rate; field++) {
239 output_frame = ff_get_video_buffer(outlink, vpp_ctx->output_width,
240 vpp_ctx->output_height);
241 if (!output_frame) {
242 err = AVERROR(ENOMEM);
243 goto fail;
244 }
245
246 err = av_frame_copy_props(output_frame, input_frame);
247 if (err < 0)
248 goto fail;
249
250 err = ff_vaapi_vpp_init_params(avctx, &params,
251 input_frame, output_frame);
252 if (err < 0)
253 goto fail;
254
255 if (!ctx->auto_enable || input_frame->interlaced_frame) {
256 vas = vaMapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0],
257 &filter_params_addr);
258 if (vas != VA_STATUS_SUCCESS) {
259 av_log(avctx, AV_LOG_ERROR, "Failed to map filter parameter "
260 "buffer: %d (%s).\n", vas, vaErrorStr(vas));
261 err = AVERROR(EIO);
262 goto fail;
263 }
264 filter_params = filter_params_addr;
265 filter_params->flags = 0;
266 if (input_frame->top_field_first) {
267 filter_params->flags |= field ? VA_DEINTERLACING_BOTTOM_FIELD : 0;
268 } else {
269 filter_params->flags |= VA_DEINTERLACING_BOTTOM_FIELD_FIRST;
270 filter_params->flags |= field ? 0 : VA_DEINTERLACING_BOTTOM_FIELD;
271 }
272 filter_params_addr = NULL;
273 vas = vaUnmapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0]);
274 if (vas != VA_STATUS_SUCCESS)
275 av_log(avctx, AV_LOG_ERROR, "Failed to unmap filter parameter "
276 "buffer: %d (%s).\n", vas, vaErrorStr(vas));
277
278 params.filters = &vpp_ctx->filter_buffers[0];
279 params.num_filters = 1;
280
281 params.forward_references = forward_references;
282 params.num_forward_references =
283 ctx->pipeline_caps.num_forward_references;
284 params.backward_references = backward_references;
285 params.num_backward_references =
286 ctx->pipeline_caps.num_backward_references;
287
288 } else {
289 params.filters = NULL;
290 params.num_filters = 0;
291 }
292
293 err = ff_vaapi_vpp_render_picture(avctx, &params, output_frame);
294 if (err < 0)
295 goto fail;
296
297 if (ctx->field_rate == 2) {
298 if (field == 0)
299 output_frame->pts = 2 * input_frame->pts;
300 else if (ctx->eof)
301 output_frame->pts = 3 * input_frame->pts - ctx->prev_pts;
302 else
303 output_frame->pts = input_frame->pts +
304 ctx->frame_queue[current_frame_index + 1]->pts;
305 }
306 output_frame->interlaced_frame = 0;
307
308 av_log(avctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
309 av_get_pix_fmt_name(output_frame->format),
310 output_frame->width, output_frame->height, output_frame->pts);
311
312 err = ff_filter_frame(outlink, output_frame);
313 if (err < 0)
314 break;
315 }
316
317 ctx->prev_pts = input_frame->pts;
318
319 return err;
320
321 fail:
322 if (filter_params_addr)
323 vaUnmapBuffer(vpp_ctx->hwctx->display, vpp_ctx->filter_buffers[0]);
324 av_frame_free(&output_frame);
325 return err;
326 }
327
328 static int deint_vaapi_request_frame(AVFilterLink *link)
329 {
330 AVFilterContext *avctx = link->src;
331 DeintVAAPIContext *ctx = avctx->priv;
332 int ret;
333
334 if (ctx->eof)
335 return AVERROR_EOF;
336
337 ret = ff_request_frame(link->src->inputs[0]);
338 if (ret == AVERROR_EOF && ctx->extra_delay_for_timestamps) {
339 ctx->eof = 1;
340 deint_vaapi_filter_frame(link->src->inputs[0], NULL);
341 } else if (ret < 0)
342 return ret;
343
344 return 0;
345 }
346
347 static av_cold int deint_vaapi_init(AVFilterContext *avctx)
348 {
349 VAAPIVPPContext *vpp_ctx = avctx->priv;
350
351 ff_vaapi_vpp_ctx_init(avctx);
352 vpp_ctx->pipeline_uninit = deint_vaapi_pipeline_uninit;
353 vpp_ctx->build_filter_params = deint_vaapi_build_filter_params;
354 vpp_ctx->output_format = AV_PIX_FMT_NONE;
355
356 return 0;
357 }
358
359 #define OFFSET(x) offsetof(DeintVAAPIContext, x)
360 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
361 static const AVOption deint_vaapi_options[] = {
362 { "mode", "Deinterlacing mode",
363 OFFSET(mode), AV_OPT_TYPE_INT, { .i64 = VAProcDeinterlacingNone },
364 VAProcDeinterlacingNone, VAProcDeinterlacingCount - 1, FLAGS, "mode" },
365 { "default", "Use the highest-numbered (and therefore possibly most advanced) deinterlacing algorithm",
366 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingNone }, 0, 0, FLAGS, "mode" },
367 { "bob", "Use the bob deinterlacing algorithm",
368 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingBob }, 0, 0, FLAGS, "mode" },
369 { "weave", "Use the weave deinterlacing algorithm",
370 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingWeave }, 0, 0, FLAGS, "mode" },
371 { "motion_adaptive", "Use the motion adaptive deinterlacing algorithm",
372 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionAdaptive }, 0, 0, FLAGS, "mode" },
373 { "motion_compensated", "Use the motion compensated deinterlacing algorithm",
374 0, AV_OPT_TYPE_CONST, { .i64 = VAProcDeinterlacingMotionCompensated }, 0, 0, FLAGS, "mode" },
375
376 { "rate", "Generate output at frame rate or field rate",
377 OFFSET(field_rate), AV_OPT_TYPE_INT, { .i64 = 1 }, 1, 2, FLAGS, "rate" },
378 { "frame", "Output at frame rate (one frame of output for each field-pair)",
379 0, AV_OPT_TYPE_CONST, { .i64 = 1 }, 0, 0, FLAGS, "rate" },
380 { "field", "Output at field rate (one frame of output for each field)",
381 0, AV_OPT_TYPE_CONST, { .i64 = 2 }, 0, 0, FLAGS, "rate" },
382
383 { "auto", "Only deinterlace fields, passing frames through unchanged",
384 OFFSET(auto_enable), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, 1, FLAGS },
385
386 { NULL },
387 };
388
389 static const AVClass deint_vaapi_class = {
390 .class_name = "deinterlace_vaapi",
391 .item_name = av_default_item_name,
392 .option = deint_vaapi_options,
393 .version = LIBAVUTIL_VERSION_INT,
394 };
395
396 static const AVFilterPad deint_vaapi_inputs[] = {
397 {
398 .name = "default",
399 .type = AVMEDIA_TYPE_VIDEO,
400 .filter_frame = &deint_vaapi_filter_frame,
401 .config_props = &ff_vaapi_vpp_config_input,
402 },
403 };
404
405 static const AVFilterPad deint_vaapi_outputs[] = {
406 {
407 .name = "default",
408 .type = AVMEDIA_TYPE_VIDEO,
409 .request_frame = &deint_vaapi_request_frame,
410 .config_props = &deint_vaapi_config_output,
411 },
412 };
413
414 const AVFilter ff_vf_deinterlace_vaapi = {
415 .name = "deinterlace_vaapi",
416 .description = NULL_IF_CONFIG_SMALL("Deinterlacing of VAAPI surfaces"),
417 .priv_size = sizeof(DeintVAAPIContext),
418 .init = &deint_vaapi_init,
419 .uninit = &ff_vaapi_vpp_ctx_uninit,
420 FILTER_INPUTS(deint_vaapi_inputs),
421 FILTER_OUTPUTS(deint_vaapi_outputs),
422 FILTER_QUERY_FUNC(&ff_vaapi_vpp_query_formats),
423 .priv_class = &deint_vaapi_class,
424 .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE,
425 };
426