PR #25316 opened by Forgejo_Fairy
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25316
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/25316.patch

With `deint=interlaced` or a disabled timeline, `bwdif_cuda` and `yadif_cuda` 
forward cloned input frames while advertising a separate output hardware frames 
context. A following `hwdownload` rejects these frames and aborts conversion.

Apply @oittaa’s attached patch from #25107, preserving authorship, to copy 
bypassed CUDA frames into the output pool and preserve their properties. A 
follow-up preserves the input pool’s allocated dimensions to satisfy 
`av_hwframe_transfer_data()` requirements, while explicitly setting visible 
output dimensions for passthrough and both deinterlaced fields.

Validation on Linux x86-64, RTX 5090, driver 595.84: 96 comparisons passed 
across NVDEC input, uploaded yuv420p/yuv444p/p010le, both output modes, 
disabled and partially enabled timelines, mixed progressive/interlaced streams, 
timestamps/checksums, 250-frame runs, and unchanged deinterlacing of padded 
NVDEC surfaces. The seven existing bwdif/yadif FATE tests also passed. 
Reproduction scripts and detailed results are preserved on 
fairy/issue25107-tooling under tools/issue25107/.

Addresses the CUDA portion of #25107 and the behavior reported in Trac #10417. 
The Vulkan and VideoToolbox passthrough paths remain unresolved; this PR should 
not close #25107.


From e691377ebc2ce7bf7e0cef40216395b6e2cbba34 Mon Sep 17 00:00:00 2001
From: oittaa <[email protected]>
Date: Thu, 15 Oct 2026 21:41:36 +0200
Subject: [PATCH 1/2] lavfi/yadif_common: copy the frames bwdif_cuda and
 yadif_cuda pass on

bwdif_cuda and yadif_cuda give their output link a frames context of
their own. The frames they don't deinterlace are passed on as they
came in, in the input's frames context. These are the progressive
frames with deint=interlaced, and every frame while the timeline turns
the filter off. A filter that checks the frames context then stops at
the first of them: hwdownload fails with "Input frame is not the in
the configured hwframe context."

Add a field to YADIFContext with which a filter has these frames copied
into a frame from its output link, and set it in both CUDA filters.
av_hwframe_transfer_data() copies one CUDA frame to another on the
device. The other filters keep passing the frames on as they are. The
CPU ones have no frames context, and av_hwframe_transfer_data() can't
copy from one Vulkan or VideoToolbox frame to another.

Fixes trac ticket #10417.

Tested on an RTX 4080 SUPER with progressive H.264 clips decoded by
NVDEC at 128x64 and 1920x1080, with send_frame and send_field, and
with deint=all and a timeline. Also with frames uploaded by
hwupload_cuda: yuv444p with scale_cuda after the filter as in the
ticket, p010le, and yuv420p from a clip that changes from progressive
to interlaced and back. Every run failed before. Now each frame passed
on equals the decoded or uploaded one, byte for byte. For the
progressive clips with deint=interlaced, the whole output equals
bwdif's or yadif's with the same options. Interlaced clips give the
same output as before, byte for byte.

Co-Authored-By: Claude Opus 5.5 <[email protected]>

Assisted-by: Fairy
---
 libavfilter/vf_bwdif_cuda.c |  1 +
 libavfilter/vf_yadif_cuda.c |  1 +
 libavfilter/yadif.h         |  8 ++++++++
 libavfilter/yadif_common.c  | 30 +++++++++++++++++++++++++++---
 4 files changed, 37 insertions(+), 3 deletions(-)

diff --git a/libavfilter/vf_bwdif_cuda.c b/libavfilter/vf_bwdif_cuda.c
index ea68f3cb4f1..260f649e840 100644
--- a/libavfilter/vf_bwdif_cuda.c
+++ b/libavfilter/vf_bwdif_cuda.c
@@ -298,6 +298,7 @@ static int config_output(AVFilterLink *link)
 
     y->csp = av_pix_fmt_desc_get(output_frames->sw_format);
     y->filter = filter;
+    y->copy_passthrough = 1;
 
     if (AV_CEIL_RSHIFT(link->w, y->csp->log2_chroma_w) < 3 || 
AV_CEIL_RSHIFT(link->h, y->csp->log2_chroma_h) < 3) {
         av_log(ctx, AV_LOG_ERROR, "Video with planes less than 3 columns or 
lines is not supported\n");
diff --git a/libavfilter/vf_yadif_cuda.c b/libavfilter/vf_yadif_cuda.c
index b3b98f3ccdf..a0bccb22bdd 100644
--- a/libavfilter/vf_yadif_cuda.c
+++ b/libavfilter/vf_yadif_cuda.c
@@ -290,6 +290,7 @@ static int config_output(AVFilterLink *link)
 
     y->csp = av_pix_fmt_desc_get(output_frames->sw_format);
     y->filter = filter;
+    y->copy_passthrough = 1;
 
     ret = CHECK_CU(cu->cuCtxPushCurrent(s->hwctx->cuda_ctx));
     if (ret < 0)
diff --git a/libavfilter/yadif.h b/libavfilter/yadif.h
index 21c6df29c2c..c32b0a6e3d1 100644
--- a/libavfilter/yadif.h
+++ b/libavfilter/yadif.h
@@ -88,6 +88,14 @@ typedef struct YADIFContext {
     int current_field;  ///< YADIFCurrentField
 
     int pts_multiplier;
+
+    /*
+     * A filter whose output link has its own hardware frames context sets
+     * this. The frames it passes on without deinterlacing are then copied
+     * into frames from that context, as the frames on a link must belong
+     * to the link's frames context.
+     */
+    int copy_passthrough;
 } YADIFContext;
 
 void ff_yadif_init_x86(YADIFContext *yadif);
diff --git a/libavfilter/yadif_common.c b/libavfilter/yadif_common.c
index 2b1c9bc1cd2..57cd3b3f491 100644
--- a/libavfilter/yadif_common.c
+++ b/libavfilter/yadif_common.c
@@ -20,6 +20,7 @@
  */
 
 #include "libavutil/avassert.h"
+#include "libavutil/hwcontext.h"
 #include "libavutil/imgutils.h"
 #include "filters.h"
 #include "video.h"
@@ -100,6 +101,29 @@ static void fixstride(AVFilterLink *link, AVFrame *f)
     av_frame_free(&dst);
 }
 
+static int pass_frame(AVFilterContext *ctx)
+{
+    YADIFContext *yadif = ctx->priv;
+    AVFilterLink *outlink = ctx->outputs[0];
+    int ret;
+
+    if (!yadif->copy_passthrough) {
+        yadif->out = av_frame_clone(yadif->cur);
+        return yadif->out ? 0 : AVERROR(ENOMEM);
+    }
+
+    yadif->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    if (!yadif->out)
+        return AVERROR(ENOMEM);
+
+    ret = av_hwframe_transfer_data(yadif->out, yadif->cur, 0);
+    if (ret >= 0)
+        ret = av_frame_copy_props(yadif->out, yadif->cur);
+    if (ret < 0)
+        av_frame_free(&yadif->out);
+    return ret;
+}
+
 int ff_yadif_filter_frame(AVFilterLink *link, AVFrame *frame)
 {
     AVFilterContext *ctx = link->dst;
@@ -152,9 +176,9 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame 
*frame)
         (yadif->deint && !(yadif->prev->flags & AV_FRAME_FLAG_INTERLACED) && 
yadif->prev->repeat_pict) ||
         (yadif->deint && !(yadif->next->flags & AV_FRAME_FLAG_INTERLACED) && 
yadif->next->repeat_pict)
     ) {
-        yadif->out  = av_frame_clone(yadif->cur);
-        if (!yadif->out)
-            return AVERROR(ENOMEM);
+        ret = pass_frame(ctx);
+        if (ret < 0)
+            return ret;
 
         ff_ccfifo_inject(&yadif->cc_fifo, yadif->out);
         av_frame_free(&yadif->prev);
-- 
2.54.0


From 3dfb0f741a22618b2c325bafdc5f83950a447adc Mon Sep 17 00:00:00 2001
From: Michael Niedermayer <[email protected]>
Date: Wed, 21 Oct 2026 14:49:32 +0000
Subject: [PATCH 2/2] avfilter/yadif: preserve CUDA allocation dimensions for
 passthrough copies

av_hwframe_transfer_data() requires matching allocated dimensions. The
input frames context can include codec padding, for example 1920x1088
for a 1920x1080 H.264 picture. Preserve those dimensions in the CUDA
output frames context instead of allocating only the visible dimensions.

Reset the visible dimensions on frames allocated by the shared yadif
code, including the second output field. Hardware frame allocation
returns the pool dimensions and av_frame_copy_props() does not copy the
visible dimensions.

Assisted-by: Fairy
---
 libavfilter/vf_bwdif_cuda.c |  5 +++--
 libavfilter/vf_yadif_cuda.c |  5 +++--
 libavfilter/yadif_common.c  | 18 +++++++++++++++---
 3 files changed, 21 insertions(+), 7 deletions(-)

diff --git a/libavfilter/vf_bwdif_cuda.c b/libavfilter/vf_bwdif_cuda.c
index 260f649e840..9be0a68d119 100644
--- a/libavfilter/vf_bwdif_cuda.c
+++ b/libavfilter/vf_bwdif_cuda.c
@@ -276,8 +276,9 @@ static int config_output(AVFilterLink *link)
 
     output_frames->format    = AV_PIX_FMT_CUDA;
     output_frames->sw_format = s->input_frames->sw_format;
-    output_frames->width     = ctx->inputs[0]->w;
-    output_frames->height    = ctx->inputs[0]->h;
+    /* Hardware transfers require matching allocated dimensions. */
+    output_frames->width     = s->input_frames->width;
+    output_frames->height    = s->input_frames->height;
 
     output_frames->initial_pool_size = 4;
 
diff --git a/libavfilter/vf_yadif_cuda.c b/libavfilter/vf_yadif_cuda.c
index a0bccb22bdd..17de6d3b669 100644
--- a/libavfilter/vf_yadif_cuda.c
+++ b/libavfilter/vf_yadif_cuda.c
@@ -268,8 +268,9 @@ static int config_output(AVFilterLink *link)
 
     output_frames->format    = AV_PIX_FMT_CUDA;
     output_frames->sw_format = s->input_frames->sw_format;
-    output_frames->width     = ctx->inputs[0]->w;
-    output_frames->height    = ctx->inputs[0]->h;
+    /* Hardware transfers require matching allocated dimensions. */
+    output_frames->width     = s->input_frames->width;
+    output_frames->height    = s->input_frames->height;
 
     output_frames->initial_pool_size = 4;
 
diff --git a/libavfilter/yadif_common.c b/libavfilter/yadif_common.c
index 57cd3b3f491..a51451b1947 100644
--- a/libavfilter/yadif_common.c
+++ b/libavfilter/yadif_common.c
@@ -26,6 +26,18 @@
 #include "video.h"
 #include "yadif.h"
 
+/* Hardware frame pools may be larger than the visible image. */
+static AVFrame *get_video_buffer(AVFilterLink *link)
+{
+    AVFrame *frame = ff_get_video_buffer(link, link->w, link->h);
+
+    if (frame) {
+        frame->width  = link->w;
+        frame->height = link->h;
+    }
+    return frame;
+}
+
 static int return_frame(AVFilterContext *ctx, int is_second)
 {
     YADIFContext *yadif = ctx->priv;
@@ -40,7 +52,7 @@ static int return_frame(AVFilterContext *ctx, int is_second)
     }
 
     if (is_second) {
-        yadif->out = ff_get_video_buffer(link, link->w, link->h);
+        yadif->out = get_video_buffer(link);
         if (!yadif->out)
             return AVERROR(ENOMEM);
 
@@ -112,7 +124,7 @@ static int pass_frame(AVFilterContext *ctx)
         return yadif->out ? 0 : AVERROR(ENOMEM);
     }
 
-    yadif->out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
+    yadif->out = get_video_buffer(outlink);
     if (!yadif->out)
         return AVERROR(ENOMEM);
 
@@ -188,7 +200,7 @@ int ff_yadif_filter_frame(AVFilterLink *link, AVFrame 
*frame)
         return ff_filter_frame(ctx->outputs[0], yadif->out);
     }
 
-    yadif->out = ff_get_video_buffer(ctx->outputs[0], link->w, link->h);
+    yadif->out = get_video_buffer(ctx->outputs[0]);
     if (!yadif->out)
         return AVERROR(ENOMEM);
 
-- 
2.54.0

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

Reply via email to