1 /*****************************************************************************
2 * dxva2.c: Video Acceleration helpers
3 *****************************************************************************
4 * Copyright (C) 2009 Geoffroy Couprie
5 * Copyright (C) 2009 Laurent Aimar
7 * Authors: Geoffroy Couprie <geal@videolan.org>
8 * Laurent Aimar <fenrir _AT_ videolan _DOT_ org>
10 * This program is free software; you can redistribute it and/or modify it
11 * under the terms of the GNU Lesser General Public License as published by
12 * the Free Software Foundation; either version 2.1 of the License, or
13 * (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU Lesser General Public License for more details.
20 * You should have received a copy of the GNU Lesser General Public License
21 * along with this program; if not, write to the Free Software Foundation,
22 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
23 *****************************************************************************/
31 #include <vlc_common.h>
32 #include <vlc_picture.h>
33 #include <vlc_plugin.h>
35 #define DXVA2API_USE_BITFIELDS
37 #include <libavcodec/dxva2.h>
38 #include "../../video_chroma/d3d9_fmt.h"
40 struct dxva2_pic_context
42 struct d3d9_pic_context ctx
;
43 struct vlc_va_surface_t
*va_surface
;
47 #define DXVA2_PICCONTEXT_FROM_PICCTX(pic_ctx) \
48 container_of((pic_ctx), struct dxva2_pic_context, ctx.s)
50 #include "directx_va.h"
52 static int Open(vlc_va_t
*, AVCodecContext
*, enum PixelFormat hwfmt
, const AVPixFmtDescriptor
*,
53 const es_format_t
*, vlc_decoder_device
*, video_format_t
*, vlc_video_context
**);
56 set_description(N_("DirectX Video Acceleration (DXVA) 2.0"))
57 set_category(CAT_INPUT
)
58 set_subcategory(SUBCAT_INPUT_VCODEC
)
59 set_va_callback(Open
, 110)
62 #include <initguid.h> /* must be last included to not redefine existing GUIDs */
63 DEFINE_GUID(DXVA2_NoEncrypt
, 0x1b81bed0, 0xa0c7, 0x11d3, 0xb9, 0x84, 0x00, 0xc0, 0x4f, 0x2e, 0x73, 0xc5);
72 /* XXX Prefered format must come first */
73 static const d3d9_format_t d3d_formats
[] = {
74 { "YV12", MAKEFOURCC('Y','V','1','2'), VLC_CODEC_YV12
},
75 { "NV12", MAKEFOURCC('N','V','1','2'), VLC_CODEC_NV12
},
76 //{ "IMC3", MAKEFOURCC('I','M','C','3'), VLC_CODEC_YV12 },
77 { "P010", MAKEFOURCC('P','0','1','0'), VLC_CODEC_P010
},
78 { "AYUV", MAKEFOURCC('A','Y','U','V'), VLC_CODEC_YUVA
},
79 { "YUY2", MAKEFOURCC('Y','U','Y','2'), VLC_CODEC_YUYV
},
80 { "Y410", MAKEFOURCC('Y','4','1','0'), VLC_CODEC_Y410
},
81 { "Y210", MAKEFOURCC('Y','2','1','0'), VLC_CODEC_Y210
},
86 static const d3d9_format_t
*D3dFindFormat(D3DFORMAT format
)
88 for (unsigned i
= 0; d3d_formats
[i
].name
; i
++) {
89 if (d3d_formats
[i
].format
== format
)
90 return &d3d_formats
[i
];
98 vlc_video_context
*vctx
;
104 IDirect3DDeviceManager9
*devmng
;
111 DXVA2_ConfigPictureDecode cfg
;
112 const directx_va_mode_t
*selected_decoder
;
113 IDirectXVideoDecoderService
*d3ddec
;
117 IDirect3DSurface9
*hw_surface
[MAX_SURFACE_COUNT
];
119 /* avcodec internals */
120 struct dxva_context hw
;
125 static int D3dCreateDevice(vlc_va_t
*);
127 static int DxGetInputList(vlc_va_t
*, input_list_t
*);
128 static int DxSetupOutput(vlc_va_t
*, const directx_va_mode_t
*, const video_format_t
*);
130 static int DxCreateVideoDecoder(vlc_va_t
*, int codec_id
,
131 const video_format_t
*, size_t surface_count
);
132 static void DxDestroyVideoDecoder(void *);
134 static void SetupAVCodecContext(void *opaque
, AVCodecContext
*avctx
)
136 vlc_va_sys_t
*sys
= opaque
;
137 sys
->hw
.cfg
= &sys
->cfg
;
138 sys
->hw
.surface
= sys
->hw_surface
;
139 sys
->hw
.workaround
= sys
->selected_decoder
->workaround
;
140 avctx
->hwaccel_context
= &sys
->hw
;
143 static void dxva2_pic_context_destroy(picture_context_t
*ctx
)
145 struct dxva2_pic_context
*pic_ctx
= DXVA2_PICCONTEXT_FROM_PICCTX(ctx
);
146 struct vlc_va_surface_t
*va_surface
= pic_ctx
->va_surface
;
147 HINSTANCE dxva2_dll
= pic_ctx
->dxva2_dll
;
148 static_assert(offsetof(struct dxva2_pic_context
, ctx
.s
) == 0,
149 "Cast assumption failure");
150 d3d9_pic_context_destroy(ctx
);
151 va_surface_Release(va_surface
);
152 FreeLibrary(dxva2_dll
);
155 static picture_context_t
*dxva2_pic_context_copy(picture_context_t
*ctx
)
157 struct dxva2_pic_context
*src_ctx
= DXVA2_PICCONTEXT_FROM_PICCTX(ctx
);
158 struct dxva2_pic_context
*pic_ctx
= malloc(sizeof(*pic_ctx
));
159 if (unlikely(pic_ctx
==NULL
))
162 pic_ctx
->dxva2_dll
= LoadLibrary(TEXT("DXVA2.DLL"));
163 vlc_video_context_Hold(pic_ctx
->ctx
.s
.vctx
);
164 va_surface_AddRef(pic_ctx
->va_surface
);
165 AcquireD3D9PictureSys(&pic_ctx
->ctx
.picsys
);
166 return &pic_ctx
->ctx
.s
;
169 static struct dxva2_pic_context
*CreatePicContext(IDirect3DSurface9
*surface
, vlc_video_context
*vctx
)
171 struct dxva2_pic_context
*pic_ctx
= calloc(1, sizeof(*pic_ctx
));
172 if (unlikely(pic_ctx
==NULL
))
174 pic_ctx
->ctx
.s
= (picture_context_t
) {
175 dxva2_pic_context_destroy
, dxva2_pic_context_copy
,
176 vlc_video_context_Hold(vctx
),
178 pic_ctx
->ctx
.picsys
.surface
= surface
;
179 AcquireD3D9PictureSys(&pic_ctx
->ctx
.picsys
);
183 static picture_context_t
* NewSurfacePicContext(vlc_va_t
*va
, vlc_va_surface_t
*va_surface
)
185 vlc_va_sys_t
*sys
= va
->sys
;
186 struct dxva2_pic_context
*pic_ctx
= CreatePicContext(sys
->hw_surface
[va_surface_GetIndex(va_surface
)], sys
->vctx
);
187 if (unlikely(pic_ctx
==NULL
))
189 pic_ctx
->va_surface
= va_surface
;
190 pic_ctx
->dxva2_dll
= LoadLibrary(TEXT("DXVA2.DLL"));
191 return &pic_ctx
->ctx
.s
;
194 static int Get(vlc_va_t
*va
, picture_t
*pic
, uint8_t **data
)
196 vlc_va_sys_t
*sys
= va
->sys
;
198 /* Check the device */
199 HRESULT hr
= IDirect3DDeviceManager9_TestDevice(sys
->devmng
, sys
->device
);
201 if (hr
== DXVA2_E_NEW_VIDEO_DEVICE
)
202 msg_Warn(va
, "New video device detected.");
204 msg_Err(va
, "device not usable. (hr=0x%lX)", hr
);
208 vlc_va_surface_t
*va_surface
= va_pool_Get(sys
->va_pool
);
209 if (unlikely(va_surface
==NULL
))
212 pic
->context
= NewSurfacePicContext(va
, va_surface
);
213 if (unlikely(pic
->context
== NULL
))
215 va_surface_Release(va_surface
);
218 data
[3] = (uint8_t*)DXVA2_PICCONTEXT_FROM_PICCTX(pic
->context
)->ctx
.picsys
.surface
;
222 static void Close(vlc_va_t
*va
)
224 vlc_va_sys_t
*sys
= va
->sys
;
227 vlc_video_context_Release(sys
->vctx
);
230 va_pool_Close(sys
->va_pool
);
233 static const struct vlc_va_operations ops
= { Get
, Close
, };
235 static int Open(vlc_va_t
*va
, AVCodecContext
*ctx
, enum PixelFormat hwfmt
, const AVPixFmtDescriptor
*desc
,
236 const es_format_t
*fmt_in
, vlc_decoder_device
*dec_device
,
237 video_format_t
*fmt_out
, vlc_video_context
**vtcx_out
)
239 int err
= VLC_EGENERIC
;
241 if ( hwfmt
!= AV_PIX_FMT_DXVA2_VLD
)
244 d3d9_decoder_device_t
*d3d9_decoder
= GetD3D9OpaqueDevice( dec_device
);
245 if ( d3d9_decoder
== NULL
)
247 assert(d3d9_decoder
->d3ddev
.dev
!= NULL
); // coming from the decoder device
249 ctx
->hwaccel_context
= NULL
;
251 vlc_va_sys_t
*sys
= calloc(1, sizeof (*sys
));
252 if (unlikely(sys
== NULL
))
255 sys
->vctx
= vlc_video_context_Create( dec_device
, VLC_VIDEO_CONTEXT_DXVA2
,
256 sizeof(d3d9_video_context_t
), &d3d9_vctx_ops
);
257 if (likely(sys
->vctx
== NULL
))
266 sys
->dxva2_dll
= LoadLibrary(TEXT("DXVA2.DLL"));
267 if (!sys
->dxva2_dll
) {
268 msg_Warn(va
, "cannot load DXVA2 decoder DLL");
270 vlc_video_context_Release(sys
->vctx
);
275 struct va_pool_cfg pool_cfg
= {
277 DxDestroyVideoDecoder
,
278 DxCreateVideoDecoder
,
283 sys
->va_pool
= va_pool_Create(va
, &pool_cfg
);
284 if (sys
->va_pool
== NULL
)
290 video_format_t final_fmt
= *fmt_out
;
291 static const directx_sys_t dx_sys
= { DxGetInputList
, DxSetupOutput
};
292 sys
->selected_decoder
= directx_va_Setup(va
, &dx_sys
, ctx
, desc
, fmt_in
, 0,
293 &final_fmt
, &sys
->hw
.surface_count
);
294 if (sys
->selected_decoder
== NULL
)
300 if (sys
->render
== MAKEFOURCC('P','0','1','0') ||
301 sys
->render
== MAKEFOURCC('Y','4','1','0') ||
302 sys
->render
== MAKEFOURCC('Y','2','1','0'))
303 final_fmt
.i_chroma
= VLC_CODEC_D3D9_OPAQUE_10B
;
305 final_fmt
.i_chroma
= VLC_CODEC_D3D9_OPAQUE
;
306 err
= va_pool_SetupDecoder(va
, sys
->va_pool
, ctx
, &final_fmt
, sys
->hw
.surface_count
);
307 if (err
!= VLC_SUCCESS
)
310 const D3DADAPTER_IDENTIFIER9
*identifier
= &d3d9_decoder
->d3ddev
.identifier
;
311 msg_Info(va
, "Using DXVA2 (%.*s, vendor %s(%lx), device %lx, revision %lx)",
312 (int)sizeof(identifier
->Description
), identifier
->Description
,
313 DxgiVendorStr(identifier
->VendorId
), identifier
->VendorId
,
314 identifier
->DeviceId
, identifier
->Revision
);
316 d3d9_video_context_t
*octx
= GetD3D9ContextPrivate(sys
->vctx
);
317 octx
->format
= sys
->render
;
320 *fmt_out
= final_fmt
;
321 *vtcx_out
= sys
->vctx
;
331 * It creates a Direct3D device usable for DXVA 2
333 static int D3dCreateDevice(vlc_va_t
*va
)
335 vlc_va_sys_t
*sys
= va
->sys
;
337 d3d9_decoder_device_t
*d3d9_decoder
= GetD3D9OpaqueContext(sys
->vctx
);
339 HRESULT (WINAPI
*CreateDeviceManager9
)(UINT
*pResetToken
,
340 IDirect3DDeviceManager9
**);
341 CreateDeviceManager9
=
342 (void *)GetProcAddress(sys
->dxva2_dll
,
343 "DXVA2CreateDirect3DDeviceManager9");
345 if (!CreateDeviceManager9
) {
346 msg_Err(va
, "cannot load function");
349 msg_Dbg(va
, "got CreateDeviceManager9");
352 if (FAILED(CreateDeviceManager9(&token
, &sys
->devmng
))) {
353 msg_Err(va
, " OurDirect3DCreateDeviceManager9 failed");
356 msg_Dbg(va
, "obtained IDirect3DDeviceManager9");
358 HRESULT hr
= IDirect3DDeviceManager9_ResetDevice(sys
->devmng
, d3d9_decoder
->d3ddev
.dev
, token
);
360 msg_Err(va
, "IDirect3DDeviceManager9_ResetDevice failed: 0x%lX)", hr
);
361 IDirect3DDeviceManager9_Release(sys
->devmng
);
365 hr
= IDirect3DDeviceManager9_OpenDeviceHandle(sys
->devmng
, &sys
->device
);
367 msg_Err(va
, "OpenDeviceHandle failed");
368 IDirect3DDeviceManager9_Release(sys
->devmng
);
373 hr
= IDirect3DDeviceManager9_GetVideoService(sys
->devmng
, sys
->device
,
374 &IID_IDirectXVideoDecoderService
, &pv
);
376 msg_Err(va
, "GetVideoService failed");
377 IDirect3DDeviceManager9_CloseDeviceHandle(sys
->devmng
, sys
->device
);
378 IDirect3DDeviceManager9_Release(sys
->devmng
);
386 static void ReleaseInputList(input_list_t
*p_list
)
388 CoTaskMemFree(p_list
->list
);
391 static int DxGetInputList(vlc_va_t
*va
, input_list_t
*p_list
)
393 vlc_va_sys_t
*sys
= va
->sys
;
394 UINT input_count
= 0;
395 GUID
*input_list
= NULL
;
396 if (FAILED(IDirectXVideoDecoderService_GetDecoderDeviceGuids(sys
->d3ddec
,
399 msg_Err(va
, "IDirectXVideoDecoderService_GetDecoderDeviceGuids failed");
403 p_list
->count
= input_count
;
404 p_list
->list
= input_list
;
405 p_list
->pf_release
= ReleaseInputList
;
409 static int DxSetupOutput(vlc_va_t
*va
, const directx_va_mode_t
*mode
, const video_format_t
*fmt
)
412 vlc_va_sys_t
*sys
= va
->sys
;
414 d3d9_decoder_device_t
*d3d9_decoder
= GetD3D9OpaqueContext(sys
->vctx
);
416 const D3DADAPTER_IDENTIFIER9
*identifier
= &d3d9_decoder
->d3ddev
.identifier
;
417 UINT driverBuild
= identifier
->DriverVersion
.LowPart
& 0xFFFF;
418 if (identifier
->VendorId
== GPU_MANUFACTURER_INTEL
&& (identifier
->DriverVersion
.LowPart
>> 16) >= 100)
420 /* new Intel driver format */
421 driverBuild
+= ((identifier
->DriverVersion
.LowPart
>> 16) - 100) * 1000;
423 if (!directx_va_canUseDecoder(va
, identifier
->VendorId
, identifier
->DeviceId
,
424 mode
->guid
, driverBuild
))
426 msg_Warn(va
, "GPU blocklisted for %s codec", mode
->name
);
430 int err
= VLC_EGENERIC
;
431 UINT output_count
= 0;
432 D3DFORMAT
*output_list
= NULL
;
433 if (FAILED(IDirectXVideoDecoderService_GetDecoderRenderTargets(sys
->d3ddec
,
437 msg_Err(va
, "IDirectXVideoDecoderService_GetDecoderRenderTargets failed");
441 for (unsigned j
= 0; j
< output_count
; j
++) {
442 const D3DFORMAT f
= output_list
[j
];
443 const d3d9_format_t
*format
= D3dFindFormat(f
);
445 msg_Dbg(va
, "%s is supported for output", format
->name
);
447 msg_Dbg(va
, "%d is supported for output (%4.4s)", f
, (const char*)&f
);
451 D3DFORMAT preferredOutput
;
452 if (mode
->bit_depth
> 8)
454 if (mode
->log2_chroma_w
== 0 && mode
->log2_chroma_h
== 0)
455 preferredOutput
= MAKEFOURCC('Y','4','1','0'); // 10 bits 4:4:4
456 else if (mode
->log2_chroma_w
== 1 && mode
->log2_chroma_h
== 0)
457 preferredOutput
= MAKEFOURCC('Y','2','1','0'); // 10 bits 4:2:2
459 preferredOutput
= MAKEFOURCC('P','0','1','0');
461 else if (mode
->log2_chroma_w
== 0 && mode
->log2_chroma_h
== 0)
462 preferredOutput
= MAKEFOURCC('A','Y','U','V'); // 8 bits 4:4:4
463 else if (mode
->log2_chroma_w
== 1 && mode
->log2_chroma_h
== 0)
464 preferredOutput
= MAKEFOURCC('Y','U','Y','2'); // 8 bits 4:2:2
466 preferredOutput
= MAKEFOURCC('N','V','1','2');
467 msg_Dbg(va
, "favor decoder format %4.4s (for 4:%d:%d %d bits)", (const char*)&preferredOutput
,
468 (2-mode
->log2_chroma_w
)*2, (2-mode
->log2_chroma_w
-mode
->log2_chroma_h
)*2, mode
->bit_depth
);
471 for (unsigned pass
= 0; pass
< 2 && err
!= VLC_SUCCESS
; ++pass
)
473 for (unsigned j
= 0; d3d_formats
[j
].name
; j
++) {
474 const d3d9_format_t
*format
= &d3d_formats
[j
];
477 bool is_supported
= false;
478 for (unsigned k
= 0; !is_supported
&& k
< output_count
; k
++) {
479 is_supported
= format
->format
== output_list
[k
];
483 if (pass
== 0 && format
->format
!= preferredOutput
)
486 /* We have our solution */
487 msg_Dbg(va
, "Using decoder output '%s'", format
->name
);
488 sys
->render
= format
->format
;
493 CoTaskMemFree(output_list
);
498 * It creates a DXVA2 decoder using the given video format
500 static int DxCreateVideoDecoder(vlc_va_t
*va
, int codec_id
,
501 const video_format_t
*fmt
, size_t surface_count
)
503 vlc_va_sys_t
*sys
= va
->sys
;
506 hr
= IDirectXVideoDecoderService_CreateSurface(sys
->d3ddec
,
513 DXVA2_VideoDecoderRenderTarget
,
517 msg_Err(va
, "IDirectXVideoAccelerationService_CreateSurface %zu failed (hr=0x%lX)", surface_count
- 1, hr
);
520 msg_Dbg(va
, "IDirectXVideoAccelerationService_CreateSurface succeed with %zu surfaces (%dx%d)",
521 surface_count
, fmt
->i_width
, fmt
->i_height
);
523 IDirect3DSurface9
*tstCrash
;
524 hr
= IDirectXVideoDecoderService_CreateSurface(sys
->d3ddec
,
531 DXVA2_VideoDecoderRenderTarget
,
535 msg_Err(va
, "extra buffer impossible, avoid a crash (hr=0x%lX)", hr
);
538 IDirect3DSurface9_Release(tstCrash
);
542 ZeroMemory(&dsc
, sizeof(dsc
));
543 dsc
.SampleWidth
= fmt
->i_width
;
544 dsc
.SampleHeight
= fmt
->i_height
;
545 dsc
.Format
= sys
->render
;
546 if (fmt
->i_frame_rate
> 0 && fmt
->i_frame_rate_base
> 0) {
547 dsc
.InputSampleFreq
.Numerator
= fmt
->i_frame_rate
;
548 dsc
.InputSampleFreq
.Denominator
= fmt
->i_frame_rate_base
;
550 dsc
.InputSampleFreq
.Numerator
= 0;
551 dsc
.InputSampleFreq
.Denominator
= 0;
553 dsc
.OutputFrameFreq
= dsc
.InputSampleFreq
;
554 dsc
.UABProtectionLevel
= FALSE
;
557 /* FIXME I am unsure we can let unknown everywhere */
558 DXVA2_ExtendedFormat
*ext
= &dsc
.SampleFormat
;
559 ext
->SampleFormat
= 0;//DXVA2_SampleUnknown;
560 ext
->VideoChromaSubsampling
= 0;//DXVA2_VideoChromaSubsampling_Unknown;
561 ext
->NominalRange
= 0;//DXVA2_NominalRange_Unknown;
562 ext
->VideoTransferMatrix
= 0;//DXVA2_VideoTransferMatrix_Unknown;
563 ext
->VideoLighting
= 0;//DXVA2_VideoLighting_Unknown;
564 ext
->VideoPrimaries
= 0;//DXVA2_VideoPrimaries_Unknown;
565 ext
->VideoTransferFunction
= 0;//DXVA2_VideoTransFunc_Unknown;
567 /* List all configurations available for the decoder */
569 DXVA2_ConfigPictureDecode
*cfg_list
= NULL
;
570 hr
= IDirectXVideoDecoderService_GetDecoderConfigurations(sys
->d3ddec
,
571 sys
->selected_decoder
->guid
,
577 msg_Err(va
, "IDirectXVideoDecoderService_GetDecoderConfigurations failed. (hr=0x%lX)", hr
);
580 msg_Dbg(va
, "we got %d decoder configurations", cfg_count
);
582 /* Select the best decoder configuration */
584 for (unsigned i
= 0; i
< cfg_count
; i
++) {
585 const DXVA2_ConfigPictureDecode
*cfg
= &cfg_list
[i
];
588 msg_Dbg(va
, "configuration[%d] ConfigBitstreamRaw %d",
589 i
, cfg
->ConfigBitstreamRaw
);
593 if (cfg
->ConfigBitstreamRaw
== 1)
595 else if (codec_id
== AV_CODEC_ID_H264
&& cfg
->ConfigBitstreamRaw
== 2)
599 if (IsEqualGUID(&cfg
->guidConfigBitstreamEncryption
, &DXVA2_NoEncrypt
))
602 if (cfg_score
< score
) {
607 CoTaskMemFree(cfg_list
);
608 if (cfg_score
<= 0) {
609 msg_Err(va
, "Failed to find a supported decoder configuration");
613 /* Create the decoder */
614 /* adds a reference on each decoder surface */
615 if (FAILED(IDirectXVideoDecoderService_CreateVideoDecoder(sys
->d3ddec
,
616 sys
->selected_decoder
->guid
,
621 &sys
->hw
.decoder
))) {
622 msg_Err(va
, "IDirectXVideoDecoderService_CreateVideoDecoder failed");
626 msg_Dbg(va
, "IDirectXVideoDecoderService_CreateVideoDecoder succeed");
629 for (size_t i
= 0; i
< surface_count
; i
++)
630 IDirect3DSurface9_Release( sys
->hw_surface
[i
] );
634 static void DxDestroyVideoDecoder(void *opaque
)
636 vlc_va_sys_t
*sys
= opaque
;
637 /* releases a reference on each decoder surface */
639 IDirectXVideoDecoder_Release(sys
->hw
.decoder
);
640 if (sys
->hw_surface
[0])
642 for (unsigned i
= 0; i
< sys
->hw
.surface_count
; i
++)
643 IDirect3DSurface9_Release(sys
->hw_surface
[i
]);
645 IDirect3DDeviceManager9_CloseDeviceHandle(sys
->devmng
, sys
->device
);
646 IDirectXVideoDecoderService_Release(sys
->d3ddec
);
647 IDirect3DDeviceManager9_Release(sys
->devmng
);
649 FreeLibrary(sys
->dxva2_dll
);