| 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 <windows.h> |
| 20 | |
| 21 | #ifdef _WIN32_WINNT |
| 22 | #undef _WIN32_WINNT |
| 23 | #endif |
| 24 | #define _WIN32_WINNT 0x0600 |
| 25 | #define DXVA2API_USE_BITFIELDS |
| 26 | #define COBJMACROS |
| 27 | |
| 28 | #include <stdint.h> |
| 29 | |
| 30 | #include <d3d9.h> |
| 31 | #include <dxva2api.h> |
| 32 | |
| 33 | #include "ffmpeg.h" |
| 34 | |
| 35 | #include "libavcodec/dxva2.h" |
| 36 | |
| 37 | #include "libavutil/avassert.h" |
| 38 | #include "libavutil/buffer.h" |
| 39 | #include "libavutil/frame.h" |
| 40 | #include "libavutil/imgutils.h" |
| 41 | #include "libavutil/pixfmt.h" |
| 42 | |
| 43 | /* define all the GUIDs used directly here, |
| 44 | to avoid problems with inconsistent dxva2api.h versions in mingw-w64 and different MSVC version */ |
| 45 | #include <initguid.h> |
| 46 | DEFINE_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551,0xd5e7,0x11d9,0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02); |
| 47 | |
| 48 | DEFINE_GUID(DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28,0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9); |
| 49 | DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD, 0x86695f12, 0x340e,0x4f04,0x9f,0xd3,0x92,0x53,0xdd,0x32,0x74,0x60); |
| 50 | DEFINE_GUID(DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); |
| 51 | DEFINE_GUID(DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); |
| 52 | DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6); |
| 53 | DEFINE_GUID(DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); |
| 54 | DEFINE_GUID(DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); |
| 55 | DEFINE_GUID(DXVA2_NoEncrypt, 0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); |
| 56 | DEFINE_GUID(GUID_NULL, 0x00000000, 0x0000,0x0000,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00); |
| 57 | |
| 58 | typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT); |
| 59 | typedef HRESULT WINAPI pCreateDeviceManager9(UINT *, IDirect3DDeviceManager9 **); |
| 60 | |
| 61 | typedef struct dxva2_mode { |
| 62 | const GUID *guid; |
| 63 | enum AVCodecID codec; |
| 64 | } dxva2_mode; |
| 65 | |
| 66 | static const dxva2_mode dxva2_modes[] = { |
| 67 | /* MPEG-2 */ |
| 68 | { &DXVA2_ModeMPEG2_VLD, AV_CODEC_ID_MPEG2VIDEO }, |
| 69 | { &DXVA2_ModeMPEG2and1_VLD, AV_CODEC_ID_MPEG2VIDEO }, |
| 70 | |
| 71 | /* H.264 */ |
| 72 | { &DXVA2_ModeH264_F, AV_CODEC_ID_H264 }, |
| 73 | { &DXVA2_ModeH264_E, AV_CODEC_ID_H264 }, |
| 74 | /* Intel specific H.264 mode */ |
| 75 | { &DXVADDI_Intel_ModeH264_E, AV_CODEC_ID_H264 }, |
| 76 | |
| 77 | /* VC-1 / WMV3 */ |
| 78 | { &DXVA2_ModeVC1_D2010, AV_CODEC_ID_VC1 }, |
| 79 | { &DXVA2_ModeVC1_D2010, AV_CODEC_ID_WMV3 }, |
| 80 | { &DXVA2_ModeVC1_D, AV_CODEC_ID_VC1 }, |
| 81 | { &DXVA2_ModeVC1_D, AV_CODEC_ID_WMV3 }, |
| 82 | |
| 83 | { NULL, 0 }, |
| 84 | }; |
| 85 | |
| 86 | typedef struct surface_info { |
| 87 | int used; |
| 88 | uint64_t age; |
| 89 | } surface_info; |
| 90 | |
| 91 | typedef struct DXVA2Context { |
| 92 | HMODULE d3dlib; |
| 93 | HMODULE dxva2lib; |
| 94 | |
| 95 | HANDLE deviceHandle; |
| 96 | |
| 97 | IDirect3D9 *d3d9; |
| 98 | IDirect3DDevice9 *d3d9device; |
| 99 | IDirect3DDeviceManager9 *d3d9devmgr; |
| 100 | IDirectXVideoDecoderService *decoder_service; |
| 101 | IDirectXVideoDecoder *decoder; |
| 102 | |
| 103 | GUID decoder_guid; |
| 104 | DXVA2_ConfigPictureDecode decoder_config; |
| 105 | |
| 106 | LPDIRECT3DSURFACE9 *surfaces; |
| 107 | surface_info *surface_infos; |
| 108 | uint32_t num_surfaces; |
| 109 | uint64_t surface_age; |
| 110 | |
| 111 | AVFrame *tmp_frame; |
| 112 | } DXVA2Context; |
| 113 | |
| 114 | typedef struct DXVA2SurfaceWrapper { |
| 115 | DXVA2Context *ctx; |
| 116 | LPDIRECT3DSURFACE9 surface; |
| 117 | IDirectXVideoDecoder *decoder; |
| 118 | } DXVA2SurfaceWrapper; |
| 119 | |
| 120 | static void dxva2_destroy_decoder(AVCodecContext *s) |
| 121 | { |
| 122 | InputStream *ist = s->opaque; |
| 123 | DXVA2Context *ctx = ist->hwaccel_ctx; |
| 124 | int i; |
| 125 | |
| 126 | if (ctx->surfaces) { |
| 127 | for (i = 0; i < ctx->num_surfaces; i++) { |
| 128 | if (ctx->surfaces[i]) |
| 129 | IDirect3DSurface9_Release(ctx->surfaces[i]); |
| 130 | } |
| 131 | } |
| 132 | av_freep(&ctx->surfaces); |
| 133 | av_freep(&ctx->surface_infos); |
| 134 | ctx->num_surfaces = 0; |
| 135 | ctx->surface_age = 0; |
| 136 | |
| 137 | if (ctx->decoder) { |
| 138 | IDirectXVideoDecoder_Release(ctx->decoder); |
| 139 | ctx->decoder = NULL; |
| 140 | } |
| 141 | } |
| 142 | |
| 143 | static void dxva2_uninit(AVCodecContext *s) |
| 144 | { |
| 145 | InputStream *ist = s->opaque; |
| 146 | DXVA2Context *ctx = ist->hwaccel_ctx; |
| 147 | |
| 148 | ist->hwaccel_uninit = NULL; |
| 149 | ist->hwaccel_get_buffer = NULL; |
| 150 | ist->hwaccel_retrieve_data = NULL; |
| 151 | |
| 152 | if (ctx->decoder) |
| 153 | dxva2_destroy_decoder(s); |
| 154 | |
| 155 | if (ctx->decoder_service) |
| 156 | IDirectXVideoDecoderService_Release(ctx->decoder_service); |
| 157 | |
| 158 | if (ctx->d3d9devmgr && ctx->deviceHandle != INVALID_HANDLE_VALUE) |
| 159 | IDirect3DDeviceManager9_CloseDeviceHandle(ctx->d3d9devmgr, ctx->deviceHandle); |
| 160 | |
| 161 | if (ctx->d3d9devmgr) |
| 162 | IDirect3DDeviceManager9_Release(ctx->d3d9devmgr); |
| 163 | |
| 164 | if (ctx->d3d9device) |
| 165 | IDirect3DDevice9_Release(ctx->d3d9device); |
| 166 | |
| 167 | if (ctx->d3d9) |
| 168 | IDirect3D9_Release(ctx->d3d9); |
| 169 | |
| 170 | if (ctx->d3dlib) |
| 171 | FreeLibrary(ctx->d3dlib); |
| 172 | |
| 173 | if (ctx->dxva2lib) |
| 174 | FreeLibrary(ctx->dxva2lib); |
| 175 | |
| 176 | av_frame_free(&ctx->tmp_frame); |
| 177 | |
| 178 | av_freep(&ist->hwaccel_ctx); |
| 179 | av_freep(&s->hwaccel_context); |
| 180 | } |
| 181 | |
| 182 | static void dxva2_release_buffer(void *opaque, uint8_t *data) |
| 183 | { |
| 184 | DXVA2SurfaceWrapper *w = opaque; |
| 185 | DXVA2Context *ctx = w->ctx; |
| 186 | int i; |
| 187 | |
| 188 | for (i = 0; i < ctx->num_surfaces; i++) { |
| 189 | if (ctx->surfaces[i] == w->surface) { |
| 190 | ctx->surface_infos[i].used = 0; |
| 191 | break; |
| 192 | } |
| 193 | } |
| 194 | IDirect3DSurface9_Release(w->surface); |
| 195 | IDirectXVideoDecoder_Release(w->decoder); |
| 196 | av_free(w); |
| 197 | } |
| 198 | |
| 199 | static int dxva2_get_buffer(AVCodecContext *s, AVFrame *frame, int flags) |
| 200 | { |
| 201 | InputStream *ist = s->opaque; |
| 202 | DXVA2Context *ctx = ist->hwaccel_ctx; |
| 203 | int i, old_unused = -1; |
| 204 | LPDIRECT3DSURFACE9 surface; |
| 205 | DXVA2SurfaceWrapper *w = NULL; |
| 206 | |
| 207 | av_assert0(frame->format == AV_PIX_FMT_DXVA2_VLD); |
| 208 | |
| 209 | for (i = 0; i < ctx->num_surfaces; i++) { |
| 210 | surface_info *info = &ctx->surface_infos[i]; |
| 211 | if (!info->used && (old_unused == -1 || info->age < ctx->surface_infos[old_unused].age)) |
| 212 | old_unused = i; |
| 213 | } |
| 214 | if (old_unused == -1) { |
| 215 | av_log(NULL, AV_LOG_ERROR, "No free DXVA2 surface!\n"); |
| 216 | return AVERROR(ENOMEM); |
| 217 | } |
| 218 | i = old_unused; |
| 219 | |
| 220 | surface = ctx->surfaces[i]; |
| 221 | |
| 222 | w = av_mallocz(sizeof(*w)); |
| 223 | if (!w) |
| 224 | return AVERROR(ENOMEM); |
| 225 | |
| 226 | frame->buf[0] = av_buffer_create((uint8_t*)surface, 0, |
| 227 | dxva2_release_buffer, w, |
| 228 | AV_BUFFER_FLAG_READONLY); |
| 229 | if (!frame->buf[0]) { |
| 230 | av_free(w); |
| 231 | return AVERROR(ENOMEM); |
| 232 | } |
| 233 | |
| 234 | w->ctx = ctx; |
| 235 | w->surface = surface; |
| 236 | IDirect3DSurface9_AddRef(w->surface); |
| 237 | w->decoder = ctx->decoder; |
| 238 | IDirectXVideoDecoder_AddRef(w->decoder); |
| 239 | |
| 240 | ctx->surface_infos[i].used = 1; |
| 241 | ctx->surface_infos[i].age = ctx->surface_age++; |
| 242 | |
| 243 | frame->data[3] = (uint8_t *)surface; |
| 244 | |
| 245 | return 0; |
| 246 | } |
| 247 | |
| 248 | static int dxva2_retrieve_data(AVCodecContext *s, AVFrame *frame) |
| 249 | { |
| 250 | LPDIRECT3DSURFACE9 surface = (LPDIRECT3DSURFACE9)frame->data[3]; |
| 251 | InputStream *ist = s->opaque; |
| 252 | DXVA2Context *ctx = ist->hwaccel_ctx; |
| 253 | D3DSURFACE_DESC surfaceDesc; |
| 254 | D3DLOCKED_RECT LockedRect; |
| 255 | HRESULT hr; |
| 256 | int ret; |
| 257 | |
| 258 | IDirect3DSurface9_GetDesc(surface, &surfaceDesc); |
| 259 | |
| 260 | ctx->tmp_frame->width = frame->width; |
| 261 | ctx->tmp_frame->height = frame->height; |
| 262 | ctx->tmp_frame->format = AV_PIX_FMT_NV12; |
| 263 | |
| 264 | ret = av_frame_get_buffer(ctx->tmp_frame, 32); |
| 265 | if (ret < 0) |
| 266 | return ret; |
| 267 | |
| 268 | hr = IDirect3DSurface9_LockRect(surface, &LockedRect, NULL, D3DLOCK_READONLY); |
| 269 | if (FAILED(hr)) { |
| 270 | av_log(NULL, AV_LOG_ERROR, "Unable to lock DXVA2 surface\n"); |
| 271 | return AVERROR_UNKNOWN; |
| 272 | } |
| 273 | |
| 274 | av_image_copy_plane(ctx->tmp_frame->data[0], ctx->tmp_frame->linesize[0], |
| 275 | (uint8_t*)LockedRect.pBits, |
| 276 | LockedRect.Pitch, frame->width, frame->height); |
| 277 | |
| 278 | av_image_copy_plane(ctx->tmp_frame->data[1], ctx->tmp_frame->linesize[1], |
| 279 | (uint8_t*)LockedRect.pBits + LockedRect.Pitch * surfaceDesc.Height, |
| 280 | LockedRect.Pitch, frame->width, frame->height / 2); |
| 281 | |
| 282 | IDirect3DSurface9_UnlockRect(surface); |
| 283 | |
| 284 | ret = av_frame_copy_props(ctx->tmp_frame, frame); |
| 285 | if (ret < 0) |
| 286 | goto fail; |
| 287 | |
| 288 | av_frame_unref(frame); |
| 289 | av_frame_move_ref(frame, ctx->tmp_frame); |
| 290 | |
| 291 | return 0; |
| 292 | fail: |
| 293 | av_frame_unref(ctx->tmp_frame); |
| 294 | return ret; |
| 295 | } |
| 296 | |
| 297 | static int dxva2_alloc(AVCodecContext *s) |
| 298 | { |
| 299 | InputStream *ist = s->opaque; |
| 300 | int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; |
| 301 | DXVA2Context *ctx; |
| 302 | pDirect3DCreate9 *createD3D = NULL; |
| 303 | pCreateDeviceManager9 *createDeviceManager = NULL; |
| 304 | HRESULT hr; |
| 305 | D3DPRESENT_PARAMETERS d3dpp = {0}; |
| 306 | D3DDISPLAYMODE d3ddm; |
| 307 | unsigned resetToken = 0; |
| 308 | UINT adapter = D3DADAPTER_DEFAULT; |
| 309 | |
| 310 | ctx = av_mallocz(sizeof(*ctx)); |
| 311 | if (!ctx) |
| 312 | return AVERROR(ENOMEM); |
| 313 | |
| 314 | ctx->deviceHandle = INVALID_HANDLE_VALUE; |
| 315 | |
| 316 | ist->hwaccel_ctx = ctx; |
| 317 | ist->hwaccel_uninit = dxva2_uninit; |
| 318 | ist->hwaccel_get_buffer = dxva2_get_buffer; |
| 319 | ist->hwaccel_retrieve_data = dxva2_retrieve_data; |
| 320 | |
| 321 | ctx->d3dlib = LoadLibrary("d3d9.dll"); |
| 322 | if (!ctx->d3dlib) { |
| 323 | av_log(NULL, loglevel, "Failed to load D3D9 library\n"); |
| 324 | goto fail; |
| 325 | } |
| 326 | ctx->dxva2lib = LoadLibrary("dxva2.dll"); |
| 327 | if (!ctx->dxva2lib) { |
| 328 | av_log(NULL, loglevel, "Failed to load DXVA2 library\n"); |
| 329 | goto fail; |
| 330 | } |
| 331 | |
| 332 | createD3D = (pDirect3DCreate9 *)GetProcAddress(ctx->d3dlib, "Direct3DCreate9"); |
| 333 | if (!createD3D) { |
| 334 | av_log(NULL, loglevel, "Failed to locate Direct3DCreate9\n"); |
| 335 | goto fail; |
| 336 | } |
| 337 | createDeviceManager = (pCreateDeviceManager9 *)GetProcAddress(ctx->dxva2lib, "DXVA2CreateDirect3DDeviceManager9"); |
| 338 | if (!createDeviceManager) { |
| 339 | av_log(NULL, loglevel, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n"); |
| 340 | goto fail; |
| 341 | } |
| 342 | |
| 343 | ctx->d3d9 = createD3D(D3D_SDK_VERSION); |
| 344 | if (!ctx->d3d9) { |
| 345 | av_log(NULL, loglevel, "Failed to create IDirect3D object\n"); |
| 346 | goto fail; |
| 347 | } |
| 348 | |
| 349 | if (ist->hwaccel_device) { |
| 350 | adapter = atoi(ist->hwaccel_device); |
| 351 | av_log(NULL, AV_LOG_INFO, "Using HWAccel device %d\n", adapter); |
| 352 | } |
| 353 | |
| 354 | IDirect3D9_GetAdapterDisplayMode(ctx->d3d9, adapter, &d3ddm); |
| 355 | d3dpp.Windowed = TRUE; |
| 356 | d3dpp.BackBufferWidth = 640; |
| 357 | d3dpp.BackBufferHeight = 480; |
| 358 | d3dpp.BackBufferCount = 0; |
| 359 | d3dpp.BackBufferFormat = d3ddm.Format; |
| 360 | d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; |
| 361 | d3dpp.Flags = D3DPRESENTFLAG_VIDEO; |
| 362 | |
| 363 | hr = IDirect3D9_CreateDevice(ctx->d3d9, adapter, D3DDEVTYPE_HAL, GetShellWindow(), |
| 364 | D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, |
| 365 | &d3dpp, &ctx->d3d9device); |
| 366 | if (FAILED(hr)) { |
| 367 | av_log(NULL, loglevel, "Failed to create Direct3D device\n"); |
| 368 | goto fail; |
| 369 | } |
| 370 | |
| 371 | hr = createDeviceManager(&resetToken, &ctx->d3d9devmgr); |
| 372 | if (FAILED(hr)) { |
| 373 | av_log(NULL, loglevel, "Failed to create Direct3D device manager\n"); |
| 374 | goto fail; |
| 375 | } |
| 376 | |
| 377 | hr = IDirect3DDeviceManager9_ResetDevice(ctx->d3d9devmgr, ctx->d3d9device, resetToken); |
| 378 | if (FAILED(hr)) { |
| 379 | av_log(NULL, loglevel, "Failed to bind Direct3D device to device manager\n"); |
| 380 | goto fail; |
| 381 | } |
| 382 | |
| 383 | hr = IDirect3DDeviceManager9_OpenDeviceHandle(ctx->d3d9devmgr, &ctx->deviceHandle); |
| 384 | if (FAILED(hr)) { |
| 385 | av_log(NULL, loglevel, "Failed to open device handle\n"); |
| 386 | goto fail; |
| 387 | } |
| 388 | |
| 389 | hr = IDirect3DDeviceManager9_GetVideoService(ctx->d3d9devmgr, ctx->deviceHandle, &IID_IDirectXVideoDecoderService, (void **)&ctx->decoder_service); |
| 390 | if (FAILED(hr)) { |
| 391 | av_log(NULL, loglevel, "Failed to create IDirectXVideoDecoderService\n"); |
| 392 | goto fail; |
| 393 | } |
| 394 | |
| 395 | ctx->tmp_frame = av_frame_alloc(); |
| 396 | if (!ctx->tmp_frame) |
| 397 | goto fail; |
| 398 | |
| 399 | s->hwaccel_context = av_mallocz(sizeof(struct dxva_context)); |
| 400 | if (!s->hwaccel_context) |
| 401 | goto fail; |
| 402 | |
| 403 | return 0; |
| 404 | fail: |
| 405 | dxva2_uninit(s); |
| 406 | return AVERROR(EINVAL); |
| 407 | } |
| 408 | |
| 409 | static int dxva2_get_decoder_configuration(AVCodecContext *s, const GUID *device_guid, |
| 410 | const DXVA2_VideoDesc *desc, |
| 411 | DXVA2_ConfigPictureDecode *config) |
| 412 | { |
| 413 | InputStream *ist = s->opaque; |
| 414 | int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; |
| 415 | DXVA2Context *ctx = ist->hwaccel_ctx; |
| 416 | unsigned cfg_count = 0, best_score = 0; |
| 417 | DXVA2_ConfigPictureDecode *cfg_list = NULL; |
| 418 | DXVA2_ConfigPictureDecode best_cfg = {{0}}; |
| 419 | HRESULT hr; |
| 420 | int i; |
| 421 | |
| 422 | hr = IDirectXVideoDecoderService_GetDecoderConfigurations(ctx->decoder_service, device_guid, desc, NULL, &cfg_count, &cfg_list); |
| 423 | if (FAILED(hr)) { |
| 424 | av_log(NULL, loglevel, "Unable to retrieve decoder configurations\n"); |
| 425 | return AVERROR(EINVAL); |
| 426 | } |
| 427 | |
| 428 | for (i = 0; i < cfg_count; i++) { |
| 429 | DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; |
| 430 | |
| 431 | unsigned score; |
| 432 | if (cfg->ConfigBitstreamRaw == 1) |
| 433 | score = 1; |
| 434 | else if (s->codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2) |
| 435 | score = 2; |
| 436 | else |
| 437 | continue; |
| 438 | if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA2_NoEncrypt)) |
| 439 | score += 16; |
| 440 | if (score > best_score) { |
| 441 | best_score = score; |
| 442 | best_cfg = *cfg; |
| 443 | } |
| 444 | } |
| 445 | CoTaskMemFree(cfg_list); |
| 446 | |
| 447 | if (!best_score) { |
| 448 | av_log(NULL, loglevel, "No valid decoder configuration available\n"); |
| 449 | return AVERROR(EINVAL); |
| 450 | } |
| 451 | |
| 452 | *config = best_cfg; |
| 453 | return 0; |
| 454 | } |
| 455 | |
| 456 | static int dxva2_create_decoder(AVCodecContext *s) |
| 457 | { |
| 458 | InputStream *ist = s->opaque; |
| 459 | int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; |
| 460 | DXVA2Context *ctx = ist->hwaccel_ctx; |
| 461 | struct dxva_context *dxva_ctx = s->hwaccel_context; |
| 462 | GUID *guid_list = NULL; |
| 463 | unsigned guid_count = 0, i, j; |
| 464 | GUID device_guid = GUID_NULL; |
| 465 | D3DFORMAT target_format = 0; |
| 466 | DXVA2_VideoDesc desc = { 0 }; |
| 467 | DXVA2_ConfigPictureDecode config; |
| 468 | HRESULT hr; |
| 469 | int surface_alignment; |
| 470 | int ret; |
| 471 | |
| 472 | hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list); |
| 473 | if (FAILED(hr)) { |
| 474 | av_log(NULL, loglevel, "Failed to retrieve decoder device GUIDs\n"); |
| 475 | goto fail; |
| 476 | } |
| 477 | |
| 478 | for (i = 0; dxva2_modes[i].guid; i++) { |
| 479 | D3DFORMAT *target_list = NULL; |
| 480 | unsigned target_count = 0; |
| 481 | const dxva2_mode *mode = &dxva2_modes[i]; |
| 482 | if (mode->codec != s->codec_id) |
| 483 | continue; |
| 484 | |
| 485 | for (j = 0; j < guid_count; j++) { |
| 486 | if (IsEqualGUID(mode->guid, &guid_list[j])) |
| 487 | break; |
| 488 | } |
| 489 | if (j == guid_count) |
| 490 | continue; |
| 491 | |
| 492 | hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list); |
| 493 | if (FAILED(hr)) { |
| 494 | continue; |
| 495 | } |
| 496 | for (j = 0; j < target_count; j++) { |
| 497 | const D3DFORMAT format = target_list[j]; |
| 498 | if (format == MKTAG('N','V','1','2')) { |
| 499 | target_format = format; |
| 500 | break; |
| 501 | } |
| 502 | } |
| 503 | CoTaskMemFree(target_list); |
| 504 | if (target_format) { |
| 505 | device_guid = *mode->guid; |
| 506 | break; |
| 507 | } |
| 508 | } |
| 509 | CoTaskMemFree(guid_list); |
| 510 | |
| 511 | if (IsEqualGUID(&device_guid, &GUID_NULL)) { |
| 512 | av_log(NULL, loglevel, "No decoder device for codec found\n"); |
| 513 | goto fail; |
| 514 | } |
| 515 | |
| 516 | desc.SampleWidth = s->coded_width; |
| 517 | desc.SampleHeight = s->coded_height; |
| 518 | desc.Format = target_format; |
| 519 | |
| 520 | ret = dxva2_get_decoder_configuration(s, &device_guid, &desc, &config); |
| 521 | if (ret < 0) { |
| 522 | goto fail; |
| 523 | } |
| 524 | |
| 525 | /* decoding MPEG-2 requires additional alignment on some Intel GPUs, |
| 526 | but it causes issues for H.264 on certain AMD GPUs..... */ |
| 527 | if (s->codec_id == AV_CODEC_ID_MPEG2VIDEO) |
| 528 | surface_alignment = 32; |
| 529 | else |
| 530 | surface_alignment = 16; |
| 531 | |
| 532 | /* 4 base work surfaces */ |
| 533 | ctx->num_surfaces = 4; |
| 534 | |
| 535 | /* add surfaces based on number of possible refs */ |
| 536 | if (s->codec_id == AV_CODEC_ID_H264) |
| 537 | ctx->num_surfaces += 16; |
| 538 | else |
| 539 | ctx->num_surfaces += 2; |
| 540 | |
| 541 | /* add extra surfaces for frame threading */ |
| 542 | if (s->active_thread_type & FF_THREAD_FRAME) |
| 543 | ctx->num_surfaces += s->thread_count; |
| 544 | |
| 545 | ctx->surfaces = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surfaces)); |
| 546 | ctx->surface_infos = av_mallocz(ctx->num_surfaces * sizeof(*ctx->surface_infos)); |
| 547 | |
| 548 | if (!ctx->surfaces || !ctx->surface_infos) { |
| 549 | av_log(NULL, loglevel, "Unable to allocate surface arrays\n"); |
| 550 | goto fail; |
| 551 | } |
| 552 | |
| 553 | hr = IDirectXVideoDecoderService_CreateSurface(ctx->decoder_service, |
| 554 | FFALIGN(s->coded_width, surface_alignment), |
| 555 | FFALIGN(s->coded_height, surface_alignment), |
| 556 | ctx->num_surfaces - 1, |
| 557 | target_format, D3DPOOL_DEFAULT, 0, |
| 558 | DXVA2_VideoDecoderRenderTarget, |
| 559 | ctx->surfaces, NULL); |
| 560 | if (FAILED(hr)) { |
| 561 | av_log(NULL, loglevel, "Failed to create %d video surfaces\n", ctx->num_surfaces); |
| 562 | goto fail; |
| 563 | } |
| 564 | |
| 565 | hr = IDirectXVideoDecoderService_CreateVideoDecoder(ctx->decoder_service, &device_guid, |
| 566 | &desc, &config, ctx->surfaces, |
| 567 | ctx->num_surfaces, &ctx->decoder); |
| 568 | if (FAILED(hr)) { |
| 569 | av_log(NULL, loglevel, "Failed to create DXVA2 video decoder\n"); |
| 570 | goto fail; |
| 571 | } |
| 572 | |
| 573 | ctx->decoder_guid = device_guid; |
| 574 | ctx->decoder_config = config; |
| 575 | |
| 576 | dxva_ctx->cfg = &ctx->decoder_config; |
| 577 | dxva_ctx->decoder = ctx->decoder; |
| 578 | dxva_ctx->surface = ctx->surfaces; |
| 579 | dxva_ctx->surface_count = ctx->num_surfaces; |
| 580 | |
| 581 | if (IsEqualGUID(&ctx->decoder_guid, &DXVADDI_Intel_ModeH264_E)) |
| 582 | dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; |
| 583 | |
| 584 | return 0; |
| 585 | fail: |
| 586 | dxva2_destroy_decoder(s); |
| 587 | return AVERROR(EINVAL); |
| 588 | } |
| 589 | |
| 590 | int dxva2_init(AVCodecContext *s) |
| 591 | { |
| 592 | InputStream *ist = s->opaque; |
| 593 | int loglevel = (ist->hwaccel_id == HWACCEL_AUTO) ? AV_LOG_VERBOSE : AV_LOG_ERROR; |
| 594 | DXVA2Context *ctx; |
| 595 | int ret; |
| 596 | |
| 597 | if (!ist->hwaccel_ctx) { |
| 598 | ret = dxva2_alloc(s); |
| 599 | if (ret < 0) |
| 600 | return ret; |
| 601 | } |
| 602 | ctx = ist->hwaccel_ctx; |
| 603 | |
| 604 | if (s->codec_id == AV_CODEC_ID_H264 && |
| 605 | (s->profile & ~FF_PROFILE_H264_CONSTRAINED) > FF_PROFILE_H264_HIGH) { |
| 606 | av_log(NULL, loglevel, "Unsupported H.264 profile for DXVA2 HWAccel: %d\n", s->profile); |
| 607 | return AVERROR(EINVAL); |
| 608 | } |
| 609 | |
| 610 | if (ctx->decoder) |
| 611 | dxva2_destroy_decoder(s); |
| 612 | |
| 613 | ret = dxva2_create_decoder(s); |
| 614 | if (ret < 0) { |
| 615 | av_log(NULL, loglevel, "Error creating the DXVA2 decoder\n"); |
| 616 | return ret; |
| 617 | } |
| 618 | |
| 619 | return 0; |
| 620 | } |