| @@ -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 | +}; |