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
;
267 nb_planes
= av_pix_fmt_count_planes(dst
->format
);
269 hr
= IDirect3DSurface9_GetDesc(surface
, &surfaceDesc
);
271 av_log(ctx
, AV_LOG_ERROR
, "Error getting a surface description\n");
272 return AVERROR_UNKNOWN
;
275 if (!(flags
& AV_HWFRAME_MAP_WRITE
))
276 lock_flags
|= D3DLOCK_READONLY
;
277 if (flags
& AV_HWFRAME_MAP_OVERWRITE
)
278 lock_flags
|= D3DLOCK_DISCARD
;
280 hr
= IDirect3DSurface9_LockRect(surface
, &LockedRect
, NULL
, lock_flags
);
282 av_log(ctx
, AV_LOG_ERROR
, "Unable to lock DXVA2 surface\n");
283 return AVERROR_UNKNOWN
;
286 map
= av_mallocz(sizeof(*map
));
290 err
= ff_hwframe_map_create(src
->hw_frames_ctx
, dst
, src
,
291 dxva2_unmap_frame
, map
);
297 for (i
= 0; i
< nb_planes
; i
++)
298 dst
->linesize
[i
] = LockedRect
.Pitch
;
300 av_image_fill_pointers(dst
->data
, dst
->format
, surfaceDesc
.Height
,
301 (uint8_t*)LockedRect
.pBits
, dst
->linesize
);
303 if (dst
->format
== AV_PIX_FMT_PAL8
)
304 dst
->data
[1] = (uint8_t*)map
->palette_dummy
;
308 IDirect3DSurface9_UnlockRect(surface
);
312 static int dxva2_transfer_data(AVHWFramesContext
*ctx
, AVFrame
*dst
,
315 int download
= !!src
->hw_frames_ctx
;
320 map
= av_frame_alloc();
322 return AVERROR(ENOMEM
);
323 map
->format
= dst
->format
;
325 ret
= dxva2_map_frame(ctx
, map
, download ? src
: dst
,
326 download ? AV_HWFRAME_MAP_READ
:
327 AV_HWFRAME_MAP_WRITE
| AV_HWFRAME_MAP_OVERWRITE
);
332 ptrdiff_t src_linesize
[4], dst_linesize
[4];
333 for (i
= 0; i
< 4; i
++) {
334 dst_linesize
[i
] = dst
->linesize
[i
];
335 src_linesize
[i
] = map
->linesize
[i
];
337 av_image_copy_uc_from(dst
->data
, dst_linesize
, map
->data
, src_linesize
,
338 ctx
->sw_format
, src
->width
, src
->height
);
340 av_image_copy(map
->data
, map
->linesize
, src
->data
, src
->linesize
,
341 ctx
->sw_format
, src
->width
, src
->height
);
349 static int dxva2_map_from(AVHWFramesContext
*ctx
,
350 AVFrame
*dst
, const AVFrame
*src
, int flags
)
354 if (dst
->format
!= AV_PIX_FMT_NONE
&& dst
->format
!= ctx
->sw_format
)
355 return AVERROR(ENOSYS
);
356 dst
->format
= ctx
->sw_format
;
358 err
= dxva2_map_frame(ctx
, dst
, src
, flags
);
362 err
= av_frame_copy_props(dst
, src
);
369 static void dxva2_device_free(AVHWDeviceContext
*ctx
)
371 AVDXVA2DeviceContext
*hwctx
= ctx
->hwctx
;
372 DXVA2DevicePriv
*priv
= ctx
->user_opaque
;
374 if (hwctx
->devmgr
&& priv
->device_handle
!= INVALID_HANDLE_VALUE
)
375 IDirect3DDeviceManager9_CloseDeviceHandle(hwctx
->devmgr
, priv
->device_handle
);
378 IDirect3DDeviceManager9_Release(hwctx
->devmgr
);
380 if (priv
->d3d9device
)
381 IDirect3DDevice9_Release(priv
->d3d9device
);
384 IDirect3D9_Release(priv
->d3d9
);
387 FreeLibrary(priv
->d3dlib
);
390 FreeLibrary(priv
->dxva2lib
);
392 av_freep(&ctx
->user_opaque
);
395 static int dxva2_device_create(AVHWDeviceContext
*ctx
, const char *device
,
396 AVDictionary
*opts
, int flags
)
398 AVDXVA2DeviceContext
*hwctx
= ctx
->hwctx
;
399 DXVA2DevicePriv
*priv
;
401 pDirect3DCreate9
*createD3D
= NULL
;
402 pCreateDeviceManager9
*createDeviceManager
= NULL
;
403 D3DPRESENT_PARAMETERS d3dpp
= {0};
404 D3DDISPLAYMODE d3ddm
;
405 unsigned resetToken
= 0;
406 UINT adapter
= D3DADAPTER_DEFAULT
;
410 adapter
= atoi(device
);
412 priv
= av_mallocz(sizeof(*priv
));
414 return AVERROR(ENOMEM
);
416 ctx
->user_opaque
= priv
;
417 ctx
->free
= dxva2_device_free
;
419 priv
->device_handle
= INVALID_HANDLE_VALUE
;
421 priv
->d3dlib
= LoadLibrary("d3d9.dll");
423 av_log(ctx
, AV_LOG_ERROR
, "Failed to load D3D9 library\n");
424 return AVERROR_UNKNOWN
;
426 priv
->dxva2lib
= LoadLibrary("dxva2.dll");
427 if (!priv
->dxva2lib
) {
428 av_log(ctx
, AV_LOG_ERROR
, "Failed to load DXVA2 library\n");
429 return AVERROR_UNKNOWN
;
432 createD3D
= (pDirect3DCreate9
*)GetProcAddress(priv
->d3dlib
, "Direct3DCreate9");
434 av_log(ctx
, AV_LOG_ERROR
, "Failed to locate Direct3DCreate9\n");
435 return AVERROR_UNKNOWN
;
437 createDeviceManager
= (pCreateDeviceManager9
*)GetProcAddress(priv
->dxva2lib
,
438 "DXVA2CreateDirect3DDeviceManager9");
439 if (!createDeviceManager
) {
440 av_log(ctx
, AV_LOG_ERROR
, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n");
441 return AVERROR_UNKNOWN
;
444 priv
->d3d9
= createD3D(D3D_SDK_VERSION
);
446 av_log(ctx
, AV_LOG_ERROR
, "Failed to create IDirect3D object\n");
447 return AVERROR_UNKNOWN
;
450 IDirect3D9_GetAdapterDisplayMode(priv
->d3d9
, adapter
, &d3ddm
);
451 d3dpp
.Windowed
= TRUE
;
452 d3dpp
.BackBufferWidth
= 640;
453 d3dpp
.BackBufferHeight
= 480;
454 d3dpp
.BackBufferCount
= 0;
455 d3dpp
.BackBufferFormat
= d3ddm
.Format
;
456 d3dpp
.SwapEffect
= D3DSWAPEFFECT_DISCARD
;
457 d3dpp
.Flags
= D3DPRESENTFLAG_VIDEO
;
459 hr
= IDirect3D9_CreateDevice(priv
->d3d9
, adapter
, D3DDEVTYPE_HAL
, GetShellWindow(),
460 D3DCREATE_SOFTWARE_VERTEXPROCESSING
| D3DCREATE_MULTITHREADED
| D3DCREATE_FPU_PRESERVE
,
461 &d3dpp
, &priv
->d3d9device
);
463 av_log(ctx
, AV_LOG_ERROR
, "Failed to create Direct3D device\n");
464 return AVERROR_UNKNOWN
;
467 hr
= createDeviceManager(&resetToken
, &hwctx
->devmgr
);
469 av_log(ctx
, AV_LOG_ERROR
, "Failed to create Direct3D device manager\n");
470 return AVERROR_UNKNOWN
;
473 hr
= IDirect3DDeviceManager9_ResetDevice(hwctx
->devmgr
, priv
->d3d9device
, resetToken
);
475 av_log(ctx
, AV_LOG_ERROR
, "Failed to bind Direct3D device to device manager\n");
476 return AVERROR_UNKNOWN
;
479 hr
= IDirect3DDeviceManager9_OpenDeviceHandle(hwctx
->devmgr
, &priv
->device_handle
);
481 av_log(ctx
, AV_LOG_ERROR
, "Failed to open device handle\n");
482 return AVERROR_UNKNOWN
;
488 const HWContextType ff_hwcontext_type_dxva2
= {
489 .type
= AV_HWDEVICE_TYPE_DXVA2
,
492 .device_hwctx_size
= sizeof(AVDXVA2DeviceContext
),
493 .frames_hwctx_size
= sizeof(AVDXVA2FramesContext
),
494 .frames_priv_size
= sizeof(DXVA2FramesContext
),
496 .device_create
= dxva2_device_create
,
497 .frames_init
= dxva2_frames_init
,
498 .frames_uninit
= dxva2_frames_uninit
,
499 .frames_get_buffer
= dxva2_get_buffer
,
500 .transfer_get_formats
= dxva2_transfer_get_formats
,
501 .transfer_data_to
= dxva2_transfer_data
,
502 .transfer_data_from
= dxva2_transfer_data
,
503 .map_from
= dxva2_map_from
,
505 .pix_fmts
= (const enum AVPixelFormat
[]){ AV_PIX_FMT_DXVA2_VLD
, AV_PIX_FMT_NONE
},