dxva2.c 26.7 KB
Newer Older
1
/*****************************************************************************
Pere Orga's avatar
Pere Orga committed
2
 * dxva2.c: Video Acceleration helpers
3 4
 *****************************************************************************
 * Copyright (C) 2009 Geoffroy Couprie
5
 * Copyright (C) 2009 Laurent Aimar
6 7 8
 * $Id$
 *
 * Authors: Geoffroy Couprie <geal@videolan.org>
9
 *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
10
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
11 12 13
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation; either version 2.1 of the License, or
14 15 16 17
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
18 19
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
 * GNU Lesser General Public License for more details.
20
 *
Jean-Baptiste Kempf's avatar
LGPL  
Jean-Baptiste Kempf committed
21 22 23
 * You should have received a copy of the GNU Lesser General Public License
 * along with this program; if not, write to the Free Software Foundation,
 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24 25 26 27 28 29
 *****************************************************************************/

#ifdef HAVE_CONFIG_H
# include "config.h"
#endif

30 31
#include <assert.h>

32
#include <vlc_common.h>
33
#include <vlc_picture.h>
34
#include <vlc_plugin.h>
35

36 37 38
#define DXVA2API_USE_BITFIELDS
#define COBJMACROS
#include <libavcodec/dxva2.h>
39
#include "../../video_chroma/d3d9_fmt.h"
40

41 42 43 44 45 46
#define D3D_Device          IDirect3DDevice9
#define D3D_DecoderType     IDirectXVideoDecoder
#define D3D_DecoderDevice   IDirectXVideoDecoderService
#define D3D_DecoderSurface  IDirect3DSurface9
#include "directx_va.h"

47
static int Open(vlc_va_t *, AVCodecContext *, enum PixelFormat,
48
                const es_format_t *, picture_sys_t *p_sys);
49
static void Close(vlc_va_t *, AVCodecContext *);
50 51 52

vlc_module_begin()
    set_description(N_("DirectX Video Acceleration (DXVA) 2.0"))
53
    set_capability("hw decoder", 0)
54 55 56 57 58
    set_category(CAT_INPUT)
    set_subcategory(SUBCAT_INPUT_VCODEC)
    set_callbacks(Open, Close)
vlc_module_end()

59 60 61 62 63 64
#include <initguid.h> /* must be last included to not redefine existing GUIDs */

/* dxva2api.h GUIDs: http://msdn.microsoft.com/en-us/library/windows/desktop/ms697067(v=vs100).aspx
 * assume that they are declared in dxva2api.h */
#define MS_GUID(name, l, w1, w2, b1, b2, b3, b4, b5, b6, b7, b8)

65 66
#ifdef __MINGW32__
# include <_mingw.h>
67

68
# if !defined(__MINGW64_VERSION_MAJOR)
69 70 71
#  undef MS_GUID
#  define MS_GUID DEFINE_GUID /* dxva2api.h fails to declare those, redefine as static */
#  define DXVA2_E_NEW_VIDEO_DEVICE MAKE_HRESULT(1, 4, 4097)
Rafaël Carré's avatar
Rafaël Carré committed
72 73
# else
#  include <dxva.h>
74
# endif
Jean-Baptiste Kempf's avatar
Jean-Baptiste Kempf committed
75

76 77 78 79 80
#endif /* __MINGW32__ */

MS_GUID(IID_IDirectXVideoDecoderService, 0xfc51a551, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02);
MS_GUID(IID_IDirectXVideoAccelerationService, 0xfc51a550, 0xd5e7, 0x11d9, 0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02);

81
DEFINE_GUID(DXVA2_NoEncrypt,                        0x1b81bed0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
82

83
DEFINE_GUID(DXVA_Intel_H264_NoFGT_ClearVideo,       0x604F8E68, 0x4951, 0x4c54, 0x88, 0xFE, 0xAB, 0xD2, 0x5C, 0x15, 0xB3, 0xD6);
84 85 86 87 88 89 90 91 92 93 94 95


/* */
typedef struct {
    const char   *name;
    D3DFORMAT    format;
    vlc_fourcc_t codec;
} d3d_format_t;
/* XXX Prefered format must come first */
static const d3d_format_t d3d_formats[] = {
    { "YV12",   MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_YV12 },
    { "NV12",   MAKEFOURCC('N','V','1','2'),    VLC_CODEC_NV12 },
Rafaël Carré's avatar
Rafaël Carré committed
96
    { "IMC3",   MAKEFOURCC('I','M','C','3'),    VLC_CODEC_YV12 },
97
    { "P010",   MAKEFOURCC('P','0','1','0'),    VLC_CODEC_P010 },
98 99

    { NULL, 0, 0 }
100 101
};

102 103 104 105 106 107 108 109
static const d3d_format_t *D3dFindFormat(D3DFORMAT format)
{
    for (unsigned i = 0; d3d_formats[i].name; i++) {
        if (d3d_formats[i].format == format)
            return &d3d_formats[i];
    }
    return NULL;
}
110

111
struct vlc_va_sys_t
112
{
113
    directx_sys_t         dx_sys;
114
    vlc_fourcc_t          i_chroma;
115

116
    /* DLL */
117
    HINSTANCE             hd3d9_dll;
118 119

    /* Direct3D */
120 121
    LPDIRECT3D9            d3dobj;
    D3DADAPTER_IDENTIFIER9 d3dai;
122 123

    /* Device manager */
124
    IDirect3DDeviceManager9  *devmng;
125 126 127 128 129 130 131 132
    HANDLE                   device;

    /* Video service */
    D3DFORMAT                    render;

    /* Video decoder */
    DXVA2_ConfigPictureDecode    cfg;

133
    /* avcodec internals */
134
    struct dxva_context hw;
135
};
136

137 138 139
static picture_t *DxAllocPicture(vlc_va_t *, const video_format_t *, unsigned index);


140
/* */
141
static int D3dCreateDevice(vlc_va_t *);
142
static void D3dDestroyDevice(vlc_va_t *);
143
static char *DxDescribe(vlc_va_sys_t *);
144

145
static int D3dCreateDeviceManager(vlc_va_t *);
146
static void D3dDestroyDeviceManager(vlc_va_t *);
147

148
static int DxCreateVideoService(vlc_va_t *);
149 150
static void DxDestroyVideoService(vlc_va_t *);
static int DxGetInputList(vlc_va_t *, input_list_t *);
151
static int DxSetupOutput(vlc_va_t *, const GUID *, const video_format_t *);
152

153
static int DxCreateVideoDecoder(vlc_va_t *,
154
                                int codec_id, const video_format_t *);
155
static void DxDestroyVideoDecoder(vlc_va_t *);
156
static int DxResetVideoDecoder(vlc_va_t *);
157
static void SetupAVCodecContext(vlc_va_t *);
158 159

/* */
160
static void Setup(vlc_va_t *va, vlc_fourcc_t *chroma)
161
{
162
    *chroma = va->sys->i_chroma;
163 164
}

165
void SetupAVCodecContext(vlc_va_t *va)
166
{
167
    vlc_va_sys_t *sys = va->sys;
168
    directx_sys_t *dx_sys = &sys->dx_sys;
169

170
    sys->hw.decoder = dx_sys->decoder;
171 172
    sys->hw.cfg = &sys->cfg;
    sys->hw.surface_count = dx_sys->surface_count;
173
    sys->hw.surface = dx_sys->hw_surface;
174

175 176 177
    if (IsEqualGUID(&dx_sys->input, &DXVA_Intel_H264_NoFGT_ClearVideo))
        sys->hw.workaround |= FF_DXVA2_WORKAROUND_INTEL_CLEARVIDEO;
}
178

179 180
static int Extract(vlc_va_t *va, picture_t *picture, uint8_t *data)
{
181 182 183
    VLC_UNUSED(va); VLC_UNUSED(data);
    struct va_pic_context *pic_ctx = (struct va_pic_context*)picture->context;
    directx_va_AddRef(pic_ctx->va_surface);
184 185
    return VLC_SUCCESS;
}
186

187
static int CheckDevice(vlc_va_t *va)
188
{
189
    vlc_va_sys_t *sys = va->sys;
190 191

    /* Check the device */
192
    HRESULT hr = IDirect3DDeviceManager9_TestDevice(sys->devmng, sys->device);
193 194 195 196
    if (hr == DXVA2_E_NEW_VIDEO_DEVICE) {
        if (DxResetVideoDecoder(va))
            return VLC_EGENERIC;
    } else if (FAILED(hr)) {
197
        msg_Err(va, "IDirect3DDeviceManager9_TestDevice %u", (unsigned)hr);
198 199
        return VLC_EGENERIC;
    }
200 201
    return VLC_SUCCESS;
}
202

203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235
static void d3d9_pic_context_destroy(struct picture_context_t *opaque)
{
    struct va_pic_context *pic_ctx = (struct va_pic_context*)opaque;
    if (pic_ctx->va_surface)
    {
        ReleasePictureSys(&pic_ctx->picsys);
        directx_va_Release(pic_ctx->va_surface);
        free(pic_ctx);
    }
}

static struct picture_context_t *CreatePicContext(vlc_va_surface_t *);

static struct picture_context_t *d3d9_pic_context_copy(struct picture_context_t *ctx)
{
    struct va_pic_context *src_ctx = (struct va_pic_context*)ctx;
    return CreatePicContext(src_ctx->va_surface);
}

static struct picture_context_t *CreatePicContext(vlc_va_surface_t *va_surface)
{
    struct va_pic_context *pic_ctx = calloc(1, sizeof(*pic_ctx));
    if (unlikely(pic_ctx==NULL))
        return NULL;
    pic_ctx->va_surface = va_surface;
    directx_va_AddRef(pic_ctx->va_surface);
    pic_ctx->s.destroy = d3d9_pic_context_destroy;
    pic_ctx->s.copy    = d3d9_pic_context_copy;
    pic_ctx->picsys.surface = va_surface->decoderSurface;
    IDirect3DSurface9_AddRef(pic_ctx->picsys.surface);
    return &pic_ctx->s;
}

236
static int Get(vlc_va_t *va, picture_t *pic, uint8_t **data)
237
{
238 239 240
    vlc_va_surface_t *va_surface = directx_va_Get(va, &va->sys->dx_sys);
    if (unlikely(va_surface==NULL))
        return VLC_EGENERIC;
241 242 243 244
    pic->context = CreatePicContext(va_surface);
    directx_va_Release(va_surface);
    if (unlikely(pic->context==NULL))
        return VLC_EGENERIC;
245 246
    *data = (uint8_t*)va_surface->decoderSurface;
    return VLC_SUCCESS;
247
}
248

249
static void Close(vlc_va_t *va, AVCodecContext *ctx)
250
{
251
    vlc_va_sys_t *sys = va->sys;
252

253
    (void) ctx;
254 255 256

    directx_va_Close(va, &sys->dx_sys);

257 258
    if (sys->hd3d9_dll)
        FreeLibrary(sys->hd3d9_dll);
259

260
    free((char *)va->description);
261
    free(sys);
262 263
}

Steve Lhomme's avatar
Steve Lhomme committed
264
static vlc_fourcc_t d3d9va_fourcc(enum PixelFormat swfmt)
265 266 267 268 269 270 271 272 273 274 275 276 277
{
    switch (swfmt)
    {
        case AV_PIX_FMT_YUV420P10LE:
            return VLC_CODEC_D3D9_OPAQUE_10B;
        case AV_PIX_FMT_YUVJ420P:
        case AV_PIX_FMT_YUV420P:
            return VLC_CODEC_D3D9_OPAQUE;
        default:
            return VLC_CODEC_D3D9_OPAQUE;
    }
}

278 279 280 281
static void ReleasePic(void *opaque, uint8_t *data)
{
    (void)data;
    picture_t *pic = opaque;
282 283
    struct va_pic_context *pic_ctx = (struct va_pic_context*)pic->context;
    directx_va_Release(pic_ctx->va_surface);
284 285 286
    picture_Release(pic);
}

287
static int Open(vlc_va_t *va, AVCodecContext *ctx, enum PixelFormat pix_fmt,
288
                const es_format_t *fmt, picture_sys_t *p_sys)
289
{
290 291 292
    int err = VLC_EGENERIC;
    directx_sys_t *dx_sys;

293 294 295
    if (pix_fmt != AV_PIX_FMT_DXVA2_VLD)
        return VLC_EGENERIC;

296 297 298
    vlc_va_sys_t *sys = calloc(1, sizeof (*sys));
    if (unlikely(sys == NULL))
        return VLC_ENOMEM;
299 300

    /* Load dll*/
301 302 303
    sys->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
    if (!sys->hd3d9_dll) {
        msg_Warn(va, "cannot load d3d9.dll");
304 305
        goto error;
    }
306

307
    dx_sys = &sys->dx_sys;
308

309 310 311 312 313 314 315 316 317 318 319 320
    dx_sys->pf_check_device            = CheckDevice;
    dx_sys->pf_create_device           = D3dCreateDevice;
    dx_sys->pf_destroy_device          = D3dDestroyDevice;
    dx_sys->pf_create_device_manager   = D3dCreateDeviceManager;
    dx_sys->pf_destroy_device_manager  = D3dDestroyDeviceManager;
    dx_sys->pf_create_video_service    = DxCreateVideoService;
    dx_sys->pf_destroy_video_service   = DxDestroyVideoService;
    dx_sys->pf_create_decoder_surfaces = DxCreateVideoDecoder;
    dx_sys->pf_destroy_surfaces        = DxDestroyVideoDecoder;
    dx_sys->pf_setup_avcodec_ctx       = SetupAVCodecContext;
    dx_sys->pf_get_input_list          = DxGetInputList;
    dx_sys->pf_setup_output            = DxSetupOutput;
321
    dx_sys->pf_alloc_surface_pic       = DxAllocPicture;
322
    dx_sys->psz_decoder_dll            = TEXT("DXVA2.DLL");
323

324
    va->sys = sys;
325

326 327
    dx_sys->d3ddev = NULL;
    if (p_sys!=NULL)
328 329 330 331
    {
        D3DSURFACE_DESC src;
        if (SUCCEEDED(IDirect3DSurface9_GetDesc(p_sys->surface, &src)))
            sys->render = src.Format;
332
        IDirect3DSurface9_GetDevice(p_sys->surface, &dx_sys->d3ddev );
333
    }
334

335 336
    sys->i_chroma = d3d9va_fourcc(ctx->sw_pix_fmt);

337
    err = directx_va_Open(va, &sys->dx_sys, ctx, fmt, true);
338
    if (err!=VLC_SUCCESS)
339
        goto error;
340

341 342 343 344 345 346
    err = directx_va_Setup(va, &sys->dx_sys, ctx);
    if (err != VLC_SUCCESS)
        goto error;

    ctx->hwaccel_context = &sys->hw;

347
    /* TODO print the hardware name/vendor for debugging purposes */
348 349 350
    va->description = DxDescribe(sys);
    va->setup   = Setup;
    va->get     = Get;
351
    va->release = ReleasePic;
352
    va->extract = Extract;
353
    return VLC_SUCCESS;
354 355

error:
356
    Close(va, ctx);
357
    return VLC_EGENERIC;
358 359
}
/* */
360

361 362 363
/**
 * It creates a Direct3D device usable for DXVA 2
 */
364
static int D3dCreateDevice(vlc_va_t *va)
365
{
366 367
    vlc_va_sys_t *sys = va->sys;

368 369 370 371 372 373
    if (sys->dx_sys.d3ddev) {
        msg_Dbg(va, "Reusing Direct3D9 device");
        IDirect3DDevice9_AddRef(sys->dx_sys.d3ddev);
        return VLC_SUCCESS;
    }

374 375
    /* */
    LPDIRECT3D9 (WINAPI *Create9)(UINT SDKVersion);
376
    Create9 = (void *)GetProcAddress(sys->hd3d9_dll, "Direct3DCreate9");
377
    if (!Create9) {
378
        msg_Err(va, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
379
        return VLC_EGENERIC;
380 381
    }

382 383 384 385
    /* */
    LPDIRECT3D9 d3dobj;
    d3dobj = Create9(D3D_SDK_VERSION);
    if (!d3dobj) {
386
        msg_Err(va, "Direct3DCreate9 failed");
387
        return VLC_EGENERIC;
388
    }
389
    sys->d3dobj = d3dobj;
390

391
    /* */
392 393
    D3DADAPTER_IDENTIFIER9 *d3dai = &sys->d3dai;
    if (FAILED(IDirect3D9_GetAdapterIdentifier(sys->d3dobj,
394
                                               D3DADAPTER_DEFAULT, 0, d3dai))) {
395
        msg_Warn(va, "IDirect3D9_GetAdapterIdentifier failed");
396 397 398
        ZeroMemory(d3dai, sizeof(*d3dai));
    }

399
    /* */
400 401 402 403 404 405 406 407 408 409 410 411 412
    D3DPRESENT_PARAMETERS d3dpp;
    ZeroMemory(&d3dpp, sizeof(d3dpp));
    d3dpp.Flags                  = D3DPRESENTFLAG_VIDEO;
    d3dpp.Windowed               = TRUE;
    d3dpp.hDeviceWindow          = NULL;
    d3dpp.SwapEffect             = D3DSWAPEFFECT_DISCARD;
    d3dpp.MultiSampleType        = D3DMULTISAMPLE_NONE;
    d3dpp.PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
    d3dpp.BackBufferCount        = 0;                  /* FIXME what to put here */
    d3dpp.BackBufferFormat       = D3DFMT_X8R8G8B8;    /* FIXME what to put here */
    d3dpp.BackBufferWidth        = 0;
    d3dpp.BackBufferHeight       = 0;
    d3dpp.EnableAutoDepthStencil = FALSE;
413

414 415
    /* Direct3D needs a HWND to create a device, even without using ::Present
    this HWND is used to alert Direct3D when there's a change of focus window.
416
    For now, use GetDesktopWindow, as it looks harmless */
417 418
    LPDIRECT3DDEVICE9 d3ddev;
    if (FAILED(IDirect3D9_CreateDevice(d3dobj, D3DADAPTER_DEFAULT,
419
                                       D3DDEVTYPE_HAL, GetDesktopWindow(),
420 421
                                       D3DCREATE_SOFTWARE_VERTEXPROCESSING |
                                       D3DCREATE_MULTITHREADED,
422
                                       &d3dpp, &d3ddev))) {
423
        msg_Err(va, "IDirect3D9_CreateDevice failed");
424 425
        return VLC_EGENERIC;
    }
426
    sys->dx_sys.d3ddev = d3ddev;
427 428 429

    return VLC_SUCCESS;
}
430

431 432 433
/**
 * It releases a Direct3D device and its resources.
 */
434
static void D3dDestroyDevice(vlc_va_t *va)
435
{
436 437
    if (va->sys->d3dobj)
        IDirect3D9_Release(va->sys->d3dobj);
438 439 440 441
}
/**
 * It describes our Direct3D object
 */
442
static char *DxDescribe(vlc_va_sys_t *va)
443
{
444 445 446 447 448 449
    static const struct {
        unsigned id;
        char     name[32];
    } vendors [] = {
        { 0x1002, "ATI" },
        { 0x10DE, "NVIDIA" },
450
        { 0x1106, "VIA" },
451 452 453 454
        { 0x8086, "Intel" },
        { 0x5333, "S3 Graphics" },
        { 0, "" }
    };
455
    D3DADAPTER_IDENTIFIER9 *id = &va->d3dai;
456 457 458 459 460 461 462 463 464

    const char *vendor = "Unknown";
    for (int i = 0; vendors[i].id != 0; i++) {
        if (vendors[i].id == id->VendorId) {
            vendor = vendors[i].name;
            break;
        }
    }

465
    char *description;
Rafaël Carré's avatar
Rafaël Carré committed
466
    if (asprintf(&description, "DXVA2 (%.*s, vendor %lu(%s), device %lu, revision %lu)",
467
                 (int)sizeof(id->Description), id->Description,
468
                 id->VendorId, vendor, id->DeviceId, id->Revision) < 0)
469 470 471 472 473 474 475
        return NULL;
    return description;
}

/**
 * It creates a Direct3D device manager
 */
476
static int D3dCreateDeviceManager(vlc_va_t *va)
477
{
478
    vlc_va_sys_t *sys = va->sys;
479
    directx_sys_t *dx_sys = &va->sys->dx_sys;
480

481 482 483
    HRESULT (WINAPI *CreateDeviceManager9)(UINT *pResetToken,
                                           IDirect3DDeviceManager9 **);
    CreateDeviceManager9 =
484
      (void *)GetProcAddress(dx_sys->hdecoder_dll,
485
                             "DXVA2CreateDirect3DDeviceManager9");
486 487

    if (!CreateDeviceManager9) {
488
        msg_Err(va, "cannot load function");
489 490
        return VLC_EGENERIC;
    }
491
    msg_Dbg(va, "OurDirect3DCreateDeviceManager9 Success!");
492 493

    UINT token;
494
    IDirect3DDeviceManager9 *devmng;
495
    if (FAILED(CreateDeviceManager9(&token, &devmng))) {
496
        msg_Err(va, " OurDirect3DCreateDeviceManager9 failed");
497 498
        return VLC_EGENERIC;
    }
499 500
    sys->devmng = devmng;
    msg_Info(va, "obtained IDirect3DDeviceManager9");
501

502
    HRESULT hr = IDirect3DDeviceManager9_ResetDevice(devmng, dx_sys->d3ddev, token);
503
    if (FAILED(hr)) {
504
        msg_Err(va, "IDirect3DDeviceManager9_ResetDevice failed: %08x", (unsigned)hr);
505 506 507 508 509 510 511
        return VLC_EGENERIC;
    }
    return VLC_SUCCESS;
}
/**
 * It destroys a Direct3D device manager
 */
512
static void D3dDestroyDeviceManager(vlc_va_t *va)
513
{
514 515
    if (va->sys->devmng)
        IDirect3DDeviceManager9_Release(va->sys->devmng);
516 517 518 519 520
}

/**
 * It creates a DirectX video service
 */
521
static int DxCreateVideoService(vlc_va_t *va)
522
{
523
    vlc_va_sys_t *sys = va->sys;
524
    directx_sys_t *dx_sys = &va->sys->dx_sys;
525

526 527 528 529
    HRESULT (WINAPI *CreateVideoService)(IDirect3DDevice9 *,
                                         REFIID riid,
                                         void **ppService);
    CreateVideoService =
530
      (void *)GetProcAddress(dx_sys->hdecoder_dll, "DXVA2CreateVideoService");
531 532

    if (!CreateVideoService) {
533
        msg_Err(va, "cannot load function");
534 535
        return 4;
    }
536
    msg_Info(va, "DXVA2CreateVideoService Success!");
537 538 539 540

    HRESULT hr;

    HANDLE device;
541
    hr = IDirect3DDeviceManager9_OpenDeviceHandle(sys->devmng, &device);
542
    if (FAILED(hr)) {
543
        msg_Err(va, "OpenDeviceHandle failed");
544 545
        return VLC_EGENERIC;
    }
546
    sys->device = device;
547

548 549 550
    void *pv;
    hr = IDirect3DDeviceManager9_GetVideoService(sys->devmng, device,
                                        &IID_IDirectXVideoDecoderService, &pv);
551
    if (FAILED(hr)) {
552
        msg_Err(va, "GetVideoService failed");
553 554
        return VLC_EGENERIC;
    }
555
    dx_sys->d3ddec = pv;
556 557 558

    return VLC_SUCCESS;
}
559

560 561 562
/**
 * It destroys a DirectX video service
 */
563
static void DxDestroyVideoService(vlc_va_t *va)
564
{
565 566
    if (va->sys->device)
        IDirect3DDeviceManager9_CloseDeviceHandle(va->sys->devmng, va->sys->device);
567
}
568

569
static void ReleaseInputList(input_list_t *p_list)
570
{
571
    CoTaskMemFree(p_list->list);
572 573
}

574
static int DxGetInputList(vlc_va_t *va, input_list_t *p_list)
575
{
576
    directx_sys_t *dx_sys = &va->sys->dx_sys;
577 578
    UINT input_count = 0;
    GUID *input_list = NULL;
579
    if (FAILED(IDirectXVideoDecoderService_GetDecoderDeviceGuids(dx_sys->d3ddec,
580 581
                                                                 &input_count,
                                                                 &input_list))) {
582
        msg_Err(va, "IDirectXVideoDecoderService_GetDecoderDeviceGuids failed");
583 584
        return VLC_EGENERIC;
    }
585 586 587 588 589 590 591

    p_list->count = input_count;
    p_list->list = input_list;
    p_list->pf_release = ReleaseInputList;
    return VLC_SUCCESS;
}

592
static int DxSetupOutput(vlc_va_t *va, const GUID *input, const video_format_t *fmt)
593
{
594
    VLC_UNUSED(fmt);
595 596 597
    int err = VLC_EGENERIC;
    UINT      output_count = 0;
    D3DFORMAT *output_list = NULL;
598
    if (FAILED(IDirectXVideoDecoderService_GetDecoderRenderTargets(va->sys->dx_sys.d3ddec,
599 600 601 602 603 604 605 606 607 608 609 610
                                                                   input,
                                                                   &output_count,
                                                                   &output_list))) {
        msg_Err(va, "IDirectXVideoDecoderService_GetDecoderRenderTargets failed");
        return VLC_EGENERIC;
    }

    for (unsigned j = 0; j < output_count; j++) {
        const D3DFORMAT f = output_list[j];
        const d3d_format_t *format = D3dFindFormat(f);
        if (format) {
            msg_Dbg(va, "%s is supported for output", format->name);
611
        } else {
612
            msg_Dbg(va, "%d is supported for output (%4.4s)", f, (const char*)&f);
613
        }
614 615
    }

616
    /* */
617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636
    for (unsigned pass = 0; pass < 2 && err != VLC_SUCCESS; ++pass)
    {
        for (unsigned j = 0; d3d_formats[j].name; j++) {
            const d3d_format_t *format = &d3d_formats[j];

            /* */
            bool is_supported = false;
            for (unsigned k = 0; !is_supported && k < output_count; k++) {
                is_supported = format->format == output_list[k];
            }
            if (!is_supported)
                continue;
            if (pass == 0 && format->format != va->sys->render)
                continue;

            /* We have our solution */
            msg_Dbg(va, "Using decoder output '%s'", format->name);
            va->sys->render = format->format;
            err = VLC_SUCCESS;
            break;
637
        }
638
    }
639 640
    CoTaskMemFree(output_list);
    return err;
641 642
}

643 644 645
/**
 * It creates a DXVA2 decoder using the given video format
 */
646
static int DxCreateVideoDecoder(vlc_va_t *va, int codec_id, const video_format_t *fmt)
647
{
648 649
    vlc_va_sys_t *p_sys = va->sys;
    directx_sys_t *sys = &va->sys->dx_sys;
650
    HRESULT hr;
651

652
    hr = IDirectXVideoDecoderService_CreateSurface(sys->d3ddec,
653 654
                                                         sys->surface_width,
                                                         sys->surface_height,
655
                                                         sys->surface_count - 1,
656
                                                         p_sys->render,
657 658 659
                                                         D3DPOOL_DEFAULT,
                                                         0,
                                                         DXVA2_VideoDecoderRenderTarget,
660
                                                         sys->hw_surface,
661 662
                                                         NULL);
    if (FAILED(hr)) {
663
        msg_Err(va, "IDirectXVideoAccelerationService_CreateSurface %d failed (hr=0x%0lx)", sys->surface_count - 1, hr);
664
        sys->surface_count = 0;
665 666
        return VLC_EGENERIC;
    }
667
    msg_Dbg(va, "IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)",
668
            sys->surface_count, sys->surface_width, sys->surface_height);
669

670
    IDirect3DSurface9 *tstCrash;
671
    hr = IDirectXVideoDecoderService_CreateSurface(sys->d3ddec,
672 673 674 675 676 677 678 679 680 681 682
                                                         sys->surface_width,
                                                         sys->surface_height,
                                                         0,
                                                         p_sys->render,
                                                         D3DPOOL_DEFAULT,
                                                         0,
                                                         DXVA2_VideoDecoderRenderTarget,
                                                         &tstCrash,
                                                         NULL);
    if (FAILED(hr)) {
        msg_Err(va, "extra buffer impossible, avoid a crash (hr=0x%0lx)", hr);
683
        for (int i = 0; i < sys->surface_count; i++)
684
            IDirect3DSurface9_Release( sys->hw_surface[i] );
685
        sys->surface_count = 0;
686 687 688 689
        return VLC_EGENERIC;
    }
    IDirect3DSurface9_Release(tstCrash);

690 691 692 693 694
    /* */
    DXVA2_VideoDesc dsc;
    ZeroMemory(&dsc, sizeof(dsc));
    dsc.SampleWidth     = fmt->i_width;
    dsc.SampleHeight    = fmt->i_height;
695
    dsc.Format          = p_sys->render;
696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719
    if (fmt->i_frame_rate > 0 && fmt->i_frame_rate_base > 0) {
        dsc.InputSampleFreq.Numerator   = fmt->i_frame_rate;
        dsc.InputSampleFreq.Denominator = fmt->i_frame_rate_base;
    } else {
        dsc.InputSampleFreq.Numerator   = 0;
        dsc.InputSampleFreq.Denominator = 0;
    }
    dsc.OutputFrameFreq = dsc.InputSampleFreq;
    dsc.UABProtectionLevel = FALSE;
    dsc.Reserved = 0;

    /* FIXME I am unsure we can let unknown everywhere */
    DXVA2_ExtendedFormat *ext = &dsc.SampleFormat;
    ext->SampleFormat = 0;//DXVA2_SampleUnknown;
    ext->VideoChromaSubsampling = 0;//DXVA2_VideoChromaSubsampling_Unknown;
    ext->NominalRange = 0;//DXVA2_NominalRange_Unknown;
    ext->VideoTransferMatrix = 0;//DXVA2_VideoTransferMatrix_Unknown;
    ext->VideoLighting = 0;//DXVA2_VideoLighting_Unknown;
    ext->VideoPrimaries = 0;//DXVA2_VideoPrimaries_Unknown;
    ext->VideoTransferFunction = 0;//DXVA2_VideoTransFunc_Unknown;

    /* List all configurations available for the decoder */
    UINT                      cfg_count = 0;
    DXVA2_ConfigPictureDecode *cfg_list = NULL;
720
    if (FAILED(IDirectXVideoDecoderService_GetDecoderConfigurations(sys->d3ddec,
721
                                                                    &sys->input,
722 723 724 725
                                                                    &dsc,
                                                                    NULL,
                                                                    &cfg_count,
                                                                    &cfg_list))) {
726
        msg_Err(va, "IDirectXVideoDecoderService_GetDecoderConfigurations failed");
727
        for (int i = 0; i < sys->surface_count; i++)
728
            IDirect3DSurface9_Release( sys->hw_surface[i] );
729
        sys->surface_count = 0;
730 731
        return VLC_EGENERIC;
    }
732
    msg_Dbg(va, "we got %d decoder configurations", cfg_count);
733 734

    /* Select the best decoder configuration */
735
    int cfg_score = 0;
736 737 738 739
    for (unsigned i = 0; i < cfg_count; i++) {
        const DXVA2_ConfigPictureDecode *cfg = &cfg_list[i];

        /* */
740
        msg_Dbg(va, "configuration[%d] ConfigBitstreamRaw %d",
741 742 743
                i, cfg->ConfigBitstreamRaw);

        /* */
744 745 746
        int score;
        if (cfg->ConfigBitstreamRaw == 1)
            score = 1;
747
        else if (codec_id == AV_CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2)
748 749 750
            score = 2;
        else
            continue;
751
        if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA2_NoEncrypt))
752 753 754
            score += 16;

        if (cfg_score < score) {
755
            p_sys->cfg = *cfg;
756
            cfg_score = score;
757 758 759
        }
    }
    CoTaskMemFree(cfg_list);
760
    if (cfg_score <= 0) {
761
        msg_Err(va, "Failed to find a supported decoder configuration");
762 763
        return VLC_EGENERIC;
    }
764

765
    /* Create the decoder */
766
    IDirectXVideoDecoder *decoder;
767
    if (FAILED(IDirectXVideoDecoderService_CreateVideoDecoder(sys->d3ddec,
768
                                                              &sys->input,
769
                                                              &dsc,
770
                                                              &p_sys->cfg,
771
                                                              sys->hw_surface,
772
                                                              sys->surface_count,
773
                                                              &decoder))) {
774
        msg_Err(va, "IDirectXVideoDecoderService_CreateVideoDecoder failed");
775
        for (int i = 0; i < sys->surface_count; i++)
776
            IDirect3DSurface9_Release( sys->hw_surface[i] );
777
        sys->surface_count = 0;
778 779
        return VLC_EGENERIC;
    }
780
    sys->decoder = decoder;
781

782
    msg_Dbg(va, "IDirectXVideoDecoderService_CreateVideoDecoder succeed");
783 784
    return VLC_SUCCESS;
}
785

786
static void DxDestroyVideoDecoder(vlc_va_t *va)
787
{
788
    VLC_UNUSED(va);
789
}
790 791

static int DxResetVideoDecoder(vlc_va_t *va)
792
{
793
    msg_Err(va, "DxResetVideoDecoder unimplemented");
794
    return VLC_EGENERIC;
795 796
}

797 798 799
static picture_t *DxAllocPicture(vlc_va_t *va, const video_format_t *fmt, unsigned index)
{
    video_format_t src_fmt = *fmt;
800
    src_fmt.i_chroma = va->sys->i_chroma;
801 802 803
    picture_sys_t *pic_sys = calloc(1, sizeof(*pic_sys));
    if (unlikely(pic_sys == NULL))
        return NULL;
804
    pic_sys->surface = va->sys->dx_sys.hw_surface[index];
805 806