/src/ffmpeg/libavutil/hwcontext.c
Line | Count | Source |
1 | | /* |
2 | | * This file is part of FFmpeg. |
3 | | * |
4 | | * FFmpeg 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 | | * FFmpeg 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 FFmpeg; if not, write to the Free Software |
16 | | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
17 | | */ |
18 | | |
19 | | #include "config.h" |
20 | | |
21 | | #include "avassert.h" |
22 | | #include "buffer.h" |
23 | | #include "common.h" |
24 | | #include "frame_internal.h" |
25 | | #include "hwcontext.h" |
26 | | #include "hwcontext_internal.h" |
27 | | #include "imgutils.h" |
28 | | #include "log.h" |
29 | | #include "mem.h" |
30 | | #include "pixdesc.h" |
31 | | #include "pixfmt.h" |
32 | | |
33 | | static const HWContextType * const hw_table[] = { |
34 | | #if CONFIG_CUDA |
35 | | &ff_hwcontext_type_cuda, |
36 | | #endif |
37 | | #if CONFIG_D3D11VA |
38 | | &ff_hwcontext_type_d3d11va, |
39 | | #endif |
40 | | #if CONFIG_D3D12VA |
41 | | &ff_hwcontext_type_d3d12va, |
42 | | #endif |
43 | | #if CONFIG_LIBDRM |
44 | | &ff_hwcontext_type_drm, |
45 | | #endif |
46 | | #if CONFIG_DXVA2 |
47 | | &ff_hwcontext_type_dxva2, |
48 | | #endif |
49 | | #if CONFIG_OPENCL |
50 | | &ff_hwcontext_type_opencl, |
51 | | #endif |
52 | | #if CONFIG_QSV |
53 | | &ff_hwcontext_type_qsv, |
54 | | #endif |
55 | | #if CONFIG_VAAPI |
56 | | &ff_hwcontext_type_vaapi, |
57 | | #endif |
58 | | #if CONFIG_VDPAU |
59 | | &ff_hwcontext_type_vdpau, |
60 | | #endif |
61 | | #if CONFIG_VIDEOTOOLBOX |
62 | | &ff_hwcontext_type_videotoolbox, |
63 | | #endif |
64 | | #if CONFIG_MEDIACODEC |
65 | | &ff_hwcontext_type_mediacodec, |
66 | | #endif |
67 | | #if CONFIG_VULKAN |
68 | | &ff_hwcontext_type_vulkan, |
69 | | #endif |
70 | | #if CONFIG_AMF |
71 | | &ff_hwcontext_type_amf, |
72 | | #endif |
73 | | #if CONFIG_OHCODEC |
74 | | &ff_hwcontext_type_oh, |
75 | | #endif |
76 | | NULL, |
77 | | }; |
78 | | |
79 | | static const char *const hw_type_names[] = { |
80 | | [AV_HWDEVICE_TYPE_CUDA] = "cuda", |
81 | | [AV_HWDEVICE_TYPE_DRM] = "drm", |
82 | | [AV_HWDEVICE_TYPE_DXVA2] = "dxva2", |
83 | | [AV_HWDEVICE_TYPE_D3D11VA] = "d3d11va", |
84 | | [AV_HWDEVICE_TYPE_D3D12VA] = "d3d12va", |
85 | | [AV_HWDEVICE_TYPE_OPENCL] = "opencl", |
86 | | [AV_HWDEVICE_TYPE_QSV] = "qsv", |
87 | | [AV_HWDEVICE_TYPE_VAAPI] = "vaapi", |
88 | | [AV_HWDEVICE_TYPE_VDPAU] = "vdpau", |
89 | | [AV_HWDEVICE_TYPE_VIDEOTOOLBOX] = "videotoolbox", |
90 | | [AV_HWDEVICE_TYPE_MEDIACODEC] = "mediacodec", |
91 | | [AV_HWDEVICE_TYPE_VULKAN] = "vulkan", |
92 | | [AV_HWDEVICE_TYPE_AMF] = "amf", |
93 | | [AV_HWDEVICE_TYPE_OHCODEC] = "ohcodec", |
94 | | }; |
95 | | |
96 | | typedef struct FFHWDeviceContext { |
97 | | /** |
98 | | * The public AVHWDeviceContext. See hwcontext.h for it. |
99 | | */ |
100 | | AVHWDeviceContext p; |
101 | | |
102 | | const HWContextType *hw_type; |
103 | | |
104 | | /** |
105 | | * For a derived device, a reference to the original device |
106 | | * context it was derived from. |
107 | | */ |
108 | | AVBufferRef *source_device; |
109 | | } FFHWDeviceContext; |
110 | | |
111 | | enum AVHWDeviceType av_hwdevice_find_type_by_name(const char *name) |
112 | 0 | { |
113 | 0 | int type; |
114 | 0 | for (type = 0; type < FF_ARRAY_ELEMS(hw_type_names); type++) { |
115 | 0 | if (hw_type_names[type] && !strcmp(hw_type_names[type], name)) |
116 | 0 | return type; |
117 | 0 | } |
118 | 0 | return AV_HWDEVICE_TYPE_NONE; |
119 | 0 | } |
120 | | |
121 | | const char *av_hwdevice_get_type_name(enum AVHWDeviceType type) |
122 | 0 | { |
123 | 0 | if (type > AV_HWDEVICE_TYPE_NONE && |
124 | 0 | type < FF_ARRAY_ELEMS(hw_type_names)) |
125 | 0 | return hw_type_names[type]; |
126 | 0 | else |
127 | 0 | return NULL; |
128 | 0 | } |
129 | | |
130 | | enum AVHWDeviceType av_hwdevice_iterate_types(enum AVHWDeviceType prev) |
131 | 0 | { |
132 | 0 | enum AVHWDeviceType next; |
133 | 0 | int i, set = 0; |
134 | 0 | for (i = 0; hw_table[i]; i++) { |
135 | 0 | if (prev != AV_HWDEVICE_TYPE_NONE && hw_table[i]->type <= prev) |
136 | 0 | continue; |
137 | 0 | if (!set || hw_table[i]->type < next) { |
138 | 0 | next = hw_table[i]->type; |
139 | 0 | set = 1; |
140 | 0 | } |
141 | 0 | } |
142 | 0 | return set ? next : AV_HWDEVICE_TYPE_NONE; |
143 | 0 | } |
144 | | |
145 | | static const char *hwdevice_ctx_get_name(void *ptr) |
146 | 0 | { |
147 | 0 | FFHWDeviceContext *ctx = ptr; |
148 | 0 | return ctx->hw_type->name; |
149 | 0 | } |
150 | | |
151 | | static const AVClass hwdevice_ctx_class = { |
152 | | .class_name = "AVHWDeviceContext", |
153 | | .item_name = hwdevice_ctx_get_name, |
154 | | .category = AV_CLASS_CATEGORY_HWDEVICE, |
155 | | .version = LIBAVUTIL_VERSION_INT, |
156 | | }; |
157 | | |
158 | | static void hwdevice_ctx_free(void *opaque, uint8_t *data) |
159 | 0 | { |
160 | 0 | FFHWDeviceContext *ctxi = (FFHWDeviceContext*)data; |
161 | 0 | AVHWDeviceContext *ctx = &ctxi->p; |
162 | | |
163 | | /* uninit might still want access the hw context and the user |
164 | | * free() callback might destroy it, so uninit has to be called first */ |
165 | 0 | if (ctxi->hw_type->device_uninit) |
166 | 0 | ctxi->hw_type->device_uninit(ctx); |
167 | |
|
168 | 0 | if (ctx->free) |
169 | 0 | ctx->free(ctx); |
170 | |
|
171 | 0 | av_buffer_unref(&ctxi->source_device); |
172 | |
|
173 | 0 | av_freep(&ctx->hwctx); |
174 | 0 | av_freep(&ctx); |
175 | 0 | } |
176 | | |
177 | | AVBufferRef *av_hwdevice_ctx_alloc(enum AVHWDeviceType type) |
178 | 0 | { |
179 | 0 | FFHWDeviceContext *ctxi; |
180 | 0 | AVHWDeviceContext *ctx; |
181 | 0 | AVBufferRef *buf; |
182 | 0 | const HWContextType *hw_type = NULL; |
183 | 0 | int i; |
184 | |
|
185 | 0 | for (i = 0; hw_table[i]; i++) { |
186 | 0 | if (hw_table[i]->type == type) { |
187 | 0 | hw_type = hw_table[i]; |
188 | 0 | break; |
189 | 0 | } |
190 | 0 | } |
191 | 0 | if (!hw_type) |
192 | 0 | return NULL; |
193 | | |
194 | 0 | ctxi = av_mallocz(sizeof(*ctxi)); |
195 | 0 | if (!ctxi) |
196 | 0 | return NULL; |
197 | 0 | ctx = &ctxi->p; |
198 | |
|
199 | 0 | if (hw_type->device_hwctx_size) { |
200 | 0 | ctx->hwctx = av_mallocz(hw_type->device_hwctx_size); |
201 | 0 | if (!ctx->hwctx) |
202 | 0 | goto fail; |
203 | 0 | } |
204 | | |
205 | 0 | buf = av_buffer_create((uint8_t*)ctx, sizeof(*ctx), |
206 | 0 | hwdevice_ctx_free, NULL, |
207 | 0 | AV_BUFFER_FLAG_READONLY); |
208 | 0 | if (!buf) |
209 | 0 | goto fail; |
210 | | |
211 | 0 | ctx->type = type; |
212 | 0 | ctx->av_class = &hwdevice_ctx_class; |
213 | |
|
214 | 0 | ctxi->hw_type = hw_type; |
215 | |
|
216 | 0 | return buf; |
217 | | |
218 | 0 | fail: |
219 | 0 | av_freep(&ctx->hwctx); |
220 | 0 | av_freep(&ctx); |
221 | 0 | return NULL; |
222 | 0 | } |
223 | | |
224 | | int av_hwdevice_ctx_init(AVBufferRef *ref) |
225 | 0 | { |
226 | 0 | FFHWDeviceContext *ctxi = (FFHWDeviceContext*)ref->data; |
227 | 0 | AVHWDeviceContext *ctx = &ctxi->p; |
228 | 0 | int ret = 0; |
229 | |
|
230 | 0 | if (ctxi->hw_type->device_init) |
231 | 0 | ret = ctxi->hw_type->device_init(ctx); |
232 | |
|
233 | 0 | return ret; |
234 | 0 | } |
235 | | |
236 | | static const AVClass hwframe_ctx_class = { |
237 | | .class_name = "AVHWFramesContext", |
238 | | .item_name = av_default_item_name, |
239 | | .version = LIBAVUTIL_VERSION_INT, |
240 | | }; |
241 | | |
242 | | static void hwframe_ctx_free(void *opaque, uint8_t *data) |
243 | 0 | { |
244 | 0 | FFHWFramesContext *ctxi = (FFHWFramesContext*)data; |
245 | 0 | AVHWFramesContext *ctx = &ctxi->p; |
246 | |
|
247 | 0 | if (ctxi->pool_internal) |
248 | 0 | av_buffer_pool_uninit(&ctxi->pool_internal); |
249 | |
|
250 | 0 | av_buffer_pool_uninit(&ctxi->transfer_pool); |
251 | 0 | ff_mutex_destroy(&ctxi->transfer_pool_lock); |
252 | |
|
253 | 0 | if (ctxi->hw_type->frames_uninit) |
254 | 0 | ctxi->hw_type->frames_uninit(ctx); |
255 | |
|
256 | 0 | if (ctx->free) |
257 | 0 | ctx->free(ctx); |
258 | |
|
259 | 0 | av_buffer_unref(&ctxi->source_frames); |
260 | |
|
261 | 0 | av_buffer_unref(&ctx->device_ref); |
262 | |
|
263 | 0 | av_freep(&ctx->hwctx); |
264 | 0 | av_freep(&ctx); |
265 | 0 | } |
266 | | |
267 | | AVBufferRef *av_hwframe_ctx_alloc(AVBufferRef *device_ref_in) |
268 | 0 | { |
269 | 0 | FFHWDeviceContext *device_ctx = (FFHWDeviceContext*)device_ref_in->data; |
270 | 0 | const HWContextType *hw_type = device_ctx->hw_type; |
271 | 0 | FFHWFramesContext *ctxi; |
272 | 0 | AVHWFramesContext *ctx; |
273 | 0 | AVBufferRef *buf, *device_ref = NULL; |
274 | |
|
275 | 0 | ctxi = av_mallocz(sizeof(*ctxi)); |
276 | 0 | if (!ctxi) |
277 | 0 | return NULL; |
278 | 0 | ctx = &ctxi->p; |
279 | |
|
280 | 0 | if (ff_mutex_init(&ctxi->transfer_pool_lock, NULL)) { |
281 | 0 | av_free(ctxi); |
282 | 0 | return NULL; |
283 | 0 | } |
284 | | |
285 | 0 | if (hw_type->frames_hwctx_size) { |
286 | 0 | ctx->hwctx = av_mallocz(hw_type->frames_hwctx_size); |
287 | 0 | if (!ctx->hwctx) |
288 | 0 | goto fail; |
289 | 0 | } |
290 | | |
291 | 0 | device_ref = av_buffer_ref(device_ref_in); |
292 | 0 | if (!device_ref) |
293 | 0 | goto fail; |
294 | | |
295 | 0 | buf = av_buffer_create((uint8_t*)ctx, sizeof(*ctx), |
296 | 0 | hwframe_ctx_free, NULL, |
297 | 0 | AV_BUFFER_FLAG_READONLY); |
298 | 0 | if (!buf) |
299 | 0 | goto fail; |
300 | | |
301 | 0 | ctx->av_class = &hwframe_ctx_class; |
302 | 0 | ctx->device_ref = device_ref; |
303 | 0 | ctx->device_ctx = &device_ctx->p; |
304 | 0 | ctx->format = AV_PIX_FMT_NONE; |
305 | 0 | ctx->sw_format = AV_PIX_FMT_NONE; |
306 | |
|
307 | 0 | ctxi->hw_type = hw_type; |
308 | |
|
309 | 0 | return buf; |
310 | | |
311 | 0 | fail: |
312 | 0 | av_buffer_unref(&device_ref); |
313 | 0 | ff_mutex_destroy(&ctxi->transfer_pool_lock); |
314 | 0 | av_freep(&ctx->hwctx); |
315 | 0 | av_freep(&ctx); |
316 | 0 | return NULL; |
317 | 0 | } |
318 | | |
319 | | static int hwframe_pool_prealloc(AVBufferRef *ref) |
320 | 0 | { |
321 | 0 | AVHWFramesContext *ctx = (AVHWFramesContext*)ref->data; |
322 | 0 | AVFrame **frames; |
323 | 0 | int i, ret = 0; |
324 | |
|
325 | 0 | frames = av_calloc(ctx->initial_pool_size, sizeof(*frames)); |
326 | 0 | if (!frames) |
327 | 0 | return AVERROR(ENOMEM); |
328 | | |
329 | 0 | for (i = 0; i < ctx->initial_pool_size; i++) { |
330 | 0 | frames[i] = av_frame_alloc(); |
331 | 0 | if (!frames[i]) |
332 | 0 | goto fail; |
333 | | |
334 | 0 | ret = av_hwframe_get_buffer(ref, frames[i], 0); |
335 | 0 | if (ret < 0) |
336 | 0 | goto fail; |
337 | 0 | } |
338 | | |
339 | 0 | fail: |
340 | 0 | for (i = 0; i < ctx->initial_pool_size; i++) |
341 | 0 | av_frame_free(&frames[i]); |
342 | 0 | av_freep(&frames); |
343 | |
|
344 | 0 | return ret; |
345 | 0 | } |
346 | | |
347 | | int av_hwframe_ctx_init(AVBufferRef *ref) |
348 | 0 | { |
349 | 0 | FFHWFramesContext *ctxi = (FFHWFramesContext*)ref->data; |
350 | 0 | AVHWFramesContext *ctx = &ctxi->p; |
351 | 0 | const enum AVPixelFormat *pix_fmt; |
352 | 0 | int ret; |
353 | |
|
354 | 0 | if (ctxi->source_frames) { |
355 | | /* A derived frame context is already initialised. */ |
356 | 0 | return 0; |
357 | 0 | } |
358 | | |
359 | | /* validate the pixel format */ |
360 | 0 | for (pix_fmt = ctxi->hw_type->pix_fmts; *pix_fmt != AV_PIX_FMT_NONE; pix_fmt++) { |
361 | 0 | if (*pix_fmt == ctx->format) |
362 | 0 | break; |
363 | 0 | } |
364 | 0 | if (*pix_fmt == AV_PIX_FMT_NONE) { |
365 | 0 | av_log(ctx, AV_LOG_ERROR, |
366 | 0 | "The hardware pixel format '%s' is not supported by the device type '%s'\n", |
367 | 0 | av_get_pix_fmt_name(ctx->format), ctxi->hw_type->name); |
368 | 0 | return AVERROR(ENOSYS); |
369 | 0 | } |
370 | | |
371 | | /* validate the dimensions */ |
372 | 0 | ret = av_image_check_size(ctx->width, ctx->height, 0, ctx); |
373 | 0 | if (ret < 0) |
374 | 0 | return ret; |
375 | | |
376 | | /* format-specific init */ |
377 | 0 | if (ctxi->hw_type->frames_init) { |
378 | 0 | ret = ctxi->hw_type->frames_init(ctx); |
379 | 0 | if (ret < 0) |
380 | 0 | return ret; |
381 | 0 | } |
382 | | |
383 | 0 | if (ctxi->pool_internal && !ctx->pool) |
384 | 0 | ctx->pool = ctxi->pool_internal; |
385 | | |
386 | | /* preallocate the frames in the pool, if requested */ |
387 | 0 | if (ctx->initial_pool_size > 0) { |
388 | 0 | ret = hwframe_pool_prealloc(ref); |
389 | 0 | if (ret < 0) |
390 | 0 | return ret; |
391 | 0 | } |
392 | | |
393 | 0 | return 0; |
394 | 0 | } |
395 | | |
396 | | int av_hwframe_transfer_get_formats(AVBufferRef *hwframe_ref, |
397 | | enum AVHWFrameTransferDirection dir, |
398 | | enum AVPixelFormat **formats, int flags) |
399 | 0 | { |
400 | 0 | FFHWFramesContext *ctxi = (FFHWFramesContext*)hwframe_ref->data; |
401 | |
|
402 | 0 | if (!ctxi->hw_type->transfer_get_formats) |
403 | 0 | return AVERROR(ENOSYS); |
404 | | |
405 | 0 | return ctxi->hw_type->transfer_get_formats(&ctxi->p, dir, formats); |
406 | 0 | } |
407 | | |
408 | | static void transfer_pool_free(void *opaque) |
409 | 0 | { |
410 | 0 | AVBufferRef *device_ref = opaque; |
411 | 0 | av_buffer_unref(&device_ref); |
412 | 0 | } |
413 | | |
414 | | static AVBufferRef *transfer_buffer_alloc(void *opaque, size_t size) |
415 | 0 | { |
416 | 0 | FFHWFramesContext *ctxi = opaque; |
417 | 0 | AVBufferRef *buf = NULL; |
418 | |
|
419 | 0 | ff_mutex_lock(&ctxi->transfer_pool_lock); |
420 | 0 | if (ctxi->transfer_pool_size != size) { |
421 | | /* buffers of the hardware context type need the device to be freed */ |
422 | 0 | AVBufferRef *device_ref = ctxi->hw_type->transfer_buffer_alloc ? |
423 | 0 | av_buffer_ref(ctxi->p.device_ref) : NULL; |
424 | |
|
425 | 0 | av_buffer_pool_uninit(&ctxi->transfer_pool); |
426 | 0 | ctxi->transfer_pool = device_ref ? |
427 | 0 | av_buffer_pool_init2(size, device_ref, |
428 | 0 | ctxi->hw_type->transfer_buffer_alloc, |
429 | 0 | transfer_pool_free) : |
430 | 0 | av_buffer_pool_init(size, NULL); |
431 | 0 | ctxi->transfer_pool_size = ctxi->transfer_pool ? size : 0; |
432 | 0 | if (!ctxi->transfer_pool) |
433 | 0 | av_buffer_unref(&device_ref); |
434 | 0 | } |
435 | 0 | if (ctxi->transfer_pool) |
436 | 0 | buf = av_buffer_pool_get(ctxi->transfer_pool); |
437 | 0 | ff_mutex_unlock(&ctxi->transfer_pool_lock); |
438 | |
|
439 | 0 | return buf ? buf : av_buffer_alloc(size); |
440 | 0 | } |
441 | | |
442 | | int av_hwframe_get_transfer_buffer(AVBufferRef *hwframe_ctx, AVFrame *frame) |
443 | 0 | { |
444 | 0 | return ff_frame_get_video_buffer(frame, 0, transfer_buffer_alloc, |
445 | 0 | hwframe_ctx->data); |
446 | 0 | } |
447 | | |
448 | | static int transfer_data_alloc(AVFrame *dst, const AVFrame *src, int flags) |
449 | 0 | { |
450 | 0 | AVHWFramesContext *ctx; |
451 | 0 | AVFrame *frame_tmp; |
452 | 0 | int ret = 0; |
453 | |
|
454 | 0 | if (!src->hw_frames_ctx) |
455 | 0 | return AVERROR(EINVAL); |
456 | 0 | ctx = (AVHWFramesContext*)src->hw_frames_ctx->data; |
457 | |
|
458 | 0 | frame_tmp = av_frame_alloc(); |
459 | 0 | if (!frame_tmp) |
460 | 0 | return AVERROR(ENOMEM); |
461 | | |
462 | | /* if the format is set, use that |
463 | | * otherwise pick the first supported one */ |
464 | 0 | if (dst->format >= 0) { |
465 | 0 | frame_tmp->format = dst->format; |
466 | 0 | } else { |
467 | 0 | enum AVPixelFormat *formats; |
468 | |
|
469 | 0 | ret = av_hwframe_transfer_get_formats(src->hw_frames_ctx, |
470 | 0 | AV_HWFRAME_TRANSFER_DIRECTION_FROM, |
471 | 0 | &formats, 0); |
472 | 0 | if (ret < 0) |
473 | 0 | goto fail; |
474 | 0 | frame_tmp->format = formats[0]; |
475 | 0 | av_freep(&formats); |
476 | 0 | } |
477 | 0 | frame_tmp->width = ctx->width; |
478 | 0 | frame_tmp->height = ctx->height; |
479 | |
|
480 | 0 | ret = av_hwframe_get_transfer_buffer(src->hw_frames_ctx, frame_tmp); |
481 | 0 | if (ret < 0) |
482 | 0 | goto fail; |
483 | | |
484 | 0 | ret = av_hwframe_transfer_data(frame_tmp, src, flags); |
485 | 0 | if (ret < 0) |
486 | 0 | goto fail; |
487 | | |
488 | 0 | frame_tmp->width = src->width; |
489 | 0 | frame_tmp->height = src->height; |
490 | |
|
491 | 0 | av_frame_move_ref(dst, frame_tmp); |
492 | |
|
493 | 0 | fail: |
494 | 0 | av_frame_free(&frame_tmp); |
495 | 0 | return ret; |
496 | 0 | } |
497 | | |
498 | | int av_hwframe_transfer_data(AVFrame *dst, const AVFrame *src, int flags) |
499 | 0 | { |
500 | 0 | int ret; |
501 | |
|
502 | 0 | if (!dst->buf[0]) |
503 | 0 | return transfer_data_alloc(dst, src, flags); |
504 | | |
505 | | /* |
506 | | * Hardware -> Hardware Transfer. |
507 | | * Unlike Software -> Hardware or Hardware -> Software, the transfer |
508 | | * function could be provided by either the src or dst, depending on |
509 | | * the specific combination of hardware. |
510 | | */ |
511 | 0 | if (src->hw_frames_ctx && dst->hw_frames_ctx) { |
512 | 0 | FFHWFramesContext *src_ctx = |
513 | 0 | (FFHWFramesContext*)src->hw_frames_ctx->data; |
514 | 0 | FFHWFramesContext *dst_ctx = |
515 | 0 | (FFHWFramesContext*)dst->hw_frames_ctx->data; |
516 | |
|
517 | 0 | if (src_ctx->source_frames) { |
518 | 0 | av_log(src_ctx, AV_LOG_ERROR, |
519 | 0 | "A device with a derived frame context cannot be used as " |
520 | 0 | "the source of a HW -> HW transfer."); |
521 | 0 | return AVERROR(ENOSYS); |
522 | 0 | } |
523 | | |
524 | 0 | if (dst_ctx->source_frames) { |
525 | 0 | av_log(src_ctx, AV_LOG_ERROR, |
526 | 0 | "A device with a derived frame context cannot be used as " |
527 | 0 | "the destination of a HW -> HW transfer."); |
528 | 0 | return AVERROR(ENOSYS); |
529 | 0 | } |
530 | | |
531 | 0 | ret = AVERROR(ENOSYS); |
532 | 0 | if (src_ctx->hw_type->transfer_data_from) |
533 | 0 | ret = src_ctx->hw_type->transfer_data_from(&src_ctx->p, dst, src); |
534 | 0 | if (ret == AVERROR(ENOSYS) && dst_ctx->hw_type->transfer_data_to) |
535 | 0 | ret = dst_ctx->hw_type->transfer_data_to(&dst_ctx->p, dst, src); |
536 | 0 | if (ret < 0) |
537 | 0 | return ret; |
538 | 0 | } else { |
539 | 0 | if (src->hw_frames_ctx) { |
540 | 0 | FFHWFramesContext *ctx = (FFHWFramesContext*)src->hw_frames_ctx->data; |
541 | |
|
542 | 0 | if (!ctx->hw_type->transfer_data_from) |
543 | 0 | return AVERROR(ENOSYS); |
544 | | |
545 | 0 | ret = ctx->hw_type->transfer_data_from(&ctx->p, dst, src); |
546 | 0 | if (ret < 0) |
547 | 0 | return ret; |
548 | 0 | } else if (dst->hw_frames_ctx) { |
549 | 0 | FFHWFramesContext *ctx = (FFHWFramesContext*)dst->hw_frames_ctx->data; |
550 | |
|
551 | 0 | if (!ctx->hw_type->transfer_data_to) |
552 | 0 | return AVERROR(ENOSYS); |
553 | | |
554 | 0 | ret = ctx->hw_type->transfer_data_to(&ctx->p, dst, src); |
555 | 0 | if (ret < 0) |
556 | 0 | return ret; |
557 | 0 | } else { |
558 | 0 | return AVERROR(ENOSYS); |
559 | 0 | } |
560 | 0 | } |
561 | 0 | return 0; |
562 | 0 | } |
563 | | |
564 | | int av_hwframe_get_buffer(AVBufferRef *hwframe_ref, AVFrame *frame, int flags) |
565 | 0 | { |
566 | 0 | FFHWFramesContext *ctxi = (FFHWFramesContext*)hwframe_ref->data; |
567 | 0 | AVHWFramesContext *ctx = &ctxi->p; |
568 | 0 | int ret; |
569 | |
|
570 | 0 | if (ctxi->source_frames) { |
571 | | // This is a derived frame context, so we allocate in the source |
572 | | // and map the frame immediately. |
573 | 0 | AVFrame *src_frame; |
574 | |
|
575 | 0 | frame->format = ctx->format; |
576 | 0 | frame->hw_frames_ctx = av_buffer_ref(hwframe_ref); |
577 | 0 | if (!frame->hw_frames_ctx) |
578 | 0 | return AVERROR(ENOMEM); |
579 | | |
580 | 0 | src_frame = av_frame_alloc(); |
581 | 0 | if (!src_frame) |
582 | 0 | return AVERROR(ENOMEM); |
583 | | |
584 | 0 | ret = av_hwframe_get_buffer(ctxi->source_frames, |
585 | 0 | src_frame, 0); |
586 | 0 | if (ret < 0) { |
587 | 0 | av_frame_free(&src_frame); |
588 | 0 | return ret; |
589 | 0 | } |
590 | | |
591 | 0 | ret = av_hwframe_map(frame, src_frame, |
592 | 0 | ctxi->source_allocation_map_flags); |
593 | 0 | if (ret) { |
594 | 0 | av_log(ctx, AV_LOG_ERROR, "Failed to map frame into derived " |
595 | 0 | "frame context: %d.\n", ret); |
596 | 0 | av_frame_free(&src_frame); |
597 | 0 | return ret; |
598 | 0 | } |
599 | | |
600 | | // Free the source frame immediately - the mapped frame still |
601 | | // contains a reference to it. |
602 | 0 | av_frame_free(&src_frame); |
603 | |
|
604 | 0 | return 0; |
605 | 0 | } |
606 | | |
607 | 0 | if (!ctxi->hw_type->frames_get_buffer) |
608 | 0 | return AVERROR(ENOSYS); |
609 | | |
610 | 0 | if (!ctx->pool) |
611 | 0 | return AVERROR(EINVAL); |
612 | | |
613 | 0 | frame->hw_frames_ctx = av_buffer_ref(hwframe_ref); |
614 | 0 | if (!frame->hw_frames_ctx) |
615 | 0 | return AVERROR(ENOMEM); |
616 | | |
617 | 0 | ret = ctxi->hw_type->frames_get_buffer(ctx, frame); |
618 | 0 | if (ret < 0) { |
619 | 0 | av_buffer_unref(&frame->hw_frames_ctx); |
620 | 0 | return ret; |
621 | 0 | } |
622 | | |
623 | 0 | frame->extended_data = frame->data; |
624 | |
|
625 | 0 | return 0; |
626 | 0 | } |
627 | | |
628 | | void *av_hwdevice_hwconfig_alloc(AVBufferRef *ref) |
629 | 0 | { |
630 | 0 | FFHWDeviceContext *ctx = (FFHWDeviceContext*)ref->data; |
631 | 0 | const HWContextType *hw_type = ctx->hw_type; |
632 | |
|
633 | 0 | if (hw_type->device_hwconfig_size == 0) |
634 | 0 | return NULL; |
635 | | |
636 | 0 | return av_mallocz(hw_type->device_hwconfig_size); |
637 | 0 | } |
638 | | |
639 | | AVHWFramesConstraints *av_hwdevice_get_hwframe_constraints(AVBufferRef *ref, |
640 | | const void *hwconfig) |
641 | 0 | { |
642 | 0 | FFHWDeviceContext *ctx = (FFHWDeviceContext*)ref->data; |
643 | 0 | const HWContextType *hw_type = ctx->hw_type; |
644 | 0 | AVHWFramesConstraints *constraints; |
645 | |
|
646 | 0 | if (!hw_type->frames_get_constraints) |
647 | 0 | return NULL; |
648 | | |
649 | 0 | constraints = av_mallocz(sizeof(*constraints)); |
650 | 0 | if (!constraints) |
651 | 0 | return NULL; |
652 | | |
653 | 0 | constraints->min_width = constraints->min_height = 0; |
654 | 0 | constraints->max_width = constraints->max_height = INT_MAX; |
655 | |
|
656 | 0 | if (hw_type->frames_get_constraints(&ctx->p, hwconfig, constraints) >= 0) { |
657 | 0 | return constraints; |
658 | 0 | } else { |
659 | 0 | av_hwframe_constraints_free(&constraints); |
660 | 0 | return NULL; |
661 | 0 | } |
662 | 0 | } |
663 | | |
664 | | void av_hwframe_constraints_free(AVHWFramesConstraints **constraints) |
665 | 0 | { |
666 | 0 | if (*constraints) { |
667 | 0 | av_freep(&(*constraints)->valid_hw_formats); |
668 | 0 | av_freep(&(*constraints)->valid_sw_formats); |
669 | 0 | } |
670 | 0 | av_freep(constraints); |
671 | 0 | } |
672 | | |
673 | | int av_hwdevice_ctx_create(AVBufferRef **pdevice_ref, enum AVHWDeviceType type, |
674 | | const char *device, AVDictionary *opts, int flags) |
675 | 0 | { |
676 | 0 | AVBufferRef *device_ref = NULL; |
677 | 0 | FFHWDeviceContext *device_ctx; |
678 | 0 | int ret = 0; |
679 | |
|
680 | 0 | device_ref = av_hwdevice_ctx_alloc(type); |
681 | 0 | if (!device_ref) { |
682 | 0 | ret = AVERROR(ENOMEM); |
683 | 0 | goto fail; |
684 | 0 | } |
685 | 0 | device_ctx = (FFHWDeviceContext*)device_ref->data; |
686 | |
|
687 | 0 | if (!device_ctx->hw_type->device_create) { |
688 | 0 | ret = AVERROR(ENOSYS); |
689 | 0 | goto fail; |
690 | 0 | } |
691 | | |
692 | 0 | ret = device_ctx->hw_type->device_create(&device_ctx->p, device, |
693 | 0 | opts, flags); |
694 | 0 | if (ret < 0) |
695 | 0 | goto fail; |
696 | | |
697 | 0 | ret = av_hwdevice_ctx_init(device_ref); |
698 | 0 | if (ret < 0) |
699 | 0 | goto fail; |
700 | | |
701 | 0 | *pdevice_ref = device_ref; |
702 | 0 | return 0; |
703 | 0 | fail: |
704 | 0 | av_buffer_unref(&device_ref); |
705 | 0 | *pdevice_ref = NULL; |
706 | 0 | return ret; |
707 | 0 | } |
708 | | |
709 | | int av_hwdevice_ctx_create_derived_opts(AVBufferRef **dst_ref_ptr, |
710 | | enum AVHWDeviceType type, |
711 | | AVBufferRef *src_ref, |
712 | | AVDictionary *options, int flags) |
713 | 0 | { |
714 | 0 | AVBufferRef *dst_ref = NULL, *tmp_ref; |
715 | 0 | FFHWDeviceContext *dst_ctx; |
716 | 0 | int ret = 0; |
717 | |
|
718 | 0 | tmp_ref = src_ref; |
719 | 0 | while (tmp_ref) { |
720 | 0 | FFHWDeviceContext *tmp_ctx = (FFHWDeviceContext*)tmp_ref->data; |
721 | 0 | if (tmp_ctx->p.type == type) { |
722 | 0 | dst_ref = av_buffer_ref(tmp_ref); |
723 | 0 | if (!dst_ref) { |
724 | 0 | ret = AVERROR(ENOMEM); |
725 | 0 | goto fail; |
726 | 0 | } |
727 | 0 | goto done; |
728 | 0 | } |
729 | 0 | tmp_ref = tmp_ctx->source_device; |
730 | 0 | } |
731 | | |
732 | 0 | dst_ref = av_hwdevice_ctx_alloc(type); |
733 | 0 | if (!dst_ref) { |
734 | 0 | ret = AVERROR(ENOMEM); |
735 | 0 | goto fail; |
736 | 0 | } |
737 | 0 | dst_ctx = (FFHWDeviceContext*)dst_ref->data; |
738 | |
|
739 | 0 | tmp_ref = src_ref; |
740 | 0 | while (tmp_ref) { |
741 | 0 | FFHWDeviceContext *tmp_ctx = (FFHWDeviceContext*)tmp_ref->data; |
742 | 0 | if (dst_ctx->hw_type->device_derive) { |
743 | 0 | ret = dst_ctx->hw_type->device_derive(&dst_ctx->p, |
744 | 0 | &tmp_ctx->p, |
745 | 0 | options, flags); |
746 | 0 | if (ret == 0) { |
747 | 0 | dst_ctx->source_device = av_buffer_ref(src_ref); |
748 | 0 | if (!dst_ctx->source_device) { |
749 | 0 | ret = AVERROR(ENOMEM); |
750 | 0 | goto fail; |
751 | 0 | } |
752 | 0 | ret = av_hwdevice_ctx_init(dst_ref); |
753 | 0 | if (ret < 0) |
754 | 0 | goto fail; |
755 | 0 | goto done; |
756 | 0 | } |
757 | 0 | if (ret != AVERROR(ENOSYS)) |
758 | 0 | goto fail; |
759 | 0 | } |
760 | 0 | tmp_ref = tmp_ctx->source_device; |
761 | 0 | } |
762 | | |
763 | 0 | ret = AVERROR(ENOSYS); |
764 | 0 | goto fail; |
765 | | |
766 | 0 | done: |
767 | 0 | *dst_ref_ptr = dst_ref; |
768 | 0 | return 0; |
769 | | |
770 | 0 | fail: |
771 | 0 | av_buffer_unref(&dst_ref); |
772 | 0 | *dst_ref_ptr = NULL; |
773 | 0 | return ret; |
774 | 0 | } |
775 | | |
776 | | int av_hwdevice_ctx_create_derived(AVBufferRef **dst_ref_ptr, |
777 | | enum AVHWDeviceType type, |
778 | | AVBufferRef *src_ref, int flags) |
779 | 0 | { |
780 | 0 | return av_hwdevice_ctx_create_derived_opts(dst_ref_ptr, type, src_ref, |
781 | 0 | NULL, flags); |
782 | 0 | } |
783 | | |
784 | | static void ff_hwframe_unmap(void *opaque, uint8_t *data) |
785 | 0 | { |
786 | 0 | HWMapDescriptor *hwmap = (HWMapDescriptor*)data; |
787 | 0 | AVHWFramesContext *ctx = opaque; |
788 | |
|
789 | 0 | if (hwmap->unmap) |
790 | 0 | hwmap->unmap(ctx, hwmap); |
791 | |
|
792 | 0 | av_frame_free(&hwmap->source); |
793 | |
|
794 | 0 | av_buffer_unref(&hwmap->hw_frames_ctx); |
795 | |
|
796 | 0 | av_free(hwmap); |
797 | 0 | } |
798 | | |
799 | | int ff_hwframe_map_create(AVBufferRef *hwframe_ref, |
800 | | AVFrame *dst, const AVFrame *src, |
801 | | void (*unmap)(AVHWFramesContext *ctx, |
802 | | HWMapDescriptor *hwmap), |
803 | | void *priv) |
804 | 0 | { |
805 | 0 | AVHWFramesContext *ctx = (AVHWFramesContext*)hwframe_ref->data; |
806 | 0 | HWMapDescriptor *hwmap; |
807 | 0 | int ret; |
808 | |
|
809 | 0 | hwmap = av_mallocz(sizeof(*hwmap)); |
810 | 0 | if (!hwmap) { |
811 | 0 | ret = AVERROR(ENOMEM); |
812 | 0 | goto fail; |
813 | 0 | } |
814 | | |
815 | 0 | hwmap->source = av_frame_alloc(); |
816 | 0 | if (!hwmap->source) { |
817 | 0 | ret = AVERROR(ENOMEM); |
818 | 0 | goto fail; |
819 | 0 | } |
820 | 0 | ret = av_frame_ref(hwmap->source, src); |
821 | 0 | if (ret < 0) |
822 | 0 | goto fail; |
823 | | |
824 | 0 | hwmap->hw_frames_ctx = av_buffer_ref(hwframe_ref); |
825 | 0 | if (!hwmap->hw_frames_ctx) { |
826 | 0 | ret = AVERROR(ENOMEM); |
827 | 0 | goto fail; |
828 | 0 | } |
829 | | |
830 | 0 | hwmap->unmap = unmap; |
831 | 0 | hwmap->priv = priv; |
832 | |
|
833 | 0 | dst->buf[0] = av_buffer_create((uint8_t*)hwmap, sizeof(*hwmap), |
834 | 0 | &ff_hwframe_unmap, ctx, 0); |
835 | 0 | if (!dst->buf[0]) { |
836 | 0 | ret = AVERROR(ENOMEM); |
837 | 0 | goto fail; |
838 | 0 | } |
839 | | |
840 | 0 | return 0; |
841 | | |
842 | 0 | fail: |
843 | 0 | if (hwmap) { |
844 | 0 | av_buffer_unref(&hwmap->hw_frames_ctx); |
845 | 0 | av_frame_free(&hwmap->source); |
846 | 0 | } |
847 | 0 | av_free(hwmap); |
848 | 0 | return ret; |
849 | 0 | } |
850 | | |
851 | | int av_hwframe_map(AVFrame *dst, const AVFrame *src, int flags) |
852 | 0 | { |
853 | 0 | AVBufferRef *orig_dst_frames = dst->hw_frames_ctx; |
854 | 0 | enum AVPixelFormat orig_dst_fmt = dst->format; |
855 | 0 | HWMapDescriptor *hwmap; |
856 | 0 | int ret; |
857 | |
|
858 | 0 | if (src->hw_frames_ctx && dst->hw_frames_ctx) { |
859 | 0 | FFHWFramesContext *src_frames = (FFHWFramesContext*)src->hw_frames_ctx->data; |
860 | 0 | FFHWFramesContext *dst_frames = (FFHWFramesContext*)dst->hw_frames_ctx->data; |
861 | |
|
862 | 0 | if ((src_frames == dst_frames && |
863 | 0 | src->format == dst_frames->p.sw_format && |
864 | 0 | dst->format == dst_frames->p.format) || |
865 | 0 | (src_frames->source_frames && |
866 | 0 | src_frames->source_frames->data == |
867 | 0 | (uint8_t*)dst_frames)) { |
868 | | // This is an unmap operation. We don't need to directly |
869 | | // do anything here other than fill in the original frame, |
870 | | // because the real unmap will be invoked when the last |
871 | | // reference to the mapped frame disappears. |
872 | 0 | if (!src->buf[0]) { |
873 | 0 | av_log(src_frames, AV_LOG_ERROR, "Invalid mapping " |
874 | 0 | "found when attempting unmap.\n"); |
875 | 0 | return AVERROR(EINVAL); |
876 | 0 | } |
877 | 0 | hwmap = (HWMapDescriptor*)src->buf[0]->data; |
878 | 0 | return av_frame_replace(dst, hwmap->source); |
879 | 0 | } |
880 | 0 | } |
881 | | |
882 | 0 | if (src->hw_frames_ctx) { |
883 | 0 | FFHWFramesContext *src_frames = (FFHWFramesContext*)src->hw_frames_ctx->data; |
884 | |
|
885 | 0 | if (src_frames->p.format == src->format && |
886 | 0 | src_frames->hw_type->map_from) { |
887 | 0 | ret = src_frames->hw_type->map_from(&src_frames->p, |
888 | 0 | dst, src, flags); |
889 | 0 | if (ret >= 0) |
890 | 0 | return ret; |
891 | 0 | else if (ret != AVERROR(ENOSYS)) |
892 | 0 | goto fail; |
893 | 0 | } |
894 | 0 | } |
895 | | |
896 | 0 | if (dst->hw_frames_ctx) { |
897 | 0 | FFHWFramesContext *dst_frames = (FFHWFramesContext*)dst->hw_frames_ctx->data; |
898 | |
|
899 | 0 | if (dst_frames->p.format == dst->format && |
900 | 0 | dst_frames->hw_type->map_to) { |
901 | 0 | ret = dst_frames->hw_type->map_to(&dst_frames->p, |
902 | 0 | dst, src, flags); |
903 | 0 | if (ret >= 0) |
904 | 0 | return ret; |
905 | 0 | else if (ret != AVERROR(ENOSYS)) |
906 | 0 | goto fail; |
907 | 0 | } |
908 | 0 | } |
909 | | |
910 | 0 | return AVERROR(ENOSYS); |
911 | | |
912 | 0 | fail: |
913 | | // if the caller provided dst frames context, it should be preserved |
914 | | // by this function |
915 | 0 | av_assert0(orig_dst_frames == NULL || |
916 | 0 | orig_dst_frames == dst->hw_frames_ctx); |
917 | | |
918 | | // preserve user-provided dst frame fields, but clean |
919 | | // anything we might have set |
920 | 0 | dst->hw_frames_ctx = NULL; |
921 | 0 | av_frame_unref(dst); |
922 | |
|
923 | 0 | dst->hw_frames_ctx = orig_dst_frames; |
924 | 0 | dst->format = orig_dst_fmt; |
925 | |
|
926 | 0 | return ret; |
927 | 0 | } |
928 | | |
929 | | int av_hwframe_ctx_create_derived(AVBufferRef **derived_frame_ctx, |
930 | | enum AVPixelFormat format, |
931 | | AVBufferRef *derived_device_ctx, |
932 | | AVBufferRef *source_frame_ctx, |
933 | | int flags) |
934 | 0 | { |
935 | 0 | AVBufferRef *dst_ref = NULL; |
936 | 0 | FFHWFramesContext *dsti = NULL; |
937 | 0 | FFHWFramesContext *srci = (FFHWFramesContext*)source_frame_ctx->data; |
938 | 0 | AVHWFramesContext *dst, *src = &srci->p; |
939 | 0 | int ret; |
940 | |
|
941 | 0 | if (srci->source_frames) { |
942 | 0 | AVHWFramesContext *src_src = |
943 | 0 | (AVHWFramesContext*)srci->source_frames->data; |
944 | 0 | AVHWDeviceContext *dst_dev = |
945 | 0 | (AVHWDeviceContext*)derived_device_ctx->data; |
946 | |
|
947 | 0 | if (src_src->device_ctx == dst_dev) { |
948 | | // This is actually an unmapping, so we just return a |
949 | | // reference to the source frame context. |
950 | 0 | *derived_frame_ctx = av_buffer_ref(srci->source_frames); |
951 | 0 | if (!*derived_frame_ctx) { |
952 | 0 | ret = AVERROR(ENOMEM); |
953 | 0 | goto fail; |
954 | 0 | } |
955 | 0 | return 0; |
956 | 0 | } |
957 | 0 | } |
958 | | |
959 | 0 | dst_ref = av_hwframe_ctx_alloc(derived_device_ctx); |
960 | 0 | if (!dst_ref) { |
961 | 0 | ret = AVERROR(ENOMEM); |
962 | 0 | goto fail; |
963 | 0 | } |
964 | | |
965 | 0 | dsti = (FFHWFramesContext*)dst_ref->data; |
966 | 0 | dst = &dsti->p; |
967 | |
|
968 | 0 | dst->format = format; |
969 | 0 | dst->sw_format = src->sw_format; |
970 | 0 | dst->width = src->width; |
971 | 0 | dst->height = src->height; |
972 | |
|
973 | 0 | dsti->source_frames = av_buffer_ref(source_frame_ctx); |
974 | 0 | if (!dsti->source_frames) { |
975 | 0 | ret = AVERROR(ENOMEM); |
976 | 0 | goto fail; |
977 | 0 | } |
978 | | |
979 | 0 | dsti->source_allocation_map_flags = |
980 | 0 | flags & (AV_HWFRAME_MAP_READ | |
981 | 0 | AV_HWFRAME_MAP_WRITE | |
982 | 0 | AV_HWFRAME_MAP_OVERWRITE | |
983 | 0 | AV_HWFRAME_MAP_DIRECT); |
984 | |
|
985 | 0 | ret = AVERROR(ENOSYS); |
986 | 0 | if (srci->hw_type->frames_derive_from) |
987 | 0 | ret = srci->hw_type->frames_derive_from(dst, src, flags); |
988 | 0 | if (ret == AVERROR(ENOSYS) && |
989 | 0 | dsti->hw_type->frames_derive_to) |
990 | 0 | ret = dsti->hw_type->frames_derive_to(dst, src, flags); |
991 | 0 | if (ret == AVERROR(ENOSYS)) |
992 | 0 | ret = 0; |
993 | 0 | if (ret) |
994 | 0 | goto fail; |
995 | | |
996 | 0 | *derived_frame_ctx = dst_ref; |
997 | 0 | return 0; |
998 | | |
999 | 0 | fail: |
1000 | 0 | if (dsti) |
1001 | 0 | av_buffer_unref(&dsti->source_frames); |
1002 | 0 | av_buffer_unref(&dst_ref); |
1003 | 0 | return ret; |
1004 | 0 | } |
1005 | | |
1006 | | int ff_hwframe_map_replace(AVFrame *dst, const AVFrame *src) |
1007 | 0 | { |
1008 | 0 | HWMapDescriptor *hwmap = (HWMapDescriptor*)dst->buf[0]->data; |
1009 | 0 | return av_frame_replace(hwmap->source, src); |
1010 | 0 | } |