FFmpeg
Loading...
Searching...
No Matches
hwcontext_cuda.c
Go to the documentation of this file.
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 "buffer.h"
20#include "common.h"
21#include "hwcontext.h"
22#include "hwcontext_internal.h"
24#if CONFIG_VULKAN
25#include "hwcontext_vulkan.h"
26#endif
27#include "cuda_check.h"
28#include "mem.h"
29#include "pixdesc.h"
30#include "pixfmt.h"
31#include "imgutils.h"
32
41
46
47
48
49#define CHECK_CU(x) FF_CUDA_CHECK_DL(device_ctx, cu, x)
50
51static CUarray_format cuda_array_format_for_pix_fmt(enum AVPixelFormat fmt);
52
54 const void *hwconfig,
55 AVHWFramesConstraints *constraints)
56{
57 const AVCUDAHWConfig *config = hwconfig;
58 enum AVPixelFormat req_fmt = config ? config->hw_format : AV_PIX_FMT_NONE;
59 int n = 0;
60
61 if (req_fmt == AV_PIX_FMT_CUDA || req_fmt == AV_PIX_FMT_CUARRAY) {
62 constraints->valid_hw_formats = av_malloc_array(2, sizeof(*constraints->valid_hw_formats));
63 if (!constraints->valid_hw_formats)
64 return AVERROR(ENOMEM);
65
66 constraints->valid_hw_formats[0] = req_fmt;
67 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
68 } else {
69 constraints->valid_hw_formats = av_malloc_array(2 + HAVE_FFNVCODEC_CUARRAY, sizeof(*constraints->valid_hw_formats));
70 if (!constraints->valid_hw_formats)
71 return AVERROR(ENOMEM);
72
73 constraints->valid_hw_formats[0] = AV_PIX_FMT_CUDA;
74#if HAVE_FFNVCODEC_CUARRAY
75 constraints->valid_hw_formats[1] = AV_PIX_FMT_CUARRAY;
76 constraints->valid_hw_formats[2] = AV_PIX_FMT_NONE;
77#else
78 constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
79#endif
80 }
81
83 sizeof(*constraints->valid_sw_formats));
84 if (!constraints->valid_sw_formats)
85 return AVERROR(ENOMEM);
86
87 n = 0;
88 for (int i = 0; i < AV_PIX_FMT_NB; i++) {
89 if (req_fmt == AV_PIX_FMT_CUARRAY) {
91 constraints->valid_sw_formats[n++] = i;
92 } else {
94 /* Palette formats carry their palette in a separate zero-linesize
95 * data[1] plane that the transfer path does not handle, so exclude
96 * them along with the hwaccel formats. */
98 constraints->valid_sw_formats[n++] = i;
99 }
100 }
101#if CONFIG_VULKAN
102 if (req_fmt != AV_PIX_FMT_CUARRAY)
103 constraints->valid_sw_formats[n++] = AV_PIX_FMT_VULKAN;
104#endif
105 constraints->valid_sw_formats[n] = AV_PIX_FMT_NONE;
106
107 return 0;
108}
109
110static void cuda_buffer_free(void *opaque, uint8_t *data)
111{
112 AVHWFramesContext *ctx = opaque;
113 AVHWDeviceContext *device_ctx = ctx->device_ctx;
114 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
115 CudaFunctions *cu = hwctx->internal->cuda_dl;
116
117 CUcontext dummy;
118
119 CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
120
121 if (ctx->format == AV_PIX_FMT_CUARRAY) {
123 CHECK_CU(cu->cuArrayDestroy(desc->array));
124 av_free(desc);
125 } else {
126 CHECK_CU(cu->cuMemFree((CUdeviceptr)data));
127 }
128
129 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
130}
131
132static AVBufferRef *cuda_pool_alloc(void *opaque, size_t size)
133{
134 AVHWFramesContext *ctx = opaque;
135 CUDAFramesContext *priv = ctx->hwctx;
136 AVHWDeviceContext *device_ctx = ctx->device_ctx;
137 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
138 CudaFunctions *cu = hwctx->internal->cuda_dl;
139
140 AVBufferRef *ret = NULL;
141 CUcontext dummy = NULL;
142 CUdeviceptr data;
143 int err;
144
145 err = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
146 if (err < 0)
147 return NULL;
148
149 if (ctx->format == AV_PIX_FMT_CUARRAY) {
151 if (!desc)
152 goto done;
153
154 if (priv->p.cuarray_num_surfaces > 0) {
156 av_log(ctx, AV_LOG_ERROR, "Static surface pool size exceeded.\n");
157 av_free(desc);
158 goto done;
159 }
160 desc->index = priv->cuarray_num_surfaces_used++;
161 desc->array = priv->p.cuarray_surfaces[desc->index];
162 } else {
163 err = CHECK_CU(cu->cuArray3DCreate(&desc->array, &priv->p.cuarray_desc));
164 if (err < 0) {
165 av_free(desc);
166 goto done;
167 }
168 }
169
170 ret = av_buffer_create((uint8_t*)desc, sizeof(*desc), cuda_buffer_free, ctx, 0);
171 if (!ret) {
172 // It is okay (and necessary) to free a pool array here,
173 // since cuarray_num_surfaces_used is already incremented.
174 CHECK_CU(cu->cuArrayDestroy(desc->array));
175 av_free(desc);
176 goto done;
177 }
178
179 goto done;
180 }
181
182 err = CHECK_CU(cu->cuMemAlloc(&data, size));
183 if (err < 0)
184 goto done;
185
186 ret = av_buffer_create((uint8_t*)data, size, cuda_buffer_free, ctx, 0);
187 if (!ret) {
188 CHECK_CU(cu->cuMemFree(data));
189 goto done;
190 }
191
192 // Common exit: reached on both success (ret holds the buffer) and
193 // failure (ret == NULL); restores the CUDA context and returns ret.
194done:
195 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
196 return ret;
197}
198
200{
201 AVHWDeviceContext *device_ctx = ctx->device_ctx;
202 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
203 CUDAFramesContext *priv = ctx->hwctx;
204 CudaFunctions *cu = hwctx->internal->cuda_dl;
205
206 if (priv->p.cuarray_surfaces) {
207 CUcontext dummy;
208 CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
209
210 // Make sure we don't free surfaces that have been adopted by the pool already
211 for (int i = priv->cuarray_num_surfaces_used; i < priv->p.cuarray_num_surfaces; i++)
212 if (priv->p.cuarray_surfaces[i])
213 CHECK_CU(cu->cuArrayDestroy(priv->p.cuarray_surfaces[i]));
214
215 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
216
218 priv->p.cuarray_num_surfaces = 0;
219 }
220}
221
222static CUarray_format cuda_array_format_for_pix_fmt(enum AVPixelFormat fmt)
223{
224 switch (fmt) {
225#if HAVE_FFNVCODEC_CUARRAY
226 case AV_PIX_FMT_NV12: return CU_AD_FORMAT_NV12;
227 case AV_PIX_FMT_P010:
228 case AV_PIX_FMT_P012:
229 case AV_PIX_FMT_P016: return CU_AD_FORMAT_P016;
230 case AV_PIX_FMT_NV16: return CU_AD_FORMAT_NV16;
231 case AV_PIX_FMT_P210:
232 case AV_PIX_FMT_P212:
233 case AV_PIX_FMT_P216: return CU_AD_FORMAT_P216;
234 case AV_PIX_FMT_NV24: return CU_AD_FORMAT_YUV444_8BIT_SEMIPLANAR;
235 case AV_PIX_FMT_P410:
236 case AV_PIX_FMT_P412:
237 case AV_PIX_FMT_P416: return CU_AD_FORMAT_YUV444_16BIT_SEMIPLANAR;
238 case AV_PIX_FMT_YUV420P: return CU_AD_FORMAT_UINT8_PLANAR_420;
239 case AV_PIX_FMT_YUV422P: return CU_AD_FORMAT_UINT8_PLANAR_422;
240 case AV_PIX_FMT_YUV444P: return CU_AD_FORMAT_UINT8_PLANAR_444;
241 case AV_PIX_FMT_YUV420P10: return CU_AD_FORMAT_UINT16_PLANAR_420;
242 case AV_PIX_FMT_YUV422P10: return CU_AD_FORMAT_UINT16_PLANAR_422;
246 case AV_PIX_FMT_YUV444P16: return CU_AD_FORMAT_UINT16_PLANAR_444;
249 case AV_PIX_FMT_RGB32:
250 case AV_PIX_FMT_BGR32: return CU_AD_FORMAT_UNSIGNED_INT8;
251#endif
252 default: return 0;
253 }
254}
255
256#if HAVE_FFNVCODEC_CUARRAY
257static unsigned int cuda_array_numchannels_for_pix_fmt(enum AVPixelFormat fmt)
258{
259 switch (fmt) {
262 case AV_PIX_FMT_RGB32:
263 case AV_PIX_FMT_BGR32: return 4;
264 default: return 3;
265 }
266}
267#endif
268
270{
271 AVHWDeviceContext *device_ctx = ctx->device_ctx;
272 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
273 CUDAFramesContext *priv = ctx->hwctx;
274 CudaFunctions *cu = hwctx->internal->cuda_dl;
275 const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(ctx->sw_format);
276 int err;
277
278 if (!desc) {
279 av_log(ctx, AV_LOG_ERROR, "Invalid pixel format\n");
280 return AVERROR(EINVAL);
281 }
282
283 /* Palette formats keep their palette in a separate zero-linesize plane
284 * that the transfer path does not copy, so they are not supported. */
285 if (desc->flags & AV_PIX_FMT_FLAG_PAL) {
286 av_log(ctx, AV_LOG_ERROR, "Palette formats are not supported\n");
287 return AVERROR(ENOSYS);
288 }
289
290#if HAVE_FFNVCODEC_CUARRAY
291 if (ctx->format == AV_PIX_FMT_CUARRAY && !cu->cuArrayGetPlane) {
292 av_log(ctx, AV_LOG_ERROR, "cuArrayGetPlane not available, update your driver\n");
293 return AVERROR(ENOSYS);
294 }
295#else
296 if (ctx->format == AV_PIX_FMT_CUARRAY) {
297 av_log(ctx, AV_LOG_ERROR, "Missing support for cuarray frames. Rebuild with newer ffnvcodec headers.\n");
298 return AVERROR(ENOSYS);
299 }
300#endif
301
302 err = CHECK_CU(cu->cuDeviceGetAttribute(&priv->tex_alignment,
303 14 /* CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT */,
304 hwctx->internal->cuda_device));
305 if (err < 0)
306 return err;
307
308 av_log(ctx, AV_LOG_DEBUG, "CUDA texture alignment: %d\n", priv->tex_alignment);
309
310 // YUV420P is a special case.
311 // Since nvenc expects the U/V planes to have half the linesize of the Y plane
312 // alignment has to be doubled to ensure the U/V planes still end up aligned.
313 if (ctx->sw_format == AV_PIX_FMT_YUV420P)
314 priv->tex_alignment *= 2;
315
317
318#if HAVE_FFNVCODEC_CUARRAY
319 if (ctx->format == AV_PIX_FMT_CUARRAY) {
320 if (!priv->p.cuarray_desc.Width)
321 priv->p.cuarray_desc.Width = ctx->width;
322 if (!priv->p.cuarray_desc.Height)
323 priv->p.cuarray_desc.Height = ctx->height;
324 if (!priv->p.cuarray_desc.NumChannels)
325 priv->p.cuarray_desc.NumChannels = cuda_array_numchannels_for_pix_fmt(ctx->sw_format);
326
327 if (priv->p.cuarray_desc.Depth) {
328 av_log(ctx, AV_LOG_ERROR, "CUarrays with non-zero depth are not supported.\n");
329 return AVERROR(EINVAL);
330 }
331
332 if (!priv->p.cuarray_desc.Format)
333 priv->p.cuarray_desc.Format = cuda_array_format_for_pix_fmt(ctx->sw_format);
334 if (!priv->p.cuarray_desc.Format) {
335 av_log(ctx, AV_LOG_ERROR, "Invalid CUarray pixel format\n");
336 return AVERROR_BUG;
337 }
338
339 priv->p.cuarray_desc.Flags |= CUDA_ARRAY3D_SURFACE_LDST | CUDA_ARRAY3D_VIDEO_ENCODE_DECODE;
340 }
341
342 if (ctx->format == AV_PIX_FMT_CUARRAY && priv->p.cuarray_num_surfaces > 0) {
343 CUcontext dummy;
344
345 priv->p.cuarray_surfaces = av_calloc(priv->p.cuarray_num_surfaces, sizeof(*priv->p.cuarray_surfaces));
346 if (!priv->p.cuarray_surfaces)
347 return AVERROR(ENOMEM);
348
349 err = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
350 if (err < 0) {
352 return err;
353 }
354
355 for (int i = 0; i < priv->p.cuarray_num_surfaces; i++) {
356 err = CHECK_CU(cu->cuArray3DCreate(&priv->p.cuarray_surfaces[i], &priv->p.cuarray_desc));
357 if (err < 0) {
358 for (i = i - 1; i >= 0; i--)
359 CHECK_CU(cu->cuArrayDestroy(priv->p.cuarray_surfaces[i]));
360 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
361
363 return err;
364 }
365 }
366
367 err = CHECK_CU(cu->cuCtxPopCurrent(&dummy));
368 if (err < 0)
369 goto fail;
370
371 av_log(ctx, AV_LOG_DEBUG, "allocated %d CUarray surfaces (%zux%zu)\n",
372 priv->p.cuarray_num_surfaces, priv->p.cuarray_desc.Width, priv->p.cuarray_desc.Height);
373 }
374#endif
375
376 if (!ctx->pool) {
377 int size = av_image_get_buffer_size(ctx->sw_format, ctx->width, ctx->height, priv->tex_alignment);
378 if (size < 0) {
379 err = size;
380 goto fail;
381 }
382
385 if (!ffhwframesctx(ctx)->pool_internal) {
386 err = AVERROR(ENOMEM);
387 goto fail;
388 }
389 }
390
391 return 0;
392
393fail:
395 return err;
396}
397
399{
400 CUDAFramesContext *priv = ctx->hwctx;
401#if HAVE_FFNVCODEC_CUARRAY
402 AVHWDeviceContext *device_ctx = ctx->device_ctx;
403 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
404 CudaFunctions *cu = hwctx->internal->cuda_dl;
405
406 CUcontext dummy;
407#endif
408 int res;
409
410 frame->buf[0] = av_buffer_pool_get(ctx->pool);
411 if (!frame->buf[0])
412 return AVERROR(ENOMEM);
413
414 if (ctx->format == AV_PIX_FMT_CUARRAY) {
415#if HAVE_FFNVCODEC_CUARRAY
417 if (!desc) {
418 frame->format = ctx->format;
419 frame->width = ctx->width;
420 frame->height = ctx->height;
421 return 0;
422 }
423 frame->data[0] = (uint8_t*)desc->array;
424 frame->data[1] = (uint8_t*)desc->index;
425
426 res = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
427 if (res < 0)
428 return res;
429
430 for (int i = 0; i < FF_ARRAY_ELEMS(frame->linesize); i++) {
431 CUDA_ARRAY3D_DESCRIPTOR plane_desc = { 0 };
432 CUarray plane_array;
433 CUresult arr_plane_res = cu->cuArrayGetPlane(&plane_array, desc->array, i);
434
435 if (arr_plane_res == CUDA_ERROR_INVALID_VALUE) {
436 if (i > 0)
437 break;
438 /* Non-planar format (e.g. UNSIGNED_INT8 x4 for packed RGB):
439 * cuArrayGetPlane is unsupported, query the array directly. */
440 res = CHECK_CU(cu->cuArray3DGetDescriptor(&plane_desc, desc->array));
441 if (res < 0)
442 goto fail;
443 } else if (arr_plane_res != CUDA_SUCCESS) {
444 res = CHECK_CU(arr_plane_res);
445 goto fail;
446 } else {
447 res = CHECK_CU(cu->cuArray3DGetDescriptor(&plane_desc, plane_array));
448 if (res < 0)
449 goto fail;
450 }
451
452 int elem_size = ff_cuda_cuarray_elem_size(plane_desc.Format);
453 if (elem_size <= 0) {
454 res = AVERROR_BUG;
455 goto fail;
456 }
457
458 frame->linesize[i] = plane_desc.Width * plane_desc.NumChannels * elem_size;
459
460 if (arr_plane_res == CUDA_ERROR_INVALID_VALUE)
461 break;
462 }
463
464 res = CHECK_CU(cu->cuCtxPopCurrent(&dummy));
465 if (res < 0)
466 return res;
467#else
468 return AVERROR(ENOSYS);
469#endif
470 } else {
471 res = av_image_fill_arrays(frame->data, frame->linesize, frame->buf[0]->data,
472 ctx->sw_format, ctx->width, ctx->height, priv->tex_alignment);
473 if (res < 0)
474 return res;
475
476 // YUV420P is a special case.
477 // Nvenc expects the U/V planes in swapped order from how ffmpeg expects them, also chroma is half-aligned
478 if (ctx->sw_format == AV_PIX_FMT_YUV420P) {
479 frame->linesize[1] = frame->linesize[2] = frame->linesize[0] / 2;
480 frame->data[2] = frame->data[1];
481 frame->data[1] = frame->data[2] + frame->linesize[2] * AV_CEIL_RSHIFT(ctx->height, 1);
482 }
483 }
484
485 frame->format = ctx->format;
486 frame->width = ctx->width;
487 frame->height = ctx->height;
488
489 return 0;
490
491#if HAVE_FFNVCODEC_CUARRAY
492fail:
493 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
494 return res;
495#endif
496}
497
499{
500 if (!frame->hw_frames_ctx)
501 return AV_PIX_FMT_NONE;
502 return ((AVHWFramesContext *)frame->hw_frames_ctx->data)->format;
503}
504
507 enum AVPixelFormat **formats)
508{
509 enum AVPixelFormat *fmts;
510
511 fmts = av_malloc_array(2, sizeof(*fmts));
512 if (!fmts)
513 return AVERROR(ENOMEM);
514
515 fmts[0] = ctx->sw_format;
516 fmts[1] = AV_PIX_FMT_NONE;
517
518 *formats = fmts;
519
520 return 0;
521}
522
524 const AVFrame *src)
525{
526 CUDAFramesContext *priv = ctx->hwctx;
527 AVHWDeviceContext *device_ctx = ctx->device_ctx;
528 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
529 CudaFunctions *cu = hwctx->internal->cuda_dl;
530
531 CUcontext dummy;
532 int i, ret, copy_queued = 0;
533
534 {
535 enum AVPixelFormat src_fmt = cuda_frame_hw_format(src);
536 enum AVPixelFormat dst_fmt = cuda_frame_hw_format(dst);
537 if ((src_fmt != AV_PIX_FMT_NONE &&
538 src_fmt != AV_PIX_FMT_CUDA && src_fmt != AV_PIX_FMT_CUARRAY) ||
539 (dst_fmt != AV_PIX_FMT_NONE &&
540 dst_fmt != AV_PIX_FMT_CUDA && dst_fmt != AV_PIX_FMT_CUARRAY))
541 return AVERROR(ENOSYS);
542 }
543
544 ret = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
545 if (ret < 0)
546 return ret;
547
548 /*
549 * Copy one plane per iteration, bounded by the AVFrame linesizes. src and
550 * dst always share the same sw_format (this path never converts), so they
551 * are either both multi-planar or both single-plane.
552 *
553 * For a CUARRAY side, cuArrayGetPlane() returns the sub-array for plane i
554 * of a multi-planar CUarray (NV12, P0xx, NV24, planar 4:4:4, ...).
555 * cuArrayGetPlane() returns CUDA_ERROR_INVALID_VALUE either when the array
556 * is not multi-planar or when the plane index exceeds its plane count:
557 * - i == 0: the array is packed (e.g. RGB), i.e. a single plane, so the
558 * whole array is used as that plane.
559 * - i > 0: the planes of a multi-planar array always have a non-zero
560 * linesize, so a valid plane is never skipped by the loop bound;
561 * getting INVALID_VALUE here means src/dst disagree on the plane count,
562 * which is an inconsistency and is treated as an error (handled by the
563 * generic cures != CUDA_SUCCESS branch) rather than silently copying a
564 * subset of the planes.
565 */
566 for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->linesize[i]; i++) {
567 int src_is_nonplanar_cuarray = 0;
568 int dst_is_nonplanar_cuarray = 0;
569
570 CUDA_MEMCPY2D cpy = {
571 .srcPitch = src->linesize[i],
572 .dstPitch = dst->linesize[i],
573 .WidthInBytes = FFMIN(src->linesize[i], dst->linesize[i]),
574 .Height = AV_CEIL_RSHIFT(src->height, ((i == 0 || i == 3) ? 0 : priv->shift_height)),
575 };
576
577 if (src->format == AV_PIX_FMT_CUDA) {
578 cpy.srcMemoryType = CU_MEMORYTYPE_DEVICE;
579 cpy.srcDevice = (CUdeviceptr)src->data[i];
580 } else if (src->format == AV_PIX_FMT_CUARRAY) {
581#if HAVE_FFNVCODEC_CUARRAY
582 CUarray array;
583 CUresult cures = cu->cuArrayGetPlane(&array, (CUarray)src->data[0], i);
584 if (cures == CUDA_ERROR_INVALID_VALUE && i == 0) {
585 /* Not a multi-planar array (packed format): the whole array is
586 * the single plane. */
587 array = (CUarray)src->data[0];
588 src_is_nonplanar_cuarray = 1;
589 } else if (cures != CUDA_SUCCESS) {
590 ret = CHECK_CU(cures);
591 goto fail;
592 }
593
594 cpy.srcMemoryType = CU_MEMORYTYPE_ARRAY;
595 cpy.srcArray = array;
596#else
597 ret = AVERROR(ENOSYS);
598 goto exit;
599#endif
600 } else {
601 cpy.srcMemoryType = CU_MEMORYTYPE_HOST;
602 cpy.srcHost = src->data[i];
603 }
604
605 if (dst->format == AV_PIX_FMT_CUDA) {
606 cpy.dstMemoryType = CU_MEMORYTYPE_DEVICE;
607 cpy.dstDevice = (CUdeviceptr)dst->data[i];
608 } else if (dst->format == AV_PIX_FMT_CUARRAY) {
609#if HAVE_FFNVCODEC_CUARRAY
610 CUarray array;
611 CUresult cures = cu->cuArrayGetPlane(&array, (CUarray)dst->data[0], i);
612 if (cures == CUDA_ERROR_INVALID_VALUE && i == 0) {
613 /* Not a multi-planar array (packed format): the whole array is
614 * the single plane. */
615 array = (CUarray)dst->data[0];
616 dst_is_nonplanar_cuarray = 1;
617 } else if (cures != CUDA_SUCCESS) {
618 ret = CHECK_CU(cures);
619 goto fail;
620 }
621
622 cpy.dstMemoryType = CU_MEMORYTYPE_ARRAY;
623 cpy.dstArray = array;
624#else
625 ret = AVERROR(ENOSYS);
626 goto exit;
627#endif
628 } else {
629 cpy.dstMemoryType = CU_MEMORYTYPE_HOST;
630 cpy.dstHost = dst->data[i];
631 }
632
633 ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, hwctx->stream));
634 if (ret < 0)
635 goto fail;
636 copy_queued = 1;
637
638 if (src_is_nonplanar_cuarray || dst_is_nonplanar_cuarray)
639 break;
640 }
641
642 if (!dst->hw_frames_ctx) {
643 ret = CHECK_CU(cu->cuStreamSynchronize(hwctx->stream));
644 if (ret < 0)
645 goto exit;
646 }
647
648fail:
649 if (ret < 0 && copy_queued)
650 CHECK_CU(cu->cuStreamSynchronize(hwctx->stream));
651exit:
652 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
653
654 return ret;
655}
656
657static void cuda_device_uninit(AVHWDeviceContext *device_ctx)
658{
659 CUDADeviceContext *hwctx = device_ctx->hwctx;
660
661 if (hwctx->p.internal) {
662 CudaFunctions *cu = hwctx->internal.cuda_dl;
663
664 if (hwctx->internal.is_allocated && hwctx->p.cuda_ctx) {
666 CHECK_CU(cu->cuDevicePrimaryCtxRelease(hwctx->internal.cuda_device));
667 else if (!(hwctx->internal.flags & AV_CUDA_USE_CURRENT_CONTEXT))
668 CHECK_CU(cu->cuCtxDestroy(hwctx->p.cuda_ctx));
669
670 hwctx->p.cuda_ctx = NULL;
671 }
672
673 cuda_free_functions(&hwctx->internal.cuda_dl);
674 memset(&hwctx->internal, 0, sizeof(hwctx->internal));
675 hwctx->p.internal = NULL;
676 }
677}
678
680{
681 CUDADeviceContext *hwctx = ctx->hwctx;
682 int ret;
683
684 hwctx->p.internal = &hwctx->internal;
685
686 if (!hwctx->internal.cuda_dl) {
687 ret = cuda_load_functions(&hwctx->internal.cuda_dl, ctx);
688 if (ret < 0) {
689 av_log(ctx, AV_LOG_ERROR, "Could not dynamically load CUDA\n");
690 goto error;
691 }
692 }
693
694 return 0;
695
696error:
698 return ret;
699}
700
701static int cuda_context_init(AVHWDeviceContext *device_ctx, int flags) {
702 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
703 CudaFunctions *cu;
704 CUcontext dummy;
705 int ret, dev_active = 0;
706 unsigned int dev_flags = 0;
707
708 const unsigned int desired_flags = CU_CTX_SCHED_BLOCKING_SYNC;
709
710 cu = hwctx->internal->cuda_dl;
711
712 hwctx->internal->flags = flags;
713
715 ret = CHECK_CU(cu->cuDevicePrimaryCtxGetState(hwctx->internal->cuda_device,
716 &dev_flags, &dev_active));
717 if (ret < 0)
718 return ret;
719
720 if (dev_active && dev_flags != desired_flags) {
721 av_log(device_ctx, AV_LOG_ERROR, "Primary context already active with incompatible flags.\n");
722 return AVERROR(ENOTSUP);
723 } else if (dev_flags != desired_flags) {
724 ret = CHECK_CU(cu->cuDevicePrimaryCtxSetFlags(hwctx->internal->cuda_device,
725 desired_flags));
726 if (ret < 0)
727 return ret;
728 }
729
730 ret = CHECK_CU(cu->cuDevicePrimaryCtxRetain(&hwctx->cuda_ctx,
731 hwctx->internal->cuda_device));
732 if (ret < 0)
733 return ret;
734 } else if (flags & AV_CUDA_USE_CURRENT_CONTEXT) {
735 ret = CHECK_CU(cu->cuCtxGetCurrent(&hwctx->cuda_ctx));
736 if (ret < 0)
737 return ret;
738 av_log(device_ctx, AV_LOG_INFO, "Using current CUDA context.\n");
739 } else {
740 ret = CHECK_CU(cu->cuCtxCreate(&hwctx->cuda_ctx, desired_flags,
741 hwctx->internal->cuda_device));
742 if (ret < 0)
743 return ret;
744
745 CHECK_CU(cu->cuCtxPopCurrent(&dummy));
746 }
747
748 hwctx->internal->is_allocated = 1;
749
750 // Setting stream to NULL will make functions automatically use the default CUstream
751 hwctx->stream = NULL;
752
753 return 0;
754}
755
757 AVDictionary *opts, int *flags)
758{
759 AVDictionaryEntry *primary_ctx_opt = av_dict_get(opts, "primary_ctx", NULL, 0);
760 AVDictionaryEntry *current_ctx_opt = av_dict_get(opts, "current_ctx", NULL, 0);
761
762 int use_primary_ctx = 0, use_current_ctx = 0;
763 if (primary_ctx_opt)
764 use_primary_ctx = strtol(primary_ctx_opt->value, NULL, 10);
765
766 if (current_ctx_opt)
767 use_current_ctx = strtol(current_ctx_opt->value, NULL, 10);
768
769 if (use_primary_ctx && use_current_ctx) {
770 av_log(device_ctx, AV_LOG_ERROR, "Requested both primary and current CUDA context simultaneously.\n");
771 return AVERROR(EINVAL);
772 }
773
774 if (primary_ctx_opt && use_primary_ctx) {
775 av_log(device_ctx, AV_LOG_VERBOSE, "Using CUDA primary device context\n");
777 } else if (primary_ctx_opt) {
778 av_log(device_ctx, AV_LOG_VERBOSE, "Disabling use of CUDA primary device context\n");
780 }
781
782 if (current_ctx_opt && use_current_ctx) {
783 av_log(device_ctx, AV_LOG_VERBOSE, "Using CUDA current device context\n");
785 } else if (current_ctx_opt) {
786 av_log(device_ctx, AV_LOG_VERBOSE, "Disabling use of CUDA current device context\n");
788 }
789
790 return 0;
791}
792
794 const char *device,
795 AVDictionary *opts, int flags)
796{
797 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
798 CudaFunctions *cu;
799 int ret, device_idx = 0;
800
801 ret = cuda_flags_from_opts(device_ctx, opts, &flags);
802 if (ret < 0)
803 goto error;
804
805 if (device)
806 device_idx = strtol(device, NULL, 0);
807
808 ret = cuda_device_init(device_ctx);
809 if (ret < 0)
810 goto error;
811
812 cu = hwctx->internal->cuda_dl;
813
814 ret = CHECK_CU(cu->cuInit(0));
815 if (ret < 0)
816 goto error;
817
818 ret = CHECK_CU(cu->cuDeviceGet(&hwctx->internal->cuda_device, device_idx));
819 if (ret < 0)
820 goto error;
821
822 ret = cuda_context_init(device_ctx, flags);
823 if (ret < 0)
824 goto error;
825
826 return 0;
827
828error:
829 cuda_device_uninit(device_ctx);
830 return ret;
831}
832
835 int flags) {
836 AVCUDADeviceContext *hwctx = device_ctx->hwctx;
837 CudaFunctions *cu;
838 const char *src_uuid = NULL;
839#if CONFIG_VULKAN
840 VkPhysicalDeviceIDProperties vk_idp;
841#endif
842 int ret, i, device_count;
843
844 ret = cuda_flags_from_opts(device_ctx, opts, &flags);
845 if (ret < 0)
846 goto error;
847
848#if CONFIG_VULKAN
849 vk_idp = (VkPhysicalDeviceIDProperties) {
850 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_ID_PROPERTIES,
851 };
852#endif
853
854 switch (src_ctx->type) {
855#if CONFIG_VULKAN
856#define TYPE PFN_vkGetPhysicalDeviceProperties2
858 AVVulkanDeviceContext *vkctx = src_ctx->hwctx;
859 TYPE prop_fn = (TYPE)vkctx->get_proc_addr(vkctx->inst, "vkGetPhysicalDeviceProperties2");
860 VkPhysicalDeviceProperties2 vk_dev_props = {
861 .sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_PROPERTIES_2,
862 .pNext = &vk_idp,
863 };
864 prop_fn(vkctx->phys_dev, &vk_dev_props);
865 src_uuid = vk_idp.deviceUUID;
866 break;
867 }
868#undef TYPE
869#endif
870 default:
871 ret = AVERROR(ENOSYS);
872 goto error;
873 }
874
875 if (!src_uuid) {
876 av_log(device_ctx, AV_LOG_ERROR,
877 "Failed to get UUID of source device.\n");
878 ret = AVERROR(EINVAL);
879 goto error;
880 }
881
882 ret = cuda_device_init(device_ctx);
883 if (ret < 0)
884 goto error;
885
886 cu = hwctx->internal->cuda_dl;
887
888 ret = CHECK_CU(cu->cuInit(0));
889 if (ret < 0)
890 goto error;
891
892 ret = CHECK_CU(cu->cuDeviceGetCount(&device_count));
893 if (ret < 0)
894 goto error;
895
896 hwctx->internal->cuda_device = -1;
897 for (i = 0; i < device_count; i++) {
898 CUdevice dev;
899 CUuuid uuid;
900
901 ret = CHECK_CU(cu->cuDeviceGet(&dev, i));
902 if (ret < 0)
903 goto error;
904
905 ret = CHECK_CU(cu->cuDeviceGetUuid(&uuid, dev));
906 if (ret < 0)
907 goto error;
908
909 if (memcmp(src_uuid, uuid.bytes, sizeof (uuid.bytes)) == 0) {
910 hwctx->internal->cuda_device = dev;
911 break;
912 }
913 }
914
915 if (hwctx->internal->cuda_device == -1) {
916 av_log(device_ctx, AV_LOG_ERROR, "Could not derive CUDA device.\n");
917 ret = AVERROR(ENODEV);
918 goto error;
919 }
920
921 ret = cuda_context_init(device_ctx, flags);
922 if (ret < 0)
923 goto error;
924
925 return 0;
926
927error:
928 cuda_device_uninit(device_ctx);
929 return ret;
930}
931
933 .type = AV_HWDEVICE_TYPE_CUDA,
934 .name = "CUDA",
935
936 .device_hwctx_size = sizeof(CUDADeviceContext),
937 .frames_hwctx_size = sizeof(CUDAFramesContext),
938
939 .device_create = cuda_device_create,
940 .device_derive = cuda_device_derive,
941 .device_init = cuda_device_init,
942 .device_uninit = cuda_device_uninit,
943 .frames_get_constraints = cuda_frames_get_constraints,
944 .frames_init = cuda_frames_init,
945 .frames_uninit = cuda_frames_uninit,
946 .frames_get_buffer = cuda_get_buffer,
947 .transfer_get_formats = cuda_transfer_get_formats,
948 .transfer_data_to = cuda_transfer_data,
949 .transfer_data_from = cuda_transfer_data,
950
951 .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA,
952#if HAVE_FFNVCODEC_CUARRAY
954#endif
956};
uint8_t ptrdiff_t const uint8_t ptrdiff_t int intptr_t intptr_t int int16_t * dst
Definition dsp.h:87
static AVFormatContext * ctx
static AVDictionary * opts
refcounted data buffer API
#define flags(name, subs,...)
Definition cbs_h264.c:74
#define i(width, name, range_min, range_max)
Definition cbs_h264.c:63
common internal and external API header
#define AV_CEIL_RSHIFT(a, b)
Definition common.h:60
#define NULL
Definition coverity.c:32
static AVFrame * frame
static int dummy
Definition ffplay.c:3754
#define TYPE
Definition ffv1dec.c:90
#define fail
Definition test.h:479
#define AV_CUDA_USE_CURRENT_CONTEXT
Use current device context instead of creating a new one.
#define AV_CUDA_USE_PRIMARY_CONTEXT
Use primary device context instead of creating a new one.
AVBufferRef * av_buffer_create(uint8_t *data, size_t size, void(*free)(void *opaque, uint8_t *data), void *opaque, int flags)
Create an AVBuffer from an existing array.
Definition buffer.c:55
AVBufferRef * av_buffer_pool_get(AVBufferPool *pool)
Allocate a new AVBuffer, reusing an old buffer from the pool when available.
Definition buffer.c:390
AVBufferPool * av_buffer_pool_init2(size_t size, void *opaque, AVBufferRef *(*alloc)(void *opaque, size_t size), void(*pool_free)(void *opaque))
Allocate and initialize a buffer pool with a more complex allocator.
Definition buffer.c:259
AVDictionaryEntry * av_dict_get(const AVDictionary *m, const char *key, const AVDictionaryEntry *prev, int flags)
Get a dictionary entry with matching key.
Definition dict.c:60
#define AVERROR_BUG
Internal bug, also see AVERROR_BUG2.
Definition error.h:52
#define AVERROR(e)
Definition error.h:45
#define AV_LOG_DEBUG
Stuff which is only useful for libav* developers.
Definition log.h:231
#define AV_LOG_VERBOSE
Detailed information.
Definition log.h:226
#define AV_LOG_INFO
Standard information.
Definition log.h:221
#define AV_LOG_ERROR
Something went wrong and cannot losslessly be recovered.
Definition log.h:210
int av_image_get_buffer_size(enum AVPixelFormat pix_fmt, int width, int height, int align)
Return the size in bytes of the amount of data required to store an image with the given parameters.
Definition imgutils.c:466
int av_image_fill_arrays(uint8_t *dst_data[4], int dst_linesize[4], const uint8_t *src, enum AVPixelFormat pix_fmt, int width, int height, int align)
Setup the data pointers and linesizes based on the specified image parameters and the provided array.
Definition imgutils.c:446
AVHWFrameTransferDirection
Definition hwcontext.h:406
@ AV_HWDEVICE_TYPE_VULKAN
Definition hwcontext.h:39
@ AV_HWDEVICE_TYPE_CUDA
Definition hwcontext.h:30
static void cuda_device_uninit(AVHWDeviceContext *device_ctx)
static CUarray_format cuda_array_format_for_pix_fmt(enum AVPixelFormat fmt)
static int cuda_transfer_get_formats(AVHWFramesContext *ctx, enum AVHWFrameTransferDirection dir, enum AVPixelFormat **formats)
static int cuda_flags_from_opts(AVHWDeviceContext *device_ctx, AVDictionary *opts, int *flags)
static enum AVPixelFormat cuda_frame_hw_format(const AVFrame *frame)
static AVBufferRef * cuda_pool_alloc(void *opaque, size_t size)
static int cuda_frames_get_constraints(AVHWDeviceContext *ctx, const void *hwconfig, AVHWFramesConstraints *constraints)
static int cuda_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
static void cuda_buffer_free(void *opaque, uint8_t *data)
static int cuda_device_init(AVHWDeviceContext *ctx)
static int cuda_frames_init(AVHWFramesContext *ctx)
static int cuda_context_init(AVHWDeviceContext *device_ctx, int flags)
static void cuda_frames_uninit(AVHWFramesContext *ctx)
const HWContextType ff_hwcontext_type_cuda
#define CHECK_CU(x)
static int cuda_device_create(AVHWDeviceContext *device_ctx, const char *device, AVDictionary *opts, int flags)
static int cuda_device_derive(AVHWDeviceContext *device_ctx, AVHWDeviceContext *src_ctx, AVDictionary *opts, int flags)
static int cuda_transfer_data(AVHWFramesContext *ctx, AVFrame *dst, const AVFrame *src)
FFmpeg internal API for CUDA.
static int ff_cuda_cuarray_elem_size(CUarray_format fmt)
Return the element size in bytes for a CUarray_format, or 0 for unknown.
static FFHWFramesContext * ffhwframesctx(AVHWFramesContext *ctx)
API-specific header for AV_HWDEVICE_TYPE_VULKAN.
misc image utilities
const char * desc
Definition libsvtav1.c:83
#define FFMIN(a, b)
Definition macros.h:49
void * av_calloc(size_t nmemb, size_t size)
Definition mem.c:264
Memory handling functions.
const char data[16]
Definition mxf.c:149
int av_pix_fmt_get_chroma_sub_sample(enum AVPixelFormat pix_fmt, int *h_shift, int *v_shift)
Utility function to access log2_chroma_w log2_chroma_h from the pixel format AVPixFmtDescriptor.
Definition pixdesc.c:3488
const AVPixFmtDescriptor * av_pix_fmt_desc_get(enum AVPixelFormat pix_fmt)
Definition pixdesc.c:3460
#define AV_PIX_FMT_FLAG_HWACCEL
Pixel format is an HW accelerated format.
Definition pixdesc.h:128
#define AV_PIX_FMT_FLAG_PAL
Pixel format has a palette in data[1], values are indexes in this palette.
Definition pixdesc.h:120
pixel format definitions
#define AV_PIX_FMT_0RGB32
Definition pixfmt.h:521
#define AV_PIX_FMT_P212
Definition pixfmt.h:624
#define AV_PIX_FMT_YUV420P10
Definition pixfmt.h:545
#define AV_PIX_FMT_P412
Definition pixfmt.h:625
#define AV_PIX_FMT_P210
Definition pixfmt.h:622
#define AV_PIX_FMT_P012
Definition pixfmt.h:609
#define AV_PIX_FMT_P216
Definition pixfmt.h:626
#define AV_PIX_FMT_P010
Definition pixfmt.h:608
#define AV_PIX_FMT_P016
Definition pixfmt.h:610
#define AV_PIX_FMT_YUV422P10
Definition pixfmt.h:546
#define AV_PIX_FMT_YUV444P12MSB
Definition pixfmt.h:561
#define AV_PIX_FMT_BGR32
Definition pixfmt.h:519
#define AV_PIX_FMT_P410
Definition pixfmt.h:623
AVPixelFormat
Pixel format.
Definition pixfmt.h:71
@ AV_PIX_FMT_NV12
planar YUV 4:2:0, 12bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (firs...
Definition pixfmt.h:96
@ AV_PIX_FMT_NONE
Definition pixfmt.h:72
@ AV_PIX_FMT_VULKAN
Vulkan hardware images.
Definition pixfmt.h:379
@ AV_PIX_FMT_YUV420P
planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
Definition pixfmt.h:73
@ AV_PIX_FMT_NB
number of pixel formats, DO NOT USE THIS if you want to link with shared libav* because the number of...
Definition pixfmt.h:508
@ AV_PIX_FMT_YUV422P
planar YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:77
@ AV_PIX_FMT_CUDA
HW acceleration through CUDA.
Definition pixfmt.h:260
@ AV_PIX_FMT_NV24
planar YUV 4:4:4, 24bpp, 1 plane for Y and 1 plane for the UV components, which are interleaved (firs...
Definition pixfmt.h:371
@ AV_PIX_FMT_NV16
interleaved chroma YUV 4:2:2, 16bpp, (1 Cr & Cb sample per 2x1 Y samples)
Definition pixfmt.h:198
@ AV_PIX_FMT_YUV444P
planar YUV 4:4:4, 24bpp, (1 Cr & Cb sample per 1x1 Y samples)
Definition pixfmt.h:78
@ AV_PIX_FMT_CUARRAY
hardware decoding through openharmony
Definition pixfmt.h:506
#define AV_PIX_FMT_P416
Definition pixfmt.h:627
#define AV_PIX_FMT_YUV444P16
Definition pixfmt.h:558
#define AV_PIX_FMT_YUV444P10MSB
Definition pixfmt.h:560
#define AV_PIX_FMT_0BGR32
Definition pixfmt.h:522
#define AV_PIX_FMT_RGB32
Definition pixfmt.h:517
#define AV_PIX_FMT_YUV444P10
Definition pixfmt.h:548
formats
Definition signature.h:47
#define FF_ARRAY_ELEMS(a)
A reference to a data buffer.
Definition buffer.h:82
CUDA frame descriptor for pool allocation of AV_PIX_FMT_CUARRAY frames.
This struct is allocated as AVHWDeviceContext.hwctx.
AVCUDADeviceContextInternal * internal
This struct is allocated as AVHWFramesContext.hwctx.
CUarray * cuarray_surfaces
If cuarray_num_surfaces is >0, this contains the array of pre-allocated surfaces.
CUDA_ARRAY3D_DESCRIPTOR cuarray_desc
CUDA_ARRAY3D_DESCRIPTOR CUarrays will be initialized with.
int cuarray_num_surfaces
If >0, pre-allocate a fixed pool of surfaces.
CUDA hardware pipeline configuration details.
char * value
Definition dict.h:92
This structure describes decoded (raw) audio or video data.
Definition frame.h:472
This struct aggregates all the (hardware/vendor-specific) "high-level" state, i.e.
Definition hwcontext.h:63
void * hwctx
The format-specific data, allocated and freed by libavutil along with this context.
Definition hwcontext.h:88
enum AVHWDeviceType type
This field identifies the underlying API used for hardware access.
Definition hwcontext.h:75
This struct describes the constraints on hardware frames attached to a given device with a hardware-s...
Definition hwcontext.h:444
enum AVPixelFormat * valid_hw_formats
A list of possible values for format in the hw_frames_ctx, terminated by AV_PIX_FMT_NONE.
Definition hwcontext.h:449
enum AVPixelFormat * valid_sw_formats
A list of possible values for sw_format in the hw_frames_ctx, terminated by AV_PIX_FMT_NONE.
Definition hwcontext.h:456
This struct describes a set or pool of "hardware" frames (i.e.
Definition hwcontext.h:118
Descriptor that unambiguously describes how the bits of a pixel are stored in the up to 4 data planes...
Definition pixdesc.h:69
Main Vulkan context, allocated as AVHWDeviceContext.hwctx.
VkPhysicalDevice phys_dev
Physical device.
VkInstance inst
Vulkan instance.
PFN_vkGetInstanceProcAddr get_proc_addr
Pointer to a vkGetInstanceProcAddr loading function.
AVCUDADeviceContextInternal internal
AVCUDADeviceContext p
AVCUDAFramesContext p
AVBufferPool * pool_internal
#define av_free(p)
#define av_malloc_array(a, b)
#define av_mallocz(s)
#define av_freep(p)
#define av_log(a,...)
static void error(const char *err)
#define src
Definition vp8dsp.c:248
static int array[MAX_W *MAX_W]
int size