Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel
 help / color / mirror / Atom feed
* [FFmpeg-devel] [PR] swscale/graph: nuke SwsImg struct, just use AVFrame everywhere (PR #22298)
@ 2026-02-26 18:36 Niklas Haas via ffmpeg-devel
  0 siblings, 0 replies; only message in thread
From: Niklas Haas via ffmpeg-devel @ 2026-02-26 18:36 UTC (permalink / raw)
  To: ffmpeg-devel; +Cc: Niklas Haas

PR #22298 opened by Niklas Haas (haasn)
URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22298
Patch URL: https://code.ffmpeg.org/FFmpeg/FFmpeg/pulls/22298.patch

Marginally simpler in a handful of places, especially moving forwards with support for hardware frames


>From 1c0c96983b3a41c3b33307bd0a82027db3065eb1 Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Thu, 26 Feb 2026 16:34:31 +0100
Subject: [PATCH 1/7] swscale/ops: avoid stack copies of SwsImg

Also IMHO simplifies this code a bit. In particular, setup() has no
business even calling this helper function, since it doesn't adjust
anything anyways.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
---
 libswscale/ops.c | 94 +++++++++++++++++++++++-------------------------
 1 file changed, 44 insertions(+), 50 deletions(-)

diff --git a/libswscale/ops.c b/libswscale/ops.c
index 3aba144246..233c42f394 100644
--- a/libswscale/ops.c
+++ b/libswscale/ops.c
@@ -935,27 +935,26 @@ static void op_pass_free(void *ptr)
     av_free(p);
 }
 
-static inline SwsImg img_shift_idx(const SwsImg *base, const int y,
-                                   const int plane_idx[4])
+static inline void img_shift_idx(const SwsImg *img, const int y,
+                                 const int plane_idx[4], uint8_t *data[4])
 {
-    SwsImg img = *base;
     for (int i = 0; i < 4; i++) {
         const int idx = plane_idx[i];
         if (idx >= 0) {
-            const int yshift = y >> ff_fmt_vshift(base->fmt, idx);
-            img.data[i] = base->data[idx] + yshift * base->linesize[idx];
+            av_assert1(img->data[idx]);
+            const int yshift = y >> ff_fmt_vshift(img->fmt, idx);
+            data[i] = img->data[idx] + yshift * img->linesize[idx];
         } else {
-            img.data[i] = NULL;
+            data[i] = NULL;
         }
     }
-    return img;
 }
 
-static void op_pass_setup(const SwsImg *out_base, const SwsImg *in_base,
+static void op_pass_setup(const SwsImg *out, const SwsImg *in,
                           const SwsPass *pass)
 {
-    const AVPixFmtDescriptor *indesc  = av_pix_fmt_desc_get(in_base->fmt);
-    const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out_base->fmt);
+    const AVPixFmtDescriptor *indesc  = av_pix_fmt_desc_get(in->fmt);
+    const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->fmt);
 
     SwsOpPass *p = pass->priv;
     SwsOpExec *exec = &p->exec_base;
@@ -974,12 +973,6 @@ static void op_pass_setup(const SwsImg *out_base, const SwsImg *in_base,
     p->memcpy_in     = false;
     p->memcpy_out    = false;
 
-    const SwsImg in  = img_shift_idx(in_base,  0, p->idx_in);
-    const SwsImg out = img_shift_idx(out_base, 0, p->idx_out);
-
-    exec->src_frame_ptr = in.frame_ptr;
-    exec->dst_frame_ptr = out.frame_ptr;
-
     for (int i = 0; i < p->planes_in; i++) {
         const int idx        = p->idx_in[i];
         const int sub_x      = (idx == 1 || idx == 2) ? indesc->log2_chroma_w : 0;
@@ -987,8 +980,8 @@ static void op_pass_setup(const SwsImg *out_base, const SwsImg *in_base,
         const int plane_pad  = (comp->over_read + sub_x) >> sub_x;
         const int plane_size = plane_w * p->pixel_bits_in >> 3;
         if (comp->slice_align)
-            p->memcpy_in |= plane_size + plane_pad > in.linesize[i];
-        exec->in_stride[i] = in.linesize[i];
+            p->memcpy_in |= plane_size + plane_pad > in->linesize[i];
+        exec->in_stride[i] = in->linesize[i];
     }
 
     for (int i = 0; i < p->planes_out; i++) {
@@ -998,8 +991,8 @@ static void op_pass_setup(const SwsImg *out_base, const SwsImg *in_base,
         const int plane_pad  = (comp->over_write + sub_x) >> sub_x;
         const int plane_size = plane_w * p->pixel_bits_out >> 3;
         if (comp->slice_align)
-            p->memcpy_out |= plane_size + plane_pad > out.linesize[i];
-        exec->out_stride[i] = out.linesize[i];
+            p->memcpy_out |= plane_size + plane_pad > out->linesize[i];
+        exec->out_stride[i] = out->linesize[i];
     }
 
     /* Pre-fill pointer bump for the main section only; this value does not
@@ -1007,16 +1000,19 @@ static void op_pass_setup(const SwsImg *out_base, const SwsImg *in_base,
      * process a single line */
     const int blocks_main = p->num_blocks - p->memcpy_out;
     for (int i = 0; i < 4; i++) {
-        exec->in_bump[i]  = in.linesize[i]  - blocks_main * exec->block_size_in;
-        exec->out_bump[i] = out.linesize[i] - blocks_main * exec->block_size_out;
+        exec->in_bump[i]  = in->linesize[i]  - blocks_main * exec->block_size_in;
+        exec->out_bump[i] = out->linesize[i] - blocks_main * exec->block_size_out;
     }
+
+    exec->src_frame_ptr = in->frame_ptr;
+    exec->dst_frame_ptr = out->frame_ptr;
 }
 
 /* Dispatch kernel over the last column of the image using memcpy */
 static av_always_inline void
 handle_tail(const SwsOpPass *p, SwsOpExec *exec,
-            const SwsImg *out_base, const bool copy_out,
-            const SwsImg *in_base, const bool copy_in,
+            const SwsImg *out, const bool copy_out,
+            const SwsImg *in,  const bool copy_in,
             int y, const int h)
 {
     DECLARE_ALIGNED_64(uint8_t, tmp)[2][4][sizeof(uint32_t[128])];
@@ -1026,25 +1022,27 @@ handle_tail(const SwsOpPass *p, SwsOpExec *exec,
     const int tail_size_out = p->tail_size_out;
     const int bx = p->num_blocks - 1;
 
-    SwsImg in  = img_shift_idx(in_base,  y, p->idx_in);
-    SwsImg out = img_shift_idx(out_base, y, p->idx_out);
+    uint8_t *in_data[4], *out_data[4];
+    img_shift_idx(in,  y, p->idx_in,  in_data);
+    img_shift_idx(out, y, p->idx_out, out_data);
+
     for (int i = 0; i < p->planes_in; i++) {
-        in.data[i]  += p->tail_off_in;
+        in_data[i] += p->tail_off_in;
         if (copy_in) {
             exec->in[i] = (void *) tmp[0][i];
             exec->in_stride[i] = sizeof(tmp[0][i]);
         } else {
-            exec->in[i] = in.data[i];
+            exec->in[i] = in_data[i];
         }
     }
 
     for (int i = 0; i < p->planes_out; i++) {
-        out.data[i] += p->tail_off_out;
+        out_data[i] += p->tail_off_out;
         if (copy_out) {
             exec->out[i] = (void *) tmp[1][i];
             exec->out_stride[i] = sizeof(tmp[1][i]);
         } else {
-            exec->out[i] = out.data[i];
+            exec->out[i] = out_data[i];
         }
     }
 
@@ -1052,8 +1050,8 @@ handle_tail(const SwsOpPass *p, SwsOpExec *exec,
         if (copy_in) {
             for (int i = 0; i < p->planes_in; i++) {
                 av_assert2(tmp[0][i] + tail_size_in < (uint8_t *) tmp[1]);
-                memcpy(tmp[0][i], in.data[i], tail_size_in);
-                in.data[i] += in.linesize[i];
+                memcpy(tmp[0][i], in_data[i], tail_size_in);
+                in_data[i] += in->linesize[i];
             }
         }
 
@@ -1062,39 +1060,35 @@ handle_tail(const SwsOpPass *p, SwsOpExec *exec,
         if (copy_out) {
             for (int i = 0; i < p->planes_out; i++) {
                 av_assert2(tmp[1][i] + tail_size_out < (uint8_t *) tmp[2]);
-                memcpy(out.data[i], tmp[1][i], tail_size_out);
-                out.data[i] += out.linesize[i];
+                memcpy(out_data[i], tmp[1][i], tail_size_out);
+                out_data[i] += out->linesize[i];
             }
         }
 
         for (int i = 0; i < 4; i++) {
             if (!copy_in && exec->in[i])
-                exec->in[i] += in.linesize[i];
+                exec->in[i] += in->linesize[i];
             if (!copy_out && exec->out[i])
-                exec->out[i] += out.linesize[i];
+                exec->out[i] += out->linesize[i];
         }
     }
 }
 
-static void op_pass_run(const SwsImg *out_base, const SwsImg *in_base,
-                        const int y, const int h, const SwsPass *pass)
+static void op_pass_run(const SwsImg *out, const SwsImg *in, const int y,
+                        const int h, const SwsPass *pass)
 {
     const SwsOpPass *p = pass->priv;
     const SwsCompiledOp *comp = &p->comp;
-    const SwsImg in  = img_shift_idx(in_base,  y, p->idx_in);
-    const SwsImg out = img_shift_idx(out_base, y, p->idx_out);
 
     /* Fill exec metadata for this slice */
     DECLARE_ALIGNED_32(SwsOpExec, exec) = p->exec_base;
     exec.slice_y = y;
     exec.slice_h = h;
-    for (int i = 0; i < 4; i++) {
-        exec.in[i]  = in.data[i];
-        exec.out[i] = out.data[i];
-    }
+    img_shift_idx(in,  y, p->idx_in,  (uint8_t **) exec.in);
+    img_shift_idx(out, y, p->idx_out, exec.out);
 
-    exec.src_frame_ptr = in_base->frame_ptr;
-    exec.dst_frame_ptr = out_base->frame_ptr;
+    exec.src_frame_ptr = in->frame_ptr;
+    exec.dst_frame_ptr = out->frame_ptr;
 
     /**
      *  To ensure safety, we need to consider the following:
@@ -1126,17 +1120,17 @@ static void op_pass_run(const SwsImg *out_base, const SwsImg *in_base,
     if (memcpy_in) {
         /* Safe part of last row */
         for (int i = 0; i < 4; i++) {
-            exec.in[i]  += h_main * in.linesize[i];
-            exec.out[i] += h_main * out.linesize[i];
+            exec.in[i]  += h_main * in->linesize[i];
+            exec.out[i] += h_main * out->linesize[i];
         }
         comp->func(&exec, comp->priv, 0, y + h_main, num_blocks - 1, y + h);
     }
 
     /* Handle last column via memcpy, takes over `exec` so call these last */
     if (memcpy_out)
-        handle_tail(p, &exec, out_base, true, in_base, false, y, h_main);
+        handle_tail(p, &exec, out, true, in, false, y, h_main);
     if (memcpy_in)
-        handle_tail(p, &exec, out_base, memcpy_out, in_base, true, y + h_main, 1);
+        handle_tail(p, &exec, out, memcpy_out, in, true, y + h_main, 1);
 }
 
 static int rw_planes(const SwsOp *op)
-- 
2.52.0


>From 0cee4c1e41206e460e668e3b2f07f5a2091af8c0 Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Sat, 21 Feb 2026 16:50:40 +0100
Subject: [PATCH 2/7] swscale/graph: simplify output buffer management

This function was originally written to support the use case of e.g.
partially allocated planes that implicitly reference the original input
image, but I've decided that this is stupid and doesn't currently work
anyways.

Plus, I have plans to kill SwsImg, so we need to simplify this mess.

Signed-off-by: Niklas Haas <git@haasn.dev>
---
 libswscale/graph.c | 21 ++-------------------
 1 file changed, 2 insertions(+), 19 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index 1c3a089cfe..55636a2816 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -844,23 +844,6 @@ void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
     ff_color_update_dynamic(&graph->src.color, color);
 }
 
-static SwsImg pass_output(const SwsPass *pass, const SwsImg *fallback)
-{
-    if (!pass)
-        return *fallback;
-
-    SwsImg img = pass->output->img;
-    img.frame_ptr = fallback->frame_ptr;
-    for (int i = 0; i < FF_ARRAY_ELEMS(img.data); i++) {
-        if (!img.data[i]) {
-            img.data[i]     = fallback->data[i];
-            img.linesize[i] = fallback->linesize[i];
-        }
-    }
-
-    return img;
-}
-
 void ff_sws_graph_run(SwsGraph *graph, const SwsImg *output, const SwsImg *input)
 {
     av_assert0(output->fmt == graph->dst.hw_format ||
@@ -871,8 +854,8 @@ void ff_sws_graph_run(SwsGraph *graph, const SwsImg *output, const SwsImg *input
     for (int i = 0; i < graph->num_passes; i++) {
         const SwsPass *pass = graph->passes[i];
         graph->exec.pass   = pass;
-        graph->exec.input  = pass_output(pass->input, input);
-        graph->exec.output = pass_output(pass, output);
+        graph->exec.input  = pass->input ? pass->input->output->img : *input;
+        graph->exec.output = pass->output->buf[0] ? pass->output->img : *output;
         if (pass->setup)
             pass->setup(&graph->exec.output, &graph->exec.input, pass);
         avpriv_slicethread_execute(graph->slicethread, pass->num_slices, 0);
-- 
2.52.0


>From f82fcd58cdd16967c9ffb1f65c681176a64bdf2f Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Thu, 26 Feb 2026 16:49:19 +0100
Subject: [PATCH 3/7] swscale/graph: avoid stack copies of SwsImg

In the process of nuking this abstraction in favor of AVFrame.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
---
 libswscale/graph.c | 69 +++++++++++++++++++++++++++-------------------
 libswscale/graph.h |  8 ------
 2 files changed, 41 insertions(+), 36 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index 55636a2816..eb5398f324 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -157,26 +157,37 @@ static int pass_append(SwsGraph *graph, enum AVPixelFormat fmt, int w, int h,
     return 0;
 }
 
-static void run_copy(const SwsImg *out_base, const SwsImg *in_base,
-                     int y, int h, const SwsPass *pass)
+static void img_shift(const SwsImg *img, const int y, uint8_t *data[4])
 {
-    SwsImg in  = ff_sws_img_shift(in_base,  y);
-    SwsImg out = ff_sws_img_shift(out_base, y);
+    for (int i = 0; i < 4; i++) {
+        if (img->data[i])
+            data[i] = img->data[i] + (y >> ff_fmt_vshift(img->fmt, i)) * img->linesize[i];
+        else
+            data[i] = NULL;
+    }
+}
 
-    for (int i = 0; i < FF_ARRAY_ELEMS(out.data) && out.data[i]; i++) {
-        const int lines = h >> ff_fmt_vshift(in.fmt, i);
-        av_assert1(in.data[i]);
+static void run_copy(const SwsImg *out, const SwsImg *in, int y, int h,
+                     const SwsPass *pass)
+{
+    uint8_t *in_data[4], *out_data[4];
+    img_shift(in,  y, in_data);
+    img_shift(out, y, out_data);
 
-        if (in.data[i] == out.data[i]) {
-            av_assert0(in.linesize[i] == out.linesize[i]);
-        } else if (in.linesize[i] == out.linesize[i]) {
-            memcpy(out.data[i], in.data[i], lines * out.linesize[i]);
+    for (int i = 0; i < FF_ARRAY_ELEMS(out_data) && out_data[i]; i++) {
+        const int lines = h >> ff_fmt_vshift(in->fmt, i);
+        av_assert1(in_data[i]);
+
+        if (in_data[i] == out_data[i]) {
+            av_assert0(in->linesize[i] == out->linesize[i]);
+        } else if (in->linesize[i] == out->linesize[i]) {
+            memcpy(out_data[i], in_data[i], lines * out->linesize[i]);
         } else {
-            const int linesize = FFMIN(out.linesize[i], in.linesize[i]);
+            const int linesize = FFMIN(out->linesize[i], in->linesize[i]);
             for (int j = 0; j < lines; j++) {
-                memcpy(out.data[i], in.data[i], linesize);
-                in.data[i]  += in.linesize[i];
-                out.data[i] += out.linesize[i];
+                memcpy(out_data[i], in_data[i], linesize);
+                in_data[i]  += in->linesize[i];
+                out_data[i] += out->linesize[i];
             }
         }
     }
@@ -266,26 +277,27 @@ static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
     return sws;
 }
 
-static void run_legacy_unscaled(const SwsImg *out, const SwsImg *in_base,
+static void run_legacy_unscaled(const SwsImg *out, const SwsImg *in,
                                 int y, int h, const SwsPass *pass)
 {
     SwsContext *sws = slice_ctx(pass, y);
     SwsInternal *c = sws_internal(sws);
-    const SwsImg in = ff_sws_img_shift(in_base, y);
+    const uint8_t *in_data[4];
+    img_shift(in, y, (uint8_t **) in_data);
 
-    c->convert_unscaled(c, (const uint8_t *const *) in.data, in.linesize, y, h,
-                        out->data, out->linesize);
+    c->convert_unscaled(c, in_data, in->linesize, y, h, out->data, out->linesize);
 }
 
-static void run_legacy_swscale(const SwsImg *out_base, const SwsImg *in,
+static void run_legacy_swscale(const SwsImg *out, const SwsImg *in,
                                int y, int h, const SwsPass *pass)
 {
     SwsContext *sws = slice_ctx(pass, y);
     SwsInternal *c = sws_internal(sws);
-    const SwsImg out = ff_sws_img_shift(out_base, y);
+    uint8_t *out_data[4];
+    img_shift(out, y, out_data);
 
     ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0,
-               sws->src_h, out.data, out.linesize, y, h);
+               sws->src_h, out_data, out->linesize, y, h);
 }
 
 static void get_chroma_pos(SwsGraph *graph, int *h_chr_pos, int *v_chr_pos,
@@ -621,15 +633,16 @@ static void setup_lut3d(const SwsImg *out, const SwsImg *in, const SwsPass *pass
     ff_sws_lut3d_update(lut, &pass->graph->src.color);
 }
 
-static void run_lut3d(const SwsImg *out_base, const SwsImg *in_base,
-                      int y, int h, const SwsPass *pass)
+static void run_lut3d(const SwsImg *out, const SwsImg *in, int y, int h,
+                      const SwsPass *pass)
 {
     SwsLut3D *lut = pass->priv;
-    const SwsImg in  = ff_sws_img_shift(in_base,  y);
-    const SwsImg out = ff_sws_img_shift(out_base, y);
+    uint8_t *in_data[4], *out_data[4];
+    img_shift(in,  y, in_data);
+    img_shift(out, y, out_data);
 
-    ff_sws_lut3d_apply(lut, in.data[0], in.linesize[0], out.data[0],
-                       out.linesize[0], pass->width, h);
+    ff_sws_lut3d_apply(lut, in_data[0], in->linesize[0], out_data[0],
+                       out->linesize[0], pass->width, h);
 }
 
 static int adapt_colors(SwsGraph *graph, SwsFormat src, SwsFormat dst,
diff --git a/libswscale/graph.h b/libswscale/graph.h
index c93c37a98e..94b2051d94 100644
--- a/libswscale/graph.h
+++ b/libswscale/graph.h
@@ -45,14 +45,6 @@ static av_always_inline av_const int ff_fmt_vshift(enum AVPixelFormat fmt, int p
     return (plane == 1 || plane == 2) ? desc->log2_chroma_h : 0;
 }
 
-static av_const inline SwsImg ff_sws_img_shift(const SwsImg *base, const int y)
-{
-    SwsImg img = *base;
-    for (int i = 0; i < 4 && img.data[i]; i++)
-        img.data[i] += (y >> ff_fmt_vshift(img.fmt, i)) * img.linesize[i];
-    return img;
-}
-
 typedef struct SwsPass  SwsPass;
 typedef struct SwsGraph SwsGraph;
 
-- 
2.52.0


>From 0743e99ab66edfc20d9bf83b7213ee484948b73c Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Thu, 26 Feb 2026 18:07:58 +0100
Subject: [PATCH 4/7] swscale/graph: use AVFrame to track internal allocation

I want to split this functional refactor off from the subsequent commit
for sake of easier reviewing. This commit replaces the AVBufferRef inside
SwsPassBuffer by an AVFrame, in anticipation of the SwsImg removal.

Incidentally, we could also now just use av_frame_get_buffer() here, but
I decided to preserve the current (custom) allocator for a number of reasons,
mainly for compatibility with a different series I'm in the process of
posting / merging.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
---
 libswscale/graph.c | 68 ++++++++++++++++++++++++++++------------------
 libswscale/graph.h |  2 +-
 2 files changed, 42 insertions(+), 28 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index eb5398f324..fa25810260 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -38,42 +38,29 @@
 #include "graph.h"
 #include "ops.h"
 
-static int buffer_get_sizes(SwsPassBuffer *buffer, size_t sizes[4])
+/* Allocates one buffer per plane */
+static int frame_alloc_planes(AVFrame *dst)
 {
-    const int align  = av_cpu_max_align();
-    const int format = buffer->img.fmt;
-    const int width  = FFALIGN(buffer->width, align);
-    const int height = buffer->height;
-    int ret;
-
-    ret = av_image_check_size2(width, height, INT64_MAX, format, 0, NULL);
+    int ret = av_image_check_size2(dst->width, dst->height, INT64_MAX,
+                                   dst->format, 0, NULL);
     if (ret < 0)
         return ret;
 
-    int *linesize = buffer->img.linesize;
-    ret = av_image_fill_linesizes(linesize, format, width);
+    const int align = av_cpu_max_align();
+    const int aligned_w = FFALIGN(dst->width, align);
+    ret = av_image_fill_linesizes(dst->linesize, dst->format, aligned_w);
     if (ret < 0)
         return ret;
 
     ptrdiff_t linesize1[4];
     for (int i = 0; i < 4; i++)
-        linesize1[i] = linesize[i] = FFALIGN(linesize[i], align);
-
-    return av_image_fill_plane_sizes(sizes, format, height, linesize1);
-}
-
-static int pass_alloc_output(SwsPass *pass)
-{
-    if (!pass || pass->output->buf[0])
-        return 0;
+        linesize1[i] = dst->linesize[i] = FFALIGN(dst->linesize[i], align);
 
     size_t sizes[4];
-    SwsPassBuffer *output = pass->output;
-    int ret = buffer_get_sizes(output, sizes);
+    ret = av_image_fill_plane_sizes(sizes, dst->format, dst->height, linesize1);
     if (ret < 0)
         return ret;
 
-    const int align = av_cpu_max_align();
     for (int i = 0; i < 4; i++) {
         if (!sizes[i])
             break;
@@ -83,17 +70,44 @@ static int pass_alloc_output(SwsPass *pass)
         AVBufferRef *buf = av_buffer_alloc(sizes[i] + align);
         if (!buf)
             return AVERROR(ENOMEM);
-        output->img.data[i] = (uint8_t *) FFALIGN((uintptr_t) buf->data, align);
-        output->buf[i] = buf;
+        dst->data[i] = (uint8_t *) FFALIGN((uintptr_t) buf->data, align);
+        dst->buf[i] = buf;
     }
+
+    return 0;
+}
+
+static int pass_alloc_output(SwsPass *pass)
+{
+    if (!pass || pass->output->frame)
+        return 0;
+
+    SwsPassBuffer *buffer = pass->output;
+    AVFrame *frame = av_frame_alloc();
+    if (!frame)
+        return AVERROR(ENOMEM);
+    frame->format = pass->format;
+    frame->width  = buffer->width;
+    frame->height = buffer->height;
+
+    int ret = frame_alloc_planes(frame);
+    if (ret < 0) {
+        av_frame_free(&frame);
+        return ret;
+    }
+
+    buffer->frame = frame;
+    buffer->img.fmt = pass->format;
+    buffer->img.frame_ptr = frame;
+    memcpy(buffer->img.data, frame->data, sizeof(frame->data));
+    memcpy(buffer->img.linesize, frame->linesize, sizeof(frame->linesize));
     return 0;
 }
 
 static void free_buffer(AVRefStructOpaque opaque, void *obj)
 {
     SwsPassBuffer *buffer = obj;
-    for (int i = 0; i < FF_ARRAY_ELEMS(buffer->buf); i++)
-        av_buffer_unref(&buffer->buf[i]);
+    av_frame_free(&buffer->frame);
 }
 
 SwsPass *ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt,
@@ -868,7 +882,7 @@ void ff_sws_graph_run(SwsGraph *graph, const SwsImg *output, const SwsImg *input
         const SwsPass *pass = graph->passes[i];
         graph->exec.pass   = pass;
         graph->exec.input  = pass->input ? pass->input->output->img : *input;
-        graph->exec.output = pass->output->buf[0] ? pass->output->img : *output;
+        graph->exec.output = pass->output->frame ? pass->output->img : *output;
         if (pass->setup)
             pass->setup(&graph->exec.output, &graph->exec.input, pass);
         avpriv_slicethread_execute(graph->slicethread, pass->num_slices, 0);
diff --git a/libswscale/graph.h b/libswscale/graph.h
index 94b2051d94..e231938e72 100644
--- a/libswscale/graph.h
+++ b/libswscale/graph.h
@@ -61,7 +61,7 @@ typedef void (*sws_filter_run_t)(const SwsImg *out, const SwsImg *in,
 typedef struct SwsPassBuffer {
     SwsImg img;
     int width, height; /* dimensions of this buffer */
-    AVBufferRef *buf[4]; /* one per plane */
+    AVFrame *frame; /* backing storage for frame data */
 } SwsPassBuffer;
 
 /**
-- 
2.52.0


>From 708cfa9c20dd5f26debbd29cbb206ecc48e33354 Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Thu, 26 Feb 2026 18:15:02 +0100
Subject: [PATCH 5/7] swscale/ops: don't set src/dst_frame_ptr in op_pass_run()

Already set by setup().

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
---
 libswscale/ops.c | 3 ---
 1 file changed, 3 deletions(-)

diff --git a/libswscale/ops.c b/libswscale/ops.c
index 233c42f394..370ab4c854 100644
--- a/libswscale/ops.c
+++ b/libswscale/ops.c
@@ -1087,9 +1087,6 @@ static void op_pass_run(const SwsImg *out, const SwsImg *in, const int y,
     img_shift_idx(in,  y, p->idx_in,  (uint8_t **) exec.in);
     img_shift_idx(out, y, p->idx_out, exec.out);
 
-    exec.src_frame_ptr = in->frame_ptr;
-    exec.dst_frame_ptr = out->frame_ptr;
-
     /**
      *  To ensure safety, we need to consider the following:
      *
-- 
2.52.0


>From c4ee245af99bd5bed4a11c4c9c68bb8f47dc6c66 Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Thu, 26 Feb 2026 19:13:13 +0100
Subject: [PATCH 6/7] swscale/graph: move frame->field init logic to SwsGraph

And have ff_sws_graph_run() just take a bare AVFrame. This will help with
an upcoming change, aside from being a bit friendlier towards API users
in general.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
---
 libswscale/graph.c   | 48 +++++++++++++++++++++++++++++++++++++-------
 libswscale/graph.h   |  5 +++--
 libswscale/swscale.c | 38 +----------------------------------
 3 files changed, 45 insertions(+), 46 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index fa25810260..f187ddbcfb 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -871,18 +871,52 @@ void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
     ff_color_update_dynamic(&graph->src.color, color);
 }
 
-void ff_sws_graph_run(SwsGraph *graph, const SwsImg *output, const SwsImg *input)
+static SwsImg get_field(const AVFrame *frame, int field)
 {
-    av_assert0(output->fmt == graph->dst.hw_format ||
-               output->fmt == graph->dst.format);
-    av_assert0(input->fmt  == graph->src.hw_format ||
-               input->fmt  == graph->src.format);
+    SwsImg img = {0};
+
+    img.frame_ptr = frame;
+    img.fmt = frame->format;
+    for (int i = 0; i < 4; i++) {
+        img.data[i]     = frame->data[i];
+        img.linesize[i] = frame->linesize[i];
+    }
+
+    if (!(frame->flags & AV_FRAME_FLAG_INTERLACED)) {
+        av_assert1(!field);
+        return img;
+    }
+
+    if (field == FIELD_BOTTOM) {
+        /* Odd rows, offset by one line */
+        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
+        for (int i = 0; i < 4; i++) {
+            if (img.data[i])
+                img.data[i] += img.linesize[i];
+            if (desc->flags & AV_PIX_FMT_FLAG_PAL)
+                break;
+        }
+    }
+
+    /* Take only every second line */
+    for (int i = 0; i < 4; i++)
+        img.linesize[i] <<= 1;
+
+    return img;
+}
+
+void ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src)
+{
+    av_assert0(dst->format == graph->dst.hw_format || dst->format == graph->dst.format);
+    av_assert0(src->format == graph->src.hw_format || src->format == graph->src.format);
+    SwsImg input  = get_field(src, graph->field);
+    SwsImg output = get_field(dst, graph->field);
 
     for (int i = 0; i < graph->num_passes; i++) {
         const SwsPass *pass = graph->passes[i];
         graph->exec.pass   = pass;
-        graph->exec.input  = pass->input ? pass->input->output->img : *input;
-        graph->exec.output = pass->output->frame ? pass->output->img : *output;
+        graph->exec.input  = pass->input ? pass->input->output->img : input;
+        graph->exec.output = pass->output->frame ? pass->output->img : output;
         if (pass->setup)
             pass->setup(&graph->exec.output, &graph->exec.input, pass);
         avpriv_slicethread_execute(graph->slicethread, pass->num_slices, 0);
diff --git a/libswscale/graph.h b/libswscale/graph.h
index e231938e72..6d3db640fd 100644
--- a/libswscale/graph.h
+++ b/libswscale/graph.h
@@ -189,8 +189,9 @@ int ff_sws_graph_reinit(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *
                         int field, SwsGraph **graph);
 
 /**
- * Dispatch the filter graph on a single field. Internally threaded.
+ * Dispatch the filter graph on a single field of the given frames. Internally
+ * threaded.
  */
-void ff_sws_graph_run(SwsGraph *graph, const SwsImg *output, const SwsImg *input);
+void ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src);
 
 #endif /* SWSCALE_GRAPH_H */
diff --git a/libswscale/swscale.c b/libswscale/swscale.c
index 5dbd93e0a2..b04df78a5d 100644
--- a/libswscale/swscale.c
+++ b/libswscale/swscale.c
@@ -1321,40 +1321,6 @@ int sws_receive_slice(SwsContext *sws, unsigned int slice_start,
                           dst, c->frame_dst->linesize, slice_start, slice_height);
 }
 
-static SwsImg get_frame_img(const AVFrame *frame, int field)
-{
-    SwsImg img = {0};
-
-    img.frame_ptr = frame;
-    img.fmt = frame->format;
-    for (int i = 0; i < 4; i++) {
-        img.data[i]     = frame->data[i];
-        img.linesize[i] = frame->linesize[i];
-    }
-
-    if (!(frame->flags & AV_FRAME_FLAG_INTERLACED)) {
-        av_assert1(!field);
-        return img;
-    }
-
-    if (field == FIELD_BOTTOM) {
-        /* Odd rows, offset by one line */
-        const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
-        for (int i = 0; i < 4; i++) {
-            if (img.data[i])
-                img.data[i] += img.linesize[i];
-            if (desc->flags & AV_PIX_FMT_FLAG_PAL)
-                break;
-        }
-    }
-
-    /* Take only every second line */
-    for (int i = 0; i < 4; i++)
-        img.linesize[i] <<= 1;
-
-    return img;
-}
-
 /* Subset of av_frame_ref() that only references (video) data buffers */
 static int frame_ref(AVFrame *dst, const AVFrame *src)
 {
@@ -1419,9 +1385,7 @@ int sws_scale_frame(SwsContext *sws, AVFrame *dst, const AVFrame *src)
 
         for (int field = 0; field < 2; field++) {
             SwsGraph *graph = c->graph[field];
-            SwsImg input  = get_frame_img(src, field);
-            SwsImg output = get_frame_img(dst, field);
-            ff_sws_graph_run(graph, &output, &input);
+            ff_sws_graph_run(graph, dst, src);
             if (!graph->dst.interlaced)
                 break;
         }
-- 
2.52.0


>From 1b6ce805867eb43ed631ab31ab11e419cbd9b6fc Mon Sep 17 00:00:00 2001
From: Niklas Haas <git@haasn.dev>
Date: Thu, 26 Feb 2026 19:22:45 +0100
Subject: [PATCH 7/7] swscale/graph: nuke SwsImg

This has now become fully redundant with AVFrame, especially since the
existence of SwsPassBuffer. Delete it, simplifying a lot of things and
avoiding reinventing the wheel everywhere.

Also generally reduces overhead, since there is less redundant copying
going on.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git@haasn.dev>
---
 libswscale/graph.c | 98 +++++++++++++++++++++++-----------------------
 libswscale/graph.h | 27 ++++++-------
 libswscale/ops.c   | 34 ++++++++--------
 3 files changed, 77 insertions(+), 82 deletions(-)

diff --git a/libswscale/graph.c b/libswscale/graph.c
index f187ddbcfb..eccb6da2a2 100644
--- a/libswscale/graph.c
+++ b/libswscale/graph.c
@@ -97,10 +97,6 @@ static int pass_alloc_output(SwsPass *pass)
     }
 
     buffer->frame = frame;
-    buffer->img.fmt = pass->format;
-    buffer->img.frame_ptr = frame;
-    memcpy(buffer->img.data, frame->data, sizeof(frame->data));
-    memcpy(buffer->img.linesize, frame->linesize, sizeof(frame->linesize));
     return 0;
 }
 
@@ -144,9 +140,8 @@ SwsPass *ff_sws_graph_add_pass(SwsGraph *graph, enum AVPixelFormat fmt,
     }
 
     /* Align output buffer to include extra slice padding */
-    pass->output->img.fmt = fmt;
-    pass->output->width   = pass->width;
-    pass->output->height  = pass->slice_h * pass->num_slices;
+    pass->output->width  = pass->width;
+    pass->output->height = pass->slice_h * pass->num_slices;
 
     ret = av_dynarray_add_nofree(&graph->passes, &graph->num_passes, pass);
     if (ret < 0)
@@ -171,25 +166,25 @@ static int pass_append(SwsGraph *graph, enum AVPixelFormat fmt, int w, int h,
     return 0;
 }
 
-static void img_shift(const SwsImg *img, const int y, uint8_t *data[4])
+static void frame_shift(const AVFrame *f, const int y, uint8_t *data[4])
 {
     for (int i = 0; i < 4; i++) {
-        if (img->data[i])
-            data[i] = img->data[i] + (y >> ff_fmt_vshift(img->fmt, i)) * img->linesize[i];
+        if (f->data[i])
+            data[i] = f->data[i] + (y >> ff_fmt_vshift(f->format, i)) * f->linesize[i];
         else
             data[i] = NULL;
     }
 }
 
-static void run_copy(const SwsImg *out, const SwsImg *in, int y, int h,
+static void run_copy(const AVFrame *out, const AVFrame *in, int y, int h,
                      const SwsPass *pass)
 {
     uint8_t *in_data[4], *out_data[4];
-    img_shift(in,  y, in_data);
-    img_shift(out, y, out_data);
+    frame_shift(in,  y, in_data);
+    frame_shift(out, y, out_data);
 
     for (int i = 0; i < FF_ARRAY_ELEMS(out_data) && out_data[i]; i++) {
-        const int lines = h >> ff_fmt_vshift(in->fmt, i);
+        const int lines = h >> ff_fmt_vshift(in->format, i);
         av_assert1(in_data[i]);
 
         if (in_data[i] == out_data[i]) {
@@ -207,7 +202,7 @@ static void run_copy(const SwsImg *out, const SwsImg *in, int y, int h,
     }
 }
 
-static void run_rgb0(const SwsImg *out, const SwsImg *in, int y, int h,
+static void run_rgb0(const AVFrame *out, const AVFrame *in, int y, int h,
                      const SwsPass *pass)
 {
     SwsInternal *c = pass->priv;
@@ -228,7 +223,7 @@ static void run_rgb0(const SwsImg *out, const SwsImg *in, int y, int h,
     }
 }
 
-static void run_xyz2rgb(const SwsImg *out, const SwsImg *in, int y, int h,
+static void run_xyz2rgb(const AVFrame *out, const AVFrame *in, int y, int h,
                         const SwsPass *pass)
 {
     const SwsInternal *c = pass->priv;
@@ -237,7 +232,7 @@ static void run_xyz2rgb(const SwsImg *out, const SwsImg *in, int y, int h,
                     pass->width, h);
 }
 
-static void run_rgb2xyz(const SwsImg *out, const SwsImg *in, int y, int h,
+static void run_rgb2xyz(const AVFrame *out, const AVFrame *in, int y, int h,
                         const SwsPass *pass)
 {
     const SwsInternal *c = pass->priv;
@@ -258,7 +253,7 @@ static void free_legacy_swscale(void *priv)
     sws_free_context(&sws);
 }
 
-static void setup_legacy_swscale(const SwsImg *out, const SwsImg *in,
+static void setup_legacy_swscale(const AVFrame *out, const AVFrame *in,
                                  const SwsPass *pass)
 {
     SwsContext *sws = pass->priv;
@@ -291,24 +286,24 @@ static inline SwsContext *slice_ctx(const SwsPass *pass, int y)
     return sws;
 }
 
-static void run_legacy_unscaled(const SwsImg *out, const SwsImg *in,
+static void run_legacy_unscaled(const AVFrame *out, const AVFrame *in,
                                 int y, int h, const SwsPass *pass)
 {
     SwsContext *sws = slice_ctx(pass, y);
     SwsInternal *c = sws_internal(sws);
     const uint8_t *in_data[4];
-    img_shift(in, y, (uint8_t **) in_data);
+    frame_shift(in, y, (uint8_t **) in_data);
 
     c->convert_unscaled(c, in_data, in->linesize, y, h, out->data, out->linesize);
 }
 
-static void run_legacy_swscale(const SwsImg *out, const SwsImg *in,
+static void run_legacy_swscale(const AVFrame *out, const AVFrame *in,
                                int y, int h, const SwsPass *pass)
 {
     SwsContext *sws = slice_ctx(pass, y);
     SwsInternal *c = sws_internal(sws);
     uint8_t *out_data[4];
-    img_shift(out, y, out_data);
+    frame_shift(out, y, out_data);
 
     ff_swscale(c, (const uint8_t *const *) in->data, in->linesize, 0,
                sws->src_h, out_data, out->linesize, y, h);
@@ -639,7 +634,7 @@ static void free_lut3d(void *priv)
     ff_sws_lut3d_free(&lut);
 }
 
-static void setup_lut3d(const SwsImg *out, const SwsImg *in, const SwsPass *pass)
+static void setup_lut3d(const AVFrame *out, const AVFrame *in, const SwsPass *pass)
 {
     SwsLut3D *lut = pass->priv;
 
@@ -647,13 +642,13 @@ static void setup_lut3d(const SwsImg *out, const SwsImg *in, const SwsPass *pass
     ff_sws_lut3d_update(lut, &pass->graph->src.color);
 }
 
-static void run_lut3d(const SwsImg *out, const SwsImg *in, int y, int h,
+static void run_lut3d(const AVFrame *out, const AVFrame *in, int y, int h,
                       const SwsPass *pass)
 {
     SwsLut3D *lut = pass->priv;
     uint8_t *in_data[4], *out_data[4];
-    img_shift(in,  y, in_data);
-    img_shift(out, y, out_data);
+    frame_shift(in,  y, in_data);
+    frame_shift(out, y, out_data);
 
     ff_sws_lut3d_apply(lut, in_data[0], in->linesize[0], out_data[0],
                        out->linesize[0], pass->width, h);
@@ -771,7 +766,7 @@ static void sws_graph_worker(void *priv, int jobnr, int threadnr, int nb_jobs,
     const int slice_y = jobnr * pass->slice_h;
     const int slice_h = FFMIN(pass->slice_h, pass->height - slice_y);
 
-    pass->run(&graph->exec.output, &graph->exec.input, slice_y, slice_h, pass);
+    pass->run(graph->exec.output, graph->exec.input, slice_y, slice_h, pass);
 }
 
 int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *src,
@@ -788,6 +783,14 @@ int ff_sws_graph_create(SwsContext *ctx, const SwsFormat *dst, const SwsFormat *
     graph->field = field;
     graph->opts_copy = *ctx;
 
+    for (int i = 0; i < FF_ARRAY_ELEMS(graph->field_tmp); i++) {
+        graph->field_tmp[i] = av_frame_alloc();
+        if (!graph->field_tmp[i]) {
+            ret = AVERROR(ENOMEM);
+            goto error;
+        }
+    }
+
     ret = avpriv_slicethread_create(&graph->slicethread, (void *) graph,
                                     sws_graph_worker, NULL, ctx->threads);
     if (ret == AVERROR(ENOSYS))
@@ -826,6 +829,9 @@ void ff_sws_graph_free(SwsGraph **pgraph)
     }
     av_free(graph->passes);
 
+    for (int i = 0; i < FF_ARRAY_ELEMS(graph->field_tmp); i++)
+        av_frame_free(&graph->field_tmp[i]);
+
     av_free(graph);
     *pgraph = NULL;
 }
@@ -871,28 +877,22 @@ void ff_sws_graph_update_metadata(SwsGraph *graph, const SwsColor *color)
     ff_color_update_dynamic(&graph->src.color, color);
 }
 
-static SwsImg get_field(const AVFrame *frame, int field)
+static const AVFrame *get_field(SwsGraph *graph, const AVFrame *frame,
+                                AVFrame *restrict tmp)
 {
-    SwsImg img = {0};
-
-    img.frame_ptr = frame;
-    img.fmt = frame->format;
-    for (int i = 0; i < 4; i++) {
-        img.data[i]     = frame->data[i];
-        img.linesize[i] = frame->linesize[i];
-    }
-
     if (!(frame->flags & AV_FRAME_FLAG_INTERLACED)) {
-        av_assert1(!field);
-        return img;
+        av_assert1(!graph->field);
+        return frame;
     }
 
-    if (field == FIELD_BOTTOM) {
+    *tmp = *frame;
+
+    if (graph->field == FIELD_BOTTOM) {
         /* Odd rows, offset by one line */
         const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(frame->format);
         for (int i = 0; i < 4; i++) {
-            if (img.data[i])
-                img.data[i] += img.linesize[i];
+            if (tmp->data[i])
+                tmp->data[i] += frame->linesize[i];
             if (desc->flags & AV_PIX_FMT_FLAG_PAL)
                 break;
         }
@@ -900,25 +900,25 @@ static SwsImg get_field(const AVFrame *frame, int field)
 
     /* Take only every second line */
     for (int i = 0; i < 4; i++)
-        img.linesize[i] <<= 1;
+        tmp->linesize[i] <<= 1;
 
-    return img;
+    return tmp;
 }
 
 void ff_sws_graph_run(SwsGraph *graph, const AVFrame *dst, const AVFrame *src)
 {
     av_assert0(dst->format == graph->dst.hw_format || dst->format == graph->dst.format);
     av_assert0(src->format == graph->src.hw_format || src->format == graph->src.format);
-    SwsImg input  = get_field(src, graph->field);
-    SwsImg output = get_field(dst, graph->field);
+    const AVFrame *src_field = get_field(graph, src, graph->field_tmp[0]);
+    const AVFrame *dst_field = get_field(graph, dst, graph->field_tmp[1]);
 
     for (int i = 0; i < graph->num_passes; i++) {
         const SwsPass *pass = graph->passes[i];
         graph->exec.pass   = pass;
-        graph->exec.input  = pass->input ? pass->input->output->img : input;
-        graph->exec.output = pass->output->frame ? pass->output->img : output;
+        graph->exec.input  = pass->input ? pass->input->output->frame : src_field;
+        graph->exec.output = pass->output->frame ? pass->output->frame : dst_field;
         if (pass->setup)
-            pass->setup(&graph->exec.output, &graph->exec.input, pass);
+            pass->setup(graph->exec.output, graph->exec.input, pass);
         avpriv_slicethread_execute(graph->slicethread, pass->num_slices, 0);
     }
 }
diff --git a/libswscale/graph.h b/libswscale/graph.h
index 6d3db640fd..9ddf2ad1c9 100644
--- a/libswscale/graph.h
+++ b/libswscale/graph.h
@@ -29,16 +29,6 @@
 #include "swscale.h"
 #include "format.h"
 
-/**
- * Represents a view into a single field of frame data.
- */
-typedef struct SwsImg {
-    enum AVPixelFormat fmt;
-    uint8_t *data[4]; /* points to y=0 */
-    int linesize[4];
-    const AVFrame *frame_ptr; /* Pointer to the original AVframe */
-} SwsImg;
-
 static av_always_inline av_const int ff_fmt_vshift(enum AVPixelFormat fmt, int plane)
 {
     const AVPixFmtDescriptor *desc = av_pix_fmt_desc_get(fmt);
@@ -52,16 +42,15 @@ typedef struct SwsGraph SwsGraph;
  * Output `h` lines of filtered data. `out` and `in` point to the
  * start of the image buffer for this pass.
  */
-typedef void (*sws_filter_run_t)(const SwsImg *out, const SwsImg *in,
+typedef void (*sws_filter_run_t)(const AVFrame *out, const AVFrame *in,
                                  int y, int h, const SwsPass *pass);
 
 /**
  * Represents an allocated output buffer for a filter pass.
  */
 typedef struct SwsPassBuffer {
-    SwsImg img;
     int width, height; /* dimensions of this buffer */
-    AVFrame *frame; /* backing storage for frame data */
+    AVFrame *frame;
 } SwsPassBuffer;
 
 /**
@@ -97,7 +86,7 @@ struct SwsPass {
     /**
      * Called once from the main thread before running the filter. Optional.
      */
-    void (*setup)(const SwsImg *out, const SwsImg *in, const SwsPass *pass);
+    void (*setup)(const AVFrame *out, const AVFrame *in, const SwsPass *pass);
 
     /**
      * Optional private state and associated free() function.
@@ -134,14 +123,20 @@ typedef struct SwsGraph {
     SwsFormat src, dst;
     int field;
 
+    /**
+     * Temporary storage to hold individual fields of the input frames.
+     * No actual ownership over the data.
+     */
+    AVFrame *field_tmp[2];
+
     /**
      * Temporary execution state inside ff_sws_graph_run(); used to pass
      * data to worker threads.
      */
     struct {
         const SwsPass *pass; /* current filter pass */
-        SwsImg input; /* current filter pass input/output */
-        SwsImg output;
+        const AVFrame *input; /* current filter pass input/output */
+        const AVFrame *output;
     } exec;
 } SwsGraph;
 
diff --git a/libswscale/ops.c b/libswscale/ops.c
index 370ab4c854..24f861fcb1 100644
--- a/libswscale/ops.c
+++ b/libswscale/ops.c
@@ -935,26 +935,26 @@ static void op_pass_free(void *ptr)
     av_free(p);
 }
 
-static inline void img_shift_idx(const SwsImg *img, const int y,
-                                 const int plane_idx[4], uint8_t *data[4])
+static inline void frame_shift_idx(const AVFrame *frame, const int y,
+                                   const int plane_idx[4], uint8_t *data[4])
 {
     for (int i = 0; i < 4; i++) {
         const int idx = plane_idx[i];
         if (idx >= 0) {
-            av_assert1(img->data[idx]);
-            const int yshift = y >> ff_fmt_vshift(img->fmt, idx);
-            data[i] = img->data[idx] + yshift * img->linesize[idx];
+            av_assert1(frame->data[idx]);
+            const int yshift = y >> ff_fmt_vshift(frame->format, idx);
+            data[i] = frame->data[idx] + yshift * frame->linesize[idx];
         } else {
             data[i] = NULL;
         }
     }
 }
 
-static void op_pass_setup(const SwsImg *out, const SwsImg *in,
+static void op_pass_setup(const AVFrame *out, const AVFrame *in,
                           const SwsPass *pass)
 {
-    const AVPixFmtDescriptor *indesc  = av_pix_fmt_desc_get(in->fmt);
-    const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->fmt);
+    const AVPixFmtDescriptor *indesc  = av_pix_fmt_desc_get(in->format);
+    const AVPixFmtDescriptor *outdesc = av_pix_fmt_desc_get(out->format);
 
     SwsOpPass *p = pass->priv;
     SwsOpExec *exec = &p->exec_base;
@@ -1004,15 +1004,15 @@ static void op_pass_setup(const SwsImg *out, const SwsImg *in,
         exec->out_bump[i] = out->linesize[i] - blocks_main * exec->block_size_out;
     }
 
-    exec->src_frame_ptr = in->frame_ptr;
-    exec->dst_frame_ptr = out->frame_ptr;
+    exec->src_frame_ptr = in;
+    exec->dst_frame_ptr = out;
 }
 
 /* Dispatch kernel over the last column of the image using memcpy */
 static av_always_inline void
 handle_tail(const SwsOpPass *p, SwsOpExec *exec,
-            const SwsImg *out, const bool copy_out,
-            const SwsImg *in,  const bool copy_in,
+            const AVFrame *out, const bool copy_out,
+            const AVFrame *in,  const bool copy_in,
             int y, const int h)
 {
     DECLARE_ALIGNED_64(uint8_t, tmp)[2][4][sizeof(uint32_t[128])];
@@ -1023,8 +1023,8 @@ handle_tail(const SwsOpPass *p, SwsOpExec *exec,
     const int bx = p->num_blocks - 1;
 
     uint8_t *in_data[4], *out_data[4];
-    img_shift_idx(in,  y, p->idx_in,  in_data);
-    img_shift_idx(out, y, p->idx_out, out_data);
+    frame_shift_idx(in,  y, p->idx_in,  in_data);
+    frame_shift_idx(out, y, p->idx_out, out_data);
 
     for (int i = 0; i < p->planes_in; i++) {
         in_data[i] += p->tail_off_in;
@@ -1074,7 +1074,7 @@ handle_tail(const SwsOpPass *p, SwsOpExec *exec,
     }
 }
 
-static void op_pass_run(const SwsImg *out, const SwsImg *in, const int y,
+static void op_pass_run(const AVFrame *out, const AVFrame *in, const int y,
                         const int h, const SwsPass *pass)
 {
     const SwsOpPass *p = pass->priv;
@@ -1084,8 +1084,8 @@ static void op_pass_run(const SwsImg *out, const SwsImg *in, const int y,
     DECLARE_ALIGNED_32(SwsOpExec, exec) = p->exec_base;
     exec.slice_y = y;
     exec.slice_h = h;
-    img_shift_idx(in,  y, p->idx_in,  (uint8_t **) exec.in);
-    img_shift_idx(out, y, p->idx_out, exec.out);
+    frame_shift_idx(in,  y, p->idx_in,  (uint8_t **) exec.in);
+    frame_shift_idx(out, y, p->idx_out, exec.out);
 
     /**
      *  To ensure safety, we need to consider the following:
-- 
2.52.0

_______________________________________________
ffmpeg-devel mailing list -- ffmpeg-devel@ffmpeg.org
To unsubscribe send an email to ffmpeg-devel-leave@ffmpeg.org

^ permalink raw reply	[flat|nested] only message in thread

only message in thread, other threads:[~2026-02-26 18:38 UTC | newest]

Thread overview: (only message) (download: mbox.gz / follow: Atom feed)
-- links below jump to the message on this page --
2026-02-26 18:36 [FFmpeg-devel] [PR] swscale/graph: nuke SwsImg struct, just use AVFrame everywhere (PR #22298) Niklas Haas via ffmpeg-devel

Git Inbox Mirror of the ffmpeg-devel mailing list - see https://ffmpeg.org/mailman/listinfo/ffmpeg-devel

This inbox may be cloned and mirrored by anyone:

	git clone --mirror https://master.gitmailbox.com/ffmpegdev/0 ffmpegdev/git/0.git

	# If you have public-inbox 1.1+ installed, you may
	# initialize and index your mirror using the following commands:
	public-inbox-init -V2 ffmpegdev ffmpegdev/ https://master.gitmailbox.com/ffmpegdev \
		ffmpegdev@gitmailbox.com
	public-inbox-index ffmpegdev

Example config snippet for mirrors.


AGPL code for this site: git clone https://public-inbox.org/public-inbox.git