blob: a5f5be7d66d794de1cad241d280a5a0046cbdfed
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 | /** |
20 | * @file |
21 | * scale video filter - QSV |
22 | */ |
23 | |
24 | #include <mfx/mfxvideo.h> |
25 | |
26 | #include <stdio.h> |
27 | #include <string.h> |
28 | |
29 | #include "libavutil/avstring.h" |
30 | #include "libavutil/common.h" |
31 | #include "libavutil/eval.h" |
32 | #include "libavutil/hwcontext.h" |
33 | #include "libavutil/hwcontext_qsv.h" |
34 | #include "libavutil/internal.h" |
35 | #include "libavutil/mathematics.h" |
36 | #include "libavutil/opt.h" |
37 | #include "libavutil/pixdesc.h" |
38 | #include "libavutil/time.h" |
39 | |
40 | #include "avfilter.h" |
41 | #include "formats.h" |
42 | #include "internal.h" |
43 | #include "video.h" |
44 | |
45 | static const char *const var_names[] = { |
46 | "PI", |
47 | "PHI", |
48 | "E", |
49 | "in_w", "iw", |
50 | "in_h", "ih", |
51 | "out_w", "ow", |
52 | "out_h", "oh", |
53 | "a", "dar", |
54 | "sar", |
55 | NULL |
56 | }; |
57 | |
58 | enum var_name { |
59 | VAR_PI, |
60 | VAR_PHI, |
61 | VAR_E, |
62 | VAR_IN_W, VAR_IW, |
63 | VAR_IN_H, VAR_IH, |
64 | VAR_OUT_W, VAR_OW, |
65 | VAR_OUT_H, VAR_OH, |
66 | VAR_A, VAR_DAR, |
67 | VAR_SAR, |
68 | VARS_NB |
69 | }; |
70 | |
71 | typedef struct QSVScaleContext { |
72 | const AVClass *class; |
73 | |
74 | AVBufferRef *out_frames_ref; |
75 | /* a clone of the main session, used internally for scaling */ |
76 | mfxSession session; |
77 | |
78 | mfxMemId *mem_ids_in; |
79 | int nb_mem_ids_in; |
80 | |
81 | mfxMemId *mem_ids_out; |
82 | int nb_mem_ids_out; |
83 | |
84 | mfxFrameSurface1 **surface_ptrs_in; |
85 | int nb_surface_ptrs_in; |
86 | |
87 | mfxFrameSurface1 **surface_ptrs_out; |
88 | int nb_surface_ptrs_out; |
89 | |
90 | mfxExtOpaqueSurfaceAlloc opaque_alloc; |
91 | mfxExtBuffer *ext_buffers[1]; |
92 | |
93 | int shift_width, shift_height; |
94 | |
95 | /** |
96 | * New dimensions. Special values are: |
97 | * 0 = original width/height |
98 | * -1 = keep original aspect |
99 | */ |
100 | int w, h; |
101 | |
102 | /** |
103 | * Output sw format. AV_PIX_FMT_NONE for no conversion. |
104 | */ |
105 | enum AVPixelFormat format; |
106 | |
107 | char *w_expr; ///< width expression string |
108 | char *h_expr; ///< height expression string |
109 | char *format_str; |
110 | } QSVScaleContext; |
111 | |
112 | static int qsvscale_init(AVFilterContext *ctx) |
113 | { |
114 | QSVScaleContext *s = ctx->priv; |
115 | |
116 | if (!strcmp(s->format_str, "same")) { |
117 | s->format = AV_PIX_FMT_NONE; |
118 | } else { |
119 | s->format = av_get_pix_fmt(s->format_str); |
120 | if (s->format == AV_PIX_FMT_NONE) { |
121 | av_log(ctx, AV_LOG_ERROR, "Unrecognized pixel format: %s\n", s->format_str); |
122 | return AVERROR(EINVAL); |
123 | } |
124 | } |
125 | |
126 | return 0; |
127 | } |
128 | |
129 | static void qsvscale_uninit(AVFilterContext *ctx) |
130 | { |
131 | QSVScaleContext *s = ctx->priv; |
132 | |
133 | if (s->session) { |
134 | MFXClose(s->session); |
135 | s->session = NULL; |
136 | } |
137 | av_buffer_unref(&s->out_frames_ref); |
138 | |
139 | av_freep(&s->mem_ids_in); |
140 | av_freep(&s->mem_ids_out); |
141 | s->nb_mem_ids_in = 0; |
142 | s->nb_mem_ids_out = 0; |
143 | |
144 | av_freep(&s->surface_ptrs_in); |
145 | av_freep(&s->surface_ptrs_out); |
146 | s->nb_surface_ptrs_in = 0; |
147 | s->nb_surface_ptrs_out = 0; |
148 | } |
149 | |
150 | static int qsvscale_query_formats(AVFilterContext *ctx) |
151 | { |
152 | static const enum AVPixelFormat pixel_formats[] = { |
153 | AV_PIX_FMT_QSV, AV_PIX_FMT_NONE, |
154 | }; |
155 | AVFilterFormats *pix_fmts = ff_make_format_list(pixel_formats); |
156 | int ret; |
157 | |
158 | if ((ret = ff_set_common_formats(ctx, pix_fmts)) < 0) |
159 | return ret; |
160 | |
161 | return 0; |
162 | } |
163 | |
164 | static int init_out_pool(AVFilterContext *ctx, |
165 | int out_width, int out_height) |
166 | { |
167 | QSVScaleContext *s = ctx->priv; |
168 | |
169 | AVHWFramesContext *in_frames_ctx; |
170 | AVHWFramesContext *out_frames_ctx; |
171 | AVQSVFramesContext *in_frames_hwctx; |
172 | AVQSVFramesContext *out_frames_hwctx; |
173 | enum AVPixelFormat in_format; |
174 | enum AVPixelFormat out_format; |
175 | int i, ret; |
176 | |
177 | /* check that we have a hw context */ |
178 | if (!ctx->inputs[0]->hw_frames_ctx) { |
179 | av_log(ctx, AV_LOG_ERROR, "No hw context provided on input\n"); |
180 | return AVERROR(EINVAL); |
181 | } |
182 | in_frames_ctx = (AVHWFramesContext*)ctx->inputs[0]->hw_frames_ctx->data; |
183 | in_frames_hwctx = in_frames_ctx->hwctx; |
184 | |
185 | in_format = in_frames_ctx->sw_format; |
186 | out_format = (s->format == AV_PIX_FMT_NONE) ? in_format : s->format; |
187 | |
188 | s->out_frames_ref = av_hwframe_ctx_alloc(in_frames_ctx->device_ref); |
189 | if (!s->out_frames_ref) |
190 | return AVERROR(ENOMEM); |
191 | out_frames_ctx = (AVHWFramesContext*)s->out_frames_ref->data; |
192 | out_frames_hwctx = out_frames_ctx->hwctx; |
193 | |
194 | out_frames_ctx->format = AV_PIX_FMT_QSV; |
195 | out_frames_ctx->width = FFALIGN(out_width, 32); |
196 | out_frames_ctx->height = FFALIGN(out_height, 32); |
197 | out_frames_ctx->sw_format = out_format; |
198 | out_frames_ctx->initial_pool_size = 32; |
199 | |
200 | out_frames_hwctx->frame_type = in_frames_hwctx->frame_type; |
201 | |
202 | ret = av_hwframe_ctx_init(s->out_frames_ref); |
203 | if (ret < 0) |
204 | return ret; |
205 | |
206 | for (i = 0; i < out_frames_hwctx->nb_surfaces; i++) { |
207 | mfxFrameInfo *info = &out_frames_hwctx->surfaces[i].Info; |
208 | info->CropW = out_width; |
209 | info->CropH = out_height; |
210 | } |
211 | |
212 | return 0; |
213 | } |
214 | |
215 | static mfxStatus frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req, |
216 | mfxFrameAllocResponse *resp) |
217 | { |
218 | AVFilterContext *ctx = pthis; |
219 | QSVScaleContext *s = ctx->priv; |
220 | |
221 | if (!(req->Type & MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET) || |
222 | !(req->Type & (MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_FROM_VPPOUT)) || |
223 | !(req->Type & MFX_MEMTYPE_EXTERNAL_FRAME)) |
224 | return MFX_ERR_UNSUPPORTED; |
225 | |
226 | if (req->Type & MFX_MEMTYPE_FROM_VPPIN) { |
227 | resp->mids = s->mem_ids_in; |
228 | resp->NumFrameActual = s->nb_mem_ids_in; |
229 | } else { |
230 | resp->mids = s->mem_ids_out; |
231 | resp->NumFrameActual = s->nb_mem_ids_out; |
232 | } |
233 | |
234 | return MFX_ERR_NONE; |
235 | } |
236 | |
237 | static mfxStatus frame_free(mfxHDL pthis, mfxFrameAllocResponse *resp) |
238 | { |
239 | return MFX_ERR_NONE; |
240 | } |
241 | |
242 | static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) |
243 | { |
244 | return MFX_ERR_UNSUPPORTED; |
245 | } |
246 | |
247 | static mfxStatus frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) |
248 | { |
249 | return MFX_ERR_UNSUPPORTED; |
250 | } |
251 | |
252 | static mfxStatus frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) |
253 | { |
254 | *hdl = mid; |
255 | return MFX_ERR_NONE; |
256 | } |
257 | |
258 | static const mfxHandleType handle_types[] = { |
259 | MFX_HANDLE_VA_DISPLAY, |
260 | MFX_HANDLE_D3D9_DEVICE_MANAGER, |
261 | MFX_HANDLE_D3D11_DEVICE, |
262 | }; |
263 | |
264 | static int init_out_session(AVFilterContext *ctx) |
265 | { |
266 | |
267 | QSVScaleContext *s = ctx->priv; |
268 | AVHWFramesContext *in_frames_ctx = (AVHWFramesContext*)ctx->inputs[0]->hw_frames_ctx->data; |
269 | AVHWFramesContext *out_frames_ctx = (AVHWFramesContext*)s->out_frames_ref->data; |
270 | AVQSVFramesContext *in_frames_hwctx = in_frames_ctx->hwctx; |
271 | AVQSVFramesContext *out_frames_hwctx = out_frames_ctx->hwctx; |
272 | AVQSVDeviceContext *device_hwctx = in_frames_ctx->device_ctx->hwctx; |
273 | |
274 | int opaque = !!(in_frames_hwctx->frame_type & MFX_MEMTYPE_OPAQUE_FRAME); |
275 | |
276 | mfxHDL handle = NULL; |
277 | mfxHandleType handle_type; |
278 | mfxVersion ver; |
279 | mfxIMPL impl; |
280 | mfxVideoParam par; |
281 | mfxStatus err; |
282 | int i; |
283 | |
284 | /* extract the properties of the "master" session given to us */ |
285 | err = MFXQueryIMPL(device_hwctx->session, &impl); |
286 | if (err == MFX_ERR_NONE) |
287 | err = MFXQueryVersion(device_hwctx->session, &ver); |
288 | if (err != MFX_ERR_NONE) { |
289 | av_log(ctx, AV_LOG_ERROR, "Error querying the session attributes\n"); |
290 | return AVERROR_UNKNOWN; |
291 | } |
292 | |
293 | for (i = 0; i < FF_ARRAY_ELEMS(handle_types); i++) { |
294 | err = MFXVideoCORE_GetHandle(device_hwctx->session, handle_types[i], &handle); |
295 | if (err == MFX_ERR_NONE) { |
296 | handle_type = handle_types[i]; |
297 | break; |
298 | } |
299 | } |
300 | |
301 | /* create a "slave" session with those same properties, to be used for |
302 | * actual scaling */ |
303 | err = MFXInit(impl, &ver, &s->session); |
304 | if (err != MFX_ERR_NONE) { |
305 | av_log(ctx, AV_LOG_ERROR, "Error initializing a session for scaling\n"); |
306 | return AVERROR_UNKNOWN; |
307 | } |
308 | |
309 | if (handle) { |
310 | err = MFXVideoCORE_SetHandle(s->session, handle_type, handle); |
311 | if (err != MFX_ERR_NONE) |
312 | return AVERROR_UNKNOWN; |
313 | } |
314 | |
315 | memset(&par, 0, sizeof(par)); |
316 | |
317 | if (opaque) { |
318 | s->surface_ptrs_in = av_mallocz_array(in_frames_hwctx->nb_surfaces, |
319 | sizeof(*s->surface_ptrs_in)); |
320 | if (!s->surface_ptrs_in) |
321 | return AVERROR(ENOMEM); |
322 | for (i = 0; i < in_frames_hwctx->nb_surfaces; i++) |
323 | s->surface_ptrs_in[i] = in_frames_hwctx->surfaces + i; |
324 | s->nb_surface_ptrs_in = in_frames_hwctx->nb_surfaces; |
325 | |
326 | s->surface_ptrs_out = av_mallocz_array(out_frames_hwctx->nb_surfaces, |
327 | sizeof(*s->surface_ptrs_out)); |
328 | if (!s->surface_ptrs_out) |
329 | return AVERROR(ENOMEM); |
330 | for (i = 0; i < out_frames_hwctx->nb_surfaces; i++) |
331 | s->surface_ptrs_out[i] = out_frames_hwctx->surfaces + i; |
332 | s->nb_surface_ptrs_out = out_frames_hwctx->nb_surfaces; |
333 | |
334 | s->opaque_alloc.In.Surfaces = s->surface_ptrs_in; |
335 | s->opaque_alloc.In.NumSurface = s->nb_surface_ptrs_in; |
336 | s->opaque_alloc.In.Type = in_frames_hwctx->frame_type; |
337 | |
338 | s->opaque_alloc.Out.Surfaces = s->surface_ptrs_out; |
339 | s->opaque_alloc.Out.NumSurface = s->nb_surface_ptrs_out; |
340 | s->opaque_alloc.Out.Type = out_frames_hwctx->frame_type; |
341 | |
342 | s->opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; |
343 | s->opaque_alloc.Header.BufferSz = sizeof(s->opaque_alloc); |
344 | |
345 | s->ext_buffers[0] = (mfxExtBuffer*)&s->opaque_alloc; |
346 | |
347 | par.ExtParam = s->ext_buffers; |
348 | par.NumExtParam = FF_ARRAY_ELEMS(s->ext_buffers); |
349 | |
350 | par.IOPattern = MFX_IOPATTERN_IN_OPAQUE_MEMORY | MFX_IOPATTERN_OUT_OPAQUE_MEMORY; |
351 | } else { |
352 | mfxFrameAllocator frame_allocator = { |
353 | .pthis = ctx, |
354 | .Alloc = frame_alloc, |
355 | .Lock = frame_lock, |
356 | .Unlock = frame_unlock, |
357 | .GetHDL = frame_get_hdl, |
358 | .Free = frame_free, |
359 | }; |
360 | |
361 | s->mem_ids_in = av_mallocz_array(in_frames_hwctx->nb_surfaces, |
362 | sizeof(*s->mem_ids_in)); |
363 | if (!s->mem_ids_in) |
364 | return AVERROR(ENOMEM); |
365 | for (i = 0; i < in_frames_hwctx->nb_surfaces; i++) |
366 | s->mem_ids_in[i] = in_frames_hwctx->surfaces[i].Data.MemId; |
367 | s->nb_mem_ids_in = in_frames_hwctx->nb_surfaces; |
368 | |
369 | s->mem_ids_out = av_mallocz_array(out_frames_hwctx->nb_surfaces, |
370 | sizeof(*s->mem_ids_out)); |
371 | if (!s->mem_ids_out) |
372 | return AVERROR(ENOMEM); |
373 | for (i = 0; i < out_frames_hwctx->nb_surfaces; i++) |
374 | s->mem_ids_out[i] = out_frames_hwctx->surfaces[i].Data.MemId; |
375 | s->nb_mem_ids_out = out_frames_hwctx->nb_surfaces; |
376 | |
377 | err = MFXVideoCORE_SetFrameAllocator(s->session, &frame_allocator); |
378 | if (err != MFX_ERR_NONE) |
379 | return AVERROR_UNKNOWN; |
380 | |
381 | par.IOPattern = MFX_IOPATTERN_IN_VIDEO_MEMORY | MFX_IOPATTERN_OUT_VIDEO_MEMORY; |
382 | } |
383 | |
384 | par.AsyncDepth = 1; // TODO async |
385 | |
386 | par.vpp.In = in_frames_hwctx->surfaces[0].Info; |
387 | par.vpp.Out = out_frames_hwctx->surfaces[0].Info; |
388 | |
389 | /* Apparently VPP requires the frame rate to be set to some value, otherwise |
390 | * init will fail (probably for the framerate conversion filter). Since we |
391 | * are only doing scaling here, we just invent an arbitrary |
392 | * value */ |
393 | par.vpp.In.FrameRateExtN = 25; |
394 | par.vpp.In.FrameRateExtD = 1; |
395 | par.vpp.Out.FrameRateExtN = 25; |
396 | par.vpp.Out.FrameRateExtD = 1; |
397 | |
398 | err = MFXVideoVPP_Init(s->session, &par); |
399 | if (err != MFX_ERR_NONE) { |
400 | av_log(ctx, AV_LOG_ERROR, "Error opening the VPP for scaling\n"); |
401 | return AVERROR_UNKNOWN; |
402 | } |
403 | |
404 | return 0; |
405 | } |
406 | |
407 | static int init_scale_session(AVFilterContext *ctx, int in_width, int in_height, |
408 | int out_width, int out_height) |
409 | { |
410 | QSVScaleContext *s = ctx->priv; |
411 | |
412 | int ret; |
413 | |
414 | qsvscale_uninit(ctx); |
415 | |
416 | ret = init_out_pool(ctx, out_width, out_height); |
417 | if (ret < 0) |
418 | return ret; |
419 | |
420 | ret = init_out_session(ctx); |
421 | if (ret < 0) |
422 | return ret; |
423 | |
424 | av_buffer_unref(&ctx->outputs[0]->hw_frames_ctx); |
425 | ctx->outputs[0]->hw_frames_ctx = av_buffer_ref(s->out_frames_ref); |
426 | if (!ctx->outputs[0]->hw_frames_ctx) |
427 | return AVERROR(ENOMEM); |
428 | |
429 | return 0; |
430 | } |
431 | |
432 | static int qsvscale_config_props(AVFilterLink *outlink) |
433 | { |
434 | AVFilterContext *ctx = outlink->src; |
435 | AVFilterLink *inlink = outlink->src->inputs[0]; |
436 | QSVScaleContext *s = ctx->priv; |
437 | int64_t w, h; |
438 | double var_values[VARS_NB], res; |
439 | char *expr; |
440 | int ret; |
441 | |
442 | var_values[VAR_PI] = M_PI; |
443 | var_values[VAR_PHI] = M_PHI; |
444 | var_values[VAR_E] = M_E; |
445 | var_values[VAR_IN_W] = var_values[VAR_IW] = inlink->w; |
446 | var_values[VAR_IN_H] = var_values[VAR_IH] = inlink->h; |
447 | var_values[VAR_OUT_W] = var_values[VAR_OW] = NAN; |
448 | var_values[VAR_OUT_H] = var_values[VAR_OH] = NAN; |
449 | var_values[VAR_A] = (double) inlink->w / inlink->h; |
450 | var_values[VAR_SAR] = inlink->sample_aspect_ratio.num ? |
451 | (double) inlink->sample_aspect_ratio.num / inlink->sample_aspect_ratio.den : 1; |
452 | var_values[VAR_DAR] = var_values[VAR_A] * var_values[VAR_SAR]; |
453 | |
454 | /* evaluate width and height */ |
455 | av_expr_parse_and_eval(&res, (expr = s->w_expr), |
456 | var_names, var_values, |
457 | NULL, NULL, NULL, NULL, NULL, 0, ctx); |
458 | s->w = var_values[VAR_OUT_W] = var_values[VAR_OW] = res; |
459 | if ((ret = av_expr_parse_and_eval(&res, (expr = s->h_expr), |
460 | var_names, var_values, |
461 | NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) |
462 | goto fail; |
463 | s->h = var_values[VAR_OUT_H] = var_values[VAR_OH] = res; |
464 | /* evaluate again the width, as it may depend on the output height */ |
465 | if ((ret = av_expr_parse_and_eval(&res, (expr = s->w_expr), |
466 | var_names, var_values, |
467 | NULL, NULL, NULL, NULL, NULL, 0, ctx)) < 0) |
468 | goto fail; |
469 | s->w = res; |
470 | |
471 | w = s->w; |
472 | h = s->h; |
473 | |
474 | /* sanity check params */ |
475 | if (w < -1 || h < -1) { |
476 | av_log(ctx, AV_LOG_ERROR, "Size values less than -1 are not acceptable.\n"); |
477 | return AVERROR(EINVAL); |
478 | } |
479 | if (w == -1 && h == -1) |
480 | s->w = s->h = 0; |
481 | |
482 | if (!(w = s->w)) |
483 | w = inlink->w; |
484 | if (!(h = s->h)) |
485 | h = inlink->h; |
486 | if (w == -1) |
487 | w = av_rescale(h, inlink->w, inlink->h); |
488 | if (h == -1) |
489 | h = av_rescale(w, inlink->h, inlink->w); |
490 | |
491 | if (w > INT_MAX || h > INT_MAX || |
492 | (h * inlink->w) > INT_MAX || |
493 | (w * inlink->h) > INT_MAX) |
494 | av_log(ctx, AV_LOG_ERROR, "Rescaled value for width or height is too big.\n"); |
495 | |
496 | outlink->w = w; |
497 | outlink->h = h; |
498 | |
499 | ret = init_scale_session(ctx, inlink->w, inlink->h, w, h); |
500 | if (ret < 0) |
501 | return ret; |
502 | |
503 | av_log(ctx, AV_LOG_VERBOSE, "w:%d h:%d -> w:%d h:%d\n", |
504 | inlink->w, inlink->h, outlink->w, outlink->h); |
505 | |
506 | if (inlink->sample_aspect_ratio.num) |
507 | outlink->sample_aspect_ratio = av_mul_q((AVRational){outlink->h*inlink->w, |
508 | outlink->w*inlink->h}, |
509 | inlink->sample_aspect_ratio); |
510 | else |
511 | outlink->sample_aspect_ratio = inlink->sample_aspect_ratio; |
512 | |
513 | return 0; |
514 | |
515 | fail: |
516 | av_log(NULL, AV_LOG_ERROR, |
517 | "Error when evaluating the expression '%s'\n", expr); |
518 | return ret; |
519 | } |
520 | |
521 | static int qsvscale_filter_frame(AVFilterLink *link, AVFrame *in) |
522 | { |
523 | AVFilterContext *ctx = link->dst; |
524 | QSVScaleContext *s = ctx->priv; |
525 | AVFilterLink *outlink = ctx->outputs[0]; |
526 | |
527 | mfxSyncPoint sync = NULL; |
528 | mfxStatus err; |
529 | |
530 | AVFrame *out = NULL; |
531 | int ret = 0; |
532 | |
533 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
534 | if (!out) { |
535 | ret = AVERROR(ENOMEM); |
536 | goto fail; |
537 | } |
538 | |
539 | do { |
540 | err = MFXVideoVPP_RunFrameVPPAsync(s->session, |
541 | (mfxFrameSurface1*)in->data[3], |
542 | (mfxFrameSurface1*)out->data[3], |
543 | NULL, &sync); |
544 | if (err == MFX_WRN_DEVICE_BUSY) |
545 | av_usleep(1); |
546 | } while (err == MFX_WRN_DEVICE_BUSY); |
547 | |
548 | if (err < 0 || !sync) { |
549 | av_log(ctx, AV_LOG_ERROR, "Error during scaling\n"); |
550 | ret = AVERROR_UNKNOWN; |
551 | goto fail; |
552 | } |
553 | |
554 | do { |
555 | err = MFXVideoCORE_SyncOperation(s->session, sync, 1000); |
556 | } while (err == MFX_WRN_IN_EXECUTION); |
557 | if (err < 0) { |
558 | av_log(ctx, AV_LOG_ERROR, "Error synchronizing the operation: %d\n", err); |
559 | ret = AVERROR_UNKNOWN; |
560 | goto fail; |
561 | } |
562 | |
563 | ret = av_frame_copy_props(out, in); |
564 | if (ret < 0) |
565 | goto fail; |
566 | |
567 | out->width = outlink->w; |
568 | out->height = outlink->h; |
569 | |
570 | av_reduce(&out->sample_aspect_ratio.num, &out->sample_aspect_ratio.den, |
571 | (int64_t)in->sample_aspect_ratio.num * outlink->h * link->w, |
572 | (int64_t)in->sample_aspect_ratio.den * outlink->w * link->h, |
573 | INT_MAX); |
574 | |
575 | av_frame_free(&in); |
576 | return ff_filter_frame(outlink, out); |
577 | fail: |
578 | av_frame_free(&in); |
579 | av_frame_free(&out); |
580 | return ret; |
581 | } |
582 | |
583 | #define OFFSET(x) offsetof(QSVScaleContext, x) |
584 | #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM |
585 | static const AVOption options[] = { |
586 | { "w", "Output video width", OFFSET(w_expr), AV_OPT_TYPE_STRING, { .str = "iw" }, .flags = FLAGS }, |
587 | { "h", "Output video height", OFFSET(h_expr), AV_OPT_TYPE_STRING, { .str = "ih" }, .flags = FLAGS }, |
588 | { "format", "Output pixel format", OFFSET(format_str), AV_OPT_TYPE_STRING, { .str = "same" }, .flags = FLAGS }, |
589 | |
590 | { NULL }, |
591 | }; |
592 | |
593 | static const AVClass qsvscale_class = { |
594 | .class_name = "qsvscale", |
595 | .item_name = av_default_item_name, |
596 | .option = options, |
597 | .version = LIBAVUTIL_VERSION_INT, |
598 | }; |
599 | |
600 | static const AVFilterPad qsvscale_inputs[] = { |
601 | { |
602 | .name = "default", |
603 | .type = AVMEDIA_TYPE_VIDEO, |
604 | .filter_frame = qsvscale_filter_frame, |
605 | }, |
606 | { NULL } |
607 | }; |
608 | |
609 | static const AVFilterPad qsvscale_outputs[] = { |
610 | { |
611 | .name = "default", |
612 | .type = AVMEDIA_TYPE_VIDEO, |
613 | .config_props = qsvscale_config_props, |
614 | }, |
615 | { NULL } |
616 | }; |
617 | |
618 | AVFilter ff_vf_scale_qsv = { |
619 | .name = "scale_qsv", |
620 | .description = NULL_IF_CONFIG_SMALL("QuickSync video scaling and format conversion"), |
621 | |
622 | .init = qsvscale_init, |
623 | .uninit = qsvscale_uninit, |
624 | .query_formats = qsvscale_query_formats, |
625 | |
626 | .priv_size = sizeof(QSVScaleContext), |
627 | .priv_class = &qsvscale_class, |
628 | |
629 | .inputs = qsvscale_inputs, |
630 | .outputs = qsvscale_outputs, |
631 | |
632 | .flags_internal = FF_FILTER_FLAG_HWFRAME_AWARE, |
633 | }; |
634 |