From 213c6114715eba662ccfc107351e09b32ea2f0c8 Mon Sep 17 00:00:00 2001 From: Kevin Mitchell Date: Sat, 26 Mar 2016 07:35:02 -0700 Subject: dxva2: rewrite to use shared code with d3d11va This is basically a full rewrite to make it look more like d3d11va.c --- video/decode/dxva2.c | 844 +++++++++++++++++++++------------------------------ 1 file changed, 350 insertions(+), 494 deletions(-) (limited to 'video') diff --git a/video/decode/dxva2.c b/video/decode/dxva2.c index d255cb7b1a..79416d52b7 100644 --- a/video/decode/dxva2.c +++ b/video/decode/dxva2.c @@ -19,14 +19,7 @@ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA */ -#include - #define DXVA2API_USE_BITFIELDS - -#include - -#include - #include #include "lavc.h" @@ -34,597 +27,465 @@ #include "common/av_common.h" #include "osdep/windows_utils.h" #include "video/fmt-conversion.h" -#include "video/dxva2.h" #include "video/mp_image_pool.h" #include "video/hwdec.h" + #include "video/d3d.h" +#include "video/dxva2.h" +#include "d3d.h" #define ADDITIONAL_SURFACES (4 + HWDEC_DELAY_QUEUE_COUNT) -// A minor evil. -#ifndef FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO -#define FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO 2 -#endif - -/* define all the GUIDs used directly here, - to avoid problems with inconsistent dxva2api.h versions in mingw-w64 and different MSVC version */ -#include -DEFINE_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551,0xd5e7,0x11d9,0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02); - -DEFINE_GUID(DXVA2_ModeMPEG2_VLD, 0xee27417f, 0x5e28,0x4e65,0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9); -DEFINE_GUID(DXVA2_ModeMPEG2and1_VLD, 0x86695f12, 0x340e,0x4f04,0x9f,0xd3,0x92,0x53,0xdd,0x32,0x74,0x60); -DEFINE_GUID(DXVA2_ModeH264_E, 0x1b81be68, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(DXVA2_ModeH264_F, 0x1b81be69, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(DXVADDI_Intel_ModeH264_E, 0x604F8E68, 0x4951,0x4C54,0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6); -DEFINE_GUID(DXVA2_ModeVC1_D, 0x1b81beA3, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(DXVA2_ModeVC1_D2010, 0x1b81beA4, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); -DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main, 0x5b11d51b, 0x2f4c,0x4452,0xbc,0xc3,0x09,0xf2,0xa1,0x16,0x0c,0xc0); -DEFINE_GUID(DXVA2_ModeHEVC_VLD_Main10,0x107af0e0, 0xef1a,0x4d19,0xab,0xa8,0x67,0xa1,0x63,0x07,0x3d,0x13); -DEFINE_GUID(DXVA2_NoEncrypt, 0x1b81beD0, 0xa0c7,0x11d3,0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5); - -typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT); -typedef HRESULT WINAPI pCreateDeviceManager9(UINT *, IDirect3DDeviceManager9 **); - -typedef struct dxva2_mode { - const GUID *guid; - const char *name; - enum AVCodecID codec; - int depth; // defaults to 8 -} dxva2_mode; - -#define MODE(id) &MP_CONCAT(DXVA2_Mode, id), # id - -static const dxva2_mode dxva2_modes[] = { - /* MPEG-2 */ - { MODE(MPEG2_VLD), AV_CODEC_ID_MPEG2VIDEO }, - { MODE(MPEG2and1_VLD), AV_CODEC_ID_MPEG2VIDEO }, - - /* H.264 */ - { MODE(H264_F), AV_CODEC_ID_H264 }, - { MODE(H264_E), AV_CODEC_ID_H264 }, - /* Intel specific H.264 mode */ - { &DXVADDI_Intel_ModeH264_E, "Intel_ModeH264_E", AV_CODEC_ID_H264 }, - - /* VC-1 / WMV3 */ - { MODE(VC1_D2010), AV_CODEC_ID_VC1 }, - { MODE(VC1_D2010), AV_CODEC_ID_WMV3 }, - { MODE(VC1_D), AV_CODEC_ID_VC1 }, - { MODE(VC1_D), AV_CODEC_ID_WMV3 }, - - { MODE(HEVC_VLD_Main), AV_CODEC_ID_HEVC }, - { MODE(HEVC_VLD_Main10),AV_CODEC_ID_HEVC, .depth = 10}, - - { NULL, 0 }, -}; - -#undef MODE - -struct dxva2_decoder { - DXVA2_ConfigPictureDecode config; - IDirectXVideoDecoder *decoder; - LPDIRECT3DSURFACE9 *surfaces; - int num_surfaces; - struct mp_image_pool *pool; -}; - -typedef struct DXVA2Context { +struct priv { struct mp_log *log; - HMODULE d3dlib; - HMODULE dxva2lib; - - HANDLE deviceHandle; - + HMODULE d3d9_dll; + HMODULE dxva2_dll; IDirect3D9 *d3d9; - IDirect3DDevice9 *d3d9device; - IDirect3DDeviceManager9 *d3d9devmgr; + IDirect3DDevice9 *device; + HANDLE device_handle; + IDirect3DDeviceManager9 *device_manager; IDirectXVideoDecoderService *decoder_service; - struct dxva2_decoder *decoder; + struct mp_image_pool *decoder_pool; struct mp_image_pool *sw_pool; - int mp_format; -} DXVA2Context; - -static void dxva2_uninit(struct lavc_ctx *s) -{ - DXVA2Context *ctx = s->hwdec_priv; - if (!ctx) - return; - - talloc_free(ctx->decoder); - - if (ctx->decoder_service) - IDirectXVideoDecoderService_Release(ctx->decoder_service); - - if (ctx->d3d9devmgr && ctx->deviceHandle != INVALID_HANDLE_VALUE) - IDirect3DDeviceManager9_CloseDeviceHandle(ctx->d3d9devmgr, ctx->deviceHandle); - - if (ctx->d3d9devmgr) - IDirect3DDeviceManager9_Release(ctx->d3d9devmgr); - - if (ctx->d3d9device) - IDirect3DDevice9_Release(ctx->d3d9device); - - if (ctx->d3d9) - IDirect3D9_Release(ctx->d3d9); - - if (ctx->d3dlib) - FreeLibrary(ctx->d3dlib); - - if (ctx->dxva2lib) - FreeLibrary(ctx->dxva2lib); - - av_freep(&s->avctx->hwaccel_context); - talloc_free(ctx); - s->hwdec_priv = NULL; -} + int mpfmt_decoded; +}; static struct mp_image *dxva2_allocate_image(struct lavc_ctx *s, int w, int h) { - DXVA2Context *ctx = s->hwdec_priv; - - struct mp_image *img = mp_image_pool_get_no_alloc(ctx->decoder->pool, + struct priv *p = s->hwdec_priv; + struct mp_image *img = mp_image_pool_get_no_alloc(p->decoder_pool, IMGFMT_DXVA2, w, h); if (!img) - MP_ERR(ctx, "Failed to allocate additional DXVA2 surface.\n"); + MP_ERR(p, "Failed to allocate additional DXVA2 surface.\n"); return img; } -static void copy_nv12(struct mp_image *dest, uint8_t *src_bits, - unsigned src_pitch, unsigned surf_height) -{ - struct mp_image buf = {0}; - mp_image_setfmt(&buf, dest->imgfmt); - mp_image_set_size(&buf, dest->w, dest->h); - - buf.planes[0] = src_bits; - buf.stride[0] = src_pitch; - buf.planes[1] = src_bits + src_pitch * surf_height; - buf.stride[1] = src_pitch; - mp_image_copy_gpu(dest, &buf); -} - static struct mp_image *dxva2_retrieve_image(struct lavc_ctx *s, struct mp_image *img) { - DXVA2Context *ctx = s->hwdec_priv; - LPDIRECT3DSURFACE9 surface = d3d9_surface_in_mp_image(img); - D3DSURFACE_DESC surfaceDesc; - D3DLOCKED_RECT LockedRect; - HRESULT hr; - - IDirect3DSurface9_GetDesc(surface, &surfaceDesc); + HRESULT hr; + struct priv *p = s->hwdec_priv; + IDirect3DSurface9 *surface = d3d9_surface_in_mp_image(img); - if (surfaceDesc.Width < img->w || surfaceDesc.Height < img->h) + if (!surface) { + MP_ERR(p, "Failed to get Direct3D surface from mp_image\n"); return img; + } - struct mp_image *sw_img = mp_image_pool_get(ctx->sw_pool, ctx->mp_format, - surfaceDesc.Width, - surfaceDesc.Height); + D3DSURFACE_DESC surface_desc; + IDirect3DSurface9_GetDesc(surface, &surface_desc); + if (surface_desc.Width < img->w || surface_desc.Height < img->h) { + MP_ERR(p, "Direct3D11 texture smaller than mp_image dimensions\n"); + return img; + } - if (!sw_img) + struct mp_image *sw_img = mp_image_pool_get(p->sw_pool, + p->mpfmt_decoded, + surface_desc.Width, + surface_desc.Height); + if (!sw_img) { + MP_ERR(p, "Failed to get %s surface from CPU pool\n", + mp_imgfmt_to_name(p->mpfmt_decoded)); return img; + } - hr = IDirect3DSurface9_LockRect(surface, &LockedRect, NULL, D3DLOCK_READONLY); + D3DLOCKED_RECT lock; + hr = IDirect3DSurface9_LockRect(surface, &lock, NULL, D3DLOCK_READONLY); if (FAILED(hr)) { - MP_ERR(ctx, "Unable to lock DXVA2 surface: %s\n", + MP_ERR(p, "Unable to lock DXVA2 surface: %s\n", mp_HRESULT_to_str(hr)); talloc_free(sw_img); return img; } + copy_nv12(sw_img, lock.pBits, lock.Pitch, surface_desc.Height); + IDirect3DSurface9_UnlockRect(surface); - copy_nv12(sw_img, LockedRect.pBits, LockedRect.Pitch, surfaceDesc.Height); mp_image_set_size(sw_img, img->w, img->h); mp_image_copy_attributes(sw_img, img); - - IDirect3DSurface9_UnlockRect(surface); - talloc_free(img); return sw_img; } -static int create_device(struct lavc_ctx *s) -{ - DXVA2Context *ctx = s->hwdec_priv; - pDirect3DCreate9 *createD3D = NULL; - HRESULT hr; - D3DPRESENT_PARAMETERS d3dpp = {0}; - D3DDISPLAYMODE d3ddm; - UINT adapter = D3DADAPTER_DEFAULT; +struct d3d9_format { + D3DFORMAT format; + int depth; +}; - if (s->hwdec_info && s->hwdec_info->hwctx && s->hwdec_info->hwctx->d3d_ctx) { - ctx->d3d9device = s->hwdec_info->hwctx->d3d_ctx->d3d9_device; - if (ctx->d3d9device) { - IDirect3D9_AddRef(ctx->d3d9device); - MP_VERBOSE(ctx, "Using VO-supplied device %p.\n", ctx->d3d9device); - return 0; - } - } +static const struct d3d9_format d3d9_formats[] = { + {MKTAG('N','V','1','2'), 8}, + {MKTAG('P','0','1','0'), 10}, + {MKTAG('P','0','1','6'), 16}, +}; - ctx->d3dlib = LoadLibrary(L"d3d9.dll"); - if (!ctx->d3dlib) { - MP_ERR(ctx, "Failed to load D3D9 library\n"); - goto fail; - } +static void dump_decoder_info(struct lavc_ctx *s, + GUID *device_guids, UINT n_guids) +{ + struct priv *p = s->hwdec_priv; + MP_VERBOSE(p, "%u decoder devices:\n", (unsigned)n_guids); + for (UINT i = 0; i < n_guids; i++) { + GUID *guid = &device_guids[i]; + char *description = d3d_decoder_guid_to_desc(guid); + + D3DFORMAT *formats = NULL; + UINT n_formats = 0; + HRESULT hr = IDirectXVideoDecoderService_GetDecoderRenderTargets( + p->decoder_service, guid, &n_formats, &formats); + if (FAILED(hr)) { + MP_ERR(p, "Failed to get render targets for decoder %s:%s", + description, mp_HRESULT_to_str(hr)); + } - createD3D = (pDirect3DCreate9 *)GetProcAddress(ctx->d3dlib, "Direct3DCreate9"); - if (!createD3D) { - MP_ERR(ctx, "Failed to locate Direct3DCreate9\n"); - goto fail; - } + char fmts[256] = {0}; + for (UINT j = 0; j < n_formats; j++) { + mp_snprintf_cat(fmts, sizeof(fmts), + " %s", mp_tag_str(formats[j])); + } + CoTaskMemFree(formats); - ctx->d3d9 = createD3D(D3D_SDK_VERSION); - if (!ctx->d3d9) { - MP_ERR(ctx, "Failed to create IDirect3D object\n"); - goto fail; + MP_VERBOSE(p, "%s %s\n", description, fmts); } +} - IDirect3D9_GetAdapterDisplayMode(ctx->d3d9, adapter, &d3ddm); - d3dpp.Windowed = TRUE; - d3dpp.BackBufferWidth = 640; - d3dpp.BackBufferHeight = 480; - d3dpp.BackBufferCount = 0; - d3dpp.BackBufferFormat = d3ddm.Format; - d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; - d3dpp.Flags = D3DPRESENTFLAG_VIDEO; - - hr = IDirect3D9_CreateDevice(ctx->d3d9, adapter, D3DDEVTYPE_HAL, GetShellWindow(), - D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, - &d3dpp, &ctx->d3d9device); +static DWORD get_dxfmt_cb(struct lavc_ctx *s, const GUID *guid, int depth) +{ + DWORD ret = 0; + struct priv *p = s->hwdec_priv; + D3DFORMAT *formats = NULL; + UINT n_formats = 0; + HRESULT hr = IDirectXVideoDecoderService_GetDecoderRenderTargets( + p->decoder_service, guid, &n_formats, &formats); if (FAILED(hr)) { - MP_ERR(ctx, "Failed to create Direct3D device: %s\n", - mp_HRESULT_to_str(hr)); - goto fail; + MP_ERR(p, "Callback failed to get render targets for decoder %s: %s", + d3d_decoder_guid_to_desc(guid), mp_HRESULT_to_str(hr)); + return 0; } - return 0; + for (int i = 0; i < MP_ARRAY_SIZE(d3d9_formats); i++) { + const struct d3d9_format *d3d9_fmt = &d3d9_formats[i]; + if (d3d9_fmt->depth < depth) + continue; -fail: - return -1; + for (UINT j = 0; j < n_formats; j++) { + if (formats[i] == d3d9_fmt->format) { + ret = formats[i]; + goto done; + } + } + } +done: + CoTaskMemFree(formats); + return ret; } -static int dxva2_init(struct lavc_ctx *s) +static int dxva2_init_decoder(struct lavc_ctx *s, int w, int h) { - DXVA2Context *ctx; - pCreateDeviceManager9 *createDeviceManager = NULL; HRESULT hr; - unsigned resetToken = 0; - - ctx = talloc_zero(NULL, DXVA2Context); - if (!ctx) - return -1; - s->hwdec_priv = ctx; - - ctx->log = mp_log_new(s, s->log, "dxva2"); - - if (s->hwdec->type == HWDEC_DXVA2_COPY) { - mp_check_gpu_memcpy(ctx->log, NULL); - ctx->sw_pool = talloc_steal(ctx, mp_image_pool_new(17)); - } - - ctx->deviceHandle = INVALID_HANDLE_VALUE; - - ctx->dxva2lib = LoadLibrary(L"dxva2.dll"); - if (!ctx->dxva2lib) { - MP_ERR(ctx, "Failed to load DXVA2 library\n"); - goto fail; + int ret = -1; + struct priv *p = s->hwdec_priv; + TA_FREEP(&p->decoder_pool); + + int n_surfaces = hwdec_get_max_refs(s) + ADDITIONAL_SURFACES; + IDirect3DSurface9 **surfaces = NULL; + IDirectXVideoDecoder *decoder = NULL; + void *tmp = talloc_new(NULL); + + UINT n_guids; + GUID *device_guids; + hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids( + p->decoder_service, &n_guids, &device_guids); + if (FAILED(hr)) { + MP_ERR(p, "Failed to retrieve decoder device GUIDs: %s\n", + mp_HRESULT_to_str(hr)); + goto done; } - if (create_device(s) < 0) - goto fail; + dump_decoder_info(s, device_guids, n_guids); - createDeviceManager = (pCreateDeviceManager9 *)GetProcAddress(ctx->dxva2lib, "DXVA2CreateDirect3DDeviceManager9"); - if (!createDeviceManager) { - MP_ERR(ctx, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n"); - goto fail; + struct d3d_decoder_fmt fmt = + d3d_select_decoder_mode(s, device_guids, n_guids, get_dxfmt_cb); + CoTaskMemFree(device_guids); + if (fmt.mpfmt_decoded == IMGFMT_NONE) { + MP_ERR(p, "Failed to find a suitable decoder\n"); + goto done; } - hr = createDeviceManager(&resetToken, &ctx->d3d9devmgr); + p->mpfmt_decoded = fmt.mpfmt_decoded; + struct mp_image_pool *decoder_pool = + talloc_steal(tmp, mp_image_pool_new(n_surfaces)); + DXVA2_ConfigPictureDecode *decoder_config = + talloc_zero(decoder_pool, DXVA2_ConfigPictureDecode); + + int w_align = w, h_align = h; + d3d_surface_align(s, &w_align, &h_align); + DXVA2_VideoDesc video_desc ={ + .SampleWidth = w, + .SampleHeight = h, + .Format = fmt.dxfmt_decoded, + }; + UINT n_configs = 0; + DXVA2_ConfigPictureDecode *configs = NULL; + hr = IDirectXVideoDecoderService_GetDecoderConfigurations( + p->decoder_service, fmt.guid, &video_desc, NULL, + &n_configs, &configs); if (FAILED(hr)) { - MP_ERR(ctx, "Failed to create Direct3D device manager: %s\n", + MP_ERR(p, "Unable to retrieve decoder configurations: %s\n", mp_HRESULT_to_str(hr)); - goto fail; + goto done; } - hr = IDirect3DDeviceManager9_ResetDevice(ctx->d3d9devmgr, ctx->d3d9device, resetToken); - if (FAILED(hr)) { - MP_ERR(ctx, "Failed to bind Direct3D device to device manager: %s\n", - mp_HRESULT_to_str(hr)); - goto fail; + unsigned max_score = 0; + for (UINT i = 0; i < n_configs; i++) { + unsigned score = d3d_decoder_config_score( + s, &configs[i].guidConfigBitstreamEncryption, + configs[i].ConfigBitstreamRaw); + if (score > max_score) { + max_score = score; + *decoder_config = configs[i]; + } + } + CoTaskMemFree(configs); + if (!max_score) { + MP_ERR(p, "Failed to find a suitable decoder configuration\n"); + goto done; } - hr = IDirect3DDeviceManager9_OpenDeviceHandle(ctx->d3d9devmgr, &ctx->deviceHandle); + surfaces = talloc_zero_array(decoder_pool, IDirect3DSurface9*, n_surfaces); + hr = IDirectXVideoDecoderService_CreateSurface( + p->decoder_service, + w_align, h_align, + n_surfaces - 1, fmt.dxfmt_decoded, D3DPOOL_DEFAULT, 0, + DXVA2_VideoDecoderRenderTarget, surfaces, NULL); if (FAILED(hr)) { - MP_ERR(ctx, "Failed to open device handle: %s\n", - mp_HRESULT_to_str(hr)); - goto fail; + MP_ERR(p, "Failed to create %d video surfaces: %s\n", + n_surfaces, mp_HRESULT_to_str(hr)); + goto done; } - hr = IDirect3DDeviceManager9_GetVideoService(ctx->d3d9devmgr, ctx->deviceHandle, &IID_IDirectXVideoDecoderService, (void **)&ctx->decoder_service); + hr = IDirectXVideoDecoderService_CreateVideoDecoder( + p->decoder_service, fmt.guid, &video_desc, decoder_config, + surfaces, n_surfaces, &decoder); if (FAILED(hr)) { - MP_ERR(ctx, "Failed to create IDirectXVideoDecoderService: %s\n", + MP_ERR(p, "Failed to create DXVA2 video decoder: %s\n", mp_HRESULT_to_str(hr)); - goto fail; + goto done; } - s->avctx->hwaccel_context = av_mallocz(sizeof(struct dxva_context)); - if (!s->avctx->hwaccel_context) - goto fail; + for (int i = 0; i < n_surfaces; i++) { + struct mp_image *img = dxva2_new_ref(decoder, surfaces[i], w, h); + if (!img) { + MP_ERR(p, "Failed to create DXVA2 image\n"); + goto done; + } + mp_image_pool_add(decoder_pool, img); // transferred to pool + } - return 0; -fail: - dxva2_uninit(s); - return -1; + // Pass required information on to ffmpeg. + struct dxva_context *dxva_ctx = s->avctx->hwaccel_context; + dxva_ctx->cfg = decoder_config; + dxva_ctx->decoder = decoder; + dxva_ctx->surface_count = n_surfaces; + dxva_ctx->surface = surfaces; + dxva_ctx->workaround = is_clearvideo(fmt.guid) ? + FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO : 0; + + p->decoder_pool = talloc_steal(NULL, decoder_pool); + ret = 0; +done: + // On success, `p->decoder_pool` mp_images still hold refs to `surfaces` and + // `decoder`, so the pointers in the ffmpeg `dxva_context` strcture remain + // valid for the lifetime of the pool. + if (surfaces) { + for (int i = 0; i < n_surfaces; i++) + IDirect3DSurface9_Release(surfaces[i]); + } + if (decoder) + IDirectXVideoDecoder_Release(decoder); + + talloc_free(tmp); + return ret; } -static int dxva2_get_decoder_configuration(struct lavc_ctx *s, - enum AVCodecID codec_id, - const GUID *device_guid, - const DXVA2_VideoDesc *desc, - DXVA2_ConfigPictureDecode *config) +static void destroy_device(struct lavc_ctx *s) { - DXVA2Context *ctx = s->hwdec_priv; - unsigned cfg_count = 0, best_score = 0; - DXVA2_ConfigPictureDecode *cfg_list = NULL; - DXVA2_ConfigPictureDecode best_cfg = {{0}}; - HRESULT hr; - int i; + struct priv *p = s->hwdec_priv; - hr = IDirectXVideoDecoderService_GetDecoderConfigurations(ctx->decoder_service, device_guid, desc, NULL, &cfg_count, &cfg_list); - if (FAILED(hr)) { - MP_ERR(ctx, "Unable to retrieve decoder configurations: %s\n", - mp_HRESULT_to_str(hr)); - return -1; - } + if (p->device) + IDirect3DDevice9_Release(p->device); - for (i = 0; i < cfg_count; i++) { - DXVA2_ConfigPictureDecode *cfg = &cfg_list[i]; + if (p->d3d9) + IDirect3D9_Release(p->d3d9); - unsigned score; - if (cfg->ConfigBitstreamRaw == 1) - score = 1; - else if (codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2) - score = 2; - else - continue; - if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA2_NoEncrypt)) - score += 16; - if (score > best_score) { - best_score = score; - best_cfg = *cfg; - } - } - CoTaskMemFree(cfg_list); + if (p->d3d9_dll) + FreeLibrary(p->d3d9_dll); - if (!best_score) { - MP_ERR(ctx, "No valid decoder configuration available\n"); - return -1; - } - - *config = best_cfg; - return 0; + if (p->dxva2_dll) + FreeLibrary(p->dxva2_dll); } -static void dxva2_destroy_decoder(void *arg) +static bool create_device(struct lavc_ctx *s) { - struct dxva2_decoder *decoder = arg; - if (decoder->decoder) - IDirectXVideoDecoder_Release(decoder->decoder); + struct priv *p = s->hwdec_priv; + p->d3d9_dll = LoadLibrary(L"d3d9.dll"); + if (!p->d3d9_dll) { + MP_ERR(p, "Failed to load D3D9 library\n"); + return false; + } - if (decoder->surfaces) { - for (int i = 0; i < decoder->num_surfaces; i++) - IDirect3DSurface9_Release(decoder->surfaces[i]); + IDirect3D9* (WINAPI *Direct3DCreate9)(UINT) = + (void *)GetProcAddress(p->d3d9_dll, "Direct3DCreate9"); + if (!Direct3DCreate9) { + MP_ERR(p, "Failed to locate Direct3DCreate9\n"); + return false; } -} -static int dxva2_create_decoder(struct lavc_ctx *s, int w, int h, - enum AVCodecID codec_id, int profile) -{ - DXVA2Context *ctx = s->hwdec_priv; - struct dxva_context *dxva_ctx = s->avctx->hwaccel_context; - void *tmp = talloc_new(NULL); - GUID *guid_list = NULL; - unsigned guid_count = 0, i, j; - GUID device_guid = GUID_NULL; - D3DFORMAT target_format = 0; - DXVA2_VideoDesc desc = { 0 }; - HRESULT hr; - struct dxva2_decoder *decoder; - int surface_alignment; - int ret = -1; + p->d3d9 = Direct3DCreate9(D3D_SDK_VERSION); + if (!p->d3d9) { + MP_ERR(p, "Failed to create IDirect3D object\n"); + return false; + } - hr = IDirectXVideoDecoderService_GetDecoderDeviceGuids(ctx->decoder_service, &guid_count, &guid_list); + UINT adapter = D3DADAPTER_DEFAULT; + D3DDISPLAYMODE display_mode; + IDirect3D9_GetAdapterDisplayMode(p->d3d9, adapter, &display_mode); + D3DPRESENT_PARAMETERS present_params = { + .Windowed = TRUE, + .BackBufferWidth = 640, + .BackBufferHeight = 480, + .BackBufferCount = 0, + .BackBufferFormat = display_mode.Format, + .SwapEffect = D3DSWAPEFFECT_DISCARD, + .Flags = D3DPRESENTFLAG_VIDEO, + }; + HRESULT hr = IDirect3D9_CreateDevice(p->d3d9, adapter, + D3DDEVTYPE_HAL, + GetShellWindow(), + D3DCREATE_SOFTWARE_VERTEXPROCESSING | + D3DCREATE_MULTITHREADED | + D3DCREATE_FPU_PRESERVE, + &present_params, + &p->device); if (FAILED(hr)) { - MP_ERR(ctx, "Failed to retrieve decoder device GUIDs: %s\n", + MP_ERR(p, "Failed to create Direct3D device: %s\n", mp_HRESULT_to_str(hr)); - goto fail; + return false; } + return true; +} - // dump all decoder info - MP_VERBOSE(ctx, "%d decoder devices:\n", (int)guid_count); - for (j = 0; j < guid_count; j++) { - GUID *guid = &guid_list[j]; +static void dxva2_uninit(struct lavc_ctx *s) +{ + struct priv *p = s->hwdec_priv; + if (!p) + return; - const char *name = ""; - for (i = 0; dxva2_modes[i].guid; i++) { - if (IsEqualGUID(dxva2_modes[i].guid, guid)) - name = dxva2_modes[i].name; - } + av_freep(&s->avctx->hwaccel_context); + talloc_free(p->decoder_pool); - D3DFORMAT *target_list = NULL; - unsigned target_count = 0; - hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, guid, &target_count, &target_list); - if (FAILED(hr)) - continue; - char fmts[256] = {0}; - for (i = 0; i < target_count; i++) - mp_snprintf_cat(fmts, sizeof(fmts), " %s", mp_tag_str(target_list[i])); - CoTaskMemFree(target_list); - MP_VERBOSE(ctx, "%s %s %s\n", mp_GUID_to_str(guid), name, fmts); - } + if (p->decoder_service) + IDirectXVideoDecoderService_Release(p->decoder_service); - // find a suitable decoder - for (i = 0; dxva2_modes[i].guid; i++) { - D3DFORMAT *target_list = NULL; - unsigned target_count = 0; - const dxva2_mode *mode = &dxva2_modes[i]; - int depth = mode->depth; - if (mode->codec != codec_id) - continue; + if (p->device_manager && p->device_handle != INVALID_HANDLE_VALUE) + IDirect3DDeviceManager9_CloseDeviceHandle(p->device_manager, p->device_handle); - for (j = 0; j < guid_count; j++) { - if (IsEqualGUID(mode->guid, &guid_list[j])) - break; - } - if (j == guid_count) - continue; + if (p->device_manager) + IDirect3DDeviceManager9_Release(p->device_manager); - if (codec_id == AV_CODEC_ID_HEVC) { - if ((mode->depth > 8) != (s->avctx->profile == FF_PROFILE_HEVC_MAIN_10)) - continue; - } + destroy_device(s); - hr = IDirectXVideoDecoderService_GetDecoderRenderTargets(ctx->decoder_service, mode->guid, &target_count, &target_list); - if (FAILED(hr)) { - continue; - } - for (j = 0; j < target_count; j++) { - const D3DFORMAT format = target_list[j]; - if (depth <= 8) { - if (format == MKTAG('N','V','1','2')) { - ctx->mp_format = IMGFMT_NV12; - target_format = format; - } - } else { - int p010 = mp_imgfmt_find(1, 1, 2, 10, MP_IMGFLAG_YUV_NV); - if (p010 && (format == MKTAG('P','0','1','0') || - format == MKTAG('P','0','1','6'))) - { - // There is no FFmpeg format that is like NV12 and supports - // 16 bit per component, but vo_opengl will use the lower - // bits in P010 anyway. - ctx->mp_format = p010; - target_format = format; - } - } - if (target_format) - break; - } - CoTaskMemFree(target_list); - if (target_format && ctx->mp_format) { - device_guid = *mode->guid; - break; - } - } - CoTaskMemFree(guid_list); + TA_FREEP(&s->hwdec_priv); +} - if (IsEqualGUID(&device_guid, &GUID_NULL)) { - MP_ERR(ctx, "No decoder device for codec found\n"); - goto fail; +static int dxva2_init(struct lavc_ctx *s) +{ + HRESULT hr; + struct priv *p = talloc_zero(NULL, struct priv); + if (!p) + return -1; + + s->hwdec_priv = p; + p->device_handle = INVALID_HANDLE_VALUE; + p->log = mp_log_new(s, s->log, "dxva2"); + + if (s->hwdec->type == HWDEC_DXVA2_COPY) { + mp_check_gpu_memcpy(p->log, NULL); + p->sw_pool = talloc_steal(p, mp_image_pool_new(17)); } - desc.SampleWidth = w; - desc.SampleHeight = h; - desc.Format = target_format; + if (s->hwdec_info && s->hwdec_info->hwctx && s->hwdec_info->hwctx->d3d_ctx) + p->device = s->hwdec_info->hwctx->d3d_ctx->d3d9_device; - decoder = talloc_zero(tmp, struct dxva2_decoder); - talloc_set_destructor(decoder, dxva2_destroy_decoder); - if (dxva2_get_decoder_configuration(s, codec_id, &device_guid, &desc, - &decoder->config) < 0) { + if (p->device) { + IDirect3D9_AddRef(p->device); + MP_VERBOSE(p, "Using VO-supplied device %p.\n", p->device); + } else if (s->hwdec->type == HWDEC_DXVA2) { + MP_ERR(p, "No Direct3D device provided for native dxva2 decoding\n"); goto fail; + } else { + if (!create_device(s)) + goto fail; } - /* decoding MPEG-2 requires additional alignment on some Intel GPUs, - but it causes issues for H.264 on certain AMD GPUs..... */ - if (codec_id == AV_CODEC_ID_MPEG2VIDEO) - surface_alignment = 32; - /* the HEVC DXVA2 spec asks for 128 pixel aligned surfaces to ensure - all coding features have enough room to work with */ - else if (codec_id == AV_CODEC_ID_HEVC) - surface_alignment = 128; - else - surface_alignment = 16; + p->dxva2_dll = LoadLibrary(L"dxva2.dll"); + if (!p->dxva2_dll) { + MP_ERR(p, "Failed to load DXVA2 library\n"); + goto fail; + } - decoder->num_surfaces = hwdec_get_max_refs(s) + ADDITIONAL_SURFACES; + HRESULT (WINAPI *CreateDeviceManager9)(UINT *, IDirect3DDeviceManager9 **) = + (void *)GetProcAddress(p->dxva2_dll, + "DXVA2CreateDirect3DDeviceManager9"); + if (!CreateDeviceManager9) { + MP_ERR(p, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n"); + goto fail; + } - decoder->surfaces = talloc_array(decoder, LPDIRECT3DSURFACE9, decoder->num_surfaces); - hr = IDirectXVideoDecoderService_CreateSurface( - ctx->decoder_service, - FFALIGN(w, surface_alignment), FFALIGN(h, surface_alignment), - decoder->num_surfaces - 1, target_format, D3DPOOL_DEFAULT, 0, - DXVA2_VideoDecoderRenderTarget, decoder->surfaces, NULL); + unsigned reset_token = 0; + hr = CreateDeviceManager9(&reset_token, &p->device_manager); if (FAILED(hr)) { - MP_ERR(ctx, "Failed to create %d video surfaces: %s\n", - decoder->num_surfaces, mp_HRESULT_to_str(hr)); + MP_ERR(p, "Failed to create Direct3D device manager: %s\n", + mp_HRESULT_to_str(hr)); goto fail; } - hr = IDirectXVideoDecoderService_CreateVideoDecoder( - ctx->decoder_service, &device_guid, &desc, &decoder->config, - decoder->surfaces, decoder->num_surfaces, &decoder->decoder); + hr = IDirect3DDeviceManager9_ResetDevice(p->device_manager, + p->device, reset_token); if (FAILED(hr)) { - MP_ERR(ctx, "Failed to create DXVA2 video decoder: %s\n", + MP_ERR(p, "Failed to bind Direct3D device to device manager: %s\n", mp_HRESULT_to_str(hr)); goto fail; } - decoder->pool = talloc_steal(decoder, mp_image_pool_new(decoder->num_surfaces)); - for (i = 0; i < decoder->num_surfaces; i++) { - struct mp_image *img = dxva2_new_ref(decoder->decoder, decoder->surfaces[i], w, h); - if (!img) { - MP_ERR(ctx, "Failed to create DXVA2 image\n"); - goto fail; - } - mp_image_pool_add(decoder->pool, img); - } - - // Pass required information on to ffmpeg. - dxva_ctx->cfg = &decoder->config; - dxva_ctx->decoder = decoder->decoder; - dxva_ctx->surface = decoder->surfaces; - dxva_ctx->surface_count = decoder->num_surfaces; - - if (IsEqualGUID(&device_guid, &DXVADDI_Intel_ModeH264_E)) - dxva_ctx->workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO; - - ctx->decoder = talloc_steal(NULL, decoder); - ret = 0; -fail: - talloc_free(tmp); - return ret; -} -static int dxva2_init_decoder(struct lavc_ctx *s, int w, int h) -{ - DXVA2Context *ctx = s->hwdec_priv; - - enum AVCodecID codec = s->avctx->codec_id; - int profile = s->avctx->profile; - if (codec == AV_CODEC_ID_H264 && - (profile & ~FF_PROFILE_H264_CONSTRAINED) > FF_PROFILE_H264_HIGH) - { - MP_ERR(ctx, "Unsupported H.264 profile for DXVA2 HWAccel: %d\n", profile); - return -1; - } - if (codec == AV_CODEC_ID_HEVC && profile != FF_PROFILE_HEVC_MAIN && - profile != FF_PROFILE_HEVC_MAIN_10) - { - MP_ERR(ctx, "Unsupported H.265 profile for DXVA2 HWAccel: %d\n", profile); - return -1; + hr = IDirect3DDeviceManager9_OpenDeviceHandle(p->device_manager, + &p->device_handle); + if (FAILED(hr)) { + MP_ERR(p, "Failed to open device handle: %s\n", + mp_HRESULT_to_str(hr)); + goto fail; } - talloc_free(ctx->decoder); - ctx->decoder = NULL; - - if (dxva2_create_decoder(s, w, h, codec, profile) < 0) { - MP_ERR(ctx, "Error creating the DXVA2 decoder\n"); - return -1; + hr = IDirect3DDeviceManager9_GetVideoService( + p->device_manager, p->device_handle, &IID_IDirectXVideoDecoderService, + (void **)&p->decoder_service); + if (FAILED(hr)) { + MP_ERR(p, "Failed to create IDirectXVideoDecoderService: %s\n", + mp_HRESULT_to_str(hr)); + goto fail; } + s->avctx->hwaccel_context = av_mallocz(sizeof(struct dxva_context)); + if (!s->avctx->hwaccel_context) + goto fail; + return 0; +fail: + dxva2_uninit(s); + return -1; } -static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info, - const char *decoder) +static int dxva2_probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info, + const char *decoder) { hwdec_request_api(info, "dxva2"); // dxva2-copy can do without external context; dxva2 requires it. @@ -633,31 +494,26 @@ static int probe(struct vd_lavc_hwdec *hwdec, struct mp_hwdec_info *info, info->hwctx->type == HWDEC_DXVA2_COPY) return HWDEC_ERR_NO_CTX; } - for (int i = 0; dxva2_modes[i].guid; i++) { - const dxva2_mode *mode = &dxva2_modes[i]; - if (mp_codec_to_av_codec_id(decoder) == mode->codec) - return 0; - } - return HWDEC_ERR_NO_CODEC; + return d3d_probe_codec(decoder); } const struct vd_lavc_hwdec mp_vd_lavc_dxva2 = { - .type = HWDEC_DXVA2, - .image_format = IMGFMT_DXVA2, - .probe = probe, - .init = dxva2_init, - .uninit = dxva2_uninit, - .init_decoder = dxva2_init_decoder, + .type = HWDEC_DXVA2, + .image_format = IMGFMT_DXVA2, + .probe = dxva2_probe, + .init = dxva2_init, + .uninit = dxva2_uninit, + .init_decoder = dxva2_init_decoder, .allocate_image = dxva2_allocate_image, }; const struct vd_lavc_hwdec mp_vd_lavc_dxva2_copy = { - .type = HWDEC_DXVA2_COPY, - .image_format = IMGFMT_DXVA2, - .probe = probe, - .init = dxva2_init, - .uninit = dxva2_uninit, - .init_decoder = dxva2_init_decoder, + .type = HWDEC_DXVA2_COPY, + .image_format = IMGFMT_DXVA2, + .probe = dxva2_probe, + .init = dxva2_init, + .uninit = dxva2_uninit, + .init_decoder = dxva2_init_decoder, .allocate_image = dxva2_allocate_image, - .process_image = dxva2_retrieve_image, + .process_image = dxva2_retrieve_image, }; -- cgit v1.2.3