blob: 5550ffe143dea7af9a2c5afae1ea521cd764a7e0
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 <stdint.h> |
20 | #include <string.h> |
21 | |
22 | #include <mfx/mfxvideo.h> |
23 | |
24 | #include "config.h" |
25 | |
26 | #if CONFIG_VAAPI |
27 | #include "hwcontext_vaapi.h" |
28 | #endif |
29 | #if CONFIG_DXVA2 |
30 | #include "hwcontext_dxva2.h" |
31 | #endif |
32 | |
33 | #include "buffer.h" |
34 | #include "common.h" |
35 | #include "hwcontext.h" |
36 | #include "hwcontext_internal.h" |
37 | #include "hwcontext_qsv.h" |
38 | #include "mem.h" |
39 | #include "pixfmt.h" |
40 | #include "pixdesc.h" |
41 | #include "time.h" |
42 | |
43 | typedef struct QSVDevicePriv { |
44 | AVBufferRef *child_device_ctx; |
45 | } QSVDevicePriv; |
46 | |
47 | typedef struct QSVDeviceContext { |
48 | mfxHDL handle; |
49 | mfxHandleType handle_type; |
50 | mfxVersion ver; |
51 | mfxIMPL impl; |
52 | |
53 | enum AVHWDeviceType child_device_type; |
54 | enum AVPixelFormat child_pix_fmt; |
55 | } QSVDeviceContext; |
56 | |
57 | typedef struct QSVFramesContext { |
58 | mfxSession session_download; |
59 | mfxSession session_upload; |
60 | |
61 | AVBufferRef *child_frames_ref; |
62 | mfxFrameSurface1 *surfaces_internal; |
63 | int nb_surfaces_used; |
64 | |
65 | // used in the frame allocator for non-opaque surfaces |
66 | mfxMemId *mem_ids; |
67 | // used in the opaque alloc request for opaque surfaces |
68 | mfxFrameSurface1 **surface_ptrs; |
69 | |
70 | mfxExtOpaqueSurfaceAlloc opaque_alloc; |
71 | mfxExtBuffer *ext_buffers[1]; |
72 | } QSVFramesContext; |
73 | |
74 | static const struct { |
75 | mfxHandleType handle_type; |
76 | enum AVHWDeviceType device_type; |
77 | enum AVPixelFormat pix_fmt; |
78 | } supported_handle_types[] = { |
79 | #if CONFIG_VAAPI |
80 | { MFX_HANDLE_VA_DISPLAY, AV_HWDEVICE_TYPE_VAAPI, AV_PIX_FMT_VAAPI }, |
81 | #endif |
82 | #if CONFIG_DXVA2 |
83 | { MFX_HANDLE_D3D9_DEVICE_MANAGER, AV_HWDEVICE_TYPE_DXVA2, AV_PIX_FMT_DXVA2_VLD }, |
84 | #endif |
85 | { 0 }, |
86 | }; |
87 | |
88 | static const struct { |
89 | enum AVPixelFormat pix_fmt; |
90 | uint32_t fourcc; |
91 | } supported_pixel_formats[] = { |
92 | { AV_PIX_FMT_NV12, MFX_FOURCC_NV12 }, |
93 | { AV_PIX_FMT_P010, MFX_FOURCC_P010 }, |
94 | { AV_PIX_FMT_PAL8, MFX_FOURCC_P8 }, |
95 | }; |
96 | |
97 | static int qsv_device_init(AVHWDeviceContext *ctx) |
98 | { |
99 | AVQSVDeviceContext *hwctx = ctx->hwctx; |
100 | QSVDeviceContext *s = ctx->internal->priv; |
101 | |
102 | mfxStatus err; |
103 | int i; |
104 | |
105 | for (i = 0; supported_handle_types[i].handle_type; i++) { |
106 | err = MFXVideoCORE_GetHandle(hwctx->session, supported_handle_types[i].handle_type, |
107 | &s->handle); |
108 | if (err == MFX_ERR_NONE) { |
109 | s->handle_type = supported_handle_types[i].handle_type; |
110 | s->child_device_type = supported_handle_types[i].device_type; |
111 | s->child_pix_fmt = supported_handle_types[i].pix_fmt; |
112 | break; |
113 | } |
114 | } |
115 | if (!s->handle) { |
116 | av_log(ctx, AV_LOG_VERBOSE, "No supported hw handle could be retrieved " |
117 | "from the session\n"); |
118 | } |
119 | |
120 | err = MFXQueryIMPL(hwctx->session, &s->impl); |
121 | if (err == MFX_ERR_NONE) |
122 | err = MFXQueryVersion(hwctx->session, &s->ver); |
123 | if (err != MFX_ERR_NONE) { |
124 | av_log(ctx, AV_LOG_ERROR, "Error querying the session attributes\n"); |
125 | return AVERROR_UNKNOWN; |
126 | } |
127 | |
128 | return 0; |
129 | } |
130 | |
131 | static void qsv_frames_uninit(AVHWFramesContext *ctx) |
132 | { |
133 | QSVFramesContext *s = ctx->internal->priv; |
134 | |
135 | if (s->session_download) { |
136 | MFXVideoVPP_Close(s->session_download); |
137 | MFXClose(s->session_download); |
138 | } |
139 | s->session_download = NULL; |
140 | |
141 | if (s->session_upload) { |
142 | MFXVideoVPP_Close(s->session_upload); |
143 | MFXClose(s->session_upload); |
144 | } |
145 | s->session_upload = NULL; |
146 | |
147 | av_freep(&s->mem_ids); |
148 | av_freep(&s->surface_ptrs); |
149 | av_freep(&s->surfaces_internal); |
150 | av_buffer_unref(&s->child_frames_ref); |
151 | } |
152 | |
153 | static void qsv_pool_release_dummy(void *opaque, uint8_t *data) |
154 | { |
155 | } |
156 | |
157 | static AVBufferRef *qsv_pool_alloc(void *opaque, int size) |
158 | { |
159 | AVHWFramesContext *ctx = (AVHWFramesContext*)opaque; |
160 | QSVFramesContext *s = ctx->internal->priv; |
161 | AVQSVFramesContext *hwctx = ctx->hwctx; |
162 | |
163 | if (s->nb_surfaces_used < hwctx->nb_surfaces) { |
164 | s->nb_surfaces_used++; |
165 | return av_buffer_create((uint8_t*)(s->surfaces_internal + s->nb_surfaces_used - 1), |
166 | sizeof(*hwctx->surfaces), qsv_pool_release_dummy, NULL, 0); |
167 | } |
168 | |
169 | return NULL; |
170 | } |
171 | |
172 | static int qsv_init_child_ctx(AVHWFramesContext *ctx) |
173 | { |
174 | AVQSVFramesContext *hwctx = ctx->hwctx; |
175 | QSVFramesContext *s = ctx->internal->priv; |
176 | QSVDeviceContext *device_priv = ctx->device_ctx->internal->priv; |
177 | |
178 | AVBufferRef *child_device_ref = NULL; |
179 | AVBufferRef *child_frames_ref = NULL; |
180 | |
181 | AVHWDeviceContext *child_device_ctx; |
182 | AVHWFramesContext *child_frames_ctx; |
183 | |
184 | int i, ret = 0; |
185 | |
186 | if (!device_priv->handle) { |
187 | av_log(ctx, AV_LOG_ERROR, |
188 | "Cannot create a non-opaque internal surface pool without " |
189 | "a hardware handle\n"); |
190 | return AVERROR(EINVAL); |
191 | } |
192 | |
193 | child_device_ref = av_hwdevice_ctx_alloc(device_priv->child_device_type); |
194 | if (!child_device_ref) |
195 | return AVERROR(ENOMEM); |
196 | child_device_ctx = (AVHWDeviceContext*)child_device_ref->data; |
197 | |
198 | #if CONFIG_VAAPI |
199 | if (child_device_ctx->type == AV_HWDEVICE_TYPE_VAAPI) { |
200 | AVVAAPIDeviceContext *child_device_hwctx = child_device_ctx->hwctx; |
201 | child_device_hwctx->display = (VADisplay)device_priv->handle; |
202 | } |
203 | #endif |
204 | #if CONFIG_DXVA2 |
205 | if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) { |
206 | AVDXVA2DeviceContext *child_device_hwctx = child_device_ctx->hwctx; |
207 | child_device_hwctx->devmgr = (IDirect3DDeviceManager9*)device_priv->handle; |
208 | } |
209 | #endif |
210 | |
211 | ret = av_hwdevice_ctx_init(child_device_ref); |
212 | if (ret < 0) { |
213 | av_log(ctx, AV_LOG_ERROR, "Error initializing a child device context\n"); |
214 | goto fail; |
215 | } |
216 | |
217 | child_frames_ref = av_hwframe_ctx_alloc(child_device_ref); |
218 | if (!child_frames_ref) { |
219 | ret = AVERROR(ENOMEM); |
220 | goto fail; |
221 | } |
222 | child_frames_ctx = (AVHWFramesContext*)child_frames_ref->data; |
223 | |
224 | child_frames_ctx->format = device_priv->child_pix_fmt; |
225 | child_frames_ctx->sw_format = ctx->sw_format; |
226 | child_frames_ctx->initial_pool_size = ctx->initial_pool_size; |
227 | child_frames_ctx->width = ctx->width; |
228 | child_frames_ctx->height = ctx->height; |
229 | |
230 | #if CONFIG_DXVA2 |
231 | if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) { |
232 | AVDXVA2FramesContext *child_frames_hwctx = child_frames_ctx->hwctx; |
233 | if (hwctx->frame_type & MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET) |
234 | child_frames_hwctx->surface_type = DXVA2_VideoProcessorRenderTarget; |
235 | else |
236 | child_frames_hwctx->surface_type = DXVA2_VideoDecoderRenderTarget; |
237 | } |
238 | #endif |
239 | |
240 | ret = av_hwframe_ctx_init(child_frames_ref); |
241 | if (ret < 0) { |
242 | av_log(ctx, AV_LOG_ERROR, "Error initializing a child frames context\n"); |
243 | goto fail; |
244 | } |
245 | |
246 | #if CONFIG_VAAPI |
247 | if (child_device_ctx->type == AV_HWDEVICE_TYPE_VAAPI) { |
248 | AVVAAPIFramesContext *child_frames_hwctx = child_frames_ctx->hwctx; |
249 | for (i = 0; i < ctx->initial_pool_size; i++) |
250 | s->surfaces_internal[i].Data.MemId = child_frames_hwctx->surface_ids + i; |
251 | hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; |
252 | } |
253 | #endif |
254 | #if CONFIG_DXVA2 |
255 | if (child_device_ctx->type == AV_HWDEVICE_TYPE_DXVA2) { |
256 | AVDXVA2FramesContext *child_frames_hwctx = child_frames_ctx->hwctx; |
257 | for (i = 0; i < ctx->initial_pool_size; i++) |
258 | s->surfaces_internal[i].Data.MemId = (mfxMemId)child_frames_hwctx->surfaces[i]; |
259 | if (child_frames_hwctx->surface_type == DXVA2_VideoProcessorRenderTarget) |
260 | hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET; |
261 | else |
262 | hwctx->frame_type = MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET; |
263 | } |
264 | #endif |
265 | |
266 | s->child_frames_ref = child_frames_ref; |
267 | child_frames_ref = NULL; |
268 | |
269 | fail: |
270 | av_buffer_unref(&child_device_ref); |
271 | av_buffer_unref(&child_frames_ref); |
272 | return ret; |
273 | } |
274 | |
275 | static int qsv_init_pool(AVHWFramesContext *ctx, uint32_t fourcc) |
276 | { |
277 | QSVFramesContext *s = ctx->internal->priv; |
278 | AVQSVFramesContext *frames_hwctx = ctx->hwctx; |
279 | const AVPixFmtDescriptor *desc; |
280 | |
281 | int i, ret = 0; |
282 | |
283 | desc = av_pix_fmt_desc_get(ctx->sw_format); |
284 | if (!desc) |
285 | return AVERROR_BUG; |
286 | |
287 | if (ctx->initial_pool_size <= 0) { |
288 | av_log(ctx, AV_LOG_ERROR, "QSV requires a fixed frame pool size\n"); |
289 | return AVERROR(EINVAL); |
290 | } |
291 | |
292 | s->surfaces_internal = av_mallocz_array(ctx->initial_pool_size, |
293 | sizeof(*s->surfaces_internal)); |
294 | if (!s->surfaces_internal) |
295 | return AVERROR(ENOMEM); |
296 | |
297 | for (i = 0; i < ctx->initial_pool_size; i++) { |
298 | mfxFrameSurface1 *surf = &s->surfaces_internal[i]; |
299 | |
300 | surf->Info.BitDepthLuma = desc->comp[0].depth; |
301 | surf->Info.BitDepthChroma = desc->comp[0].depth; |
302 | surf->Info.Shift = desc->comp[0].depth > 8; |
303 | |
304 | if (desc->log2_chroma_w && desc->log2_chroma_h) |
305 | surf->Info.ChromaFormat = MFX_CHROMAFORMAT_YUV420; |
306 | else if (desc->log2_chroma_w) |
307 | surf->Info.ChromaFormat = MFX_CHROMAFORMAT_YUV422; |
308 | else |
309 | surf->Info.ChromaFormat = MFX_CHROMAFORMAT_YUV444; |
310 | |
311 | surf->Info.FourCC = fourcc; |
312 | surf->Info.Width = ctx->width; |
313 | surf->Info.CropW = ctx->width; |
314 | surf->Info.Height = ctx->height; |
315 | surf->Info.CropH = ctx->height; |
316 | surf->Info.FrameRateExtN = 25; |
317 | surf->Info.FrameRateExtD = 1; |
318 | } |
319 | |
320 | if (!(frames_hwctx->frame_type & MFX_MEMTYPE_OPAQUE_FRAME)) { |
321 | ret = qsv_init_child_ctx(ctx); |
322 | if (ret < 0) |
323 | return ret; |
324 | } |
325 | |
326 | ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(mfxFrameSurface1), |
327 | ctx, qsv_pool_alloc, NULL); |
328 | if (!ctx->internal->pool_internal) |
329 | return AVERROR(ENOMEM); |
330 | |
331 | frames_hwctx->surfaces = s->surfaces_internal; |
332 | frames_hwctx->nb_surfaces = ctx->initial_pool_size; |
333 | |
334 | return 0; |
335 | } |
336 | |
337 | static mfxStatus frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req, |
338 | mfxFrameAllocResponse *resp) |
339 | { |
340 | AVHWFramesContext *ctx = pthis; |
341 | QSVFramesContext *s = ctx->internal->priv; |
342 | AVQSVFramesContext *hwctx = ctx->hwctx; |
343 | mfxFrameInfo *i = &req->Info; |
344 | mfxFrameInfo *i1 = &hwctx->surfaces[0].Info; |
345 | |
346 | if (!(req->Type & MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET) || |
347 | !(req->Type & (MFX_MEMTYPE_FROM_VPPIN | MFX_MEMTYPE_FROM_VPPOUT)) || |
348 | !(req->Type & MFX_MEMTYPE_EXTERNAL_FRAME)) |
349 | return MFX_ERR_UNSUPPORTED; |
350 | if (i->Width != i1->Width || i->Height != i1->Height || |
351 | i->FourCC != i1->FourCC || i->ChromaFormat != i1->ChromaFormat) { |
352 | av_log(ctx, AV_LOG_ERROR, "Mismatching surface properties in an " |
353 | "allocation request: %dx%d %d %d vs %dx%d %d %d\n", |
354 | i->Width, i->Height, i->FourCC, i->ChromaFormat, |
355 | i1->Width, i1->Height, i1->FourCC, i1->ChromaFormat); |
356 | return MFX_ERR_UNSUPPORTED; |
357 | } |
358 | |
359 | resp->mids = s->mem_ids; |
360 | resp->NumFrameActual = hwctx->nb_surfaces; |
361 | |
362 | return MFX_ERR_NONE; |
363 | } |
364 | |
365 | static mfxStatus frame_free(mfxHDL pthis, mfxFrameAllocResponse *resp) |
366 | { |
367 | return MFX_ERR_NONE; |
368 | } |
369 | |
370 | static mfxStatus frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) |
371 | { |
372 | return MFX_ERR_UNSUPPORTED; |
373 | } |
374 | |
375 | static mfxStatus frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) |
376 | { |
377 | return MFX_ERR_UNSUPPORTED; |
378 | } |
379 | |
380 | static mfxStatus frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) |
381 | { |
382 | *hdl = mid; |
383 | return MFX_ERR_NONE; |
384 | } |
385 | |
386 | static int qsv_init_internal_session(AVHWFramesContext *ctx, |
387 | mfxSession *session, int upload) |
388 | { |
389 | QSVFramesContext *s = ctx->internal->priv; |
390 | AVQSVFramesContext *frames_hwctx = ctx->hwctx; |
391 | QSVDeviceContext *device_priv = ctx->device_ctx->internal->priv; |
392 | int opaque = !!(frames_hwctx->frame_type & MFX_MEMTYPE_OPAQUE_FRAME); |
393 | |
394 | mfxFrameAllocator frame_allocator = { |
395 | .pthis = ctx, |
396 | .Alloc = frame_alloc, |
397 | .Lock = frame_lock, |
398 | .Unlock = frame_unlock, |
399 | .GetHDL = frame_get_hdl, |
400 | .Free = frame_free, |
401 | }; |
402 | |
403 | mfxVideoParam par; |
404 | mfxStatus err; |
405 | |
406 | err = MFXInit(device_priv->impl, &device_priv->ver, session); |
407 | if (err != MFX_ERR_NONE) { |
408 | av_log(ctx, AV_LOG_ERROR, "Error initializing an internal session\n"); |
409 | return AVERROR_UNKNOWN; |
410 | } |
411 | |
412 | if (device_priv->handle) { |
413 | err = MFXVideoCORE_SetHandle(*session, device_priv->handle_type, |
414 | device_priv->handle); |
415 | if (err != MFX_ERR_NONE) |
416 | return AVERROR_UNKNOWN; |
417 | } |
418 | |
419 | if (!opaque) { |
420 | err = MFXVideoCORE_SetFrameAllocator(*session, &frame_allocator); |
421 | if (err != MFX_ERR_NONE) |
422 | return AVERROR_UNKNOWN; |
423 | } |
424 | |
425 | memset(&par, 0, sizeof(par)); |
426 | |
427 | if (opaque) { |
428 | par.ExtParam = s->ext_buffers; |
429 | par.NumExtParam = FF_ARRAY_ELEMS(s->ext_buffers); |
430 | par.IOPattern = upload ? MFX_IOPATTERN_OUT_OPAQUE_MEMORY : |
431 | MFX_IOPATTERN_IN_OPAQUE_MEMORY; |
432 | } else { |
433 | par.IOPattern = upload ? MFX_IOPATTERN_OUT_VIDEO_MEMORY : |
434 | MFX_IOPATTERN_IN_VIDEO_MEMORY; |
435 | } |
436 | |
437 | par.IOPattern |= upload ? MFX_IOPATTERN_IN_SYSTEM_MEMORY : |
438 | MFX_IOPATTERN_OUT_SYSTEM_MEMORY; |
439 | par.AsyncDepth = 1; |
440 | |
441 | par.vpp.In = frames_hwctx->surfaces[0].Info; |
442 | |
443 | /* Apparently VPP requires the frame rate to be set to some value, otherwise |
444 | * init will fail (probably for the framerate conversion filter). Since we |
445 | * are only doing data upload/download here, we just invent an arbitrary |
446 | * value */ |
447 | par.vpp.In.FrameRateExtN = 25; |
448 | par.vpp.In.FrameRateExtD = 1; |
449 | par.vpp.Out = par.vpp.In; |
450 | |
451 | err = MFXVideoVPP_Init(*session, &par); |
452 | if (err != MFX_ERR_NONE) { |
453 | av_log(ctx, AV_LOG_VERBOSE, "Error opening the internal VPP session." |
454 | "Surface upload/download will not be possible\n"); |
455 | MFXClose(*session); |
456 | *session = NULL; |
457 | } |
458 | |
459 | return 0; |
460 | } |
461 | |
462 | static int qsv_frames_init(AVHWFramesContext *ctx) |
463 | { |
464 | QSVFramesContext *s = ctx->internal->priv; |
465 | AVQSVFramesContext *frames_hwctx = ctx->hwctx; |
466 | |
467 | int opaque = !!(frames_hwctx->frame_type & MFX_MEMTYPE_OPAQUE_FRAME); |
468 | |
469 | uint32_t fourcc = 0; |
470 | int i, ret; |
471 | |
472 | for (i = 0; i < FF_ARRAY_ELEMS(supported_pixel_formats); i++) { |
473 | if (supported_pixel_formats[i].pix_fmt == ctx->sw_format) { |
474 | fourcc = supported_pixel_formats[i].fourcc; |
475 | break; |
476 | } |
477 | } |
478 | if (!fourcc) { |
479 | av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format\n"); |
480 | return AVERROR(ENOSYS); |
481 | } |
482 | |
483 | if (!ctx->pool) { |
484 | ret = qsv_init_pool(ctx, fourcc); |
485 | if (ret < 0) { |
486 | av_log(ctx, AV_LOG_ERROR, "Error creating an internal frame pool\n"); |
487 | return ret; |
488 | } |
489 | } |
490 | |
491 | if (opaque) { |
492 | s->surface_ptrs = av_mallocz_array(frames_hwctx->nb_surfaces, |
493 | sizeof(*s->surface_ptrs)); |
494 | if (!s->surface_ptrs) |
495 | return AVERROR(ENOMEM); |
496 | |
497 | for (i = 0; i < frames_hwctx->nb_surfaces; i++) |
498 | s->surface_ptrs[i] = frames_hwctx->surfaces + i; |
499 | |
500 | s->opaque_alloc.In.Surfaces = s->surface_ptrs; |
501 | s->opaque_alloc.In.NumSurface = frames_hwctx->nb_surfaces; |
502 | s->opaque_alloc.In.Type = frames_hwctx->frame_type; |
503 | |
504 | s->opaque_alloc.Out = s->opaque_alloc.In; |
505 | |
506 | s->opaque_alloc.Header.BufferId = MFX_EXTBUFF_OPAQUE_SURFACE_ALLOCATION; |
507 | s->opaque_alloc.Header.BufferSz = sizeof(s->opaque_alloc); |
508 | |
509 | s->ext_buffers[0] = (mfxExtBuffer*)&s->opaque_alloc; |
510 | } else { |
511 | s->mem_ids = av_mallocz_array(frames_hwctx->nb_surfaces, sizeof(*s->mem_ids)); |
512 | if (!s->mem_ids) |
513 | return AVERROR(ENOMEM); |
514 | |
515 | for (i = 0; i < frames_hwctx->nb_surfaces; i++) |
516 | s->mem_ids[i] = frames_hwctx->surfaces[i].Data.MemId; |
517 | } |
518 | |
519 | ret = qsv_init_internal_session(ctx, &s->session_download, 0); |
520 | if (ret < 0) |
521 | return ret; |
522 | |
523 | ret = qsv_init_internal_session(ctx, &s->session_upload, 1); |
524 | if (ret < 0) |
525 | return ret; |
526 | |
527 | return 0; |
528 | } |
529 | |
530 | static int qsv_get_buffer(AVHWFramesContext *ctx, AVFrame *frame) |
531 | { |
532 | frame->buf[0] = av_buffer_pool_get(ctx->pool); |
533 | if (!frame->buf[0]) |
534 | return AVERROR(ENOMEM); |
535 | |
536 | frame->data[3] = frame->buf[0]->data; |
537 | frame->format = AV_PIX_FMT_QSV; |
538 | frame->width = ctx->width; |
539 | frame->height = ctx->height; |
540 | |
541 | return 0; |
542 | } |
543 | |
544 | static int qsv_transfer_get_formats(AVHWFramesContext *ctx, |
545 | enum AVHWFrameTransferDirection dir, |
546 | enum AVPixelFormat **formats) |
547 | { |
548 | enum AVPixelFormat *fmts; |
549 | |
550 | fmts = av_malloc_array(2, sizeof(*fmts)); |
551 | if (!fmts) |
552 | return AVERROR(ENOMEM); |
553 | |
554 | fmts[0] = ctx->sw_format; |
555 | fmts[1] = AV_PIX_FMT_NONE; |
556 | |
557 | *formats = fmts; |
558 | |
559 | return 0; |
560 | } |
561 | |
562 | static int qsv_map_from(AVHWFramesContext *ctx, |
563 | AVFrame *dst, const AVFrame *src, int flags) |
564 | { |
565 | QSVFramesContext *s = ctx->internal->priv; |
566 | mfxFrameSurface1 *surf = (mfxFrameSurface1*)src->data[3]; |
567 | AVHWFramesContext *child_frames_ctx; |
568 | |
569 | AVFrame *dummy; |
570 | int ret = 0; |
571 | |
572 | if (!s->child_frames_ref) |
573 | return AVERROR(ENOSYS); |
574 | child_frames_ctx = (AVHWFramesContext*)s->child_frames_ref->data; |
575 | |
576 | dummy = av_frame_alloc(); |
577 | if (!dummy) |
578 | return AVERROR(ENOMEM); |
579 | |
580 | dummy->buf[0] = av_buffer_ref(src->buf[0]); |
581 | dummy->hw_frames_ctx = av_buffer_ref(s->child_frames_ref); |
582 | if (!dummy->buf[0] || !dummy->hw_frames_ctx) |
583 | goto fail; |
584 | |
585 | dummy->format = child_frames_ctx->format; |
586 | dummy->width = src->width; |
587 | dummy->height = src->height; |
588 | dummy->data[3] = surf->Data.MemId; |
589 | |
590 | ret = av_hwframe_map(dst, dummy, flags); |
591 | |
592 | fail: |
593 | av_frame_free(&dummy); |
594 | |
595 | return ret; |
596 | } |
597 | |
598 | static int qsv_transfer_data_child(AVHWFramesContext *ctx, AVFrame *dst, |
599 | const AVFrame *src) |
600 | { |
601 | QSVFramesContext *s = ctx->internal->priv; |
602 | AVHWFramesContext *child_frames_ctx = (AVHWFramesContext*)s->child_frames_ref->data; |
603 | int download = !!src->hw_frames_ctx; |
604 | mfxFrameSurface1 *surf = (mfxFrameSurface1*)(download ? src->data[3] : dst->data[3]); |
605 | |
606 | AVFrame *dummy; |
607 | int ret; |
608 | |
609 | dummy = av_frame_alloc(); |
610 | if (!dummy) |
611 | return AVERROR(ENOMEM); |
612 | |
613 | dummy->format = child_frames_ctx->format; |
614 | dummy->width = src->width; |
615 | dummy->height = src->height; |
616 | dummy->buf[0] = download ? src->buf[0] : dst->buf[0]; |
617 | dummy->data[3] = surf->Data.MemId; |
618 | dummy->hw_frames_ctx = s->child_frames_ref; |
619 | |
620 | ret = download ? av_hwframe_transfer_data(dst, dummy, 0) : |
621 | av_hwframe_transfer_data(dummy, src, 0); |
622 | |
623 | dummy->buf[0] = NULL; |
624 | dummy->data[3] = NULL; |
625 | dummy->hw_frames_ctx = NULL; |
626 | |
627 | av_frame_free(&dummy); |
628 | |
629 | return ret; |
630 | } |
631 | |
632 | static int qsv_transfer_data_from(AVHWFramesContext *ctx, AVFrame *dst, |
633 | const AVFrame *src) |
634 | { |
635 | QSVFramesContext *s = ctx->internal->priv; |
636 | mfxFrameSurface1 out = {{ 0 }}; |
637 | mfxFrameSurface1 *in = (mfxFrameSurface1*)src->data[3]; |
638 | |
639 | mfxSyncPoint sync = NULL; |
640 | mfxStatus err; |
641 | |
642 | if (!s->session_download) { |
643 | if (s->child_frames_ref) |
644 | return qsv_transfer_data_child(ctx, dst, src); |
645 | |
646 | av_log(ctx, AV_LOG_ERROR, "Surface download not possible\n"); |
647 | return AVERROR(ENOSYS); |
648 | } |
649 | |
650 | out.Info = in->Info; |
651 | out.Data.PitchLow = dst->linesize[0]; |
652 | out.Data.Y = dst->data[0]; |
653 | out.Data.U = dst->data[1]; |
654 | out.Data.V = dst->data[2]; |
655 | out.Data.A = dst->data[3]; |
656 | |
657 | do { |
658 | err = MFXVideoVPP_RunFrameVPPAsync(s->session_download, in, &out, NULL, &sync); |
659 | if (err == MFX_WRN_DEVICE_BUSY) |
660 | av_usleep(1); |
661 | } while (err == MFX_WRN_DEVICE_BUSY); |
662 | |
663 | if (err < 0 || !sync) { |
664 | av_log(ctx, AV_LOG_ERROR, "Error downloading the surface\n"); |
665 | return AVERROR_UNKNOWN; |
666 | } |
667 | |
668 | do { |
669 | err = MFXVideoCORE_SyncOperation(s->session_download, sync, 1000); |
670 | } while (err == MFX_WRN_IN_EXECUTION); |
671 | if (err < 0) { |
672 | av_log(ctx, AV_LOG_ERROR, "Error synchronizing the operation: %d\n", err); |
673 | return AVERROR_UNKNOWN; |
674 | } |
675 | |
676 | return 0; |
677 | } |
678 | |
679 | static int qsv_transfer_data_to(AVHWFramesContext *ctx, AVFrame *dst, |
680 | const AVFrame *src) |
681 | { |
682 | QSVFramesContext *s = ctx->internal->priv; |
683 | mfxFrameSurface1 in = {{ 0 }}; |
684 | mfxFrameSurface1 *out = (mfxFrameSurface1*)dst->data[3]; |
685 | |
686 | mfxSyncPoint sync = NULL; |
687 | mfxStatus err; |
688 | |
689 | if (!s->session_upload) { |
690 | if (s->child_frames_ref) |
691 | return qsv_transfer_data_child(ctx, dst, src); |
692 | |
693 | av_log(ctx, AV_LOG_ERROR, "Surface upload not possible\n"); |
694 | return AVERROR(ENOSYS); |
695 | } |
696 | |
697 | in.Info = out->Info; |
698 | in.Data.PitchLow = src->linesize[0]; |
699 | in.Data.Y = src->data[0]; |
700 | in.Data.U = src->data[1]; |
701 | in.Data.V = src->data[2]; |
702 | in.Data.A = src->data[3]; |
703 | |
704 | do { |
705 | err = MFXVideoVPP_RunFrameVPPAsync(s->session_upload, &in, out, NULL, &sync); |
706 | if (err == MFX_WRN_DEVICE_BUSY) |
707 | av_usleep(1); |
708 | } while (err == MFX_WRN_DEVICE_BUSY); |
709 | |
710 | if (err < 0 || !sync) { |
711 | av_log(ctx, AV_LOG_ERROR, "Error uploading the surface\n"); |
712 | return AVERROR_UNKNOWN; |
713 | } |
714 | |
715 | do { |
716 | err = MFXVideoCORE_SyncOperation(s->session_upload, sync, 1000); |
717 | } while (err == MFX_WRN_IN_EXECUTION); |
718 | if (err < 0) { |
719 | av_log(ctx, AV_LOG_ERROR, "Error synchronizing the operation\n"); |
720 | return AVERROR_UNKNOWN; |
721 | } |
722 | |
723 | return 0; |
724 | } |
725 | |
726 | static int qsv_frames_get_constraints(AVHWDeviceContext *ctx, |
727 | const void *hwconfig, |
728 | AVHWFramesConstraints *constraints) |
729 | { |
730 | int i; |
731 | |
732 | constraints->valid_sw_formats = av_malloc_array(FF_ARRAY_ELEMS(supported_pixel_formats) + 1, |
733 | sizeof(*constraints->valid_sw_formats)); |
734 | if (!constraints->valid_sw_formats) |
735 | return AVERROR(ENOMEM); |
736 | |
737 | for (i = 0; i < FF_ARRAY_ELEMS(supported_pixel_formats); i++) |
738 | constraints->valid_sw_formats[i] = supported_pixel_formats[i].pix_fmt; |
739 | constraints->valid_sw_formats[FF_ARRAY_ELEMS(supported_pixel_formats)] = AV_PIX_FMT_NONE; |
740 | |
741 | constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats)); |
742 | if (!constraints->valid_hw_formats) |
743 | return AVERROR(ENOMEM); |
744 | |
745 | constraints->valid_hw_formats[0] = AV_PIX_FMT_QSV; |
746 | constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE; |
747 | |
748 | return 0; |
749 | } |
750 | |
751 | static void qsv_device_free(AVHWDeviceContext *ctx) |
752 | { |
753 | AVQSVDeviceContext *hwctx = ctx->hwctx; |
754 | QSVDevicePriv *priv = ctx->user_opaque; |
755 | |
756 | if (hwctx->session) |
757 | MFXClose(hwctx->session); |
758 | |
759 | av_buffer_unref(&priv->child_device_ctx); |
760 | av_freep(&priv); |
761 | } |
762 | |
763 | static mfxIMPL choose_implementation(const char *device) |
764 | { |
765 | static const struct { |
766 | const char *name; |
767 | mfxIMPL impl; |
768 | } impl_map[] = { |
769 | { "auto", MFX_IMPL_AUTO }, |
770 | { "sw", MFX_IMPL_SOFTWARE }, |
771 | { "hw", MFX_IMPL_HARDWARE }, |
772 | { "auto_any", MFX_IMPL_AUTO_ANY }, |
773 | { "hw_any", MFX_IMPL_HARDWARE_ANY }, |
774 | { "hw2", MFX_IMPL_HARDWARE2 }, |
775 | { "hw3", MFX_IMPL_HARDWARE3 }, |
776 | { "hw4", MFX_IMPL_HARDWARE4 }, |
777 | }; |
778 | |
779 | mfxIMPL impl = MFX_IMPL_AUTO_ANY; |
780 | int i; |
781 | |
782 | if (device) { |
783 | for (i = 0; i < FF_ARRAY_ELEMS(impl_map); i++) |
784 | if (!strcmp(device, impl_map[i].name)) { |
785 | impl = impl_map[i].impl; |
786 | break; |
787 | } |
788 | if (i == FF_ARRAY_ELEMS(impl_map)) |
789 | impl = strtol(device, NULL, 0); |
790 | } |
791 | |
792 | return impl; |
793 | } |
794 | |
795 | static int qsv_device_create(AVHWDeviceContext *ctx, const char *device, |
796 | AVDictionary *opts, int flags) |
797 | { |
798 | AVQSVDeviceContext *hwctx = ctx->hwctx; |
799 | QSVDevicePriv *priv; |
800 | enum AVHWDeviceType child_device_type; |
801 | AVDictionaryEntry *e; |
802 | |
803 | mfxVersion ver = { { 3, 1 } }; |
804 | mfxIMPL impl; |
805 | mfxHDL handle; |
806 | mfxHandleType handle_type; |
807 | mfxStatus err; |
808 | int ret; |
809 | |
810 | priv = av_mallocz(sizeof(*priv)); |
811 | if (!priv) |
812 | return AVERROR(ENOMEM); |
813 | |
814 | ctx->user_opaque = priv; |
815 | ctx->free = qsv_device_free; |
816 | |
817 | e = av_dict_get(opts, "child_device", NULL, 0); |
818 | |
819 | if (CONFIG_VAAPI) |
820 | child_device_type = AV_HWDEVICE_TYPE_VAAPI; |
821 | else if (CONFIG_DXVA2) |
822 | child_device_type = AV_HWDEVICE_TYPE_DXVA2; |
823 | else { |
824 | av_log(ctx, AV_LOG_ERROR, "No supported child device type is enabled\n"); |
825 | return AVERROR(ENOSYS); |
826 | } |
827 | |
828 | ret = av_hwdevice_ctx_create(&priv->child_device_ctx, child_device_type, |
829 | e ? e->value : NULL, NULL, 0); |
830 | if (ret < 0) |
831 | return ret; |
832 | |
833 | { |
834 | AVHWDeviceContext *child_device_ctx = (AVHWDeviceContext*)priv->child_device_ctx->data; |
835 | #if CONFIG_VAAPI |
836 | AVVAAPIDeviceContext *child_device_hwctx = child_device_ctx->hwctx; |
837 | handle_type = MFX_HANDLE_VA_DISPLAY; |
838 | handle = (mfxHDL)child_device_hwctx->display; |
839 | #elif CONFIG_DXVA2 |
840 | AVDXVA2DeviceContext *child_device_hwctx = child_device_ctx->hwctx; |
841 | handle_type = MFX_HANDLE_D3D9_DEVICE_MANAGER; |
842 | handle = (mfxHDL)child_device_hwctx->devmgr; |
843 | #endif |
844 | } |
845 | |
846 | impl = choose_implementation(device); |
847 | |
848 | err = MFXInit(impl, &ver, &hwctx->session); |
849 | if (err != MFX_ERR_NONE) { |
850 | av_log(ctx, AV_LOG_ERROR, "Error initializing an MFX session\n"); |
851 | return AVERROR_UNKNOWN; |
852 | } |
853 | |
854 | err = MFXVideoCORE_SetHandle(hwctx->session, handle_type, handle); |
855 | if (err != MFX_ERR_NONE) |
856 | return AVERROR_UNKNOWN; |
857 | |
858 | return 0; |
859 | } |
860 | |
861 | const HWContextType ff_hwcontext_type_qsv = { |
862 | .type = AV_HWDEVICE_TYPE_QSV, |
863 | .name = "QSV", |
864 | |
865 | .device_hwctx_size = sizeof(AVQSVDeviceContext), |
866 | .device_priv_size = sizeof(QSVDeviceContext), |
867 | .frames_hwctx_size = sizeof(AVQSVFramesContext), |
868 | .frames_priv_size = sizeof(QSVFramesContext), |
869 | |
870 | .device_create = qsv_device_create, |
871 | .device_init = qsv_device_init, |
872 | .frames_get_constraints = qsv_frames_get_constraints, |
873 | .frames_init = qsv_frames_init, |
874 | .frames_uninit = qsv_frames_uninit, |
875 | .frames_get_buffer = qsv_get_buffer, |
876 | .transfer_get_formats = qsv_transfer_get_formats, |
877 | .transfer_data_to = qsv_transfer_data_to, |
878 | .transfer_data_from = qsv_transfer_data_from, |
879 | .map_from = qsv_map_from, |
880 | |
881 | .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_QSV, AV_PIX_FMT_NONE }, |
882 | }; |
883 |