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