]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_scale_vaapi.c
Merge commit '59c70227405c214b29971e6272f3a3ff6fcce3d0'
[ffmpeg] / libavfilter / vf_scale_vaapi.c
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 <va/va.h>
22 #include <va/va_vpp.h>
23
24 #include "libavutil/avassert.h"
25 #include "libavutil/hwcontext.h"
26 #include "libavutil/hwcontext_vaapi.h"
27 #include "libavutil/mem.h"
28 #include "libavutil/opt.h"
29 #include "libavutil/pixdesc.h"
30
31 #include "avfilter.h"
32 #include "formats.h"
33 #include "internal.h"
34 #include "scale.h"
35
36 typedef struct ScaleVAAPIContext {
37     const AVClass *class;
38
39     AVVAAPIDeviceContext *hwctx;
40     AVBufferRef *device_ref;
41
42     int valid_ids;
43     VAConfigID  va_config;
44     VAContextID va_context;
45
46     AVBufferRef       *input_frames_ref;
47     AVHWFramesContext *input_frames;
48
49     AVBufferRef       *output_frames_ref;
50     AVHWFramesContext *output_frames;
51
52     char *output_format_string;
53     enum AVPixelFormat output_format;
54
55     char *w_expr;      // width expression string
56     char *h_expr;      // height expression string
57
58     int output_width;  // computed width
59     int output_height; // computed height
60 } ScaleVAAPIContext;
61
62
63 static int scale_vaapi_query_formats(AVFilterContext *avctx)
64 {
65     enum AVPixelFormat pix_fmts[] = {
66         AV_PIX_FMT_VAAPI, AV_PIX_FMT_NONE,
67     };
68     int err;
69
70     if ((err = ff_formats_ref(ff_make_format_list(pix_fmts),
71                               &avctx->inputs[0]->out_formats)) < 0)
72         return err;
73     if ((err = ff_formats_ref(ff_make_format_list(pix_fmts),
74                               &avctx->outputs[0]->in_formats)) < 0)
75         return err;
76
77     return 0;
78 }
79
80 static int scale_vaapi_pipeline_uninit(ScaleVAAPIContext *ctx)
81 {
82     if (ctx->va_context != VA_INVALID_ID) {
83         vaDestroyContext(ctx->hwctx->display, ctx->va_context);
84         ctx->va_context = VA_INVALID_ID;
85     }
86
87     if (ctx->va_config != VA_INVALID_ID) {
88         vaDestroyConfig(ctx->hwctx->display, ctx->va_config);
89         ctx->va_config = VA_INVALID_ID;
90     }
91
92     av_buffer_unref(&ctx->output_frames_ref);
93     av_buffer_unref(&ctx->device_ref);
94     ctx->hwctx = 0;
95
96     return 0;
97 }
98
99 static int scale_vaapi_config_input(AVFilterLink *inlink)
100 {
101     AVFilterContext *avctx = inlink->dst;
102     ScaleVAAPIContext *ctx = avctx->priv;
103
104     scale_vaapi_pipeline_uninit(ctx);
105
106     if (!inlink->hw_frames_ctx) {
107         av_log(avctx, AV_LOG_ERROR, "A hardware frames reference is "
108                "required to associate the processing device.\n");
109         return AVERROR(EINVAL);
110     }
111
112     ctx->input_frames_ref = av_buffer_ref(inlink->hw_frames_ctx);
113     ctx->input_frames = (AVHWFramesContext*)ctx->input_frames_ref->data;
114
115     return 0;
116 }
117
118 static int scale_vaapi_config_output(AVFilterLink *outlink)
119 {
120     AVFilterLink *inlink = outlink->src->inputs[0];
121     AVFilterContext *avctx = outlink->src;
122     ScaleVAAPIContext *ctx = avctx->priv;
123     AVVAAPIHWConfig *hwconfig = NULL;
124     AVHWFramesConstraints *constraints = NULL;
125     AVVAAPIFramesContext *va_frames;
126     VAStatus vas;
127     int err, i;
128
129     scale_vaapi_pipeline_uninit(ctx);
130
131     ctx->device_ref = av_buffer_ref(ctx->input_frames->device_ref);
132     ctx->hwctx = ((AVHWDeviceContext*)ctx->device_ref->data)->hwctx;
133
134     av_assert0(ctx->va_config == VA_INVALID_ID);
135     vas = vaCreateConfig(ctx->hwctx->display, VAProfileNone,
136                          VAEntrypointVideoProc, 0, 0, &ctx->va_config);
137     if (vas != VA_STATUS_SUCCESS) {
138         av_log(ctx, AV_LOG_ERROR, "Failed to create processing pipeline "
139                "config: %d (%s).\n", vas, vaErrorStr(vas));
140         err = AVERROR(EIO);
141         goto fail;
142     }
143
144     hwconfig = av_hwdevice_hwconfig_alloc(ctx->device_ref);
145     if (!hwconfig) {
146         err = AVERROR(ENOMEM);
147         goto fail;
148     }
149     hwconfig->config_id = ctx->va_config;
150
151     constraints = av_hwdevice_get_hwframe_constraints(ctx->device_ref,
152                                                       hwconfig);
153     if (!constraints) {
154         err = AVERROR(ENOMEM);
155         goto fail;
156     }
157
158     if (ctx->output_format == AV_PIX_FMT_NONE)
159         ctx->output_format = ctx->input_frames->sw_format;
160     if (constraints->valid_sw_formats) {
161         for (i = 0; constraints->valid_sw_formats[i] != AV_PIX_FMT_NONE; i++) {
162             if (ctx->output_format == constraints->valid_sw_formats[i])
163                 break;
164         }
165         if (constraints->valid_sw_formats[i] == AV_PIX_FMT_NONE) {
166             av_log(ctx, AV_LOG_ERROR, "Hardware does not support output "
167                    "format %s.\n", av_get_pix_fmt_name(ctx->output_format));
168             err = AVERROR(EINVAL);
169             goto fail;
170         }
171     }
172
173     if ((err = ff_scale_eval_dimensions(ctx,
174                                         ctx->w_expr, ctx->h_expr,
175                                         inlink, outlink,
176                                         &ctx->output_width, &ctx->output_height)) < 0)
177         goto fail;
178
179     if (ctx->output_width  < constraints->min_width  ||
180         ctx->output_height < constraints->min_height ||
181         ctx->output_width  > constraints->max_width  ||
182         ctx->output_height > constraints->max_height) {
183         av_log(ctx, AV_LOG_ERROR, "Hardware does not support scaling to "
184                "size %dx%d (constraints: width %d-%d height %d-%d).\n",
185                ctx->output_width, ctx->output_height,
186                constraints->min_width,  constraints->max_width,
187                constraints->min_height, constraints->max_height);
188         err = AVERROR(EINVAL);
189         goto fail;
190     }
191
192     ctx->output_frames_ref = av_hwframe_ctx_alloc(ctx->device_ref);
193     if (!ctx->output_frames_ref) {
194         av_log(ctx, AV_LOG_ERROR, "Failed to create HW frame context "
195                "for output.\n");
196         err = AVERROR(ENOMEM);
197         goto fail;
198     }
199
200     ctx->output_frames = (AVHWFramesContext*)ctx->output_frames_ref->data;
201
202     ctx->output_frames->format    = AV_PIX_FMT_VAAPI;
203     ctx->output_frames->sw_format = ctx->output_format;
204     ctx->output_frames->width     = ctx->output_width;
205     ctx->output_frames->height    = ctx->output_height;
206
207     // The number of output frames we need is determined by what follows
208     // the filter.  If it's an encoder with complex frame reference
209     // structures then this could be very high.
210     ctx->output_frames->initial_pool_size = 10;
211
212     err = av_hwframe_ctx_init(ctx->output_frames_ref);
213     if (err < 0) {
214         av_log(ctx, AV_LOG_ERROR, "Failed to initialise VAAPI frame "
215                "context for output: %d\n", err);
216         goto fail;
217     }
218
219     va_frames = ctx->output_frames->hwctx;
220
221     av_assert0(ctx->va_context == VA_INVALID_ID);
222     vas = vaCreateContext(ctx->hwctx->display, ctx->va_config,
223                           ctx->output_width, ctx->output_height,
224                           VA_PROGRESSIVE,
225                           va_frames->surface_ids, va_frames->nb_surfaces,
226                           &ctx->va_context);
227     if (vas != VA_STATUS_SUCCESS) {
228         av_log(ctx, AV_LOG_ERROR, "Failed to create processing pipeline "
229                "context: %d (%s).\n", vas, vaErrorStr(vas));
230         return AVERROR(EIO);
231     }
232
233     outlink->w = ctx->output_width;
234     outlink->h = ctx->output_height;
235
236     outlink->hw_frames_ctx = av_buffer_ref(ctx->output_frames_ref);
237     if (!outlink->hw_frames_ctx) {
238         err = AVERROR(ENOMEM);
239         goto fail;
240     }
241
242     av_freep(&hwconfig);
243     av_hwframe_constraints_free(&constraints);
244     return 0;
245
246 fail:
247     av_buffer_unref(&ctx->output_frames_ref);
248     av_freep(&hwconfig);
249     av_hwframe_constraints_free(&constraints);
250     return err;
251 }
252
253 static int vaapi_proc_colour_standard(enum AVColorSpace av_cs)
254 {
255     switch(av_cs) {
256 #define CS(av, va) case AVCOL_SPC_ ## av: return VAProcColorStandard ## va;
257         CS(BT709,     BT709);
258         CS(BT470BG,   BT601);
259         CS(SMPTE170M, SMPTE170M);
260         CS(SMPTE240M, SMPTE240M);
261 #undef CS
262     default:
263         return VAProcColorStandardNone;
264     }
265 }
266
267 static int scale_vaapi_filter_frame(AVFilterLink *inlink, AVFrame *input_frame)
268 {
269     AVFilterContext *avctx = inlink->dst;
270     AVFilterLink *outlink = avctx->outputs[0];
271     ScaleVAAPIContext *ctx = avctx->priv;
272     AVFrame *output_frame = NULL;
273     VASurfaceID input_surface, output_surface;
274     VAProcPipelineParameterBuffer params;
275     VABufferID params_id;
276     VARectangle input_region;
277     VAStatus vas;
278     int err;
279
280     av_log(ctx, AV_LOG_DEBUG, "Filter input: %s, %ux%u (%"PRId64").\n",
281            av_get_pix_fmt_name(input_frame->format),
282            input_frame->width, input_frame->height, input_frame->pts);
283
284     if (ctx->va_context == VA_INVALID_ID)
285         return AVERROR(EINVAL);
286
287     input_surface = (VASurfaceID)(uintptr_t)input_frame->data[3];
288     av_log(ctx, AV_LOG_DEBUG, "Using surface %#x for scale input.\n",
289            input_surface);
290
291     output_frame = av_frame_alloc();
292     if (!output_frame) {
293         av_log(ctx, AV_LOG_ERROR, "Failed to allocate output frame.");
294         err = AVERROR(ENOMEM);
295         goto fail;
296     }
297
298     err = av_hwframe_get_buffer(ctx->output_frames_ref, output_frame, 0);
299     if (err < 0) {
300         av_log(ctx, AV_LOG_ERROR, "Failed to get surface for "
301                "output: %d\n.", err);
302     }
303
304     output_surface = (VASurfaceID)(uintptr_t)output_frame->data[3];
305     av_log(ctx, AV_LOG_DEBUG, "Using surface %#x for scale output.\n",
306            output_surface);
307
308     memset(&params, 0, sizeof(params));
309
310     // If there were top/left cropping, it could be taken into
311     // account here.
312     input_region = (VARectangle) {
313         .x      = 0,
314         .y      = 0,
315         .width  = input_frame->width,
316         .height = input_frame->height,
317     };
318
319     params.surface = input_surface;
320     params.surface_region = &input_region;
321     params.surface_color_standard =
322         vaapi_proc_colour_standard(input_frame->colorspace);
323
324     params.output_region = 0;
325     params.output_background_color = 0xff000000;
326     params.output_color_standard = params.surface_color_standard;
327
328     params.pipeline_flags = 0;
329     params.filter_flags = VA_FILTER_SCALING_HQ;
330
331     vas = vaBeginPicture(ctx->hwctx->display,
332                          ctx->va_context, output_surface);
333     if (vas != VA_STATUS_SUCCESS) {
334         av_log(ctx, AV_LOG_ERROR, "Failed to attach new picture: "
335                "%d (%s).\n", vas, vaErrorStr(vas));
336         err = AVERROR(EIO);
337         goto fail;
338     }
339
340     vas = vaCreateBuffer(ctx->hwctx->display, ctx->va_context,
341                          VAProcPipelineParameterBufferType,
342                          sizeof(params), 1, &params, &params_id);
343     if (vas != VA_STATUS_SUCCESS) {
344         av_log(ctx, AV_LOG_ERROR, "Failed to create parameter buffer: "
345                "%d (%s).\n", vas, vaErrorStr(vas));
346         err = AVERROR(EIO);
347         goto fail_after_begin;
348     }
349     av_log(ctx, AV_LOG_DEBUG, "Pipeline parameter buffer is %#x.\n",
350            params_id);
351
352     vas = vaRenderPicture(ctx->hwctx->display, ctx->va_context,
353                           &params_id, 1);
354     if (vas != VA_STATUS_SUCCESS) {
355         av_log(ctx, AV_LOG_ERROR, "Failed to render parameter buffer: "
356                "%d (%s).\n", vas, vaErrorStr(vas));
357         err = AVERROR(EIO);
358         goto fail_after_begin;
359     }
360
361     vas = vaEndPicture(ctx->hwctx->display, ctx->va_context);
362     if (vas != VA_STATUS_SUCCESS) {
363         av_log(ctx, AV_LOG_ERROR, "Failed to start picture processing: "
364                "%d (%s).\n", vas, vaErrorStr(vas));
365         err = AVERROR(EIO);
366         goto fail_after_render;
367     }
368
369     if (ctx->hwctx->driver_quirks &
370         AV_VAAPI_DRIVER_QUIRK_RENDER_PARAM_BUFFERS) {
371         vas = vaDestroyBuffer(ctx->hwctx->display, params_id);
372         if (vas != VA_STATUS_SUCCESS) {
373             av_log(ctx, AV_LOG_ERROR, "Failed to free parameter buffer: "
374                    "%d (%s).\n", vas, vaErrorStr(vas));
375             // And ignore.
376         }
377     }
378
379     av_frame_copy_props(output_frame, input_frame);
380     av_frame_free(&input_frame);
381
382     av_log(ctx, AV_LOG_DEBUG, "Filter output: %s, %ux%u (%"PRId64").\n",
383            av_get_pix_fmt_name(output_frame->format),
384            output_frame->width, output_frame->height, output_frame->pts);
385
386     return ff_filter_frame(outlink, output_frame);
387
388     // We want to make sure that if vaBeginPicture has been called, we also
389     // call vaRenderPicture and vaEndPicture.  These calls may well fail or
390     // do something else nasty, but once we're in this failure case there
391     // isn't much else we can do.
392 fail_after_begin:
393     vaRenderPicture(ctx->hwctx->display, ctx->va_context, &params_id, 1);
394 fail_after_render:
395     vaEndPicture(ctx->hwctx->display, ctx->va_context);
396 fail:
397     av_frame_free(&input_frame);
398     av_frame_free(&output_frame);
399     return err;
400 }
401
402 static av_cold int scale_vaapi_init(AVFilterContext *avctx)
403 {
404     ScaleVAAPIContext *ctx = avctx->priv;
405
406     ctx->va_config  = VA_INVALID_ID;
407     ctx->va_context = VA_INVALID_ID;
408     ctx->valid_ids  = 1;
409
410     if (ctx->output_format_string) {
411         ctx->output_format = av_get_pix_fmt(ctx->output_format_string);
412         if (ctx->output_format == AV_PIX_FMT_NONE) {
413             av_log(ctx, AV_LOG_ERROR, "Invalid output format.\n");
414             return AVERROR(EINVAL);
415         }
416     } else {
417         // Use the input format once that is configured.
418         ctx->output_format = AV_PIX_FMT_NONE;
419     }
420
421     return 0;
422 }
423
424 static av_cold void scale_vaapi_uninit(AVFilterContext *avctx)
425 {
426     ScaleVAAPIContext *ctx = avctx->priv;
427
428     if (ctx->valid_ids)
429         scale_vaapi_pipeline_uninit(ctx);
430
431     av_buffer_unref(&ctx->input_frames_ref);
432     av_buffer_unref(&ctx->output_frames_ref);
433     av_buffer_unref(&ctx->device_ref);
434 }
435
436
437 #define OFFSET(x) offsetof(ScaleVAAPIContext, x)
438 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM)
439 static const AVOption scale_vaapi_options[] = {
440     { "w", "Output video width",
441       OFFSET(w_expr), AV_OPT_TYPE_STRING, {.str = "iw"}, .flags = FLAGS },
442     { "h", "Output video height",
443       OFFSET(h_expr), AV_OPT_TYPE_STRING, {.str = "ih"}, .flags = FLAGS },
444     { "format", "Output video format (software format of hardware frames)",
445       OFFSET(output_format_string), AV_OPT_TYPE_STRING, .flags = FLAGS },
446     { NULL },
447 };
448
449 static const AVClass scale_vaapi_class = {
450     .class_name = "scale_vaapi",
451     .item_name  = av_default_item_name,
452     .option     = scale_vaapi_options,
453     .version    = LIBAVUTIL_VERSION_INT,
454 };
455
456 static const AVFilterPad scale_vaapi_inputs[] = {
457     {
458         .name         = "default",
459         .type         = AVMEDIA_TYPE_VIDEO,
460         .filter_frame = &scale_vaapi_filter_frame,
461         .config_props = &scale_vaapi_config_input,
462     },
463     { NULL }
464 };
465
466 static const AVFilterPad scale_vaapi_outputs[] = {
467     {
468         .name = "default",
469         .type = AVMEDIA_TYPE_VIDEO,
470         .config_props = &scale_vaapi_config_output,
471     },
472     { NULL }
473 };
474
475 AVFilter ff_vf_scale_vaapi = {
476     .name          = "scale_vaapi",
477     .description   = NULL_IF_CONFIG_SMALL("Scale to/from VAAPI surfaces."),
478     .priv_size     = sizeof(ScaleVAAPIContext),
479     .init          = &scale_vaapi_init,
480     .uninit        = &scale_vaapi_uninit,
481     .query_formats = &scale_vaapi_query_formats,
482     .inputs        = scale_vaapi_inputs,
483     .outputs       = scale_vaapi_outputs,
484     .priv_class    = &scale_vaapi_class,
485 };