Commit | Line | Data |
---|---|---|
d0a63d8b AK |
1 | /* |
2 | * Intel MediaSDK QSV encoder/decoder shared code | |
3 | * | |
4 | * This file is part of Libav. | |
5 | * | |
6 | * Libav is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2.1 of the License, or (at your option) any later version. | |
10 | * | |
11 | * Libav is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with Libav; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | #include <mfx/mfxvideo.h> | |
66acb76b | 22 | #include <mfx/mfxplugin.h> |
d0a63d8b | 23 | |
66acb76b AK |
24 | #include <stdio.h> |
25 | #include <string.h> | |
26 | ||
27 | #include "libavutil/avstring.h" | |
a0524d9b | 28 | #include "libavutil/common.h" |
d0a63d8b | 29 | #include "libavutil/error.h" |
a0524d9b AK |
30 | #include "libavutil/hwcontext.h" |
31 | #include "libavutil/hwcontext_qsv.h" | |
4ab61cd9 | 32 | #include "libavutil/imgutils.h" |
d0a63d8b AK |
33 | |
34 | #include "avcodec.h" | |
35 | #include "qsv_internal.h" | |
36 | ||
e0b16457 MT |
37 | #if QSV_VERSION_ATLEAST(1, 12) |
38 | #include "mfx/mfxvp8.h" | |
39 | #endif | |
40 | ||
d0a63d8b AK |
41 | int ff_qsv_codec_id_to_mfx(enum AVCodecID codec_id) |
42 | { | |
43 | switch (codec_id) { | |
44 | case AV_CODEC_ID_H264: | |
45 | return MFX_CODEC_AVC; | |
66acb76b AK |
46 | #if QSV_VERSION_ATLEAST(1, 8) |
47 | case AV_CODEC_ID_HEVC: | |
48 | return MFX_CODEC_HEVC; | |
49 | #endif | |
d0a63d8b AK |
50 | case AV_CODEC_ID_MPEG1VIDEO: |
51 | case AV_CODEC_ID_MPEG2VIDEO: | |
52 | return MFX_CODEC_MPEG2; | |
53 | case AV_CODEC_ID_VC1: | |
54 | return MFX_CODEC_VC1; | |
e0b16457 MT |
55 | #if QSV_VERSION_ATLEAST(1, 12) |
56 | case AV_CODEC_ID_VP8: | |
57 | return MFX_CODEC_VP8; | |
58 | #endif | |
d0a63d8b AK |
59 | default: |
60 | break; | |
61 | } | |
62 | ||
63 | return AVERROR(ENOSYS); | |
64 | } | |
65 | ||
cd1047f3 MT |
66 | int ff_qsv_profile_to_mfx(enum AVCodecID codec_id, int profile) |
67 | { | |
68 | if (profile == FF_PROFILE_UNKNOWN) | |
69 | return MFX_PROFILE_UNKNOWN; | |
70 | switch (codec_id) { | |
71 | case AV_CODEC_ID_H264: | |
72 | case AV_CODEC_ID_HEVC: | |
73 | return profile; | |
74 | case AV_CODEC_ID_VC1: | |
75 | return 4 * profile + 1; | |
76 | case AV_CODEC_ID_MPEG2VIDEO: | |
77 | return 0x10 * profile; | |
78 | } | |
79 | return MFX_PROFILE_UNKNOWN; | |
80 | } | |
81 | ||
95414eb2 AK |
82 | static const struct { |
83 | mfxStatus mfxerr; | |
84 | int averr; | |
85 | const char *desc; | |
86 | } qsv_errors[] = { | |
87 | { MFX_ERR_NONE, 0, "success" }, | |
88 | { MFX_ERR_UNKNOWN, AVERROR_UNKNOWN, "unknown error" }, | |
89 | { MFX_ERR_NULL_PTR, AVERROR(EINVAL), "NULL pointer" }, | |
90 | { MFX_ERR_UNSUPPORTED, AVERROR(ENOSYS), "unsupported" }, | |
91 | { MFX_ERR_MEMORY_ALLOC, AVERROR(ENOMEM), "failed to allocate memory" }, | |
92 | { MFX_ERR_NOT_ENOUGH_BUFFER, AVERROR(ENOMEM), "insufficient input/output buffer" }, | |
93 | { MFX_ERR_INVALID_HANDLE, AVERROR(EINVAL), "invalid handle" }, | |
94 | { MFX_ERR_LOCK_MEMORY, AVERROR(EIO), "failed to lock the memory block" }, | |
95 | { MFX_ERR_NOT_INITIALIZED, AVERROR_BUG, "not initialized" }, | |
96 | { MFX_ERR_NOT_FOUND, AVERROR(ENOSYS), "specified object was not found" }, | |
97 | { MFX_ERR_MORE_DATA, AVERROR(EAGAIN), "expect more data at input" }, | |
98 | { MFX_ERR_MORE_SURFACE, AVERROR(EAGAIN), "expect more surface at output" }, | |
99 | { MFX_ERR_ABORTED, AVERROR_UNKNOWN, "operation aborted" }, | |
100 | { MFX_ERR_DEVICE_LOST, AVERROR(EIO), "device lost" }, | |
101 | { MFX_ERR_INCOMPATIBLE_VIDEO_PARAM, AVERROR(EINVAL), "incompatible video parameters" }, | |
102 | { MFX_ERR_INVALID_VIDEO_PARAM, AVERROR(EINVAL), "invalid video parameters" }, | |
103 | { MFX_ERR_UNDEFINED_BEHAVIOR, AVERROR_BUG, "undefined behavior" }, | |
104 | { MFX_ERR_DEVICE_FAILED, AVERROR(EIO), "device failed" }, | |
105 | { MFX_ERR_MORE_BITSTREAM, AVERROR(EAGAIN), "expect more bitstream at output" }, | |
106 | { MFX_ERR_INCOMPATIBLE_AUDIO_PARAM, AVERROR(EINVAL), "incompatible audio parameters" }, | |
107 | { MFX_ERR_INVALID_AUDIO_PARAM, AVERROR(EINVAL), "invalid audio parameters" }, | |
108 | ||
109 | { MFX_WRN_IN_EXECUTION, 0, "operation in execution" }, | |
110 | { MFX_WRN_DEVICE_BUSY, 0, "device busy" }, | |
111 | { MFX_WRN_VIDEO_PARAM_CHANGED, 0, "video parameters changed" }, | |
112 | { MFX_WRN_PARTIAL_ACCELERATION, 0, "partial acceleration" }, | |
113 | { MFX_WRN_INCOMPATIBLE_VIDEO_PARAM, 0, "incompatible video parameters" }, | |
114 | { MFX_WRN_VALUE_NOT_CHANGED, 0, "value is saturated" }, | |
115 | { MFX_WRN_OUT_OF_RANGE, 0, "value out of range" }, | |
116 | { MFX_WRN_FILTER_SKIPPED, 0, "filter skipped" }, | |
117 | { MFX_WRN_INCOMPATIBLE_AUDIO_PARAM, 0, "incompatible audio parameters" }, | |
118 | }; | |
119 | ||
120 | int ff_qsv_map_error(mfxStatus mfx_err, const char **desc) | |
d0a63d8b | 121 | { |
95414eb2 AK |
122 | int i; |
123 | for (i = 0; i < FF_ARRAY_ELEMS(qsv_errors); i++) { | |
124 | if (qsv_errors[i].mfxerr == mfx_err) { | |
125 | if (desc) | |
126 | *desc = qsv_errors[i].desc; | |
127 | return qsv_errors[i].averr; | |
128 | } | |
d0a63d8b | 129 | } |
95414eb2 AK |
130 | if (desc) |
131 | *desc = "unknown error"; | |
132 | return AVERROR_UNKNOWN; | |
133 | } | |
134 | ||
135 | int ff_qsv_print_error(void *log_ctx, mfxStatus err, | |
136 | const char *error_string) | |
137 | { | |
138 | const char *desc; | |
139 | int ret; | |
140 | ret = ff_qsv_map_error(err, &desc); | |
141 | av_log(log_ctx, AV_LOG_ERROR, "%s: %s (%d)\n", error_string, desc, err); | |
142 | return ret; | |
d0a63d8b AK |
143 | } |
144 | ||
8e07c22e AK |
145 | int ff_qsv_print_warning(void *log_ctx, mfxStatus err, |
146 | const char *warning_string) | |
147 | { | |
148 | const char *desc; | |
149 | int ret; | |
150 | ret = ff_qsv_map_error(err, &desc); | |
151 | av_log(log_ctx, AV_LOG_WARNING, "%s: %s (%d)\n", warning_string, desc, err); | |
152 | return ret; | |
153 | } | |
154 | ||
4ab61cd9 AK |
155 | static enum AVPixelFormat qsv_map_fourcc(uint32_t fourcc) |
156 | { | |
157 | switch (fourcc) { | |
158 | case MFX_FOURCC_NV12: return AV_PIX_FMT_NV12; | |
159 | case MFX_FOURCC_P010: return AV_PIX_FMT_P010; | |
160 | case MFX_FOURCC_P8: return AV_PIX_FMT_PAL8; | |
161 | } | |
162 | return AV_PIX_FMT_NONE; | |
163 | } | |
164 | ||
536bb17e AK |
165 | int ff_qsv_map_pixfmt(enum AVPixelFormat format, uint32_t *fourcc) |
166 | { | |
167 | switch (format) { | |
168 | case AV_PIX_FMT_YUV420P: | |
169 | case AV_PIX_FMT_YUVJ420P: | |
21962261 | 170 | case AV_PIX_FMT_NV12: |
536bb17e AK |
171 | *fourcc = MFX_FOURCC_NV12; |
172 | return AV_PIX_FMT_NV12; | |
92736c74 | 173 | case AV_PIX_FMT_YUV420P10: |
21962261 | 174 | case AV_PIX_FMT_P010: |
92736c74 AK |
175 | *fourcc = MFX_FOURCC_P010; |
176 | return AV_PIX_FMT_P010; | |
536bb17e AK |
177 | default: |
178 | return AVERROR(ENOSYS); | |
179 | } | |
180 | } | |
181 | ||
00aeedd8 AK |
182 | int ff_qsv_find_surface_idx(QSVFramesContext *ctx, QSVFrame *frame) |
183 | { | |
184 | int i; | |
185 | for (i = 0; i < ctx->nb_mids; i++) { | |
186 | QSVMid *mid = &ctx->mids[i]; | |
187 | if (mid->handle == frame->surface.Data.MemId) | |
188 | return i; | |
189 | } | |
190 | return AVERROR_BUG; | |
191 | } | |
192 | ||
a0524d9b AK |
193 | static int qsv_load_plugins(mfxSession session, const char *load_plugins, |
194 | void *logctx) | |
195 | { | |
196 | if (!load_plugins || !*load_plugins) | |
197 | return 0; | |
198 | ||
199 | while (*load_plugins) { | |
200 | mfxPluginUID uid; | |
201 | mfxStatus ret; | |
202 | int i, err = 0; | |
203 | ||
204 | char *plugin = av_get_token(&load_plugins, ":"); | |
205 | if (!plugin) | |
206 | return AVERROR(ENOMEM); | |
207 | if (strlen(plugin) != 2 * sizeof(uid.Data)) { | |
208 | av_log(logctx, AV_LOG_ERROR, "Invalid plugin UID length\n"); | |
209 | err = AVERROR(EINVAL); | |
210 | goto load_plugin_fail; | |
211 | } | |
212 | ||
213 | for (i = 0; i < sizeof(uid.Data); i++) { | |
214 | err = sscanf(plugin + 2 * i, "%2hhx", uid.Data + i); | |
215 | if (err != 1) { | |
216 | av_log(logctx, AV_LOG_ERROR, "Invalid plugin UID\n"); | |
217 | err = AVERROR(EINVAL); | |
218 | goto load_plugin_fail; | |
219 | } | |
220 | ||
221 | } | |
222 | ||
223 | ret = MFXVideoUSER_Load(session, &uid, 1); | |
224 | if (ret < 0) { | |
95414eb2 AK |
225 | char errorbuf[128]; |
226 | snprintf(errorbuf, sizeof(errorbuf), | |
227 | "Could not load the requested plugin '%s'", plugin); | |
228 | err = ff_qsv_print_error(logctx, ret, errorbuf); | |
a0524d9b AK |
229 | goto load_plugin_fail; |
230 | } | |
231 | ||
232 | if (*load_plugins) | |
233 | load_plugins++; | |
234 | load_plugin_fail: | |
235 | av_freep(&plugin); | |
236 | if (err < 0) | |
237 | return err; | |
238 | } | |
239 | ||
240 | return 0; | |
241 | ||
242 | } | |
243 | ||
66acb76b AK |
244 | int ff_qsv_init_internal_session(AVCodecContext *avctx, mfxSession *session, |
245 | const char *load_plugins) | |
d0a63d8b AK |
246 | { |
247 | mfxIMPL impl = MFX_IMPL_AUTO_ANY; | |
248 | mfxVersion ver = { { QSV_VERSION_MINOR, QSV_VERSION_MAJOR } }; | |
249 | ||
250 | const char *desc; | |
251 | int ret; | |
252 | ||
253 | ret = MFXInit(impl, &ver, session); | |
95414eb2 AK |
254 | if (ret < 0) |
255 | return ff_qsv_print_error(avctx, ret, | |
256 | "Error initializing an internal MFX session"); | |
d0a63d8b | 257 | |
a0524d9b AK |
258 | ret = qsv_load_plugins(*session, load_plugins, avctx); |
259 | if (ret < 0) { | |
260 | av_log(avctx, AV_LOG_ERROR, "Error loading plugins\n"); | |
261 | return ret; | |
66acb76b AK |
262 | } |
263 | ||
ce9d7da7 LB |
264 | MFXQueryIMPL(*session, &impl); |
265 | ||
266 | switch (MFX_IMPL_BASETYPE(impl)) { | |
267 | case MFX_IMPL_SOFTWARE: | |
268 | desc = "software"; | |
269 | break; | |
270 | case MFX_IMPL_HARDWARE: | |
271 | case MFX_IMPL_HARDWARE2: | |
272 | case MFX_IMPL_HARDWARE3: | |
273 | case MFX_IMPL_HARDWARE4: | |
274 | desc = "hardware accelerated"; | |
275 | break; | |
276 | default: | |
277 | desc = "unknown"; | |
278 | } | |
279 | ||
d0a63d8b AK |
280 | av_log(avctx, AV_LOG_VERBOSE, |
281 | "Initialized an internal MFX session using %s implementation\n", | |
282 | desc); | |
283 | ||
284 | return 0; | |
285 | } | |
a0524d9b | 286 | |
4ab61cd9 AK |
287 | static void mids_buf_free(void *opaque, uint8_t *data) |
288 | { | |
289 | AVBufferRef *hw_frames_ref = opaque; | |
290 | av_buffer_unref(&hw_frames_ref); | |
291 | av_freep(&data); | |
292 | } | |
293 | ||
294 | static AVBufferRef *qsv_create_mids(AVBufferRef *hw_frames_ref) | |
295 | { | |
296 | AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hw_frames_ref->data; | |
297 | AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; | |
298 | int nb_surfaces = frames_hwctx->nb_surfaces; | |
299 | ||
300 | AVBufferRef *mids_buf, *hw_frames_ref1; | |
301 | QSVMid *mids; | |
302 | int i; | |
303 | ||
304 | hw_frames_ref1 = av_buffer_ref(hw_frames_ref); | |
305 | if (!hw_frames_ref1) | |
306 | return NULL; | |
307 | ||
308 | mids = av_mallocz_array(nb_surfaces, sizeof(*mids)); | |
309 | if (!mids) { | |
310 | av_buffer_unref(&hw_frames_ref1); | |
311 | return NULL; | |
312 | } | |
313 | ||
314 | mids_buf = av_buffer_create((uint8_t*)mids, nb_surfaces * sizeof(*mids), | |
315 | mids_buf_free, hw_frames_ref1, 0); | |
316 | if (!mids_buf) { | |
317 | av_buffer_unref(&hw_frames_ref1); | |
318 | av_freep(&mids); | |
319 | return NULL; | |
320 | } | |
321 | ||
322 | for (i = 0; i < nb_surfaces; i++) { | |
323 | QSVMid *mid = &mids[i]; | |
324 | mid->handle = frames_hwctx->surfaces[i].Data.MemId; | |
325 | mid->hw_frames_ref = hw_frames_ref1; | |
326 | } | |
327 | ||
328 | return mids_buf; | |
329 | } | |
330 | ||
331 | static int qsv_setup_mids(mfxFrameAllocResponse *resp, AVBufferRef *hw_frames_ref, | |
332 | AVBufferRef *mids_buf) | |
333 | { | |
334 | AVHWFramesContext *frames_ctx = (AVHWFramesContext*)hw_frames_ref->data; | |
335 | AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; | |
336 | QSVMid *mids = (QSVMid*)mids_buf->data; | |
337 | int nb_surfaces = frames_hwctx->nb_surfaces; | |
338 | int i; | |
339 | ||
340 | // the allocated size of the array is two larger than the number of | |
341 | // surfaces, we store the references to the frames context and the | |
342 | // QSVMid array there | |
343 | resp->mids = av_mallocz_array(nb_surfaces + 2, sizeof(*resp->mids)); | |
344 | if (!resp->mids) | |
345 | return AVERROR(ENOMEM); | |
346 | ||
347 | for (i = 0; i < nb_surfaces; i++) | |
348 | resp->mids[i] = &mids[i]; | |
349 | resp->NumFrameActual = nb_surfaces; | |
350 | ||
351 | resp->mids[resp->NumFrameActual] = (mfxMemId)av_buffer_ref(hw_frames_ref); | |
352 | if (!resp->mids[resp->NumFrameActual]) { | |
353 | av_freep(&resp->mids); | |
354 | return AVERROR(ENOMEM); | |
355 | } | |
356 | ||
357 | resp->mids[resp->NumFrameActual + 1] = av_buffer_ref(mids_buf); | |
358 | if (!resp->mids[resp->NumFrameActual + 1]) { | |
359 | av_buffer_unref((AVBufferRef**)&resp->mids[resp->NumFrameActual]); | |
360 | av_freep(&resp->mids); | |
361 | return AVERROR(ENOMEM); | |
362 | } | |
363 | ||
364 | return 0; | |
365 | } | |
366 | ||
a0524d9b AK |
367 | static mfxStatus qsv_frame_alloc(mfxHDL pthis, mfxFrameAllocRequest *req, |
368 | mfxFrameAllocResponse *resp) | |
369 | { | |
370 | QSVFramesContext *ctx = pthis; | |
4ab61cd9 | 371 | int ret; |
a0524d9b | 372 | |
4ab61cd9 AK |
373 | /* this should only be called from an encoder or decoder and |
374 | * only allocates video memory frames */ | |
375 | if (!(req->Type & (MFX_MEMTYPE_VIDEO_MEMORY_DECODER_TARGET | | |
376 | MFX_MEMTYPE_VIDEO_MEMORY_PROCESSOR_TARGET)) || | |
377 | !(req->Type & (MFX_MEMTYPE_FROM_DECODE | MFX_MEMTYPE_FROM_ENCODE))) | |
a0524d9b | 378 | return MFX_ERR_UNSUPPORTED; |
a0524d9b | 379 | |
4ab61cd9 AK |
380 | if (req->Type & MFX_MEMTYPE_EXTERNAL_FRAME) { |
381 | /* external frames -- fill from the caller-supplied frames context */ | |
382 | AVHWFramesContext *frames_ctx = (AVHWFramesContext*)ctx->hw_frames_ctx->data; | |
383 | AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; | |
384 | mfxFrameInfo *i = &req->Info; | |
385 | mfxFrameInfo *i1 = &frames_hwctx->surfaces[0].Info; | |
386 | ||
387 | if (i->Width != i1->Width || i->Height != i1->Height || | |
388 | i->FourCC != i1->FourCC || i->ChromaFormat != i1->ChromaFormat) { | |
389 | av_log(ctx->logctx, AV_LOG_ERROR, "Mismatching surface properties in an " | |
390 | "allocation request: %dx%d %d %d vs %dx%d %d %d\n", | |
391 | i->Width, i->Height, i->FourCC, i->ChromaFormat, | |
392 | i1->Width, i1->Height, i1->FourCC, i1->ChromaFormat); | |
393 | return MFX_ERR_UNSUPPORTED; | |
394 | } | |
395 | ||
396 | ret = qsv_setup_mids(resp, ctx->hw_frames_ctx, ctx->mids_buf); | |
397 | if (ret < 0) { | |
398 | av_log(ctx->logctx, AV_LOG_ERROR, | |
399 | "Error filling an external frame allocation request\n"); | |
400 | return MFX_ERR_MEMORY_ALLOC; | |
401 | } | |
402 | } else if (req->Type & MFX_MEMTYPE_INTERNAL_FRAME) { | |
403 | /* internal frames -- allocate a new hw frames context */ | |
404 | AVHWFramesContext *ext_frames_ctx = (AVHWFramesContext*)ctx->hw_frames_ctx->data; | |
405 | mfxFrameInfo *i = &req->Info; | |
406 | ||
407 | AVBufferRef *frames_ref, *mids_buf; | |
408 | AVHWFramesContext *frames_ctx; | |
409 | AVQSVFramesContext *frames_hwctx; | |
410 | ||
411 | frames_ref = av_hwframe_ctx_alloc(ext_frames_ctx->device_ref); | |
412 | if (!frames_ref) | |
413 | return MFX_ERR_MEMORY_ALLOC; | |
414 | ||
415 | frames_ctx = (AVHWFramesContext*)frames_ref->data; | |
416 | frames_hwctx = frames_ctx->hwctx; | |
417 | ||
418 | frames_ctx->format = AV_PIX_FMT_QSV; | |
419 | frames_ctx->sw_format = qsv_map_fourcc(i->FourCC); | |
420 | frames_ctx->width = i->Width; | |
421 | frames_ctx->height = i->Height; | |
422 | frames_ctx->initial_pool_size = req->NumFrameSuggested; | |
423 | ||
424 | frames_hwctx->frame_type = req->Type; | |
425 | ||
426 | ret = av_hwframe_ctx_init(frames_ref); | |
427 | if (ret < 0) { | |
428 | av_log(ctx->logctx, AV_LOG_ERROR, | |
429 | "Error initializing a frames context for an internal frame " | |
430 | "allocation request\n"); | |
431 | av_buffer_unref(&frames_ref); | |
432 | return MFX_ERR_MEMORY_ALLOC; | |
433 | } | |
00aeedd8 | 434 | |
4ab61cd9 AK |
435 | mids_buf = qsv_create_mids(frames_ref); |
436 | if (!mids_buf) { | |
437 | av_buffer_unref(&frames_ref); | |
438 | return MFX_ERR_MEMORY_ALLOC; | |
439 | } | |
00aeedd8 | 440 | |
4ab61cd9 AK |
441 | ret = qsv_setup_mids(resp, frames_ref, mids_buf); |
442 | av_buffer_unref(&mids_buf); | |
443 | av_buffer_unref(&frames_ref); | |
444 | if (ret < 0) { | |
445 | av_log(ctx->logctx, AV_LOG_ERROR, | |
446 | "Error filling an internal frame allocation request\n"); | |
447 | return MFX_ERR_MEMORY_ALLOC; | |
448 | } | |
449 | } else { | |
450 | return MFX_ERR_UNSUPPORTED; | |
451 | } | |
a0524d9b AK |
452 | |
453 | return MFX_ERR_NONE; | |
454 | } | |
455 | ||
456 | static mfxStatus qsv_frame_free(mfxHDL pthis, mfxFrameAllocResponse *resp) | |
457 | { | |
4ab61cd9 AK |
458 | av_buffer_unref((AVBufferRef**)&resp->mids[resp->NumFrameActual]); |
459 | av_buffer_unref((AVBufferRef**)&resp->mids[resp->NumFrameActual + 1]); | |
00aeedd8 | 460 | av_freep(&resp->mids); |
a0524d9b AK |
461 | return MFX_ERR_NONE; |
462 | } | |
463 | ||
464 | static mfxStatus qsv_frame_lock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) | |
465 | { | |
4ab61cd9 AK |
466 | QSVMid *qsv_mid = mid; |
467 | AVHWFramesContext *hw_frames_ctx = (AVHWFramesContext*)qsv_mid->hw_frames_ref->data; | |
468 | AVQSVFramesContext *hw_frames_hwctx = hw_frames_ctx->hwctx; | |
469 | int size; | |
470 | int ret; | |
471 | mfxStatus err; | |
472 | ||
473 | if (qsv_mid->locked_frame) | |
474 | return MFX_ERR_UNDEFINED_BEHAVIOR; | |
475 | ||
476 | /* Allocate a system memory frame that will hold the mapped data. */ | |
477 | qsv_mid->locked_frame = av_frame_alloc(); | |
478 | if (!qsv_mid->locked_frame) | |
479 | return MFX_ERR_MEMORY_ALLOC; | |
480 | qsv_mid->locked_frame->format = hw_frames_ctx->sw_format; | |
481 | ||
482 | /* wrap the provided handle in a hwaccel AVFrame */ | |
483 | qsv_mid->hw_frame = av_frame_alloc(); | |
484 | if (!qsv_mid->hw_frame) | |
485 | goto fail; | |
486 | ||
487 | qsv_mid->hw_frame->data[3] = (uint8_t*)&qsv_mid->surf; | |
488 | qsv_mid->hw_frame->format = AV_PIX_FMT_QSV; | |
489 | ||
490 | // doesn't really matter what buffer is used here | |
491 | qsv_mid->hw_frame->buf[0] = av_buffer_alloc(1); | |
492 | if (!qsv_mid->hw_frame->buf[0]) | |
493 | goto fail; | |
494 | ||
495 | qsv_mid->hw_frame->width = hw_frames_ctx->width; | |
496 | qsv_mid->hw_frame->height = hw_frames_ctx->height; | |
497 | ||
498 | qsv_mid->hw_frame->hw_frames_ctx = av_buffer_ref(qsv_mid->hw_frames_ref); | |
499 | if (!qsv_mid->hw_frame->hw_frames_ctx) | |
500 | goto fail; | |
501 | ||
502 | qsv_mid->surf.Info = hw_frames_hwctx->surfaces[0].Info; | |
503 | qsv_mid->surf.Data.MemId = qsv_mid->handle; | |
504 | ||
505 | /* map the data to the system memory */ | |
506 | ret = av_hwframe_map(qsv_mid->locked_frame, qsv_mid->hw_frame, | |
507 | AV_HWFRAME_MAP_DIRECT); | |
508 | if (ret < 0) | |
509 | goto fail; | |
510 | ||
511 | ptr->Pitch = qsv_mid->locked_frame->linesize[0]; | |
512 | ptr->Y = qsv_mid->locked_frame->data[0]; | |
513 | ptr->U = qsv_mid->locked_frame->data[1]; | |
514 | ptr->V = qsv_mid->locked_frame->data[1] + 1; | |
515 | ||
516 | return MFX_ERR_NONE; | |
517 | fail: | |
518 | av_frame_free(&qsv_mid->hw_frame); | |
519 | av_frame_free(&qsv_mid->locked_frame); | |
520 | return MFX_ERR_MEMORY_ALLOC; | |
a0524d9b AK |
521 | } |
522 | ||
523 | static mfxStatus qsv_frame_unlock(mfxHDL pthis, mfxMemId mid, mfxFrameData *ptr) | |
524 | { | |
4ab61cd9 AK |
525 | QSVMid *qsv_mid = mid; |
526 | int ret; | |
527 | ||
528 | av_frame_free(&qsv_mid->locked_frame); | |
529 | av_frame_free(&qsv_mid->hw_frame); | |
530 | ||
531 | return MFX_ERR_NONE; | |
a0524d9b AK |
532 | } |
533 | ||
534 | static mfxStatus qsv_frame_get_hdl(mfxHDL pthis, mfxMemId mid, mfxHDL *hdl) | |
535 | { | |
00aeedd8 AK |
536 | QSVMid *qsv_mid = (QSVMid*)mid; |
537 | *hdl = qsv_mid->handle; | |
a0524d9b AK |
538 | return MFX_ERR_NONE; |
539 | } | |
540 | ||
541 | int ff_qsv_init_session_hwcontext(AVCodecContext *avctx, mfxSession *psession, | |
542 | QSVFramesContext *qsv_frames_ctx, | |
543 | const char *load_plugins, int opaque) | |
544 | { | |
545 | static const mfxHandleType handle_types[] = { | |
546 | MFX_HANDLE_VA_DISPLAY, | |
547 | MFX_HANDLE_D3D9_DEVICE_MANAGER, | |
548 | MFX_HANDLE_D3D11_DEVICE, | |
549 | }; | |
550 | mfxFrameAllocator frame_allocator = { | |
551 | .pthis = qsv_frames_ctx, | |
552 | .Alloc = qsv_frame_alloc, | |
553 | .Lock = qsv_frame_lock, | |
554 | .Unlock = qsv_frame_unlock, | |
555 | .GetHDL = qsv_frame_get_hdl, | |
556 | .Free = qsv_frame_free, | |
557 | }; | |
558 | ||
559 | AVHWFramesContext *frames_ctx = (AVHWFramesContext*)qsv_frames_ctx->hw_frames_ctx->data; | |
560 | AVQSVFramesContext *frames_hwctx = frames_ctx->hwctx; | |
561 | AVQSVDeviceContext *device_hwctx = frames_ctx->device_ctx->hwctx; | |
562 | mfxSession parent_session = device_hwctx->session; | |
563 | ||
564 | mfxSession session; | |
565 | mfxVersion ver; | |
566 | mfxIMPL impl; | |
567 | mfxHDL handle = NULL; | |
568 | mfxHandleType handle_type; | |
569 | mfxStatus err; | |
570 | ||
571 | int i, ret; | |
572 | ||
573 | err = MFXQueryIMPL(parent_session, &impl); | |
574 | if (err == MFX_ERR_NONE) | |
575 | err = MFXQueryVersion(parent_session, &ver); | |
95414eb2 AK |
576 | if (err != MFX_ERR_NONE) |
577 | return ff_qsv_print_error(avctx, err, | |
578 | "Error querying the session attributes"); | |
a0524d9b AK |
579 | |
580 | for (i = 0; i < FF_ARRAY_ELEMS(handle_types); i++) { | |
581 | err = MFXVideoCORE_GetHandle(parent_session, handle_types[i], &handle); | |
582 | if (err == MFX_ERR_NONE) { | |
583 | handle_type = handle_types[i]; | |
584 | break; | |
585 | } | |
586 | handle = NULL; | |
587 | } | |
588 | if (!handle) { | |
589 | av_log(avctx, AV_LOG_VERBOSE, "No supported hw handle could be retrieved " | |
590 | "from the session\n"); | |
591 | } | |
592 | ||
593 | err = MFXInit(impl, &ver, &session); | |
95414eb2 AK |
594 | if (err != MFX_ERR_NONE) |
595 | return ff_qsv_print_error(avctx, err, | |
596 | "Error initializing a child MFX session"); | |
a0524d9b AK |
597 | |
598 | if (handle) { | |
599 | err = MFXVideoCORE_SetHandle(session, handle_type, handle); | |
95414eb2 AK |
600 | if (err != MFX_ERR_NONE) |
601 | return ff_qsv_print_error(avctx, err, | |
602 | "Error setting a HW handle"); | |
a0524d9b AK |
603 | } |
604 | ||
605 | ret = qsv_load_plugins(session, load_plugins, avctx); | |
606 | if (ret < 0) { | |
607 | av_log(avctx, AV_LOG_ERROR, "Error loading plugins\n"); | |
608 | return ret; | |
609 | } | |
610 | ||
611 | if (!opaque) { | |
4ab61cd9 | 612 | qsv_frames_ctx->logctx = avctx; |
a0524d9b | 613 | |
4ab61cd9 AK |
614 | /* allocate the memory ids for the external frames */ |
615 | av_buffer_unref(&qsv_frames_ctx->mids_buf); | |
616 | qsv_frames_ctx->mids_buf = qsv_create_mids(qsv_frames_ctx->hw_frames_ctx); | |
617 | if (!qsv_frames_ctx->mids_buf) | |
618 | return AVERROR(ENOMEM); | |
619 | qsv_frames_ctx->mids = (QSVMid*)qsv_frames_ctx->mids_buf->data; | |
a0524d9b | 620 | qsv_frames_ctx->nb_mids = frames_hwctx->nb_surfaces; |
a0524d9b AK |
621 | |
622 | err = MFXVideoCORE_SetFrameAllocator(session, &frame_allocator); | |
95414eb2 AK |
623 | if (err != MFX_ERR_NONE) |
624 | return ff_qsv_print_error(avctx, err, | |
625 | "Error setting a frame allocator"); | |
a0524d9b AK |
626 | } |
627 | ||
628 | *psession = session; | |
629 | return 0; | |
630 | } |