]> git.sesse.net Git - vlc/blob - modules/codec/avcodec/dxva2.c
DxVA2: add some more GUID
[vlc] / modules / codec / avcodec / dxva2.c
1 /*****************************************************************************
2  * va.c: Video Acceleration helpers
3  *****************************************************************************
4  * Copyright (C) 2009 Geoffroy Couprie
5  * Copyright (C) 2009 Laurent Aimar
6  * $Id$
7  *
8  * Authors: Geoffroy Couprie <geal@videolan.org>
9  *          Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
10  *
11  * This program is free software; you can redistribute it and/or modify
12  * it under the terms of the GNU General Public License as published by
13  * the Free Software Foundation; either version 2 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <vlc_common.h>
31 #include <vlc_picture.h>
32 #include <vlc_fourcc.h>
33 #include <vlc_cpu.h>
34 #include <assert.h>
35
36 #ifdef HAVE_LIBAVCODEC_AVCODEC_H
37 #   include <libavcodec/avcodec.h>
38 #   ifdef HAVE_AVCODEC_DXVA2
39 #       define DXVA2API_USE_BITFIELDS
40 #       include <libavcodec/dxva2.h>
41 #   endif
42 #else
43 #   include <avcodec.h>
44 #endif
45
46 #include "avcodec.h"
47 #include "va.h"
48 #include "copy.h"
49
50 #ifdef HAVE_AVCODEC_DXVA2
51
52 #include <windows.h>
53 #include <windowsx.h>
54 #include <ole2.h>
55 #include <commctrl.h>
56 #include <shlwapi.h>
57 #include <d3d9.h>
58
59 /* */
60 #define DXVA2_E_NOT_INITIALIZED     MAKE_HRESULT(1, 4, 4096)
61 #define DXVA2_E_NEW_VIDEO_DEVICE    MAKE_HRESULT(1, 4, 4097)
62 #define DXVA2_E_VIDEO_DEVICE_LOCKED MAKE_HRESULT(1, 4, 4098)
63 #define DXVA2_E_NOT_AVAILABLE       MAKE_HRESULT(1, 4, 4099)
64
65 static const GUID DXVA2_ModeMPEG2_MoComp = {
66     0xe6a9f44b, 0x61b0,0x4563, {0x9e,0xa4,0x63,0xd2,0xa3,0xc6,0xfe,0x66}
67 };
68 static const GUID DXVA2_ModeMPEG2_IDCT = {
69     0xbf22ad00, 0x03ea,0x4690, {0x80,0x77,0x47,0x33,0x46,0x20,0x9b,0x7e}
70 };
71 static const GUID DXVA2_ModeMPEG2_VLD = {
72     0xee27417f, 0x5e28,0x4e65, {0xbe,0xea,0x1d,0x26,0xb5,0x08,0xad,0xc9}
73 };
74 static const GUID DXVA2_ModeMPEG2and1_VLD = {
75     0x86695f12, 0x340e,0x4f04, {0x9f,0xd3,0x92,0x53,0xdd,0x32,0x74,0x60}
76 };
77
78 static const GUID DXVA2_ModeH264_A = {
79     0x1b81be64, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
80 };
81 static const GUID DXVA2_ModeH264_B = {
82     0x1b81be65, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
83 };
84 static const GUID DXVA2_ModeH264_C = {
85     0x1b81be66, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
86 };
87 static const GUID DXVA2_ModeH264_D = {
88     0x1b81be67, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
89 };
90 static const GUID DXVA2_ModeH264_E = {
91     0x1b81be68, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
92 };
93 static const GUID DXVA2_ModeH264_F = {
94     0x1b81be69, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
95 };
96 static const GUID DXVADDI_Intel_ModeH264_A = {
97     0x604F8E64, 0x4951,0x4c54, {0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6}
98 };
99 static const GUID DXVADDI_Intel_ModeH264_C = {
100     0x604F8E66, 0x4951,0x4c54, {0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6}
101 };
102 static const GUID DXVADDI_Intel_ModeH264_E = { // DXVA_Intel_H264_ClearVideo
103     0x604F8E68, 0x4951,0x4c54, {0x88,0xFE,0xAB,0xD2,0x5C,0x15,0xB3,0xD6}
104 };
105 static const GUID DXVA2_ModeWMV8_A = {
106     0x1b81be80, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
107 };
108 static const GUID DXVA2_ModeWMV8_B = {
109     0x1b81be81, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
110 };
111 static const GUID DXVA2_ModeWMV9_A = {
112     0x1b81be90, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
113 };
114 static const GUID DXVA2_ModeWMV9_B = {
115     0x1b81be91, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
116 };
117 static const GUID DXVA2_ModeWMV9_C = {
118     0x1b81be94, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
119 };
120
121 static const GUID DXVA2_ModeVC1_A = {
122     0x1b81beA0, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
123 };
124 static const GUID DXVA2_ModeVC1_B = {
125     0x1b81beA1, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
126 };
127 static const GUID DXVA2_ModeVC1_C = {
128     0x1b81beA2, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
129 };
130 static const GUID DXVA2_ModeVC1_D = {
131     0x1b81beA3, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
132 };
133
134 static const GUID DXVA_NoEncrypt = {
135     0x1b81bed0, 0xa0c7,0x11d3, {0xb9,0x84,0x00,0xc0,0x4f,0x2e,0x73,0xc5}
136 };
137
138 static const GUID DXVA_Intel_VC1_ClearVideo = {
139     0xBCC5DB6D, 0xA2B6,0x4AF0, {0xAC,0xE4,0xAD,0xB1,0xF7,0x87,0xBC,0x89}
140 };
141
142 static const GUID DXVA_nVidia_MPEG4_ASP = {
143     0x9947EC6F, 0x689B,0x11DC, {0xA3,0x20,0x00,0x19,0xDB,0xBC,0x41,0x84}
144 };
145
146 /* */
147 typedef struct {
148     const char   *name;
149     const GUID   *guid;
150     int          codec;
151 } dxva2_mode_t;
152 /* XXX Prefered modes must come first */
153 static const dxva2_mode_t dxva2_modes[] = {
154     { "MPEG-2 variable-length decoder",            &DXVA2_ModeMPEG2_VLD,     CODEC_ID_MPEG2VIDEO },
155     { "MPEG-2 & MPEG-1 variable-length decoder",   &DXVA2_ModeMPEG2and1_VLD, CODEC_ID_MPEG2VIDEO },
156     { "MPEG-2 motion compensation",                &DXVA2_ModeMPEG2_MoComp,  0 },
157     { "MPEG-2 inverse discrete cosine transform",  &DXVA2_ModeMPEG2_IDCT,    0 },
158
159     { "H.264 variable-length decoder, film grain technology",                      &DXVA2_ModeH264_F,         CODEC_ID_H264 },
160     { "H.264 variable-length decoder, no film grain technology",                   &DXVA2_ModeH264_E,         CODEC_ID_H264 },
161     { "H.264 variable-length decoder, no film grain technology (Intel ClearVideo)",&DXVADDI_Intel_ModeH264_E, CODEC_ID_H264 },
162     { "H.264 inverse discrete cosine transform, film grain technology",            &DXVA2_ModeH264_D,         0             },
163     { "H.264 inverse discrete cosine transform, no film grain technology",         &DXVA2_ModeH264_C,         0             },
164     { "H.264 inverse discrete cosine transform, no film grain technology (Intel)", &DXVADDI_Intel_ModeH264_C, 0             },
165     { "H.264 motion compensation, film grain technology",                          &DXVA2_ModeH264_B,         0             },
166     { "H.264 motion compensation, no film grain technology",                       &DXVA2_ModeH264_A,         0             },
167     { "H.264 motion compensation, no film grain technology (Intel)",               &DXVADDI_Intel_ModeH264_A, 0             },
168
169     { "Windows Media Video 8 motion compensation", &DXVA2_ModeWMV8_B, 0 },
170     { "Windows Media Video 8 post processing",     &DXVA2_ModeWMV8_A, 0 },
171
172     { "Windows Media Video 9 IDCT",                &DXVA2_ModeWMV9_C, 0 },
173     { "Windows Media Video 9 motion compensation", &DXVA2_ModeWMV9_B, 0 },
174     { "Windows Media Video 9 post processing",     &DXVA2_ModeWMV9_A, 0 },
175
176     { "VC-1 variable-length decoder",              &DXVA2_ModeVC1_D, CODEC_ID_VC1 },
177     { "VC-1 variable-length decoder",              &DXVA2_ModeVC1_D, CODEC_ID_WMV3 },
178     { "VC-1 inverse discrete cosine transform",    &DXVA2_ModeVC1_C, 0 },
179     { "VC-1 motion compensation",                  &DXVA2_ModeVC1_B, 0 },
180     { "VC-1 post processing",                      &DXVA2_ModeVC1_A, 0 },
181
182     { "VC-1 variable-length decoder (Intel)",      &DXVA_Intel_VC1_ClearVideo, 0 },
183
184     { "MPEG-4 ASP nVidia bitstream decoder",       &DXVA_nVidia_MPEG4_ASP, 0 },
185
186     { NULL, NULL, 0 }
187 };
188
189 static const dxva2_mode_t *Dxva2FindMode(const GUID *guid)
190 {
191     for (unsigned i = 0; dxva2_modes[i].name; i++) {
192         if (IsEqualGUID(dxva2_modes[i].guid, guid))
193             return &dxva2_modes[i];
194     }
195     return NULL;
196 }
197
198 /* */
199 typedef struct {
200     const char   *name;
201     D3DFORMAT    format;
202     vlc_fourcc_t codec;
203 } d3d_format_t;
204 /* XXX Prefered format must come first */
205 static const d3d_format_t d3d_formats[] = {
206     { "YV12",   MAKEFOURCC('Y','V','1','2'),    VLC_CODEC_YV12 },
207     { "NV12",   MAKEFOURCC('N','V','1','2'),    VLC_CODEC_NV12 },
208
209     { NULL, 0, 0 }
210 };
211
212 static const d3d_format_t *D3dFindFormat(D3DFORMAT format)
213 {
214     for (unsigned i = 0; d3d_formats[i].name; i++) {
215         if (d3d_formats[i].format == format)
216             return &d3d_formats[i];
217     }
218     return NULL;
219 }
220
221 static const GUID IID_IDirectXVideoDecoderService = {
222     0xfc51a551, 0xd5e7, 0x11d9, {0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02}
223 };
224 static const GUID IID_IDirectXVideoAccelerationService = {
225     0xfc51a550, 0xd5e7, 0x11d9, {0xaf,0x55,0x00,0x05,0x4e,0x43,0xff,0x02}
226 };
227
228 /* */
229 typedef struct {
230     LPDIRECT3DSURFACE9 d3d;
231     int                refcount;
232     unsigned int       order;
233 } vlc_va_surface_t;
234
235 #define VA_DXVA2_MAX_SURFACE_COUNT (64)
236 typedef struct
237 {
238     /* */
239     vlc_va_t va;
240
241     /* */
242     vlc_object_t *log;
243     int          codec_id;
244     int          width;
245     int          height;
246
247     /* DLL */
248     HINSTANCE             hd3d9_dll;
249     HINSTANCE             hdxva2_dll;
250
251     /* Direct3D */
252     D3DPRESENT_PARAMETERS  d3dpp;
253     LPDIRECT3D9            d3dobj;
254     D3DADAPTER_IDENTIFIER9 d3dai;
255     LPDIRECT3DDEVICE9      d3ddev;
256
257     /* Device manager */
258     UINT                     token;
259     IDirect3DDeviceManager9  *devmng;
260     HANDLE                   device;
261
262     /* Video service */
263     IDirectXVideoDecoderService  *vs;
264     GUID                         input;
265     D3DFORMAT                    render;
266
267     /* Video decoder */
268     DXVA2_ConfigPictureDecode    cfg;
269     IDirectXVideoDecoder         *decoder;
270
271     /* Option conversion */
272     D3DFORMAT                    output;
273     copy_cache_t                 surface_cache;
274
275     /* */
276     struct dxva_context hw;
277
278     /* */
279     unsigned     surface_count;
280     unsigned     surface_order;
281     int          surface_width;
282     int          surface_height;
283     vlc_fourcc_t surface_chroma;
284
285     vlc_va_surface_t surface[VA_DXVA2_MAX_SURFACE_COUNT];
286     LPDIRECT3DSURFACE9 hw_surface[VA_DXVA2_MAX_SURFACE_COUNT];
287 } vlc_va_dxva2_t;
288
289 /* */
290 static vlc_va_dxva2_t *vlc_va_dxva2_Get(void *external)
291 {
292     assert(external == (void*)(&((vlc_va_dxva2_t*)external)->va));
293     return external;
294 }
295
296 /* */
297 static int D3dCreateDevice(vlc_va_dxva2_t *);
298 static void D3dDestroyDevice(vlc_va_dxva2_t *);
299 static char *DxDescribe(vlc_va_dxva2_t *);
300
301 static int D3dCreateDeviceManager(vlc_va_dxva2_t *);
302 static void D3dDestroyDeviceManager(vlc_va_dxva2_t *);
303
304 static int DxCreateVideoService(vlc_va_dxva2_t *);
305 static void DxDestroyVideoService(vlc_va_dxva2_t *);
306 static int DxFindVideoServiceConversion(vlc_va_dxva2_t *, GUID *input, D3DFORMAT *output);
307
308 static int DxCreateVideoDecoder(vlc_va_dxva2_t *,
309                                 int codec_id, const video_format_t *);
310 static void DxDestroyVideoDecoder(vlc_va_dxva2_t *);
311 static int DxResetVideoDecoder(vlc_va_dxva2_t *);
312
313 static void DxCreateVideoConversion(vlc_va_dxva2_t *);
314 static void DxDestroyVideoConversion(vlc_va_dxva2_t *);
315
316 /* */
317 static int Setup(vlc_va_t *external, void **hw, vlc_fourcc_t *chroma,
318                  int width, int height)
319 {
320     vlc_va_dxva2_t *va = vlc_va_dxva2_Get(external);
321
322     if (va->width == width && va->height == height && va->decoder)
323         goto ok;
324
325     /* */
326     DxDestroyVideoConversion(va);
327     DxDestroyVideoDecoder(va);
328
329     *hw = NULL;
330     *chroma = 0;
331     if (width <= 0 || height <= 0)
332         return VLC_EGENERIC;
333
334     /* FIXME transmit a video_format_t by VaSetup directly */
335     video_format_t fmt;
336     memset(&fmt, 0, sizeof(fmt));
337     fmt.i_width = width;
338     fmt.i_height = height;
339
340     if (DxCreateVideoDecoder(va, va->codec_id, &fmt))
341         return VLC_EGENERIC;
342     /* */
343     va->hw.decoder = va->decoder;
344     va->hw.cfg = &va->cfg;
345     va->hw.surface_count = va->surface_count;
346     va->hw.surface = va->hw_surface;
347     for (unsigned i = 0; i < va->surface_count; i++)
348         va->hw.surface[i] = va->surface[i].d3d;
349
350     /* */
351     DxCreateVideoConversion(va);
352
353     /* */
354 ok:
355     *hw = &va->hw;
356     const d3d_format_t *output = D3dFindFormat(va->output);
357     *chroma = output->codec;
358
359     return VLC_SUCCESS;
360 }
361
362 static int Extract(vlc_va_t *external, picture_t *picture, AVFrame *ff)
363 {
364     vlc_va_dxva2_t *va = vlc_va_dxva2_Get(external);
365     LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)ff->data[3];
366
367     if (!va->surface_cache.buffer)
368         return VLC_EGENERIC;
369
370     /* */
371     assert(va->output == MAKEFOURCC('Y','V','1','2'));
372
373     /* */
374     D3DLOCKED_RECT lock;
375     if (FAILED(IDirect3DSurface9_LockRect(d3d, &lock, NULL, D3DLOCK_READONLY))) {
376         msg_Err(va->log, "Failed to lock surface");
377         return VLC_EGENERIC;
378     }
379
380     if (va->render == MAKEFOURCC('Y','V','1','2')) {
381         uint8_t *plane[3] = {
382             lock.pBits,
383             (uint8_t*)lock.pBits + lock.Pitch * va->surface_height,
384             (uint8_t*)lock.pBits + lock.Pitch * va->surface_height
385                                  + (lock.Pitch/2) * (va->surface_height/2)
386         };
387         size_t  pitch[3] = {
388             lock.Pitch,
389             lock.Pitch / 2,
390             lock.Pitch / 2,
391         };
392         CopyFromYv12(picture, plane, pitch,
393                      va->width, va->height,
394                      &va->surface_cache);
395     } else {
396         assert(va->render == MAKEFOURCC('N','V','1','2'));
397         uint8_t *plane[2] = {
398             lock.pBits,
399             (uint8_t*)lock.pBits + lock.Pitch * va->surface_height
400         };
401         size_t  pitch[2] = {
402             lock.Pitch,
403             lock.Pitch,
404         };
405         CopyFromNv12(picture, plane, pitch,
406                      va->width, va->height,
407                      &va->surface_cache);
408     }
409
410     /* */
411     IDirect3DSurface9_UnlockRect(d3d);
412     return VLC_SUCCESS;
413 }
414 /* FIXME it is nearly common with VAAPI */
415 static int Get(vlc_va_t *external, AVFrame *ff)
416 {
417     vlc_va_dxva2_t *va = vlc_va_dxva2_Get(external);
418
419     /* Check the device */
420     HRESULT hr = IDirect3DDeviceManager9_TestDevice(va->devmng, va->device);
421     if (hr == DXVA2_E_NEW_VIDEO_DEVICE) {
422         if (DxResetVideoDecoder(va))
423             return VLC_EGENERIC;
424     } else if (FAILED(hr)) {
425         msg_Err(va->log, "IDirect3DDeviceManager9_TestDevice %u", (unsigned)hr);
426         return VLC_EGENERIC;
427     }
428
429     /* Grab an unused surface, in case none are, try the oldest
430      * XXX using the oldest is a workaround in case a problem happens with ffmpeg */
431     unsigned i, old;
432     for (i = 0, old = 0; i < va->surface_count; i++) {
433         vlc_va_surface_t *surface = &va->surface[i];
434
435         if (!surface->refcount)
436             break;
437
438         if (surface->order < va->surface[old].order)
439             old = i;
440     }
441     if (i >= va->surface_count)
442         i = old;
443
444     vlc_va_surface_t *surface = &va->surface[i];
445
446     surface->refcount = 1;
447     surface->order = va->surface_order++;
448
449     /* */
450     for (int i = 0; i < 4; i++) {
451         ff->data[i] = NULL;
452         ff->linesize[i] = 0;
453
454         if (i == 0 || i == 3)
455             ff->data[i] = (void*)surface->d3d;/* Yummie */
456     }
457     return VLC_SUCCESS;
458 }
459 static void Release(vlc_va_t *external, AVFrame *ff)
460 {
461     vlc_va_dxva2_t *va = vlc_va_dxva2_Get(external);
462     LPDIRECT3DSURFACE9 d3d = (LPDIRECT3DSURFACE9)(uintptr_t)ff->data[3];
463
464     for (unsigned i = 0; i < va->surface_count; i++) {
465         vlc_va_surface_t *surface = &va->surface[i];
466
467         if (surface->d3d == d3d)
468             surface->refcount--;
469     }
470 }
471 static void Close(vlc_va_t *external)
472 {
473     vlc_va_dxva2_t *va = vlc_va_dxva2_Get(external);
474
475     DxDestroyVideoConversion(va);
476     DxDestroyVideoDecoder(va);
477     DxDestroyVideoService(va);
478     D3dDestroyDeviceManager(va);
479     D3dDestroyDevice(va);
480
481     if (va->hdxva2_dll)
482         FreeLibrary(va->hdxva2_dll);
483     if (va->hd3d9_dll)
484         FreeLibrary(va->hd3d9_dll);
485
486     free(va->va.description);
487     free(va);
488 }
489
490 vlc_va_t *vlc_va_NewDxva2(vlc_object_t *log, int codec_id)
491 {
492     vlc_va_dxva2_t *va = calloc(1, sizeof(*va));
493     if (!va)
494         return NULL;
495
496     /* */
497     va->log = log;
498     va->codec_id = codec_id;
499
500     /* Load dll*/
501     va->hd3d9_dll = LoadLibrary(TEXT("D3D9.DLL"));
502     if (!va->hd3d9_dll) {
503         msg_Warn(va->log, "cannot load d3d9.dll");
504         goto error;
505     }
506     va->hdxva2_dll = LoadLibrary(TEXT("DXVA2.DLL"));
507     if (!va->hdxva2_dll) {
508         msg_Warn(va->log, "cannot load dxva2.dll");
509         goto error;
510     }
511     msg_Dbg(va->log, "DLLs loaded");
512
513     /* */
514     if (D3dCreateDevice(va)) {
515         msg_Err(va->log, "Failed to create Direct3D device");
516         goto error;
517     }
518     msg_Dbg(va->log, "D3dCreateDevice succeed");
519
520     if (D3dCreateDeviceManager(va)) {
521         msg_Err(va->log, "D3dCreateDeviceManager failed");
522         goto error;
523     }
524
525     if (DxCreateVideoService(va)) {
526         msg_Err(va->log, "DxCreateVideoService failed");
527         goto error;
528     }
529
530     /* */
531     if (DxFindVideoServiceConversion(va, &va->input, &va->render)) {
532         msg_Err(va->log, "DxFindVideoServiceConversion failed");
533         goto error;
534     }
535
536     /* TODO print the hardware name/vendor for debugging purposes */
537     va->va.description = DxDescribe(va);
538     va->va.setup   = Setup;
539     va->va.get     = Get;
540     va->va.release = Release;
541     va->va.extract = Extract;
542     va->va.close   = Close;
543     return &va->va;
544
545 error:
546     Close(&va->va);
547     return NULL;
548 }
549 /* */
550
551 /**
552  * It creates a Direct3D device usable for DXVA 2
553  */
554 static int D3dCreateDevice(vlc_va_dxva2_t *va)
555 {
556     /* */
557     LPDIRECT3D9 (WINAPI *Create9)(UINT SDKVersion);
558     Create9 = (void *)GetProcAddress(va->hd3d9_dll,
559                                      TEXT("Direct3DCreate9"));
560     if (!Create9) {
561         msg_Err(va->log, "Cannot locate reference to Direct3DCreate9 ABI in DLL");
562         return VLC_EGENERIC;
563     }
564
565     /* */
566     LPDIRECT3D9 d3dobj;
567     d3dobj = Create9(D3D_SDK_VERSION);
568     if (!d3dobj) {
569         msg_Err(va->log, "Direct3DCreate9 failed");
570         return VLC_EGENERIC;
571     }
572     va->d3dobj = d3dobj;
573
574     /* */
575     D3DADAPTER_IDENTIFIER9 *d3dai = &va->d3dai;
576     if (FAILED(IDirect3D9_GetAdapterIdentifier(va->d3dobj,
577                                                D3DADAPTER_DEFAULT, 0, d3dai))) {
578         msg_Warn(va->log, "IDirect3D9_GetAdapterIdentifier failed");
579         ZeroMemory(d3dai, sizeof(*d3dai));
580     }
581
582     /* */
583     D3DPRESENT_PARAMETERS *d3dpp = &va->d3dpp;
584     ZeroMemory(d3dpp, sizeof(*d3dpp));
585     d3dpp->Flags                  = D3DPRESENTFLAG_VIDEO;
586     d3dpp->Windowed               = TRUE;
587     d3dpp->hDeviceWindow          = NULL;
588     d3dpp->SwapEffect             = D3DSWAPEFFECT_DISCARD;
589     d3dpp->MultiSampleType        = D3DMULTISAMPLE_NONE;
590     d3dpp->PresentationInterval   = D3DPRESENT_INTERVAL_DEFAULT;
591     d3dpp->BackBufferCount        = 0;                  /* FIXME what to put here */
592     d3dpp->BackBufferFormat       = D3DFMT_X8R8G8B8;    /* FIXME what to put here */
593     d3dpp->BackBufferWidth        = 0;
594     d3dpp->BackBufferHeight       = 0;
595     d3dpp->EnableAutoDepthStencil = FALSE;
596
597     /* Direct3D needs a HWND to create a device, even without using ::Present
598     this HWND is used to alert Direct3D when there's a change of focus window.
599     For now, use GetShellWindow, as it looks harmless */
600     LPDIRECT3DDEVICE9 d3ddev;
601     if (FAILED(IDirect3D9_CreateDevice(d3dobj, D3DADAPTER_DEFAULT,
602                                        D3DDEVTYPE_HAL, GetShellWindow(),
603                                        D3DCREATE_SOFTWARE_VERTEXPROCESSING |
604                                        D3DCREATE_MULTITHREADED,
605                                        d3dpp, &d3ddev))) {
606         msg_Err(va->log, "IDirect3D9_CreateDevice failed");
607         return VLC_EGENERIC;
608     }
609     va->d3ddev = d3ddev;
610
611     return VLC_SUCCESS;
612 }
613 /**
614  * It releases a Direct3D device and its resources.
615  */
616 static void D3dDestroyDevice(vlc_va_dxva2_t *va)
617 {
618     if (va->d3ddev)
619         IDirect3DDevice9_Release(va->d3ddev);
620     if (va->d3dobj)
621         IDirect3D9_Release(va->d3dobj);
622 }
623 /**
624  * It describes our Direct3D object
625  */
626 static char *DxDescribe(vlc_va_dxva2_t *va)
627 {
628     static const struct {
629         unsigned id;
630         char     name[32];
631     } vendors [] = {
632         { 0x1002, "ATI" },
633         { 0x10DE, "NVIDIA" },
634         { 0x8086, "Intel" },
635         { 0x5333, "S3 Graphics" },
636         { 0, "" }
637     };
638     D3DADAPTER_IDENTIFIER9 *id = &va->d3dai;
639
640     const char *vendor = "Unknown";
641     for (int i = 0; vendors[i].id != 0; i++) {
642         if (vendors[i].id == id->VendorId) {
643             vendor = vendors[i].name;
644             break;
645         }
646     }
647
648     char *description;
649     if (asprintf(&description, "DXVA2 (%.*s, vendor %d(%s), device %d, revision %d)",
650                  sizeof(id->Description), id->Description,
651                  id->VendorId, vendor, id->DeviceId, id->Revision) < 0)
652         return NULL;
653     return description;
654 }
655
656 /**
657  * It creates a Direct3D device manager
658  */
659 static int D3dCreateDeviceManager(vlc_va_dxva2_t *va)
660 {
661     HRESULT (WINAPI *CreateDeviceManager9)(UINT *pResetToken,
662                                            IDirect3DDeviceManager9 **);
663     CreateDeviceManager9 =
664       (void *)GetProcAddress(va->hdxva2_dll,
665                              TEXT("DXVA2CreateDirect3DDeviceManager9"));
666
667     if (!CreateDeviceManager9) {
668         msg_Err(va->log, "cannot load function");
669         return VLC_EGENERIC;
670     }
671     msg_Dbg(va->log, "OurDirect3DCreateDeviceManager9 Success!");
672
673     UINT token;
674     IDirect3DDeviceManager9 *devmng;
675     if (FAILED(CreateDeviceManager9(&token, &devmng))) {
676         msg_Err(va->log, " OurDirect3DCreateDeviceManager9 failed");
677         return VLC_EGENERIC;
678     }
679     va->token  = token;
680     va->devmng = devmng;
681     msg_Info(va->log, "obtained IDirect3DDeviceManager9");
682
683     HRESULT hr = IDirect3DDeviceManager9_ResetDevice(devmng, va->d3ddev, token);
684     if (FAILED(hr)) {
685         msg_Err(va->log, "IDirect3DDeviceManager9_ResetDevice failed: %08x", (unsigned)hr);
686         return VLC_EGENERIC;
687     }
688     return VLC_SUCCESS;
689 }
690 /**
691  * It destroys a Direct3D device manager
692  */
693 static void D3dDestroyDeviceManager(vlc_va_dxva2_t *va)
694 {
695     if (va->devmng)
696         IDirect3DDeviceManager9_Release(va->devmng);
697 }
698
699 /**
700  * It creates a DirectX video service
701  */
702 static int DxCreateVideoService(vlc_va_dxva2_t *va)
703 {
704     HRESULT (WINAPI *CreateVideoService)(IDirect3DDevice9 *,
705                                          REFIID riid,
706                                          void **ppService);
707     CreateVideoService =
708       (void *)GetProcAddress(va->hdxva2_dll,
709                              TEXT("DXVA2CreateVideoService"));
710
711     if (!CreateVideoService) {
712         msg_Err(va->log, "cannot load function");
713         return 4;
714     }
715     msg_Info(va->log, "DXVA2CreateVideoService Success!");
716
717     HRESULT hr;
718
719     HANDLE device;
720     hr = IDirect3DDeviceManager9_OpenDeviceHandle(va->devmng, &device);
721     if (FAILED(hr)) {
722         msg_Err(va->log, "OpenDeviceHandle failed");
723         return VLC_EGENERIC;
724     }
725     va->device = device;
726
727     IDirectXVideoDecoderService *vs;
728     hr = IDirect3DDeviceManager9_GetVideoService(va->devmng, device,
729                                                  &IID_IDirectXVideoDecoderService,
730                                                  &vs);
731     if (FAILED(hr)) {
732         msg_Err(va->log, "GetVideoService failed");
733         return VLC_EGENERIC;
734     }
735     va->vs = vs;
736
737     return VLC_SUCCESS;
738 }
739 /**
740  * It destroys a DirectX video service
741  */
742 static void DxDestroyVideoService(vlc_va_dxva2_t *va)
743 {
744     if (va->device)
745         IDirect3DDeviceManager9_CloseDeviceHandle(va->devmng, va->device);
746     if (va->vs)
747         IDirectXVideoDecoderService_Release(va->vs);
748 }
749 /**
750  * Find the best suited decoder mode GUID and render format.
751  */
752 static int DxFindVideoServiceConversion(vlc_va_dxva2_t *va, GUID *input, D3DFORMAT *output)
753 {
754     /* Retreive supported modes from the decoder service */
755     UINT input_count = 0;
756     GUID *input_list = NULL;
757     if (FAILED(IDirectXVideoDecoderService_GetDecoderDeviceGuids(va->vs,
758                                                                  &input_count,
759                                                                  &input_list))) {
760         msg_Err(va->log, "IDirectXVideoDecoderService_GetDecoderDeviceGuids failed");
761         return VLC_EGENERIC;
762     }
763     for (unsigned i = 0; i < input_count; i++) {
764         const GUID *g = &input_list[i];
765         const dxva2_mode_t *mode = Dxva2FindMode(g);
766         if (mode) {
767             msg_Dbg(va->log, "- '%s' is supported by hardware", mode->name);
768         } else {
769             msg_Warn(va->log, "- Unknown GUID = %08X-%04x-%04x-XXXX",
770                      (unsigned)g->Data1, g->Data2, g->Data3);
771         }
772     }
773
774     /* Try all supported mode by our priority */
775     for (unsigned i = 0; dxva2_modes[i].name; i++) {
776         const dxva2_mode_t *mode = &dxva2_modes[i];
777         if (!mode->codec || mode->codec != va->codec_id)
778             continue;
779
780         /* */
781         bool is_suported = false;
782         for (const GUID *g = &input_list[0]; !is_suported && g < &input_list[input_count]; g++) {
783             is_suported = IsEqualGUID(mode->guid, g);
784         }
785         if (!is_suported)
786             continue;
787
788         /* */
789         msg_Dbg(va->log, "Trying to use '%s' as input", mode->name);
790         UINT      output_count = 0;
791         D3DFORMAT *output_list = NULL;
792         if (FAILED(IDirectXVideoDecoderService_GetDecoderRenderTargets(va->vs, mode->guid,
793                                                                        &output_count,
794                                                                        &output_list))) {
795             msg_Err(va->log, "IDirectXVideoDecoderService_GetDecoderRenderTargets failed");
796             continue;
797         }
798         for (unsigned j = 0; j < output_count; j++) {
799             const D3DFORMAT f = output_list[j];
800             const d3d_format_t *format = D3dFindFormat(f);
801             if (format) {
802                 msg_Dbg(va->log, "%s is supported for output", format->name);
803             } else {
804                 msg_Dbg(va->log, "%d is supported for output (%4.4s)", f, (const char*)&f);
805             }
806         }
807
808         /* */
809         for (unsigned j = 0; d3d_formats[j].name; j++) {
810             const d3d_format_t *format = &d3d_formats[j];
811
812             /* */
813             bool is_suported = false;
814             for (unsigned k = 0; !is_suported && k < output_count; k++) {
815                 is_suported = format->format == output_list[k];
816             }
817             if (!is_suported)
818                 continue;
819
820             /* We have our solution */
821             msg_Dbg(va->log, "Using '%s' to decode to '%s'", mode->name, format->name);
822             *input  = *mode->guid;
823             *output = format->format;
824             CoTaskMemFree(output_list);
825             CoTaskMemFree(input_list);
826             return VLC_SUCCESS;
827         }
828         CoTaskMemFree(output_list);
829     }
830     CoTaskMemFree(input_list);
831     return VLC_EGENERIC;
832 }
833
834 /**
835  * It creates a DXVA2 decoder using the given video format
836  */
837 static int DxCreateVideoDecoder(vlc_va_dxva2_t *va,
838                                 int codec_id, const video_format_t *fmt)
839 {
840     /* */
841     msg_Dbg(va->log, "DxCreateVideoDecoder id %d %dx%d",
842             codec_id, fmt->i_width, fmt->i_height);
843
844     va->width  = fmt->i_width;
845     va->height = fmt->i_height;
846
847     /* Allocates all surfaces needed for the decoder */
848     va->surface_width  = (fmt->i_width  + 15) & ~15;
849     va->surface_height = (fmt->i_height + 15) & ~15;
850     switch (codec_id) {
851     case CODEC_ID_H264:
852         va->surface_count = 16 + 1;
853         break;
854     default:
855         va->surface_count = 2 + 1;
856         break;
857     }
858     LPDIRECT3DSURFACE9 surface_list[VA_DXVA2_MAX_SURFACE_COUNT];
859     if (FAILED(IDirectXVideoDecoderService_CreateSurface(va->vs,
860                                                          va->surface_width,
861                                                          va->surface_height,
862                                                          va->surface_count - 1,
863                                                          va->render,
864                                                          D3DPOOL_DEFAULT,
865                                                          0,
866                                                          DXVA2_VideoDecoderRenderTarget,
867                                                          surface_list,
868                                                          NULL))) {
869         msg_Err(va->log, "IDirectXVideoAccelerationService_CreateSurface failed");
870         va->surface_count = 0;
871         return VLC_EGENERIC;
872     }
873     for (unsigned i = 0; i < va->surface_count; i++) {
874         vlc_va_surface_t *surface = &va->surface[i];
875         surface->d3d = surface_list[i];
876         surface->refcount = 0;
877         surface->order = 0;
878     }
879     msg_Dbg(va->log, "IDirectXVideoAccelerationService_CreateSurface succeed with %d surfaces (%dx%d)",
880             va->surface_count, fmt->i_width, fmt->i_height);
881
882     /* */
883     DXVA2_VideoDesc dsc;
884     ZeroMemory(&dsc, sizeof(dsc));
885     dsc.SampleWidth     = fmt->i_width;
886     dsc.SampleHeight    = fmt->i_height;
887     dsc.Format          = va->render;
888     if (fmt->i_frame_rate > 0 && fmt->i_frame_rate_base > 0) {
889         dsc.InputSampleFreq.Numerator   = fmt->i_frame_rate;
890         dsc.InputSampleFreq.Denominator = fmt->i_frame_rate_base;
891     } else {
892         dsc.InputSampleFreq.Numerator   = 0;
893         dsc.InputSampleFreq.Denominator = 0;
894     }
895     dsc.OutputFrameFreq = dsc.InputSampleFreq;
896     dsc.UABProtectionLevel = FALSE;
897     dsc.Reserved = 0;
898
899     /* FIXME I am unsure we can let unknown everywhere */
900     DXVA2_ExtendedFormat *ext = &dsc.SampleFormat;
901     ext->SampleFormat = 0;//DXVA2_SampleUnknown;
902     ext->VideoChromaSubsampling = 0;//DXVA2_VideoChromaSubsampling_Unknown;
903     ext->NominalRange = 0;//DXVA2_NominalRange_Unknown;
904     ext->VideoTransferMatrix = 0;//DXVA2_VideoTransferMatrix_Unknown;
905     ext->VideoLighting = 0;//DXVA2_VideoLighting_Unknown;
906     ext->VideoPrimaries = 0;//DXVA2_VideoPrimaries_Unknown;
907     ext->VideoTransferFunction = 0;//DXVA2_VideoTransFunc_Unknown;
908
909     /* List all configurations available for the decoder */
910     UINT                      cfg_count = 0;
911     DXVA2_ConfigPictureDecode *cfg_list = NULL;
912     if (FAILED(IDirectXVideoDecoderService_GetDecoderConfigurations(va->vs,
913                                                                     &va->input,
914                                                                     &dsc,
915                                                                     NULL,
916                                                                     &cfg_count,
917                                                                     &cfg_list))) {
918         msg_Err(va->log, "IDirectXVideoDecoderService_GetDecoderConfigurations failed");
919         return VLC_EGENERIC;
920     }
921     msg_Dbg(va->log, "we got %d decoder configurations", cfg_count);
922
923     /* Select the best decoder configuration */
924     int cfg_score = 0;
925     for (unsigned i = 0; i < cfg_count; i++) {
926         const DXVA2_ConfigPictureDecode *cfg = &cfg_list[i];
927
928         /* */
929         msg_Dbg(va->log, "configuration[%d] ConfigBitstreamRaw %d",
930                 i, cfg->ConfigBitstreamRaw);
931
932         /* */
933         int score;
934         if (cfg->ConfigBitstreamRaw == 1)
935             score = 1;
936         else if (codec_id == CODEC_ID_H264 && cfg->ConfigBitstreamRaw == 2)
937             score = 2;
938         else
939             continue;
940         if (IsEqualGUID(&cfg->guidConfigBitstreamEncryption, &DXVA_NoEncrypt))
941             score += 16;
942
943         if (cfg_score < score) {
944             va->cfg = *cfg;
945             cfg_score = score;
946         }
947     }
948     CoTaskMemFree(cfg_list);
949     if (cfg_score <= 0) {
950         msg_Err(va->log, "Failed to find a supported decoder configuration");
951         return VLC_EGENERIC;
952     }
953
954     /* Create the decoder */
955     IDirectXVideoDecoder *decoder;
956     if (FAILED(IDirectXVideoDecoderService_CreateVideoDecoder(va->vs,
957                                                               &va->input,
958                                                               &dsc,
959                                                               &va->cfg,
960                                                               surface_list,
961                                                               va->surface_count,
962                                                               &decoder))) {
963         msg_Err(va->log, "IDirectXVideoDecoderService_CreateVideoDecoder failed");
964         return VLC_EGENERIC;
965     }
966     va->decoder = decoder;
967     msg_Dbg(va->log, "IDirectXVideoDecoderService_CreateVideoDecoder succeed");
968     return VLC_SUCCESS;
969 }
970 static void DxDestroyVideoDecoder(vlc_va_dxva2_t *va)
971 {
972     if (va->decoder)
973         IDirectXVideoDecoder_Release(va->decoder);
974     va->decoder = NULL;
975
976     for (unsigned i = 0; i < va->surface_count; i++)
977         IDirect3DSurface9_Release(va->surface[i].d3d);
978     va->surface_count = 0;
979 }
980 static int DxResetVideoDecoder(vlc_va_dxva2_t *va)
981 {
982     msg_Err(va->log, "DxResetVideoDecoder unimplemented");
983     return VLC_EGENERIC;
984 }
985
986 static void DxCreateVideoConversion(vlc_va_dxva2_t *va)
987 {
988     switch (va->render) {
989     case MAKEFOURCC('N','V','1','2'):
990         va->output = MAKEFOURCC('Y','V','1','2');
991         break;
992     default:
993         va->output = va->render;
994         break;
995     }
996     CopyInitCache(&va->surface_cache, va->surface_width);
997 }
998 static void DxDestroyVideoConversion(vlc_va_dxva2_t *va)
999 {
1000     CopyCleanCache(&va->surface_cache);
1001 }
1002 #else
1003 vlc_va_t *vlc_va_NewDxva2(vlc_object_t *log, int codec_id)
1004 {
1005     (void)log;
1006     (void)codec_id;
1007     return NULL;
1008 }
1009 #endif