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