summaryrefslogtreecommitdiffstats
path: root/video/decode
diff options
context:
space:
mode:
authorKevin Mitchell <kevmitch@gmail.com>2016-03-26 07:35:02 -0700
committerKevin Mitchell <kevmitch@gmail.com>2016-03-30 09:01:33 -0700
commit213c6114715eba662ccfc107351e09b32ea2f0c8 (patch)
tree98727256a5419ac456153c091586768802b4f03b /video/decode
parenta7110862c8a828e556dd2da0905c3e69f56eca29 (diff)
downloadmpv-213c6114715eba662ccfc107351e09b32ea2f0c8.tar.bz2
mpv-213c6114715eba662ccfc107351e09b32ea2f0c8.tar.xz
dxva2: rewrite to use shared code with d3d11va
This is basically a full rewrite to make it look more like d3d11va.c
Diffstat (limited to 'video/decode')
-rw-r--r--video/decode/dxva2.c844
1 files changed, 350 insertions, 494 deletions
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 <windows.h>
-
#define DXVA2API_USE_BITFIELDS
-
-#include <stdint.h>
-
-#include <ks.h>
-
#include <libavcodec/dxva2.h>
#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 <initguid.h>
-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 = "<unknown>";
- 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)