Commit | Line | Data |
---|---|---|
c46db38c AK |
1 | /* |
2 | * This file is part of Libav. | |
3 | * | |
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. | |
8 | * | |
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. | |
13 | * | |
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 | |
17 | */ | |
18 | ||
19 | #include <windows.h> | |
20 | ||
21 | #if !defined(_WIN32_WINNT) || _WIN32_WINNT < 0x0600 | |
22 | #undef _WIN32_WINNT | |
23 | #define _WIN32_WINNT 0x0600 | |
24 | #endif | |
25 | #define DXVA2API_USE_BITFIELDS | |
26 | #define COBJMACROS | |
27 | ||
28 | #include <d3d9.h> | |
29 | #include <dxva2api.h> | |
30 | #include <initguid.h> | |
31 | ||
32 | #include "common.h" | |
33 | #include "hwcontext.h" | |
34 | #include "hwcontext_dxva2.h" | |
35 | #include "hwcontext_internal.h" | |
36 | #include "imgutils.h" | |
37 | #include "pixdesc.h" | |
38 | #include "pixfmt.h" | |
39 | ||
8e70385a AK |
40 | typedef IDirect3D9* WINAPI pDirect3DCreate9(UINT); |
41 | typedef HRESULT WINAPI pCreateDeviceManager9(UINT *, IDirect3DDeviceManager9 **); | |
42 | ||
c46db38c AK |
43 | typedef struct DXVA2FramesContext { |
44 | IDirect3DSurface9 **surfaces_internal; | |
45 | int nb_surfaces_used; | |
46 | ||
47 | HANDLE device_handle; | |
48 | IDirectXVideoAccelerationService *service; | |
49 | ||
50 | D3DFORMAT format; | |
51 | } DXVA2FramesContext; | |
52 | ||
8e70385a AK |
53 | typedef struct DXVA2DevicePriv { |
54 | HMODULE d3dlib; | |
55 | HMODULE dxva2lib; | |
56 | ||
57 | HANDLE device_handle; | |
58 | ||
59 | IDirect3D9 *d3d9; | |
60 | IDirect3DDevice9 *d3d9device; | |
61 | } DXVA2DevicePriv; | |
62 | ||
c46db38c AK |
63 | static const struct { |
64 | D3DFORMAT d3d_format; | |
65 | enum AVPixelFormat pix_fmt; | |
66 | } supported_formats[] = { | |
67 | { MKTAG('N', 'V', '1', '2'), AV_PIX_FMT_NV12 }, | |
2ef87815 | 68 | { MKTAG('P', '0', '1', '0'), AV_PIX_FMT_P010 }, |
c46db38c AK |
69 | }; |
70 | ||
71 | DEFINE_GUID(video_decoder_service, 0xfc51a551, 0xd5e7, 0x11d9, 0xaf, 0x55, 0x00, 0x05, 0x4e, 0x43, 0xff, 0x02); | |
72 | DEFINE_GUID(video_processor_service, 0xfc51a552, 0xd5e7, 0x11d9, 0xaf, 0x55, 0x00, 0x05, 0x4e, 0x43, 0xff, 0x02); | |
73 | ||
74 | static void dxva2_frames_uninit(AVHWFramesContext *ctx) | |
75 | { | |
76 | AVDXVA2DeviceContext *device_hwctx = ctx->device_ctx->hwctx; | |
77 | AVDXVA2FramesContext *frames_hwctx = ctx->hwctx; | |
78 | DXVA2FramesContext *s = ctx->internal->priv; | |
79 | int i; | |
80 | ||
81 | if (frames_hwctx->decoder_to_release) | |
82 | IDirectXVideoDecoder_Release(frames_hwctx->decoder_to_release); | |
83 | ||
84 | if (s->surfaces_internal) { | |
85 | for (i = 0; i < frames_hwctx->nb_surfaces; i++) { | |
86 | if (s->surfaces_internal[i]) | |
87 | IDirect3DSurface9_Release(s->surfaces_internal[i]); | |
88 | } | |
89 | } | |
90 | av_freep(&s->surfaces_internal); | |
91 | ||
92 | if (s->service) { | |
93 | IDirectXVideoAccelerationService_Release(s->service); | |
94 | s->service = NULL; | |
95 | } | |
96 | ||
97 | if (s->device_handle != INVALID_HANDLE_VALUE) { | |
98 | IDirect3DDeviceManager9_CloseDeviceHandle(device_hwctx->devmgr, s->device_handle); | |
99 | s->device_handle = INVALID_HANDLE_VALUE; | |
100 | } | |
101 | } | |
102 | ||
103 | static AVBufferRef *dxva2_pool_alloc(void *opaque, int size) | |
104 | { | |
105 | AVHWFramesContext *ctx = (AVHWFramesContext*)opaque; | |
106 | DXVA2FramesContext *s = ctx->internal->priv; | |
107 | AVDXVA2FramesContext *hwctx = ctx->hwctx; | |
108 | ||
109 | if (s->nb_surfaces_used < hwctx->nb_surfaces) { | |
110 | s->nb_surfaces_used++; | |
111 | return av_buffer_create((uint8_t*)s->surfaces_internal[s->nb_surfaces_used - 1], | |
112 | sizeof(*hwctx->surfaces), NULL, 0, 0); | |
113 | } | |
114 | ||
115 | return NULL; | |
116 | } | |
117 | ||
118 | static int dxva2_init_pool(AVHWFramesContext *ctx) | |
119 | { | |
120 | AVDXVA2FramesContext *frames_hwctx = ctx->hwctx; | |
121 | AVDXVA2DeviceContext *device_hwctx = ctx->device_ctx->hwctx; | |
122 | DXVA2FramesContext *s = ctx->internal->priv; | |
123 | int decode = (frames_hwctx->surface_type == DXVA2_VideoDecoderRenderTarget); | |
124 | ||
125 | int i; | |
126 | HRESULT hr; | |
127 | ||
128 | if (ctx->initial_pool_size <= 0) | |
129 | return 0; | |
130 | ||
131 | hr = IDirect3DDeviceManager9_OpenDeviceHandle(device_hwctx->devmgr, &s->device_handle); | |
132 | if (FAILED(hr)) { | |
133 | av_log(ctx, AV_LOG_ERROR, "Failed to open device handle\n"); | |
134 | return AVERROR_UNKNOWN; | |
135 | } | |
136 | ||
137 | hr = IDirect3DDeviceManager9_GetVideoService(device_hwctx->devmgr, | |
138 | s->device_handle, | |
139 | decode ? &video_decoder_service : &video_processor_service, | |
140 | (void **)&s->service); | |
141 | if (FAILED(hr)) { | |
142 | av_log(ctx, AV_LOG_ERROR, "Failed to create the video service\n"); | |
143 | return AVERROR_UNKNOWN; | |
144 | } | |
145 | ||
146 | for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) { | |
147 | if (ctx->sw_format == supported_formats[i].pix_fmt) { | |
148 | s->format = supported_formats[i].d3d_format; | |
149 | break; | |
150 | } | |
151 | } | |
152 | if (i == FF_ARRAY_ELEMS(supported_formats)) { | |
153 | av_log(ctx, AV_LOG_ERROR, "Unsupported pixel format: %s\n", | |
154 | av_get_pix_fmt_name(ctx->sw_format)); | |
155 | return AVERROR(EINVAL); | |
156 | } | |
157 | ||
158 | s->surfaces_internal = av_mallocz_array(ctx->initial_pool_size, | |
159 | sizeof(*s->surfaces_internal)); | |
160 | if (!s->surfaces_internal) | |
161 | return AVERROR(ENOMEM); | |
162 | ||
163 | hr = IDirectXVideoAccelerationService_CreateSurface(s->service, | |
164 | ctx->width, ctx->height, | |
165 | ctx->initial_pool_size - 1, | |
166 | s->format, D3DPOOL_DEFAULT, 0, | |
167 | frames_hwctx->surface_type, | |
168 | s->surfaces_internal, NULL); | |
169 | if (FAILED(hr)) { | |
170 | av_log(ctx, AV_LOG_ERROR, "Could not create the surfaces\n"); | |
171 | return AVERROR_UNKNOWN; | |
172 | } | |
173 | ||
174 | ctx->internal->pool_internal = av_buffer_pool_init2(sizeof(*s->surfaces_internal), | |
175 | ctx, dxva2_pool_alloc, NULL); | |
176 | if (!ctx->internal->pool_internal) | |
177 | return AVERROR(ENOMEM); | |
178 | ||
179 | frames_hwctx->surfaces = s->surfaces_internal; | |
180 | frames_hwctx->nb_surfaces = ctx->initial_pool_size; | |
181 | ||
182 | return 0; | |
183 | } | |
184 | ||
185 | static int dxva2_frames_init(AVHWFramesContext *ctx) | |
186 | { | |
187 | AVDXVA2FramesContext *hwctx = ctx->hwctx; | |
188 | DXVA2FramesContext *s = ctx->internal->priv; | |
189 | int ret; | |
190 | ||
191 | if (hwctx->surface_type != DXVA2_VideoDecoderRenderTarget && | |
192 | hwctx->surface_type != DXVA2_VideoProcessorRenderTarget) { | |
193 | av_log(ctx, AV_LOG_ERROR, "Unknown surface type: %lu\n", | |
194 | hwctx->surface_type); | |
195 | return AVERROR(EINVAL); | |
196 | } | |
197 | ||
198 | s->device_handle = INVALID_HANDLE_VALUE; | |
199 | ||
200 | /* init the frame pool if the caller didn't provide one */ | |
201 | if (!ctx->pool) { | |
202 | ret = dxva2_init_pool(ctx); | |
203 | if (ret < 0) { | |
204 | av_log(ctx, AV_LOG_ERROR, "Error creating an internal frame pool\n"); | |
205 | return ret; | |
206 | } | |
207 | } | |
208 | ||
209 | return 0; | |
210 | } | |
211 | ||
212 | static int dxva2_get_buffer(AVHWFramesContext *ctx, AVFrame *frame) | |
213 | { | |
214 | frame->buf[0] = av_buffer_pool_get(ctx->pool); | |
215 | if (!frame->buf[0]) | |
216 | return AVERROR(ENOMEM); | |
217 | ||
218 | frame->data[3] = frame->buf[0]->data; | |
219 | frame->format = AV_PIX_FMT_DXVA2_VLD; | |
220 | frame->width = ctx->width; | |
221 | frame->height = ctx->height; | |
222 | ||
223 | return 0; | |
224 | } | |
225 | ||
226 | static int dxva2_transfer_get_formats(AVHWFramesContext *ctx, | |
227 | enum AVHWFrameTransferDirection dir, | |
228 | enum AVPixelFormat **formats) | |
229 | { | |
230 | enum AVPixelFormat *fmts; | |
231 | ||
232 | fmts = av_malloc_array(2, sizeof(*fmts)); | |
233 | if (!fmts) | |
234 | return AVERROR(ENOMEM); | |
235 | ||
236 | fmts[0] = ctx->sw_format; | |
237 | fmts[1] = AV_PIX_FMT_NONE; | |
238 | ||
239 | *formats = fmts; | |
240 | ||
241 | return 0; | |
242 | } | |
243 | ||
91097376 AK |
244 | static void dxva2_unmap_frame(AVHWFramesContext *ctx, HWMapDescriptor *hwmap) |
245 | { | |
246 | IDirect3DSurface9 *surface = (IDirect3DSurface9*)hwmap->source->data[3]; | |
247 | IDirect3DSurface9_UnlockRect(surface); | |
248 | } | |
249 | ||
250 | static int dxva2_map_frame(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src, | |
251 | int flags) | |
c46db38c | 252 | { |
91097376 | 253 | IDirect3DSurface9 *surface = (IDirect3DSurface9*)src->data[3]; |
c46db38c AK |
254 | D3DSURFACE_DESC surfaceDesc; |
255 | D3DLOCKED_RECT LockedRect; | |
256 | HRESULT hr; | |
91097376 | 257 | int i, err, nb_planes; |
c46db38c | 258 | |
91097376 | 259 | nb_planes = av_pix_fmt_count_planes(dst->format); |
c46db38c AK |
260 | |
261 | hr = IDirect3DSurface9_GetDesc(surface, &surfaceDesc); | |
262 | if (FAILED(hr)) { | |
263 | av_log(ctx, AV_LOG_ERROR, "Error getting a surface description\n"); | |
264 | return AVERROR_UNKNOWN; | |
265 | } | |
266 | ||
267 | hr = IDirect3DSurface9_LockRect(surface, &LockedRect, NULL, | |
91097376 | 268 | flags & AV_HWFRAME_MAP_READ ? D3DLOCK_READONLY : D3DLOCK_DISCARD); |
c46db38c AK |
269 | if (FAILED(hr)) { |
270 | av_log(ctx, AV_LOG_ERROR, "Unable to lock DXVA2 surface\n"); | |
271 | return AVERROR_UNKNOWN; | |
272 | } | |
273 | ||
91097376 AK |
274 | err = ff_hwframe_map_create(src->hw_frames_ctx, dst, src, |
275 | dxva2_unmap_frame, NULL); | |
276 | if (err < 0) | |
277 | goto fail; | |
278 | ||
279 | for (i = 0; i < nb_planes; i++) | |
280 | dst->linesize[i] = LockedRect.Pitch; | |
281 | ||
282 | av_image_fill_pointers(dst->data, dst->format, surfaceDesc.Height, | |
283 | (uint8_t*)LockedRect.pBits, dst->linesize); | |
284 | ||
285 | return 0; | |
286 | fail: | |
287 | IDirect3DSurface9_UnlockRect(surface); | |
288 | return err; | |
289 | } | |
290 | ||
291 | static int dxva2_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, | |
292 | const AVFrame *src) | |
293 | { | |
294 | int download = !!src->hw_frames_ctx; | |
295 | ||
296 | AVFrame *map; | |
297 | int ret, i; | |
2ef87815 | 298 | |
91097376 AK |
299 | map = av_frame_alloc(); |
300 | if (!map) | |
301 | return AVERROR(ENOMEM); | |
302 | map->format = dst->format; | |
303 | ||
304 | ret = dxva2_map_frame(ctx, map, download ? src : dst, | |
305 | download ? AV_HWFRAME_MAP_READ : AV_HWFRAME_MAP_WRITE); | |
306 | if (ret < 0) | |
307 | goto fail; | |
2ef87815 | 308 | |
c46db38c | 309 | if (download) { |
91097376 | 310 | ptrdiff_t src_linesize[4], dst_linesize[4]; |
f01f7a78 | 311 | for (i = 0; i < 4; i++) { |
91097376 AK |
312 | dst_linesize[i] = dst->linesize[i]; |
313 | src_linesize[i] = map->linesize[i]; | |
f01f7a78 | 314 | } |
91097376 | 315 | av_image_copy_uc_from(dst->data, dst_linesize, map->data, src_linesize, |
f01f7a78 | 316 | ctx->sw_format, src->width, src->height); |
c46db38c | 317 | } else { |
91097376 | 318 | av_image_copy(map->data, map->linesize, src->data, src->linesize, |
2ef87815 | 319 | ctx->sw_format, src->width, src->height); |
c46db38c AK |
320 | } |
321 | ||
91097376 AK |
322 | fail: |
323 | av_frame_free(&map); | |
324 | return ret; | |
325 | } | |
326 | ||
327 | static int dxva2_map_from(AVHWFramesContext *ctx, | |
328 | AVFrame *dst, const AVFrame *src, int flags) | |
329 | { | |
330 | int err; | |
331 | ||
332 | err = dxva2_map_frame(ctx, dst, src, flags); | |
333 | if (err < 0) | |
334 | return err; | |
335 | ||
336 | err = av_frame_copy_props(dst, src); | |
337 | if (err < 0) | |
338 | return err; | |
c46db38c AK |
339 | |
340 | return 0; | |
341 | } | |
342 | ||
8e70385a AK |
343 | static void dxva2_device_free(AVHWDeviceContext *ctx) |
344 | { | |
345 | AVDXVA2DeviceContext *hwctx = ctx->hwctx; | |
346 | DXVA2DevicePriv *priv = ctx->user_opaque; | |
347 | ||
348 | if (hwctx->devmgr && priv->device_handle != INVALID_HANDLE_VALUE) | |
349 | IDirect3DDeviceManager9_CloseDeviceHandle(hwctx->devmgr, priv->device_handle); | |
350 | ||
351 | if (hwctx->devmgr) | |
352 | IDirect3DDeviceManager9_Release(hwctx->devmgr); | |
353 | ||
354 | if (priv->d3d9device) | |
355 | IDirect3DDevice9_Release(priv->d3d9device); | |
356 | ||
357 | if (priv->d3d9) | |
358 | IDirect3D9_Release(priv->d3d9); | |
359 | ||
360 | if (priv->d3dlib) | |
361 | FreeLibrary(priv->d3dlib); | |
362 | ||
363 | if (priv->dxva2lib) | |
364 | FreeLibrary(priv->dxva2lib); | |
365 | ||
366 | av_freep(&ctx->user_opaque); | |
367 | } | |
368 | ||
369 | static int dxva2_device_create(AVHWDeviceContext *ctx, const char *device, | |
370 | AVDictionary *opts, int flags) | |
371 | { | |
372 | AVDXVA2DeviceContext *hwctx = ctx->hwctx; | |
373 | DXVA2DevicePriv *priv; | |
374 | ||
375 | pDirect3DCreate9 *createD3D = NULL; | |
376 | pCreateDeviceManager9 *createDeviceManager = NULL; | |
377 | D3DPRESENT_PARAMETERS d3dpp = {0}; | |
378 | D3DDISPLAYMODE d3ddm; | |
379 | unsigned resetToken = 0; | |
380 | UINT adapter = D3DADAPTER_DEFAULT; | |
381 | HRESULT hr; | |
382 | ||
383 | if (device) | |
384 | adapter = atoi(device); | |
385 | ||
386 | priv = av_mallocz(sizeof(*priv)); | |
387 | if (!priv) | |
388 | return AVERROR(ENOMEM); | |
389 | ||
390 | ctx->user_opaque = priv; | |
391 | ctx->free = dxva2_device_free; | |
392 | ||
393 | priv->device_handle = INVALID_HANDLE_VALUE; | |
394 | ||
395 | priv->d3dlib = LoadLibrary("d3d9.dll"); | |
396 | if (!priv->d3dlib) { | |
397 | av_log(ctx, AV_LOG_ERROR, "Failed to load D3D9 library\n"); | |
398 | return AVERROR_UNKNOWN; | |
399 | } | |
400 | priv->dxva2lib = LoadLibrary("dxva2.dll"); | |
401 | if (!priv->dxva2lib) { | |
402 | av_log(ctx, AV_LOG_ERROR, "Failed to load DXVA2 library\n"); | |
403 | return AVERROR_UNKNOWN; | |
404 | } | |
405 | ||
406 | createD3D = (pDirect3DCreate9 *)GetProcAddress(priv->d3dlib, "Direct3DCreate9"); | |
407 | if (!createD3D) { | |
408 | av_log(ctx, AV_LOG_ERROR, "Failed to locate Direct3DCreate9\n"); | |
409 | return AVERROR_UNKNOWN; | |
410 | } | |
411 | createDeviceManager = (pCreateDeviceManager9 *)GetProcAddress(priv->dxva2lib, | |
412 | "DXVA2CreateDirect3DDeviceManager9"); | |
413 | if (!createDeviceManager) { | |
414 | av_log(ctx, AV_LOG_ERROR, "Failed to locate DXVA2CreateDirect3DDeviceManager9\n"); | |
415 | return AVERROR_UNKNOWN; | |
416 | } | |
417 | ||
418 | priv->d3d9 = createD3D(D3D_SDK_VERSION); | |
419 | if (!priv->d3d9) { | |
420 | av_log(ctx, AV_LOG_ERROR, "Failed to create IDirect3D object\n"); | |
421 | return AVERROR_UNKNOWN; | |
422 | } | |
423 | ||
424 | IDirect3D9_GetAdapterDisplayMode(priv->d3d9, adapter, &d3ddm); | |
425 | d3dpp.Windowed = TRUE; | |
426 | d3dpp.BackBufferWidth = 640; | |
427 | d3dpp.BackBufferHeight = 480; | |
428 | d3dpp.BackBufferCount = 0; | |
429 | d3dpp.BackBufferFormat = d3ddm.Format; | |
430 | d3dpp.SwapEffect = D3DSWAPEFFECT_DISCARD; | |
431 | d3dpp.Flags = D3DPRESENTFLAG_VIDEO; | |
432 | ||
433 | hr = IDirect3D9_CreateDevice(priv->d3d9, adapter, D3DDEVTYPE_HAL, GetShellWindow(), | |
434 | D3DCREATE_SOFTWARE_VERTEXPROCESSING | D3DCREATE_MULTITHREADED | D3DCREATE_FPU_PRESERVE, | |
435 | &d3dpp, &priv->d3d9device); | |
436 | if (FAILED(hr)) { | |
437 | av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device\n"); | |
438 | return AVERROR_UNKNOWN; | |
439 | } | |
440 | ||
441 | hr = createDeviceManager(&resetToken, &hwctx->devmgr); | |
442 | if (FAILED(hr)) { | |
443 | av_log(ctx, AV_LOG_ERROR, "Failed to create Direct3D device manager\n"); | |
444 | return AVERROR_UNKNOWN; | |
445 | } | |
446 | ||
447 | hr = IDirect3DDeviceManager9_ResetDevice(hwctx->devmgr, priv->d3d9device, resetToken); | |
448 | if (FAILED(hr)) { | |
449 | av_log(ctx, AV_LOG_ERROR, "Failed to bind Direct3D device to device manager\n"); | |
450 | return AVERROR_UNKNOWN; | |
451 | } | |
452 | ||
453 | hr = IDirect3DDeviceManager9_OpenDeviceHandle(hwctx->devmgr, &priv->device_handle); | |
454 | if (FAILED(hr)) { | |
455 | av_log(ctx, AV_LOG_ERROR, "Failed to open device handle\n"); | |
456 | return AVERROR_UNKNOWN; | |
457 | } | |
458 | ||
459 | return 0; | |
460 | } | |
461 | ||
c46db38c AK |
462 | const HWContextType ff_hwcontext_type_dxva2 = { |
463 | .type = AV_HWDEVICE_TYPE_DXVA2, | |
464 | .name = "DXVA2", | |
465 | ||
466 | .device_hwctx_size = sizeof(AVDXVA2DeviceContext), | |
467 | .frames_hwctx_size = sizeof(AVDXVA2FramesContext), | |
468 | .frames_priv_size = sizeof(DXVA2FramesContext), | |
469 | ||
8e70385a | 470 | .device_create = dxva2_device_create, |
c46db38c AK |
471 | .frames_init = dxva2_frames_init, |
472 | .frames_uninit = dxva2_frames_uninit, | |
473 | .frames_get_buffer = dxva2_get_buffer, | |
474 | .transfer_get_formats = dxva2_transfer_get_formats, | |
475 | .transfer_data_to = dxva2_transfer_data, | |
476 | .transfer_data_from = dxva2_transfer_data, | |
91097376 | 477 | .map_from = dxva2_map_from, |
c46db38c AK |
478 | |
479 | .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_DXVA2_VLD, AV_PIX_FMT_NONE }, | |
480 | }; |