91382167454d50b4a621e4db1ca0e4e52a502d4b
[ffmpeg.git] / libavcodec / dxva2.c
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 ff_dxva2_lock(AVCodecContext *avctx)
33 {
34 #if CONFIG_D3D11VA
35     if (ff_dxva2_is_d3d11(avctx)) {
36         AVDXVAContext *ctx = DXVA_CONTEXT(avctx);
37         if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE)
38             WaitForSingleObjectEx(D3D11VA_CONTEXT(ctx)->context_mutex, INFINITE, FALSE);
39     }
40 #endif
41 }
42
43 static void ff_dxva2_unlock(AVCodecContext *avctx)
44 {
45 #if CONFIG_D3D11VA
46     if (ff_dxva2_is_d3d11(avctx)) {
47         AVDXVAContext *ctx = DXVA_CONTEXT(avctx);
48         if (D3D11VA_CONTEXT(ctx)->context_mutex != INVALID_HANDLE_VALUE)
49             ReleaseMutex(D3D11VA_CONTEXT(ctx)->context_mutex);
50     }
51 #endif
52 }
53
54 static void *get_surface(const AVFrame *frame)
55 {
56     return frame->data[3];
57 }
58
59 unsigned ff_dxva2_get_surface_index(const AVCodecContext *avctx,
60                                     const AVDXVAContext *ctx,
61                                     const AVFrame *frame)
62 {
63     void *surface = get_surface(frame);
64     unsigned i;
65
66 #if CONFIG_D3D11VA
67     if (avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD) {
68         D3D11_VIDEO_DECODER_OUTPUT_VIEW_DESC viewDesc;
69         ID3D11VideoDecoderOutputView_GetDesc((ID3D11VideoDecoderOutputView*) surface, &viewDesc);
70         return viewDesc.Texture2D.ArraySlice;
71     }
72 #endif
73 #if CONFIG_DXVA2
74     for (i = 0; i < DXVA_CONTEXT_COUNT(avctx, ctx); i++) {
75         if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD && ctx->dxva2.surface[i] == surface)
76             return i;
77     }
78 #endif
79
80     assert(0);
81     return 0;
82 }
83
84 int ff_dxva2_commit_buffer(AVCodecContext *avctx,
85                            AVDXVAContext *ctx,
86                            DECODER_BUFFER_DESC *dsc,
87                            unsigned type, const void *data, unsigned size,
88                            unsigned mb_count)
89 {
90     void     *dxva_data;
91     unsigned dxva_size;
92     int      result;
93     HRESULT hr = 0;
94
95 #if CONFIG_D3D11VA
96     if (ff_dxva2_is_d3d11(avctx))
97         hr = ID3D11VideoContext_GetDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context,
98                                                  D3D11VA_CONTEXT(ctx)->decoder,
99                                                  type,
100                                                  &dxva_size, &dxva_data);
101 #endif
102 #if CONFIG_DXVA2
103     if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD)
104         hr = IDirectXVideoDecoder_GetBuffer(DXVA2_CONTEXT(ctx)->decoder, type,
105                                             &dxva_data, &dxva_size);
106 #endif
107     if (FAILED(hr)) {
108         av_log(avctx, AV_LOG_ERROR, "Failed to get a buffer for %u: 0x%x\n",
109                type, hr);
110         return -1;
111     }
112     if (size <= dxva_size) {
113         memcpy(dxva_data, data, size);
114
115 #if CONFIG_D3D11VA
116         if (ff_dxva2_is_d3d11(avctx)) {
117             D3D11_VIDEO_DECODER_BUFFER_DESC *dsc11 = dsc;
118             memset(dsc11, 0, sizeof(*dsc11));
119             dsc11->BufferType           = type;
120             dsc11->DataSize             = size;
121             dsc11->NumMBsInBuffer       = mb_count;
122         }
123 #endif
124 #if CONFIG_DXVA2
125         if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) {
126             DXVA2_DecodeBufferDesc *dsc2 = dsc;
127             memset(dsc2, 0, sizeof(*dsc2));
128             dsc2->CompressedBufferType = type;
129             dsc2->DataSize             = size;
130             dsc2->NumMBsInBuffer       = mb_count;
131         }
132 #endif
133
134         result = 0;
135     } else {
136         av_log(avctx, AV_LOG_ERROR, "Buffer for type %u was too small\n", type);
137         result = -1;
138     }
139
140 #if CONFIG_D3D11VA
141     if (ff_dxva2_is_d3d11(avctx))
142         hr = ID3D11VideoContext_ReleaseDecoderBuffer(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder, type);
143 #endif
144 #if CONFIG_DXVA2
145     if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD)
146         hr = IDirectXVideoDecoder_ReleaseBuffer(DXVA2_CONTEXT(ctx)->decoder, type);
147 #endif
148     if (FAILED(hr)) {
149         av_log(avctx, AV_LOG_ERROR,
150                "Failed to release buffer type %u: 0x%x\n",
151                type, hr);
152         result = -1;
153     }
154     return result;
155 }
156
157 int ff_dxva2_common_end_frame(AVCodecContext *avctx, AVFrame *frame,
158                               const void *pp, unsigned pp_size,
159                               const void *qm, unsigned qm_size,
160                               int (*commit_bs_si)(AVCodecContext *,
161                                                   DECODER_BUFFER_DESC *bs,
162                                                   DECODER_BUFFER_DESC *slice))
163 {
164     AVDXVAContext *ctx = DXVA_CONTEXT(avctx);
165     unsigned               buffer_count = 0;
166 #if CONFIG_D3D11VA
167     D3D11_VIDEO_DECODER_BUFFER_DESC buffer11[4];
168 #endif
169 #if CONFIG_DXVA2
170     DXVA2_DecodeBufferDesc          buffer2[4];
171 #endif
172     DECODER_BUFFER_DESC             *buffer = NULL, *buffer_slice = NULL;
173     int result, runs = 0;
174     HRESULT hr;
175     unsigned type;
176
177     do {
178         ff_dxva2_lock(avctx);
179 #if CONFIG_D3D11VA
180         if (ff_dxva2_is_d3d11(avctx))
181             hr = ID3D11VideoContext_DecoderBeginFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder,
182                                                       get_surface(frame),
183                                                       0, NULL);
184 #endif
185 #if CONFIG_DXVA2
186         if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD)
187             hr = IDirectXVideoDecoder_BeginFrame(DXVA2_CONTEXT(ctx)->decoder,
188                                                  get_surface(frame),
189                                                  NULL);
190 #endif
191         if (hr != E_PENDING || ++runs > 50)
192             break;
193         ff_dxva2_unlock(avctx);
194         av_usleep(2000);
195     } while(1);
196
197     if (FAILED(hr)) {
198         av_log(avctx, AV_LOG_ERROR, "Failed to begin frame: 0x%x\n", hr);
199         ff_dxva2_unlock(avctx);
200         return -1;
201     }
202
203 #if CONFIG_D3D11VA
204     if (ff_dxva2_is_d3d11(avctx)) {
205         buffer = &buffer11[buffer_count];
206         type = D3D11_VIDEO_DECODER_BUFFER_PICTURE_PARAMETERS;
207     }
208 #endif
209 #if CONFIG_DXVA2
210     if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) {
211         buffer = &buffer2[buffer_count];
212         type = DXVA2_PictureParametersBufferType;
213     }
214 #endif
215     result = ff_dxva2_commit_buffer(avctx, ctx, buffer,
216                                     type,
217                                     pp, pp_size, 0);
218     if (result) {
219         av_log(avctx, AV_LOG_ERROR,
220                "Failed to add picture parameter buffer\n");
221         goto end;
222     }
223     buffer_count++;
224
225     if (qm_size > 0) {
226 #if CONFIG_D3D11VA
227         if (ff_dxva2_is_d3d11(avctx)) {
228             buffer = &buffer11[buffer_count];
229             type = D3D11_VIDEO_DECODER_BUFFER_INVERSE_QUANTIZATION_MATRIX;
230         }
231 #endif
232 #if CONFIG_DXVA2
233         if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) {
234             buffer = &buffer2[buffer_count];
235             type = DXVA2_InverseQuantizationMatrixBufferType;
236         }
237 #endif
238         result = ff_dxva2_commit_buffer(avctx, ctx, buffer,
239                                         type,
240                                         qm, qm_size, 0);
241         if (result) {
242             av_log(avctx, AV_LOG_ERROR,
243                    "Failed to add inverse quantization matrix buffer\n");
244             goto end;
245         }
246         buffer_count++;
247     }
248
249 #if CONFIG_D3D11VA
250     if (ff_dxva2_is_d3d11(avctx)) {
251         buffer       = &buffer11[buffer_count + 0];
252         buffer_slice = &buffer11[buffer_count + 1];
253     }
254 #endif
255 #if CONFIG_DXVA2
256     if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) {
257         buffer       = &buffer2[buffer_count + 0];
258         buffer_slice = &buffer2[buffer_count + 1];
259     }
260 #endif
261
262     result = commit_bs_si(avctx,
263                           buffer,
264                           buffer_slice);
265     if (result) {
266         av_log(avctx, AV_LOG_ERROR,
267                "Failed to add bitstream or slice control buffer\n");
268         goto end;
269     }
270     buffer_count += 2;
271
272     /* TODO Film Grain when possible */
273
274     assert(buffer_count == 1 + (qm_size > 0) + 2);
275
276 #if CONFIG_D3D11VA
277     if (ff_dxva2_is_d3d11(avctx))
278         hr = ID3D11VideoContext_SubmitDecoderBuffers(D3D11VA_CONTEXT(ctx)->video_context,
279                                                      D3D11VA_CONTEXT(ctx)->decoder,
280                                                      buffer_count, buffer11);
281 #endif
282 #if CONFIG_DXVA2
283     if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD) {
284         DXVA2_DecodeExecuteParams exec = {
285             .NumCompBuffers     = buffer_count,
286             .pCompressedBuffers = buffer2,
287             .pExtensionData     = NULL,
288         };
289         hr = IDirectXVideoDecoder_Execute(DXVA2_CONTEXT(ctx)->decoder, &exec);
290     }
291 #endif
292     if (FAILED(hr)) {
293         av_log(avctx, AV_LOG_ERROR, "Failed to execute: 0x%x\n", hr);
294         result = -1;
295     }
296
297 end:
298 #if CONFIG_D3D11VA
299     if (ff_dxva2_is_d3d11(avctx))
300         hr = ID3D11VideoContext_DecoderEndFrame(D3D11VA_CONTEXT(ctx)->video_context, D3D11VA_CONTEXT(ctx)->decoder);
301 #endif
302 #if CONFIG_DXVA2
303     if (avctx->pix_fmt == AV_PIX_FMT_DXVA2_VLD)
304         hr = IDirectXVideoDecoder_EndFrame(DXVA2_CONTEXT(ctx)->decoder, NULL);
305 #endif
306     ff_dxva2_unlock(avctx);
307     if (FAILED(hr)) {
308         av_log(avctx, AV_LOG_ERROR, "Failed to end frame: 0x%x\n", hr);
309         result = -1;
310     }
311
312     return result;
313 }
314
315 int ff_dxva2_is_d3d11(const AVCodecContext *avctx)
316 {
317     if (CONFIG_D3D11VA)
318         return avctx->pix_fmt == AV_PIX_FMT_D3D11VA_VLD;
319     else
320         return 0;
321 }