blob: c782f1cdc36669f0ec54948ddce89fb2d6671d37
1 | /* |
2 | * DXVA2 HW acceleration. |
3 | * |
4 | * copyright (c) 2010 Laurent Aimar |
5 | * |
6 | * This file is part of FFmpeg. |
7 | * |
8 | * FFmpeg is free software; you can redistribute it and/or |
9 | * modify it under the terms of the GNU Lesser General Public |
10 | * License as published by the Free Software Foundation; either |
11 | * version 2.1 of the License, or (at your option) any later version. |
12 | * |
13 | * FFmpeg is distributed in the hope that it will be useful, |
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU |
16 | * Lesser General Public License for more details. |
17 | * |
18 | * You should have received a copy of the GNU Lesser General Public |
19 | * License along with FFmpeg; if not, write to the Free Software |
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
21 | */ |
22 | |
23 | #include <assert.h> |
24 | #include <string.h> |
25 | |
26 | #include "libavutil/log.h" |
27 | #include "libavutil/time.h" |
28 | |
29 | #include "avcodec.h" |
30 | #include "dxva2_internal.h" |
31 | |
32 | static void *get_surface(const AVFrame *frame) |
33 | { |
34 | return frame->data[3]; |
35 | } |
36 | |
37 | unsigned ff_dxva2_get_surface_index(const AVCodecContext *avctx, |
38 | const AVDXVAContext *ctx, |
39 | const AVFrame *frame) |
40 | { |
41 | void *surface = get_surface(frame); |
42 | unsigned i; |
43 | |
44 | #if CONFIG_D3D11VA |
45 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { |
46 | D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc; |
47 | ID3D11VideoDecoderOutputView_GetDesc((ID3D11VideoDecoderOutputView*) surface, &viewDesc); |
48 | return viewDesc.Texture2D.ArraySlice; |
49 | } |
50 | #endif |
51 | #if CONFIG_DXVA2 |
52 | for (i = 0; i < DXVA_CONTEXT_COUNT(avctx, ctx); i++) { |
53 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD && ctx->dxva2.surface[i] == surface) |
54 | return i; |
55 | } |
56 | #endif |
57 | |
58 | assert(0); |
59 | return 0; |
60 | } |
61 | |
62 | int ff_dxva2_commit_buffer(AVCodecContext *avctx, |
63 | AVDXVAContext *ctx, |
64 | DECODER_BUFFER_DESC *dsc, |
65 | unsigned type, const void *data, unsigned size, |
66 | unsigned mb_count) |
67 | { |
68 | void *dxva_data; |
69 | unsigned dxva_size; |
70 | int result; |
71 | HRESULT hr = 0; |
72 | |
73 | #if CONFIG_D3D11VA |
74 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) |
75 | hr = ID3D11VideoContext_GetDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, |
76 | D3D11VA_CONTEXT(ctx)->decoder, |
77 | type, |
78 | &dxva_size, &dxva_data); |
79 | #endif |
80 | #if CONFIG_DXVA2 |
81 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) |
82 | hr = IDirectXVideoDecoder_GetBuffer(DXVA2_CONTEXT(ctx)->decoder, type, |
83 | &dxva_data, &dxva_size); |
84 | #endif |
85 | if (FAILED(hr)) { |
86 | av_log(avctx, AV_LOG_ERROR, "Failed to get a buffer for %u: 0x%lx\n", |
87 | type, hr); |
88 | return -1; |
89 | } |
90 | if (size <= dxva_size) { |
91 | memcpy(dxva_data, data, size); |
92 | |
93 | #if CONFIG_D3D11VA |
94 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { |
95 | D3D11_VIDEO_DECODER_BUFFER_DESC *dsc11 = dsc; |
96 | memset(dsc11, 0, sizeof(*dsc11)); |
97 | dsc11->BufferType = type; |
98 | dsc11->DataSize = size; |
99 | dsc11->NumMBsInBuffer = mb_count; |
100 | } |
101 | #endif |
102 | #if CONFIG_DXVA2 |
103 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) { |
104 | DXVA2_DecodeBufferDesc *dsc2 = dsc; |
105 | memset(dsc2, 0, sizeof(*dsc2)); |
106 | dsc2->CompressedBufferType = type; |
107 | dsc2->DataSize = size; |
108 | dsc2->NumMBsInBuffer = mb_count; |
109 | } |
110 | #endif |
111 | |
112 | result = 0; |
113 | } else { |
114 | av_log(avctx, AV_LOG_ERROR, "Buffer for type %u was too small\n", type); |
115 | result = -1; |
116 | } |
117 | |
118 | #if CONFIG_D3D11VA |
119 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) |
120 | hr = ID3D11VideoContext_ReleaseDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, type); |
121 | #endif |
122 | #if CONFIG_DXVA2 |
123 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) |
124 | hr = IDirectXVideoDecoder_ReleaseBuffer(DXVA2_CONTEXT(ctx)->decoder, type); |
125 | #endif |
126 | if (FAILED(hr)) { |
127 | av_log(avctx, AV_LOG_ERROR, |
128 | "Failed to release buffer type %u: 0x%lx\n", |
129 | type, hr); |
130 | result = -1; |
131 | } |
132 | return result; |
133 | } |
134 | |
135 | int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame, |
136 | const void *pp, unsigned pp_size, |
137 | const void *qm, unsigned qm_size, |
138 | int (*commit_bs_si)(AVCodecContext *, |
139 | DECODER_BUFFER_DESC *bs, |
140 | DECODER_BUFFER_DESC *slice)) |
141 | { |
142 | AVDXVAContext *ctx = avctx->hwaccel_context; |
143 | unsigned buffer_count = 0; |
144 | #if CONFIG_D3D11VA |
145 | D3D11_VIDEO_DECODER_BUFFER_DESC buffer11[4]; |
146 | #endif |
147 | #if CONFIG_DXVA2 |
148 | DXVA2_DecodeBufferDesc buffer2[4]; |
149 | #endif |
150 | DECODER_BUFFER_DESC *buffer = NULL, *buffer_slice = NULL; |
151 | int result, runs = 0; |
152 | HRESULT hr; |
153 | unsigned type; |
154 | |
155 | do { |
156 | #if CONFIG_D3D11VA |
157 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { |
158 | if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) |
159 | WaitForSingleObjectEx(D3D11VA_CONTEXT(ctx)->context_mutex, INFINITE, FALSE); |
160 | hr = ID3D11VideoContext_DecoderBeginFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, |
161 | get_surface(frame), |
162 | 0, NULL); |
163 | } |
164 | #endif |
165 | #if CONFIG_DXVA2 |
166 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) |
167 | hr = IDirectXVideoDecoder_BeginFrame(DXVA2_CONTEXT(ctx)->decoder, |
168 | get_surface(frame), |
169 | NULL); |
170 | #endif |
171 | if (hr != E_PENDING || ++runs > 50) |
172 | break; |
173 | #if CONFIG_D3D11VA |
174 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) |
175 | if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) |
176 | ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); |
177 | #endif |
178 | av_usleep(2000); |
179 | } while(1); |
180 | |
181 | if (FAILED(hr)) { |
182 | av_log(avctx, AV_LOG_ERROR, "Failed to begin frame: 0x%lx\n", hr); |
183 | #if CONFIG_D3D11VA |
184 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) |
185 | if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) |
186 | ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); |
187 | #endif |
188 | return -1; |
189 | } |
190 | |
191 | #if CONFIG_D3D11VA |
192 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { |
193 | buffer = &buffer11[buffer_count]; |
194 | type = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS; |
195 | } |
196 | #endif |
197 | #if CONFIG_DXVA2 |
198 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) { |
199 | buffer = &buffer2[buffer_count]; |
200 | type = DXVA2_PictureParametersBufferType; |
201 | } |
202 | #endif |
203 | result = ff_dxva2_commit_buffer(avctx, ctx, buffer, |
204 | type, |
205 | pp, pp_size, 0); |
206 | if (result) { |
207 | av_log(avctx, AV_LOG_ERROR, |
208 | "Failed to add picture parameter buffer\n"); |
209 | goto end; |
210 | } |
211 | buffer_count++; |
212 | |
213 | if (qm_size > 0) { |
214 | #if CONFIG_D3D11VA |
215 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { |
216 | buffer = &buffer11[buffer_count]; |
217 | type = D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX; |
218 | } |
219 | #endif |
220 | #if CONFIG_DXVA2 |
221 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) { |
222 | buffer = &buffer2[buffer_count]; |
223 | type = DXVA2_InverseQuantizationMatrixBufferType; |
224 | } |
225 | #endif |
226 | result = ff_dxva2_commit_buffer(avctx, ctx, buffer, |
227 | type, |
228 | qm, qm_size, 0); |
229 | if (result) { |
230 | av_log(avctx, AV_LOG_ERROR, |
231 | "Failed to add inverse quantization matrix buffer\n"); |
232 | goto end; |
233 | } |
234 | buffer_count++; |
235 | } |
236 | |
237 | #if CONFIG_D3D11VA |
238 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { |
239 | buffer = &buffer11[buffer_count + 0]; |
240 | buffer_slice = &buffer11[buffer_count + 1]; |
241 | } |
242 | #endif |
243 | #if CONFIG_DXVA2 |
244 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) { |
245 | buffer = &buffer2[buffer_count + 0]; |
246 | buffer_slice = &buffer2[buffer_count + 1]; |
247 | } |
248 | #endif |
249 | |
250 | result = commit_bs_si(avctx, |
251 | buffer, |
252 | buffer_slice); |
253 | if (result) { |
254 | av_log(avctx, AV_LOG_ERROR, |
255 | "Failed to add bitstream or slice control buffer\n"); |
256 | goto end; |
257 | } |
258 | buffer_count += 2; |
259 | |
260 | /* TODO Film Grain when possible */ |
261 | |
262 | assert(buffer_count == 1 + (qm_size > 0) + 2); |
263 | |
264 | #if CONFIG_D3D11VA |
265 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) |
266 | hr = ID3D11VideoContext_SubmitDecoderBuffers(D3D11VA_CONTEXT(ctx)->video_context, |
267 | D3D11VA_CONTEXT(ctx)->decoder, |
268 | buffer_count, buffer11); |
269 | #endif |
270 | #if CONFIG_DXVA2 |
271 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) { |
272 | DXVA2_DecodeExecuteParams exec = { |
273 | .NumCompBuffers = buffer_count, |
274 | .pCompressedBuffers = buffer2, |
275 | .pExtensionData = NULL, |
276 | }; |
277 | hr = IDirectXVideoDecoder_Execute(DXVA2_CONTEXT(ctx)->decoder, &exec); |
278 | } |
279 | #endif |
280 | if (FAILED(hr)) { |
281 | av_log(avctx, AV_LOG_ERROR, "Failed to execute: 0x%lx\n", hr); |
282 | result = -1; |
283 | } |
284 | |
285 | end: |
286 | #if CONFIG_D3D11VA |
287 | if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) { |
288 | hr = ID3D11VideoContext_DecoderEndFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder); |
289 | if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE) |
290 | ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex); |
291 | } |
292 | #endif |
293 | #if CONFIG_DXVA2 |
294 | if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) |
295 | hr = IDirectXVideoDecoder_EndFrame(DXVA2_CONTEXT(ctx)->decoder, NULL); |
296 | #endif |
297 | if (FAILED(hr)) { |
298 | av_log(avctx, AV_LOG_ERROR, "Failed to end frame: 0x%lx\n", hr); |
299 | result = -1; |
300 | } |
301 | |
302 | return result; |
303 | } |
304 |