Commit cb032dc37b for ffmpeg

commit cb032dc37b394dc21f31f03942604cbdc40ecf81
Author: lusoris <lusoris@proton.me>
Date:   Fri Oct 30 14:38:31 2026 +0100

    avutil/hwcontext_vulkan: keep frame internals after failed CUDA imports

    A failure while importing a frame's memory or semaphores into CUDA freed
    all of its internal state. The frame stays in its pool, so the next
    transfer on it crashed locking the freed update_mutex, and every other
    use of the frame would have done the same.

    Release only the partial CUDA import, clear the handles so the next
    transfer imports again, and take the image back from the external queue.

    Signed-off-by: lusoris <lusoris@proton.me>

diff --git a/libavutil/hwcontext_vulkan.c b/libavutil/hwcontext_vulkan.c
index 43baef0cef..023b11224e 100644
--- a/libavutil/hwcontext_vulkan.c
+++ b/libavutil/hwcontext_vulkan.c
@@ -2403,6 +2403,51 @@ static int alloc_mem(AVHWDeviceContext *ctx, VkMemoryRequirements *req,
     return 0;
 }

+#if CONFIG_CUDA
+static void vulkan_free_internal_cuda(AVVkFrameInternal *internal)
+{
+    AVHWFramesContext *cuda_fc;
+    AVHWDeviceContext *cuda_cu;
+    AVCUDADeviceContext *cuda_dev;
+    AVCUDADeviceContextInternal *cu_internal;
+    CudaFunctions *cu;
+    int planes;
+
+    if (!internal->cuda_fc_ref)
+        return;
+
+    cuda_fc = (AVHWFramesContext *)internal->cuda_fc_ref->data;
+    planes = av_pix_fmt_count_planes(cuda_fc->sw_format);
+    cuda_cu = cuda_fc->device_ctx;
+    cuda_dev = cuda_cu->hwctx;
+    cu_internal = cuda_dev->internal;
+    cu = cu_internal->cuda_dl;
+
+    for (int i = 0; i < planes; i++) {
+        if (internal->cu_sem[i])
+            CHECK_CU(cu->cuDestroyExternalSemaphore(internal->cu_sem[i]));
+        if (internal->cu_mma[i])
+            CHECK_CU(cu->cuMipmappedArrayDestroy(internal->cu_mma[i]));
+        if (internal->ext_mem[i])
+            CHECK_CU(cu->cuDestroyExternalMemory(internal->ext_mem[i]));
+        internal->cu_sem[i]   = NULL;
+        internal->cu_mma[i]   = NULL;
+        internal->cu_array[i] = NULL;
+        internal->ext_mem[i]  = NULL;
+#ifdef _WIN32
+        if (internal->ext_sem_handle[i])
+            CloseHandle(internal->ext_sem_handle[i]);
+        if (internal->ext_mem_handle[i])
+            CloseHandle(internal->ext_mem_handle[i]);
+        internal->ext_sem_handle[i] = NULL;
+        internal->ext_mem_handle[i] = NULL;
+#endif
+    }
+
+    av_buffer_unref(&internal->cuda_fc_ref);
+}
+#endif
+
 static void vulkan_free_internal(VulkanDevicePriv *p, AVVkFrame *f)
 {
     av_unused AVVkFrameInternal *internal = f->internal;
@@ -2412,31 +2457,7 @@ static void vulkan_free_internal(VulkanDevicePriv *p, AVVkFrame *f)
         return;

 #if CONFIG_CUDA
-    if (internal->cuda_fc_ref) {
-        AVHWFramesContext *cuda_fc = (AVHWFramesContext *)internal->cuda_fc_ref->data;
-        int planes = av_pix_fmt_count_planes(cuda_fc->sw_format);
-        AVHWDeviceContext *cuda_cu = cuda_fc->device_ctx;
-        AVCUDADeviceContext *cuda_dev = cuda_cu->hwctx;
-        AVCUDADeviceContextInternal *cu_internal = cuda_dev->internal;
-        CudaFunctions *cu = cu_internal->cuda_dl;
-
-        for (int i = 0; i < planes; i++) {
-            if (internal->cu_sem[i])
-                CHECK_CU(cu->cuDestroyExternalSemaphore(internal->cu_sem[i]));
-            if (internal->cu_mma[i])
-                CHECK_CU(cu->cuMipmappedArrayDestroy(internal->cu_mma[i]));
-            if (internal->ext_mem[i])
-                CHECK_CU(cu->cuDestroyExternalMemory(internal->ext_mem[i]));
-#ifdef _WIN32
-            if (internal->ext_sem_handle[i])
-                CloseHandle(internal->ext_sem_handle[i]);
-            if (internal->ext_mem_handle[i])
-                CloseHandle(internal->ext_mem_handle[i]);
-#endif
-        }
-
-        av_buffer_unref(&internal->cuda_fc_ref);
-    }
+    vulkan_free_internal_cuda(internal);
 #endif

     if (internal->drm_sync_sem != VK_NULL_HANDLE)
@@ -4275,7 +4296,9 @@ static int vulkan_export_to_cuda(AVHWFramesContext *hwfc,
     return 0;

 fail:
-    vulkan_free_internal(p, dst_f);
+    /* Only the import failed: the frame is still valid and stays in its pool,
+     * and the next transfer starts the import over. */
+    vulkan_free_internal_cuda(dst_int);
     return err;
 }

@@ -4311,10 +4334,8 @@ static int vulkan_transfer_data_from_cuda(AVHWFramesContext *hwfc,
         return err;

     err = vulkan_export_to_cuda(hwfc, src->hw_frames_ctx, dst);
-    if (err < 0) {
-        CHECK_CU(cu->cuCtxPopCurrent(&dummy));
-        return err;
-    }
+    if (err < 0)
+        goto fail;

     dst_int = dst_f->internal;

@@ -5240,10 +5261,8 @@ static int vulkan_transfer_data_to_cuda(AVHWFramesContext *hwfc, AVFrame *dst,
         return err;

     err = vulkan_export_to_cuda(hwfc, dst->hw_frames_ctx, src);
-    if (err < 0) {
-        CHECK_CU(cu->cuCtxPopCurrent(&dummy));
-        return err;
-    }
+    if (err < 0)
+        goto fail;

     dst_int = dst_f->internal;