2 * This file is part of Libav.
4 * Libav is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
9 * Libav is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
14 * You should have received a copy of the GNU Lesser General Public
15 * License along with Libav; if not, write to the Free Software
16 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
21 #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600
23 #define _WIN32_WINNT 0x0600
25 #define DXVA2API_USE_BITFIELDS
33 #include "hwcontext.h"
34 #include "hwcontext_dxva2.h"
35 #include "hwcontext_internal.h"
40 typedef IDirect3D9
* WINAPI
pDirect3DCreate9(UINT
);
41 typedef HRESULT WINAPI
pCreateDeviceManager9(UINT
*, IDirect3DDeviceManager9
**);
43 typedef struct DXVA2Mapping
{
44 uint32_t palette_dummy
[256];
47 typedef struct DXVA2FramesContext
{
48 IDirect3DSurface9
**surfaces_internal
;
52 IDirectXVideoAccelerationService
*service
;
57 typedef struct DXVA2DevicePriv
{
64 IDirect3DDevice9
*d3d9device
;
69 enum AVPixelFormat pix_fmt
;
70 } supported_formats
[] = {
71 { MKTAG('N', 'V', '1', '2'), AV_PIX_FMT_NV12
},
72 { MKTAG('P', '0', '1', '0'), AV_PIX_FMT_P010
},
73 { D3DFMT_P8
, AV_PIX_FMT_PAL8
},
76 DEFINE_GUID(video_decoder_service
, 0xfc51a551, 0xd5e7, 0x11d9, 0xaf, 0x55, 0x00, 0x05, 0x4e, 0x43, 0xff, 0x02);
77 DEFINE_GUID(video_processor_service
, 0xfc51a552, 0xd5e7, 0x11d9, 0xaf, 0x55, 0x00, 0x05, 0x4e, 0x43, 0xff, 0x02);
79 static void dxva2_frames_uninit(AVHWFramesContext
*ctx
)
81 AVDXVA2DeviceContext
*device_hwctx
= ctx
->device_ctx
->hwctx
;
82 AVDXVA2FramesContext
*frames_hwctx
= ctx
->hwctx
;
83 DXVA2FramesContext
*s
= ctx
->internal
->priv
;
86 if (frames_hwctx
->decoder_to_release
)
87 IDirectXVideoDecoder_Release(frames_hwctx
->decoder_to_release
);
89 if (s
->surfaces_internal
) {
90 for (i
= 0; i
< frames_hwctx
->nb_surfaces
; i
++) {
91 if (s
->surfaces_internal
[i
])
92 IDirect3DSurface9_Release(s
->surfaces_internal
[i
]);
95 av_freep(&s
->surfaces_internal
);
98 IDirectXVideoAccelerationService_Release(s
->service
);
102 if (s
->device_handle
!= INVALID_HANDLE_VALUE
) {
103 IDirect3DDeviceManager9_CloseDeviceHandle(device_hwctx
->devmgr
, s
->device_handle
);
104 s
->device_handle
= INVALID_HANDLE_VALUE
;
108 static AVBufferRef
*dxva2_pool_alloc(void *opaque
, int size
)
110 AVHWFramesContext
*ctx
= (AVHWFramesContext
*)opaque
;
111 DXVA2FramesContext
*s
= ctx
->internal
->priv
;
112 AVDXVA2FramesContext
*hwctx
= ctx
->hwctx
;
114 if (s
->nb_surfaces_used
< hwctx
->nb_surfaces
) {
115 s
->nb_surfaces_used
++;
116 return av_buffer_create((uint8_t*)s
->surfaces_internal
[s
->nb_surfaces_used
- 1],
117 sizeof(*hwctx
->surfaces
), NULL
, 0, 0);
123 static int dxva2_init_pool(AVHWFramesContext
*ctx
)
125 AVDXVA2FramesContext
*frames_hwctx
= ctx
->hwctx
;
126 AVDXVA2DeviceContext
*device_hwctx
= ctx
->device_ctx
->hwctx
;
127 DXVA2FramesContext
*s
= ctx
->internal
->priv
;
128 int decode
= (frames_hwctx
->surface_type
== DXVA2_VideoDecoderRenderTarget
);
133 if (ctx
->initial_pool_size
<= 0)
136 hr
= IDirect3DDeviceManager9_OpenDeviceHandle(device_hwctx
->devmgr
, &s
->device_handle
);
138 av_log(ctx
, AV_LOG_ERROR
, "Failed to open device handle\n");
139 return AVERROR_UNKNOWN
;
142 hr
= IDirect3DDeviceManager9_GetVideoService(device_hwctx
->devmgr
,
144 decode ?
&video_decoder_service
: &video_processor_service
,
145 (void **)&s
->service
);
147 av_log(ctx
, AV_LOG_ERROR
, "Failed to create the video service\n");
148 return AVERROR_UNKNOWN
;
151 for (i
= 0; i
< FF_ARRAY_ELEMS(supported_formats
); i
++) {
152 if (ctx
->sw_format
== supported_formats
[i
].pix_fmt
) {
153 s
->format
= supported_formats
[i
].d3d_format
;
157 if (i
== FF_ARRAY_ELEMS(supported_formats
)) {
158 av_log(ctx
, AV_LOG_ERROR
, "Unsupported pixel format: %s\n",
159 av_get_pix_fmt_name(ctx
->sw_format
));
160 return AVERROR(EINVAL
);
163 s
->surfaces_internal
= av_mallocz_array(ctx
->initial_pool_size
,
164 sizeof(*s
->surfaces_internal
));
165 if (!s
->surfaces_internal
)
166 return AVERROR(ENOMEM
);
168 hr
= IDirectXVideoAccelerationService_CreateSurface(s
->service
,
169 ctx
->width
, ctx
->height
,
170 ctx
->initial_pool_size
- 1,
171 s
->format
, D3DPOOL_DEFAULT
, 0,
172 frames_hwctx
->surface_type
,
173 s
->surfaces_internal
, NULL
);
175 av_log(ctx
, AV_LOG_ERROR
, "Could not create the surfaces\n");
176 return AVERROR_UNKNOWN
;
179 ctx
->internal
->pool_internal
= av_buffer_pool_init2(sizeof(*s
->surfaces_internal
),
180 ctx
, dxva2_pool_alloc
, NULL
);
181 if (!ctx
->internal
->pool_internal
)
182 return AVERROR(ENOMEM
);
184 frames_hwctx
->surfaces
= s
->surfaces_internal
;
185 frames_hwctx
->nb_surfaces
= ctx
->initial_pool_size
;
190 static int dxva2_frames_init(AVHWFramesContext
*ctx
)
192 AVDXVA2FramesContext
*hwctx
= ctx
->hwctx
;
193 DXVA2FramesContext
*s
= ctx
->internal
->priv
;
196 if (hwctx
->surface_type
!= DXVA2_VideoDecoderRenderTarget
&&
197 hwctx
->surface_type
!= DXVA2_VideoProcessorRenderTarget
) {
198 av_log(ctx
, AV_LOG_ERROR
, "Unknown surface type: %lu\n",
199 hwctx
->surface_type
);
200 return AVERROR(EINVAL
);
203 s
->device_handle
= INVALID_HANDLE_VALUE
;
205 /* init the frame pool if the caller didn't provide one */
207 ret
= dxva2_init_pool(ctx
);
209 av_log(ctx
, AV_LOG_ERROR
, "Error creating an internal frame pool\n");
217 static int dxva2_get_buffer(AVHWFramesContext
*ctx
, AVFrame
*frame
)
219 frame
->buf
[0] = av_buffer_pool_get(ctx
->pool
);
221 return AVERROR(ENOMEM
);
223 frame
->data
[3] = frame
->buf
[0]->data
;
224 frame
->format
= AV_PIX_FMT_DXVA2_VLD
;
225 frame
->width
= ctx
->width
;
226 frame
->height
= ctx
->height
;
231 static int dxva2_transfer_get_formats(AVHWFramesContext
*ctx
,
232 enum AVHWFrameTransferDirection dir
,
233 enum AVPixelFormat
**formats
)
235 enum AVPixelFormat
*fmts
;
237 fmts
= av_malloc_array(2, sizeof(*fmts
));
239 return AVERROR(ENOMEM
);
241 fmts
[0] = ctx
->sw_format
;
242 fmts
[1] = AV_PIX_FMT_NONE
;
249 static void dxva2_unmap_frame(AVHWFramesContext
*ctx
, HWMapDescriptor
*hwmap
)
251 IDirect3DSurface9
*surface
= (IDirect3DSurface9
*)hwmap
->source
->data
[3];
252 IDirect3DSurface9_UnlockRect(surface
);
253 av_freep(&hwmap
->priv
);
256 static int dxva2_map_frame(AVHWFramesContext
*ctx
, AVFrame
*dst
, const AVFrame
*src
,
259 IDirect3DSurface9
*surface
= (IDirect3DSurface9
*)src
->data
[3];
261 D3DSURFACE_DESC surfaceDesc
;
262 D3DLOCKED_RECT LockedRect
;
264 int i
, err
, nb_planes
;
266 nb_planes
= av_pix_fmt_count_planes(dst
->format
);
268 hr
= IDirect3DSurface9_GetDesc(surface
, &surfaceDesc
);
270 av_log(ctx
, AV_LOG_ERROR
, "Error getting a surface description\n");
271 return AVERROR_UNKNOWN
;
274 hr
= IDirect3DSurface9_LockRect(surface
, &LockedRect
, NULL
,
275 flags
& AV_HWFRAME_MAP_READ ? D3DLOCK_READONLY
: D3DLOCK_DISCARD
);
277 av_log(ctx
, AV_LOG_ERROR
, "Unable to lock DXVA2 surface\n");
278 return AVERROR_UNKNOWN
;
281 map
= av_mallocz(sizeof(*map
));
285 err
= ff_hwframe_map_create(src
->hw_frames_ctx
, dst
, src
,
286 dxva2_unmap_frame
, map
);
292 for (i
= 0; i
< nb_planes
; i
++)
293 dst
->linesize
[i
] = LockedRect
.Pitch
;
295 av_image_fill_pointers(dst
->data
, dst
->format
, surfaceDesc
.Height
,
296 (uint8_t*)LockedRect
.pBits
, dst
->linesize
);
298 if (dst
->format
== AV_PIX_FMT_PAL8
)
299 dst
->data
[1] = (uint8_t*)map
->palette_dummy
;
303 IDirect3DSurface9_UnlockRect(surface
);
307 static int dxva2_transfer_data(AVHWFramesContext
*ctx
, AVFrame
*dst
,
310 int download
= !!src
->hw_frames_ctx
;
315 map
= av_frame_alloc();
317 return AVERROR(ENOMEM
);
318 map
->format
= dst
->format
;
320 ret
= dxva2_map_frame(ctx
, map
, download ? src
: dst
,
321 download ? AV_HWFRAME_MAP_READ
: AV_HWFRAME_MAP_WRITE
);
326 ptrdiff_t src_linesize
[4], dst_linesize
[4];
327 for (i
= 0; i
< 4; i
++) {
328 dst_linesize
[i
] = dst
->linesize
[i
];
329 src_linesize
[i
] = map
->linesize
[i
];
331 av_image_copy_uc_from(dst
->data
, dst_linesize
, map
->data
, src_linesize
,
332 ctx
->sw_format
, src
->width
, src
->height
);
334 av_image_copy(map
->data
, map
->linesize
, src
->data
, src
->linesize
,
335 ctx
->sw_format
, src
->width
, src
->height
);
343 static int dxva2_map_from(AVHWFramesContext
*ctx
,
344 AVFrame
*dst
, const AVFrame
*src
, int flags
)
348 if (dst
->format
!= AV_PIX_FMT_NONE
&& dst
->format
!= ctx
->sw_format
)
349 return AVERROR(ENOSYS
);
350 dst
->format
= ctx
->sw_format
;
352 err
= dxva2_map_frame(ctx
, dst
, src
, flags
);
356 err
= av_frame_copy_props(dst
, src
);
363 static void dxva2_device_free(AVHWDeviceContext
*ctx
)
365 AVDXVA2DeviceContext
*hwctx
= ctx
->hwctx
;
366 DXVA2DevicePriv
*priv
= ctx
->user_opaque
;
368 if (hwctx
->devmgr
&& priv
->device_handle
!= INVALID_HANDLE_VALUE
)
369 IDirect3DDeviceManager9_CloseDeviceHandle(hwctx
->devmgr
, priv
->device_handle
);
372 IDirect3DDeviceManager9_Release(hwctx
->devmgr
);
374 if (priv
->d3d9device
)
375 IDirect3DDevice9_Release(priv
->d3d9device
);
378 IDirect3D9_Release(priv
->d3d9
);
381 FreeLibrary(priv
->d3dlib
);
384 FreeLibrary(priv
->dxva2lib
);
386 av_freep(&ctx
->user_opaque
);
389 static int dxva2_device_create(AVHWDeviceContext
*ctx
, const char *device
,
390 AVDictionary
*opts
, int flags
)
392 AVDXVA2DeviceContext
*hwctx
= ctx
->hwctx
;
393 DXVA2DevicePriv
*priv
;
395 pDirect3DCreate9
*createD3D
= NULL
;
396 pCreateDeviceManager9
*createDeviceManager
= NULL
;
397 D3DPRESENT_PARAMETERS d3dpp
= {0};
398 D3DDISPLAYMODE d3ddm
;
399 unsigned resetToken
= 0;
400 UINT adapter
= D3DADAPTER_DEFAULT
;
404 adapter
= atoi(device
);
406 priv
= av_mallocz(sizeof(*priv
));
408 return AVERROR(ENOMEM
);
410 ctx
->user_opaque
= priv
;
411 ctx
->free
= dxva2_device_free
;
413 priv
->device_handle
= INVALID_HANDLE_VALUE
;
415 priv
->d3dlib
= LoadLibrary("d3d9.dll");
417 av_log(ctx
, AV_LOG_ERROR
, "Failed to load D3D9 library\n");
418 return AVERROR_UNKNOWN
;
420 priv
->dxva2lib
= LoadLibrary("dxva2.dll");
421 if (!priv
->dxva2lib
) {
422 av_log(ctx
, AV_LOG_ERROR
, "Failed to load DXVA2 library\n");
423 return AVERROR_UNKNOWN
;
426 createD3D
= (pDirect3DCreate9
*)GetProcAddress(priv
->d3dlib
, "Direct3DCreate9");
428 av_log(ctx
, AV_LOG_ERROR
, "Failed to locate Direct3DCreate9\n");
429 return AVERROR_UNKNOWN
;
431 createDeviceManager
= (pCreateDeviceManager9
*)GetProcAddress(priv
->dxva2lib
,
432 "DXVA2CreateDirect3DDeviceManager9");
433 if (!createDeviceManager
) {
434 av_log(ctx
, AV_LOG_ERROR
, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n");
435 return AVERROR_UNKNOWN
;
438 priv
->d3d9
= createD3D(D3D_SDK_VERSION
);
440 av_log(ctx
, AV_LOG_ERROR
, "Failed to create IDirect3D object\n");
441 return AVERROR_UNKNOWN
;
444 IDirect3D9_GetAdapterDisplayMode(priv
->d3d9
, adapter
, &d3ddm
);
445 d3dpp
.Windowed
= TRUE
;
446 d3dpp
.BackBufferWidth
= 640;
447 d3dpp
.BackBufferHeight
= 480;
448 d3dpp
.BackBufferCount
= 0;
449 d3dpp
.BackBufferFormat
= d3ddm
.Format
;
450 d3dpp
.SwapEffect
= D3DSWAPEFFECT_DISCARD
;
451 d3dpp
.Flags
= D3DPRESENTFLAG_VIDEO
;
453 hr
= IDirect3D9_CreateDevice(priv
->d3d9
, adapter
, D3DDEVTYPE_HAL
, GetShellWindow(),
454 D3DCREATE_SOFTWARE_VERTEXPROCESSING
| D3DCREATE_MULTITHREADED
| D3DCREATE_FPU_PRESERVE
,
455 &d3dpp
, &priv
->d3d9device
);
457 av_log(ctx
, AV_LOG_ERROR
, "Failed to create Direct3D device\n");
458 return AVERROR_UNKNOWN
;
461 hr
= createDeviceManager(&resetToken
, &hwctx
->devmgr
);
463 av_log(ctx
, AV_LOG_ERROR
, "Failed to create Direct3D device manager\n");
464 return AVERROR_UNKNOWN
;
467 hr
= IDirect3DDeviceManager9_ResetDevice(hwctx
->devmgr
, priv
->d3d9device
, resetToken
);
469 av_log(ctx
, AV_LOG_ERROR
, "Failed to bind Direct3D device to device manager\n");
470 return AVERROR_UNKNOWN
;
473 hr
= IDirect3DDeviceManager9_OpenDeviceHandle(hwctx
->devmgr
, &priv
->device_handle
);
475 av_log(ctx
, AV_LOG_ERROR
, "Failed to open device handle\n");
476 return AVERROR_UNKNOWN
;
482 const HWContextType ff_hwcontext_type_dxva2
= {
483 .type
= AV_HWDEVICE_TYPE_DXVA2
,
486 .device_hwctx_size
= sizeof(AVDXVA2DeviceContext
),
487 .frames_hwctx_size
= sizeof(AVDXVA2FramesContext
),
488 .frames_priv_size
= sizeof(DXVA2FramesContext
),
490 .device_create
= dxva2_device_create
,
491 .frames_init
= dxva2_frames_init
,
492 .frames_uninit
= dxva2_frames_uninit
,
493 .frames_get_buffer
= dxva2_get_buffer
,
494 .transfer_get_formats
= dxva2_transfer_get_formats
,
495 .transfer_data_to
= dxva2_transfer_data
,
496 .transfer_data_from
= dxva2_transfer_data
,
497 .map_from
= dxva2_map_from
,
499 .pix_fmts
= (const enum AVPixelFormat
[]){ AV_PIX_FMT_DXVA2_VLD
, AV_PIX_FMT_NONE
},