This is an automated email from the git hooks/post-receive script.

Git pushed a commit to branch master
in repository ffmpeg.

commit ef3dcf4ea6139f2b8cad29e67b48a39f094fa3bc
Author:     Timo Rothenpieler <[email protected]>
AuthorDate: Mon Mar 16 16:11:32 2026 +0100
Commit:     Timo Rothenpieler <[email protected]>
CommitDate: Tue Jul 14 19:45:02 2026 +0000

    avutil/hwcontext_cuda: add CUarray surface pool support
    
    Extend the internal CUDA frames context (CUDAFramesContext) to manage
    block-linear CUarray surfaces alongside the existing pitched-linear
    allocations.
    
    When the frames context format is AV_PIX_FMT_CUARRAY, the caller
    (e.g. NVDEC) can fill cuarray_desc and cuarray_num_surfaces before
    calling av_hwframe_ctx_init(), which then allocates the CUarray
    surfaces.
    
    Also add cuda_transfer_data_from_cuarray() to support hwdownload
    from CUarray frames to host memory via cuMemcpy2DAsync.
    
    Co-authored-by: Diego de Souza <[email protected]>
    Signed-off-by: Diego de Souza <[email protected]>
---
 doc/APIchanges                      |   3 +
 libavutil/hwcontext_cuda.c          | 409 ++++++++++++++++++++++++++++++++----
 libavutil/hwcontext_cuda.h          |  74 ++++++-
 libavutil/hwcontext_cuda_internal.h |  21 ++
 libavutil/version.h                 |   2 +-
 5 files changed, 470 insertions(+), 39 deletions(-)

diff --git a/doc/APIchanges b/doc/APIchanges
index b24ea9d8f2..a3a978c43b 100644
--- a/doc/APIchanges
+++ b/doc/APIchanges
@@ -2,6 +2,9 @@ The last version increases of all libraries were on 2026-06-23.
 
 API changes, most recent first:
 
+2026-07-14 - xxxxxxxxxx - lavu 61.5.100 - hwcontext_cuda.h
+  Add AVCUDAFramesContext and AVCUDAArrayFrameDescriptor.
+
 2026-07-14 - xxxxxxxxxx - lavu 61.5.100 - pixfmt.h
   Add AV_PIX_FMT_CUARRAY.
 
diff --git a/libavutil/hwcontext_cuda.c b/libavutil/hwcontext_cuda.c
index a171a9e139..61fbf52885 100644
--- a/libavutil/hwcontext_cuda.c
+++ b/libavutil/hwcontext_cuda.c
@@ -31,8 +31,12 @@
 #include "imgutils.h"
 
 typedef struct CUDAFramesContext {
+    AVCUDAFramesContext p;
+
     int shift_width, shift_height;
     int tex_alignment;
+
+    int cuarray_num_surfaces_used;
 } CUDAFramesContext;
 
 typedef struct CUDADeviceContext {
@@ -46,12 +50,16 @@ static const enum AVPixelFormat supported_formats[] = {
     AV_PIX_FMT_YUV420P,
     AV_PIX_FMT_YUVA420P,
     AV_PIX_FMT_YUV444P,
+    AV_PIX_FMT_NV24,
     AV_PIX_FMT_P010,
     AV_PIX_FMT_P012,
     AV_PIX_FMT_P016,
     AV_PIX_FMT_P210,
     AV_PIX_FMT_P212,
     AV_PIX_FMT_P216,
+    AV_PIX_FMT_P410,
+    AV_PIX_FMT_P412,
+    AV_PIX_FMT_P416,
     AV_PIX_FMT_YUV422P,
     AV_PIX_FMT_YUV420P10,
     AV_PIX_FMT_YUV422P10,
@@ -70,27 +78,46 @@ static const enum AVPixelFormat supported_formats[] = {
 
 #define CHECK_CU(x) FF_CUDA_CHECK_DL(device_ctx, cu, x)
 
+static CUarray_format cuda_array_format_for_pix_fmt(enum AVPixelFormat fmt);
+static unsigned int cuda_array_numchannels_for_pix_fmt(enum AVPixelFormat fmt);
+
 static int cuda_frames_get_constraints(AVHWDeviceContext *ctx,
                                        const void *hwconfig,
                                        AVHWFramesConstraints *constraints)
 {
-    int i;
+    const AVCUDAHWConfig *config = hwconfig;
+    enum AVPixelFormat req_fmt = config ? config->hw_format : AV_PIX_FMT_NONE;
+    int i, nb_sw_formats = 0;
 
     constraints->valid_sw_formats = 
av_malloc_array(FF_ARRAY_ELEMS(supported_formats) + 1,
                                                     
sizeof(*constraints->valid_sw_formats));
     if (!constraints->valid_sw_formats)
         return AVERROR(ENOMEM);
 
-    for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++)
-        constraints->valid_sw_formats[i] = supported_formats[i];
-    constraints->valid_sw_formats[FF_ARRAY_ELEMS(supported_formats)] = 
AV_PIX_FMT_NONE;
+    for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
+        if (req_fmt == AV_PIX_FMT_CUARRAY &&
+            !cuda_array_format_for_pix_fmt(supported_formats[i]))
+            continue;
+        constraints->valid_sw_formats[nb_sw_formats++] = supported_formats[i];
+    }
+    constraints->valid_sw_formats[nb_sw_formats] = AV_PIX_FMT_NONE;
 
-    constraints->valid_hw_formats = av_malloc_array(2, 
sizeof(*constraints->valid_hw_formats));
-    if (!constraints->valid_hw_formats)
-        return AVERROR(ENOMEM);
+    if (req_fmt == AV_PIX_FMT_CUDA || req_fmt == AV_PIX_FMT_CUARRAY) {
+        constraints->valid_hw_formats = av_malloc_array(2, 
sizeof(*constraints->valid_hw_formats));
+        if (!constraints->valid_hw_formats)
+            return AVERROR(ENOMEM);
+
+        constraints->valid_hw_formats[0] = req_fmt;
+        constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
+    } else {
+        constraints->valid_hw_formats = av_malloc_array(3, 
sizeof(*constraints->valid_hw_formats));
+        if (!constraints->valid_hw_formats)
+            return AVERROR(ENOMEM);
 
-    constraints->valid_hw_formats[0] = AV_PIX_FMT_CUDA;
-    constraints->valid_hw_formats[1] = AV_PIX_FMT_NONE;
+        constraints->valid_hw_formats[0] = AV_PIX_FMT_CUDA;
+        constraints->valid_hw_formats[1] = AV_PIX_FMT_CUARRAY;
+        constraints->valid_hw_formats[2] = AV_PIX_FMT_NONE;
+    }
 
     return 0;
 }
@@ -106,7 +133,13 @@ static void cuda_buffer_free(void *opaque, uint8_t *data)
 
     CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
 
-    CHECK_CU(cu->cuMemFree((CUdeviceptr)data));
+    if (ctx->format == AV_PIX_FMT_CUARRAY) {
+        AVCUDAArrayFrameDescriptor *desc = (AVCUDAArrayFrameDescriptor*)data;
+        CHECK_CU(cu->cuArrayDestroy(desc->array));
+        av_free(desc);
+    } else {
+        CHECK_CU(cu->cuMemFree((CUdeviceptr)data));
+    }
 
     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
 }
@@ -114,6 +147,7 @@ static void cuda_buffer_free(void *opaque, uint8_t *data)
 static AVBufferRef *cuda_pool_alloc(void *opaque, size_t size)
 {
     AVHWFramesContext        *ctx = opaque;
+    CUDAFramesContext       *priv = ctx->hwctx;
     AVHWDeviceContext *device_ctx = ctx->device_ctx;
     AVCUDADeviceContext    *hwctx = device_ctx->hwctx;
     CudaFunctions             *cu = hwctx->internal->cuda_dl;
@@ -127,21 +161,122 @@ static AVBufferRef *cuda_pool_alloc(void *opaque, size_t 
size)
     if (err < 0)
         return NULL;
 
+    if (ctx->format == AV_PIX_FMT_CUARRAY) {
+        AVCUDAArrayFrameDescriptor *desc = av_mallocz(sizeof(*desc));
+        if (!desc)
+            goto done;
+
+        if (priv->p.cuarray_num_surfaces > 0) {
+            if (priv->cuarray_num_surfaces_used >= 
priv->p.cuarray_num_surfaces) {
+                av_log(ctx, AV_LOG_ERROR, "Static surface pool size 
exceeded.\n");
+                av_free(desc);
+                goto done;
+            }
+            desc->index = priv->cuarray_num_surfaces_used++;
+            desc->array = priv->p.cuarray_surfaces[desc->index];
+        } else {
+            err = CHECK_CU(cu->cuArray3DCreate(&desc->array, 
&priv->p.cuarray_desc));
+            if (err < 0) {
+                av_free(desc);
+                goto done;
+            }
+        }
+
+        ret = av_buffer_create((uint8_t*)desc, sizeof(*desc), 
cuda_buffer_free, ctx, 0);
+        if (!ret) {
+            // It is okay (and necessary) to free a pool array here,
+            // since cuarray_num_surfaces_used is already incremented.
+            CHECK_CU(cu->cuArrayDestroy(desc->array));
+            av_free(desc);
+            goto done;
+        }
+
+        goto done;
+    }
+
     err = CHECK_CU(cu->cuMemAlloc(&data, size));
     if (err < 0)
-        goto fail;
+        goto done;
 
     ret = av_buffer_create((uint8_t*)data, size, cuda_buffer_free, ctx, 0);
     if (!ret) {
         CHECK_CU(cu->cuMemFree(data));
-        goto fail;
+        goto done;
     }
 
-fail:
+    // Common exit: reached on both success (ret holds the buffer) and
+    // failure (ret == NULL); restores the CUDA context and returns ret.
+done:
     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
     return ret;
 }
 
+static void cuda_frames_uninit(AVHWFramesContext *ctx)
+{
+    AVHWDeviceContext *device_ctx = ctx->device_ctx;
+    AVCUDADeviceContext    *hwctx = device_ctx->hwctx;
+    CUDAFramesContext       *priv = ctx->hwctx;
+    CudaFunctions             *cu = hwctx->internal->cuda_dl;
+
+    if (priv->p.cuarray_surfaces) {
+        CUcontext dummy;
+        CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
+
+        // Make sure we don't free surfaces that have been adopted by the pool 
already
+        for (int i = priv->cuarray_num_surfaces_used; i < 
priv->p.cuarray_num_surfaces; i++)
+            if (priv->p.cuarray_surfaces[i])
+                CHECK_CU(cu->cuArrayDestroy(priv->p.cuarray_surfaces[i]));
+
+        CHECK_CU(cu->cuCtxPopCurrent(&dummy));
+
+        av_freep(&priv->p.cuarray_surfaces);
+        priv->p.cuarray_num_surfaces = 0;
+    }
+}
+
+static CUarray_format cuda_array_format_for_pix_fmt(enum AVPixelFormat fmt)
+{
+    switch (fmt) {
+    case AV_PIX_FMT_NV12:      return CU_AD_FORMAT_NV12;
+    case AV_PIX_FMT_P010:
+    case AV_PIX_FMT_P012:
+    case AV_PIX_FMT_P016:      return CU_AD_FORMAT_P016;
+    case AV_PIX_FMT_NV16:      return CU_AD_FORMAT_NV16;
+    case AV_PIX_FMT_P210:
+    case AV_PIX_FMT_P212:
+    case AV_PIX_FMT_P216:      return CU_AD_FORMAT_P216;
+    case AV_PIX_FMT_NV24:      return CU_AD_FORMAT_YUV444_8BIT_SEMIPLANAR;
+    case AV_PIX_FMT_P410:
+    case AV_PIX_FMT_P412:
+    case AV_PIX_FMT_P416:      return CU_AD_FORMAT_YUV444_16BIT_SEMIPLANAR;
+    case AV_PIX_FMT_YUV420P:   return CU_AD_FORMAT_UINT8_PLANAR_420;
+    case AV_PIX_FMT_YUV422P:   return CU_AD_FORMAT_UINT8_PLANAR_422;
+    case AV_PIX_FMT_YUV444P:   return CU_AD_FORMAT_UINT8_PLANAR_444;
+    case AV_PIX_FMT_YUV420P10: return CU_AD_FORMAT_UINT16_PLANAR_420;
+    case AV_PIX_FMT_YUV422P10: return CU_AD_FORMAT_UINT16_PLANAR_422;
+    case AV_PIX_FMT_YUV444P10:
+    case AV_PIX_FMT_YUV444P10MSB:
+    case AV_PIX_FMT_YUV444P12MSB:
+    case AV_PIX_FMT_YUV444P16: return CU_AD_FORMAT_UINT16_PLANAR_444;
+    case AV_PIX_FMT_0RGB32:
+    case AV_PIX_FMT_0BGR32:
+    case AV_PIX_FMT_RGB32:
+    case AV_PIX_FMT_BGR32:     return CU_AD_FORMAT_UNSIGNED_INT8;
+    default:                   return 0;
+    }
+}
+
+static unsigned int cuda_array_numchannels_for_pix_fmt(enum AVPixelFormat fmt)
+{
+    switch (fmt) {
+    case AV_PIX_FMT_0RGB32:
+    case AV_PIX_FMT_0BGR32:
+    case AV_PIX_FMT_RGB32:
+    case AV_PIX_FMT_BGR32:     return 4;
+    default:                   return 3;
+    }
+}
+
 static int cuda_frames_init(AVHWFramesContext *ctx)
 {
     AVHWDeviceContext *device_ctx = ctx->device_ctx;
@@ -150,6 +285,8 @@ static int cuda_frames_init(AVHWFramesContext *ctx)
     CudaFunctions             *cu = hwctx->internal->cuda_dl;
     int err, i;
 
+    CUcontext dummy;
+
     for (i = 0; i < FF_ARRAY_ELEMS(supported_formats); i++) {
         if (ctx->sw_format == supported_formats[i])
             break;
@@ -160,6 +297,11 @@ static int cuda_frames_init(AVHWFramesContext *ctx)
         return AVERROR(ENOSYS);
     }
 
+    if (ctx->format == AV_PIX_FMT_CUARRAY && !cu->cuArrayGetPlane) {
+        av_log(ctx, AV_LOG_ERROR, "cuArrayGetPlane not available, update your 
driver\n");
+        return AVERROR(ENOSYS);
+    }
+
     err = CHECK_CU(cu->cuDeviceGetAttribute(&priv->tex_alignment,
                                             14 /* 
CU_DEVICE_ATTRIBUTE_TEXTURE_ALIGNMENT */,
                                             hwctx->internal->cuda_device));
@@ -176,47 +318,179 @@ static int cuda_frames_init(AVHWFramesContext *ctx)
 
     av_pix_fmt_get_chroma_sub_sample(ctx->sw_format, &priv->shift_width, 
&priv->shift_height);
 
+    if (ctx->format == AV_PIX_FMT_CUARRAY) {
+        if (!priv->p.cuarray_desc.Width)
+            priv->p.cuarray_desc.Width = ctx->width;
+        if (!priv->p.cuarray_desc.Height)
+            priv->p.cuarray_desc.Height = ctx->height;
+        if (!priv->p.cuarray_desc.NumChannels)
+            priv->p.cuarray_desc.NumChannels = 
cuda_array_numchannels_for_pix_fmt(ctx->sw_format);
+
+        if (priv->p.cuarray_desc.Depth) {
+            av_log(ctx, AV_LOG_ERROR, "CUarrays with non-zero depth are not 
supported.\n");
+            return AVERROR(EINVAL);
+        }
+
+        if (!priv->p.cuarray_desc.Format)
+            priv->p.cuarray_desc.Format = 
cuda_array_format_for_pix_fmt(ctx->sw_format);
+        if (!priv->p.cuarray_desc.Format) {
+            av_log(ctx, AV_LOG_ERROR, "Invalid CUarray pixel format\n");
+            return AVERROR_BUG;
+        }
+
+        priv->p.cuarray_desc.Flags |= CUDA_ARRAY3D_SURFACE_LDST | 
CUDA_ARRAY3D_VIDEO_ENCODE_DECODE;
+    }
+
+    if (ctx->format == AV_PIX_FMT_CUARRAY && priv->p.cuarray_num_surfaces > 0) 
{
+        priv->p.cuarray_surfaces = av_calloc(priv->p.cuarray_num_surfaces, 
sizeof(*priv->p.cuarray_surfaces));
+        if (!priv->p.cuarray_surfaces)
+            return AVERROR(ENOMEM);
+
+        err = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
+        if (err < 0) {
+            av_freep(&priv->p.cuarray_surfaces);
+            return err;
+        }
+
+        for (i = 0; i < priv->p.cuarray_num_surfaces; i++) {
+            err = CHECK_CU(cu->cuArray3DCreate(&priv->p.cuarray_surfaces[i], 
&priv->p.cuarray_desc));
+            if (err < 0) {
+                for (i = i - 1; i >= 0; i--)
+                    CHECK_CU(cu->cuArrayDestroy(priv->p.cuarray_surfaces[i]));
+                CHECK_CU(cu->cuCtxPopCurrent(&dummy));
+
+                av_freep(&priv->p.cuarray_surfaces);
+                return err;
+            }
+        }
+
+        err = CHECK_CU(cu->cuCtxPopCurrent(&dummy));
+        if (err < 0)
+            goto fail;
+
+        av_log(ctx, AV_LOG_DEBUG, "allocated %d CUarray surfaces (%zux%zu)\n",
+               priv->p.cuarray_num_surfaces, priv->p.cuarray_desc.Width, 
priv->p.cuarray_desc.Height);
+    }
+
     if (!ctx->pool) {
         int size = av_image_get_buffer_size(ctx->sw_format, ctx->width, 
ctx->height, priv->tex_alignment);
-        if (size < 0)
-            return size;
+        if (size < 0) {
+            err = size;
+            goto fail;
+        }
 
         ffhwframesctx(ctx)->pool_internal =
             av_buffer_pool_init2(size, ctx, cuda_pool_alloc, NULL);
-        if (!ffhwframesctx(ctx)->pool_internal)
-            return AVERROR(ENOMEM);
+        if (!ffhwframesctx(ctx)->pool_internal) {
+            err = AVERROR(ENOMEM);
+            goto fail;
+        }
     }
 
     return 0;
+
+fail:
+    cuda_frames_uninit(ctx);
+    return err;
 }
 
 static int cuda_get_buffer(AVHWFramesContext *ctx, AVFrame *frame)
 {
-    CUDAFramesContext *priv = ctx->hwctx;
+    AVHWDeviceContext *device_ctx = ctx->device_ctx;
+    AVCUDADeviceContext    *hwctx = device_ctx->hwctx;
+    CUDAFramesContext       *priv = ctx->hwctx;
+    CudaFunctions             *cu = hwctx->internal->cuda_dl;
+
+    CUcontext dummy;
     int res;
 
     frame->buf[0] = av_buffer_pool_get(ctx->pool);
     if (!frame->buf[0])
         return AVERROR(ENOMEM);
 
-    res = av_image_fill_arrays(frame->data, frame->linesize, 
frame->buf[0]->data,
-                               ctx->sw_format, ctx->width, ctx->height, 
priv->tex_alignment);
-    if (res < 0)
-        return res;
+    if (ctx->format == AV_PIX_FMT_CUARRAY) {
+        AVCUDAArrayFrameDescriptor *desc = 
(AVCUDAArrayFrameDescriptor*)frame->buf[0]->data;
+        if (!desc) {
+            frame->format = ctx->format;
+            frame->width  = ctx->width;
+            frame->height = ctx->height;
+            return 0;
+        }
+        frame->data[0] = (uint8_t*)desc->array;
+        frame->data[1] = (uint8_t*)desc->index;
+
+        res = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
+        if (res < 0)
+            return res;
+
+        for (int i = 0; i < FF_ARRAY_ELEMS(frame->linesize); i++) {
+            CUDA_ARRAY3D_DESCRIPTOR plane_desc = { 0 };
+            CUarray plane_array;
+            CUresult arr_plane_res = cu->cuArrayGetPlane(&plane_array, 
desc->array, i);
+
+            if (arr_plane_res == CUDA_ERROR_INVALID_VALUE) {
+                if (i > 0)
+                    break;
+                /* Non-planar format (e.g. UNSIGNED_INT8 x4 for packed RGB):
+                 * cuArrayGetPlane is unsupported, query the array directly. */
+                res = CHECK_CU(cu->cuArray3DGetDescriptor(&plane_desc, 
desc->array));
+                if (res < 0)
+                    goto fail;
+            } else if (arr_plane_res != CUDA_SUCCESS) {
+                res = CHECK_CU(arr_plane_res);
+                goto fail;
+            } else {
+                res = CHECK_CU(cu->cuArray3DGetDescriptor(&plane_desc, 
plane_array));
+                if (res < 0)
+                    goto fail;
+            }
+
+            int elem_size = ff_cuda_cuarray_elem_size(plane_desc.Format);
+            if (elem_size <= 0) {
+                res = AVERROR_BUG;
+                goto fail;
+            }
+
+            frame->linesize[i] = plane_desc.Width * plane_desc.NumChannels * 
elem_size;
+
+            if (arr_plane_res == CUDA_ERROR_INVALID_VALUE)
+                break;
+        }
 
-    // YUV420P is a special case.
-    // Nvenc expects the U/V planes in swapped order from how ffmpeg expects 
them, also chroma is half-aligned
-    if (ctx->sw_format == AV_PIX_FMT_YUV420P) {
-        frame->linesize[1] = frame->linesize[2] = frame->linesize[0] / 2;
-        frame->data[2]     = frame->data[1];
-        frame->data[1]     = frame->data[2] + frame->linesize[2] * 
AV_CEIL_RSHIFT(ctx->height, 1);
+        res = CHECK_CU(cu->cuCtxPopCurrent(&dummy));
+        if (res < 0)
+            return res;
+    } else {
+        res = av_image_fill_arrays(frame->data, frame->linesize, 
frame->buf[0]->data,
+                                   ctx->sw_format, ctx->width, ctx->height, 
priv->tex_alignment);
+        if (res < 0)
+            return res;
+
+        // YUV420P is a special case.
+        // Nvenc expects the U/V planes in swapped order from how ffmpeg 
expects them, also chroma is half-aligned
+        if (ctx->sw_format == AV_PIX_FMT_YUV420P) {
+            frame->linesize[1] = frame->linesize[2] = frame->linesize[0] / 2;
+            frame->data[2]     = frame->data[1];
+            frame->data[1]     = frame->data[2] + frame->linesize[2] * 
AV_CEIL_RSHIFT(ctx->height, 1);
+        }
     }
 
-    frame->format = AV_PIX_FMT_CUDA;
+    frame->format = ctx->format;
     frame->width  = ctx->width;
     frame->height = ctx->height;
 
     return 0;
+
+fail:
+    CHECK_CU(cu->cuCtxPopCurrent(&dummy));
+    return res;
+}
+
+static enum AVPixelFormat cuda_frame_hw_format(const AVFrame *frame)
+{
+    if (!frame->hw_frames_ctx)
+        return AV_PIX_FMT_NONE;
+    return ((AVHWFramesContext *)frame->hw_frames_ctx->data)->format;
 }
 
 static int cuda_transfer_get_formats(AVHWFramesContext *ctx,
@@ -248,15 +522,42 @@ static int cuda_transfer_data(AVHWFramesContext *ctx, 
AVFrame *dst,
     CUcontext dummy;
     int i, ret;
 
-    if ((src->hw_frames_ctx && 
((AVHWFramesContext*)src->hw_frames_ctx->data)->format != AV_PIX_FMT_CUDA) ||
-        (dst->hw_frames_ctx && 
((AVHWFramesContext*)dst->hw_frames_ctx->data)->format != AV_PIX_FMT_CUDA))
-        return AVERROR(ENOSYS);
+    {
+        enum AVPixelFormat src_fmt = cuda_frame_hw_format(src);
+        enum AVPixelFormat dst_fmt = cuda_frame_hw_format(dst);
+        if ((src_fmt != AV_PIX_FMT_NONE &&
+             src_fmt != AV_PIX_FMT_CUDA && src_fmt != AV_PIX_FMT_CUARRAY) ||
+            (dst_fmt != AV_PIX_FMT_NONE &&
+             dst_fmt != AV_PIX_FMT_CUDA && dst_fmt != AV_PIX_FMT_CUARRAY))
+            return AVERROR(ENOSYS);
+    }
 
     ret = CHECK_CU(cu->cuCtxPushCurrent(hwctx->cuda_ctx));
     if (ret < 0)
         return ret;
 
-    for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->data[i]; i++) {
+    /*
+     * Copy one plane per iteration, bounded by the AVFrame linesizes. src and
+     * dst always share the same sw_format (this path never converts), so they
+     * are either both multi-planar or both single-plane.
+     *
+     * For a CUARRAY side, cuArrayGetPlane() returns the sub-array for plane i
+     * of a multi-planar CUarray (NV12, P0xx, NV24, planar 4:4:4, ...).
+     * cuArrayGetPlane() returns CUDA_ERROR_INVALID_VALUE either when the array
+     * is not multi-planar or when the plane index exceeds its plane count:
+     *   - i == 0: the array is packed (e.g. RGB), i.e. a single plane, so the
+     *     whole array is used as that plane.
+     *   - i  > 0: the planes of a multi-planar array always have a non-zero
+     *     linesize, so a valid plane is never skipped by the loop bound;
+     *     getting INVALID_VALUE here means src/dst disagree on the plane 
count,
+     *     which is an inconsistency and is treated as an error (handled by the
+     *     generic cures != CUDA_SUCCESS branch) rather than silently copying a
+     *     subset of the planes.
+     */
+    for (i = 0; i < FF_ARRAY_ELEMS(src->data) && src->linesize[i]; i++) {
+        int src_is_nonplanar_cuarray = 0;
+        int dst_is_nonplanar_cuarray = 0;
+
         CUDA_MEMCPY2D cpy = {
             .srcPitch      = src->linesize[i],
             .dstPitch      = dst->linesize[i],
@@ -264,17 +565,47 @@ static int cuda_transfer_data(AVHWFramesContext *ctx, 
AVFrame *dst,
             .Height        = AV_CEIL_RSHIFT(src->height, ((i == 0 || i == 3) ? 
0 : priv->shift_height)),
         };
 
-        if (src->hw_frames_ctx) {
+        if (src->format == AV_PIX_FMT_CUDA) {
             cpy.srcMemoryType = CU_MEMORYTYPE_DEVICE;
             cpy.srcDevice     = (CUdeviceptr)src->data[i];
+        } else if (src->format == AV_PIX_FMT_CUARRAY) {
+            CUarray array;
+            CUresult cures = cu->cuArrayGetPlane(&array, 
(CUarray)src->data[0], i);
+            if (cures == CUDA_ERROR_INVALID_VALUE && i == 0) {
+                /* Not a multi-planar array (packed format): the whole array is
+                 * the single plane. */
+                array = (CUarray)src->data[0];
+                src_is_nonplanar_cuarray = 1;
+            } else if (cures != CUDA_SUCCESS) {
+                ret = CHECK_CU(cures);
+                goto exit;
+            }
+
+            cpy.srcMemoryType = CU_MEMORYTYPE_ARRAY;
+            cpy.srcArray      = array;
         } else {
             cpy.srcMemoryType = CU_MEMORYTYPE_HOST;
             cpy.srcHost       = src->data[i];
         }
 
-        if (dst->hw_frames_ctx) {
+        if (dst->format == AV_PIX_FMT_CUDA) {
             cpy.dstMemoryType = CU_MEMORYTYPE_DEVICE;
             cpy.dstDevice     = (CUdeviceptr)dst->data[i];
+        } else if (dst->format == AV_PIX_FMT_CUARRAY) {
+            CUarray array;
+            CUresult cures = cu->cuArrayGetPlane(&array, 
(CUarray)dst->data[0], i);
+            if (cures == CUDA_ERROR_INVALID_VALUE && i == 0) {
+                /* Not a multi-planar array (packed format): the whole array is
+                 * the single plane. */
+                array = (CUarray)dst->data[0];
+                dst_is_nonplanar_cuarray = 1;
+            } else if (cures != CUDA_SUCCESS) {
+                ret = CHECK_CU(cures);
+                goto exit;
+            }
+
+            cpy.dstMemoryType = CU_MEMORYTYPE_ARRAY;
+            cpy.dstArray      = array;
         } else {
             cpy.dstMemoryType = CU_MEMORYTYPE_HOST;
             cpy.dstHost       = dst->data[i];
@@ -283,6 +614,9 @@ static int cuda_transfer_data(AVHWFramesContext *ctx, 
AVFrame *dst,
         ret = CHECK_CU(cu->cuMemcpy2DAsync(&cpy, hwctx->stream));
         if (ret < 0)
             goto exit;
+
+        if (src_is_nonplanar_cuarray || dst_is_nonplanar_cuarray)
+            break;
     }
 
     if (!dst->hw_frames_ctx) {
@@ -294,7 +628,7 @@ static int cuda_transfer_data(AVHWFramesContext *ctx, 
AVFrame *dst,
 exit:
     CHECK_CU(cu->cuCtxPopCurrent(&dummy));
 
-    return 0;
+    return ret;
 }
 
 static void cuda_device_uninit(AVHWDeviceContext *device_ctx)
@@ -585,10 +919,11 @@ const HWContextType ff_hwcontext_type_cuda = {
     .device_uninit        = cuda_device_uninit,
     .frames_get_constraints = cuda_frames_get_constraints,
     .frames_init          = cuda_frames_init,
+    .frames_uninit        = cuda_frames_uninit,
     .frames_get_buffer    = cuda_get_buffer,
     .transfer_get_formats = cuda_transfer_get_formats,
     .transfer_data_to     = cuda_transfer_data,
     .transfer_data_from   = cuda_transfer_data,
 
-    .pix_fmts             = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, 
AV_PIX_FMT_NONE },
+    .pix_fmts             = (const enum AVPixelFormat[]){ AV_PIX_FMT_CUDA, 
AV_PIX_FMT_CUARRAY, AV_PIX_FMT_NONE },
 };
diff --git a/libavutil/hwcontext_cuda.h b/libavutil/hwcontext_cuda.h
index cbad434fea..38e5369794 100644
--- a/libavutil/hwcontext_cuda.h
+++ b/libavutil/hwcontext_cuda.h
@@ -24,6 +24,8 @@
 #include <cuda.h>
 #endif
 
+#include <stdint.h>
+
 #include "pixfmt.h"
 
 /**
@@ -46,8 +48,78 @@ typedef struct AVCUDADeviceContext {
 } AVCUDADeviceContext;
 
 /**
- * AVHWFramesContext.hwctx is currently not used
+ * CUDA frame descriptor for pool allocation of AV_PIX_FMT_CUARRAY frames.
+ *
+ * In user-allocated pools, AVHWFramesContext.pool must return AVBufferRefs
+ * with the data pointer pointing at an object of this type describing the
+ * planes of the frame.
+ *
+ * This has no use outside of custom allocation, and AVFrame AVBufferRef do not
+ * necessarily point to an instance of this struct.
+ */
+typedef struct AVCUDAArrayFrameDescriptor {
+    /**
+     * The CUarray containing the frame data.
+     *
+     * Normally stored in AVFrame.data[0].
+     */
+    CUarray array;
+
+    /**
+     * The index into AVCUDAFramesContext.cuarray_surfaces, or 0 if not 
applicable.
+     *
+     * Normally stored in AVFrame.data[1] (cast from intptr_t).
+     */
+    intptr_t index;
+} AVCUDAArrayFrameDescriptor;
+
+/**
+ * This struct is allocated as AVHWFramesContext.hwctx
+ */
+typedef struct AVCUDAFramesContext {
+    /**
+     * CUDA_ARRAY3D_DESCRIPTOR CUarrays will be initialized with.
+     * Mostly used to provide external Flags.
+     *
+     * Width, Height and Format only honored if != 0.
+     * Filled with default parameters from the FramesContext otherwise.
+     *
+     * Only applicable for AV_PIX_FMT_CUARRAY.
+     */
+    CUDA_ARRAY3D_DESCRIPTOR cuarray_desc;
+
+    /**
+     * If >0, pre-allocate a fixed pool of surfaces.
+     * The surfaces will be available via cuarray_surfaces after init.
+     * Size of the pool cannot be changed afterwards.
+     *
+     * Only applicable for AV_PIX_FMT_CUARRAY.
+     */
+    int cuarray_num_surfaces;
+
+    /**
+     * If cuarray_num_surfaces is >0, this contains the array of pre-allocated 
surfaces.
+     *
+     * Only applicable for AV_PIX_FMT_CUARRAY.
+     */
+    CUarray *cuarray_surfaces;
+} AVCUDAFramesContext;
+
+/**
+ * CUDA hardware pipeline configuration details.
+ *
+ * Passed to av_hwdevice_get_hwframe_constraints() to query
+ * per-hw-format constraints. When provided, valid_sw_formats
+ * will be filtered to only those compatible with the specified
+ * hw_format.
  */
+typedef struct AVCUDAHWConfig {
+    /**
+     * The hardware pixel format to query constraints for.
+     * Must be AV_PIX_FMT_CUDA or AV_PIX_FMT_CUARRAY.
+     */
+    enum AVPixelFormat hw_format;
+} AVCUDAHWConfig;
 
 /**
  * @defgroup hwcontext_cuda Device context creation flags
diff --git a/libavutil/hwcontext_cuda_internal.h 
b/libavutil/hwcontext_cuda_internal.h
index a989c45861..9ca6edf7d3 100644
--- a/libavutil/hwcontext_cuda_internal.h
+++ b/libavutil/hwcontext_cuda_internal.h
@@ -35,4 +35,25 @@ struct AVCUDADeviceContextInternal {
     int flags;
 };
 
+/**
+ * Return the element size in bytes for a CUarray_format, or 0 for unknown.
+ *
+ * Used to compute frame->linesize[] for CUarray frames (block-linear).
+ * Callers that need a fallback should treat a 0 return as an error.
+ */
+static inline int ff_cuda_cuarray_elem_size(CUarray_format fmt)
+{
+    switch (fmt) {
+    case CU_AD_FORMAT_UNSIGNED_INT8:
+    case CU_AD_FORMAT_SIGNED_INT8:   return 1;
+    case CU_AD_FORMAT_UNSIGNED_INT16:
+    case CU_AD_FORMAT_SIGNED_INT16:
+    case CU_AD_FORMAT_HALF:          return 2;
+    case CU_AD_FORMAT_UNSIGNED_INT32:
+    case CU_AD_FORMAT_SIGNED_INT32:
+    case CU_AD_FORMAT_FLOAT:         return 4;
+    default:                         return 0;
+    }
+}
+
 #endif /* AVUTIL_HWCONTEXT_CUDA_INTERNAL_H */
diff --git a/libavutil/version.h b/libavutil/version.h
index 7ccaa6df1b..d5bf20cf89 100644
--- a/libavutil/version.h
+++ b/libavutil/version.h
@@ -79,7 +79,7 @@
  */
 
 #define LIBAVUTIL_VERSION_MAJOR  61
-#define LIBAVUTIL_VERSION_MINOR   4
+#define LIBAVUTIL_VERSION_MINOR   5
 #define LIBAVUTIL_VERSION_MICRO 100
 
 #define LIBAVUTIL_VERSION_INT   AV_VERSION_INT(LIBAVUTIL_VERSION_MAJOR, \

_______________________________________________
ffmpeg-cvslog mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to