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