Commit c744f68541 for ffmpeg

commit c744f685410dcaf6810154cbb18a286ed9b27d9a
Author: Niklas Haas <git@haasn.dev>
Date:   Tue Nov 3 16:59:46 2026 +0100

    swscale/ops_chain: track references instead of arbitrary free callbacks

    We don't really need this level of generality.

    Signed-off-by: Niklas Haas <git@haasn.dev>

diff --git a/libswscale/aarch64/ops.c b/libswscale/aarch64/ops.c
index ec11d0d871..d0412909ad 100644
--- a/libswscale/aarch64/ops.c
+++ b/libswscale/aarch64/ops.c
@@ -98,17 +98,14 @@ static int aarch64_setup_linear(const SwsAArch64OpImplParams *p,
         }
     }

-    res->priv.ptr = coeffs;
-    res->free = ff_op_priv_unref;
-
+    res->ref = res->priv.ptr = coeffs;
     return 0;
 }

 /*********************************************************************/
 static int aarch64_setup_dither(const SwsUOp *uop, SwsImplResult *res)
 {
-    res->priv.ptr = av_refstruct_ref(uop->data.ptr);
-    res->free = ff_op_priv_unref;
+    res->ref = res->priv.ptr = av_refstruct_ref(uop->data.ptr);
     return 0;
 }

@@ -186,7 +183,8 @@ static int compile_uops_aarch64(SwsContext *ctx, const SwsUOpList *uops,
         ret = aarch64_setup(&uops->ops[i], &params, &res);
         if (ret < 0)
             goto error;
-        ret = ff_sws_op_chain_append(chain, func, res.free, &res.priv);
+        ret = ff_sws_op_chain_append(chain, func, res.ref, &res.priv);
+        av_refstruct_unref(&res.ref);
         if (ret < 0)
             goto error;
     }
diff --git a/libswscale/ops_chain.c b/libswscale/ops_chain.c
index ff48ede4a2..9eb4b242dc 100644
--- a/libswscale/ops_chain.c
+++ b/libswscale/ops_chain.c
@@ -20,6 +20,7 @@

 #include "libavutil/avassert.h"
 #include "libavutil/mem.h"
+#include "libavutil/refstruct.h"

 #include "ops_chain.h"

@@ -34,26 +35,27 @@ void ff_sws_op_chain_free_cb(void *ptr)
         return;

     SwsOpChain *chain = ptr;
-    for (int i = 0; i < chain->num_impl + 1; i++) {
-        if (chain->free[i])
-            chain->free[i](&chain->impl[i].priv);
-    }
+    for (int i = 0; i < chain->num_ref; i++)
+        av_refstruct_unref(&chain->ref[i]);

     av_free(chain);
 }

-int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func,
-                           void (*free)(SwsOpPriv *), const SwsOpPriv *priv)
+int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, const void *ref,
+                           const SwsOpPriv *priv)
 {
     const int idx = chain->num_impl;
     if (idx == SWS_MAX_OPS)
         return AVERROR(EINVAL);
+    if (ref && chain->num_ref >= FF_ARRAY_ELEMS(chain->ref))
+        return AVERROR(EINVAL);

     av_assert1(func);
     chain->impl[idx].cont = func;
     chain->impl[idx + 1].priv = *priv;
-    chain->free[idx + 1] = free;
     chain->num_impl++;
+    if (ref)
+        chain->ref[chain->num_ref++] = av_refstruct_ref_c(ref);
     return 0;
 }

@@ -120,12 +122,10 @@ int ff_sws_uop_lookup(SwsContext *ctx, const SwsUOpTable *const tables[],
     }

     ret = ff_sws_op_chain_append(chain, res.func ? res.func : match->func,
-                                 res.free, &res.priv);
-    if (ret < 0) {
-        if (res.free)
-            res.free(&res.priv);
+                                 res.ref, &res.priv);
+    av_refstruct_unref(&res.ref);
+    if (ret < 0)
         return ret;
-    }

     for (int i = 0; i < 4; i++) {
         chain->over_read[i]  = FFMAX(chain->over_read[i],  res.over_read[i]);
diff --git a/libswscale/ops_chain.h b/libswscale/ops_chain.h
index 6fd9a57a94..01c96bbd43 100644
--- a/libswscale/ops_chain.h
+++ b/libswscale/ops_chain.h
@@ -85,11 +85,14 @@ static_assert(offsetof(SwsOpImpl, priv) == 16, "SwsOpImpl layout mismatch");
 typedef struct SwsOpChain {
 #define SWS_MAX_OPS 16
     SwsOpImpl impl[SWS_MAX_OPS + 1]; /* reserve extra space for the entrypoint */
-    void (*free[SWS_MAX_OPS + 1])(SwsOpPriv *);
     int num_impl;
     int cpu_flags;      /* set of all used CPU flags */
     int over_read[4];   /* chain over-reads input by this many bytes */
     int over_write[4];  /* chain over-writes output by this many bytes */
+
+    /* Extra references owned by each kernel */
+    const void *ref[16];
+    int num_ref;
 } SwsOpChain;

 SwsOpChain *ff_sws_op_chain_alloc(void);
@@ -100,8 +103,8 @@ static inline void ff_sws_op_chain_free(SwsOpChain *chain)
 }

 /* Returns 0 on success, or a negative error code. */
-int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func,
-                           void (*free)(SwsOpPriv *), const SwsOpPriv *priv);
+int ff_sws_op_chain_append(SwsOpChain *chain, SwsFuncPtr func, const void *ref,
+                           const SwsOpPriv *priv);

 /**
  * Attach a SwsOpChain and all metadata to a SwsCompiledOp (as out->priv).
@@ -120,9 +123,9 @@ typedef struct SwsImplParams {
 typedef struct SwsImplResult {
     SwsFuncPtr func; /* overrides `SwsUOpEntry.func` if non-NULL */
     SwsOpPriv priv; /* private data for this implementation instance */
-    void (*free)(SwsOpPriv *priv); /* free function for `priv` */
     int over_read[4];  /* implementation over-reads input by this many bytes */
     int over_write[4]; /* implementation over-writes output by this many bytes */
+    const void *ref; /* external data backing `priv`, ownership passes to caller */
 } SwsImplResult;

 typedef struct SwsUOpEntry {
diff --git a/libswscale/uops_tmpl.c b/libswscale/uops_tmpl.c
index 0ba058c8da..80c6391f5d 100644
--- a/libswscale/uops_tmpl.c
+++ b/libswscale/uops_tmpl.c
@@ -262,9 +262,8 @@ DECL_SETUP(setup_filter_v, params, out)
     for (int i = 0; i < filter->num_weights; i++)
         weights[i] = (float) filter->weights[i] / SWS_FILTER_SCALE;

-    out->priv.ptr = weights;
+    out->ref = out->priv.ptr = weights;
     out->priv.i32[2] = filter->filter_size;
-    out->free = ff_op_priv_unref;
     return 0;
 }

@@ -317,9 +316,8 @@ DECL_SETUP(setup_filter_h, params, out)
     static_assert(SWS_FILTER_PAD_ROWS >= SWS_BLOCK_SIZE,
                   "filter weights contain sufficient padding for block over-read");

-    out->priv.ptr = av_refstruct_ref(filter->weights);
+    out->ref = out->priv.ptr = av_refstruct_ref(filter->weights);
     out->priv.i32[2] = filter->filter_size;
-    out->free = ff_op_priv_unref;
     return 0;
 }

@@ -726,8 +724,7 @@ DECL_SETUP(setup_dither, params, out)
     const int size = 1 << dither->size_log2;
     if (size >= SWS_BLOCK_SIZE) {
         /* No extra padding needed */
-        out->priv.ptr = av_refstruct_ref(uop->data.ptr);
-        out->free = ff_op_priv_unref;
+        out->ref = out->priv.ptr = av_refstruct_ref(uop->data.ptr);
         return 0;
     }

@@ -736,8 +733,7 @@ DECL_SETUP(setup_dither, params, out)
     pixel_t *matrix = ff_refstruct_calloc(height, sizeof(pixel_t) * stride);
     if (!matrix)
         return AVERROR(ENOMEM);
-    out->priv.ptr = matrix;
-    out->free = ff_op_priv_unref;
+    out->ref = out->priv.ptr = matrix;

     /* Pad to multiple of block size. We don't need extra padding for the
      * height because ff_sws_dither_height() already includes any padding
@@ -805,11 +801,10 @@ DECL_SETUP(setup_linear, params, out)
         c.k[i] = uop->data.mat4x5[i][4].px;
     }

-    out->priv.ptr = av_refstruct_allocz(sizeof(c));
+    out->ref = out->priv.ptr = av_refstruct_allocz(sizeof(c));
     if (!out->priv.ptr)
         return AVERROR(ENOMEM);
     memcpy(out->priv.ptr, &c, sizeof(c));
-    out->free = ff_op_priv_unref;
     return 0;
 }

@@ -861,8 +856,7 @@ SWS_FOR_STRUCT(PX, LINEAR, DECL_ENTRY, .setup = fn(setup_linear) )
 DECL_SETUP(setup_lut3d, params, out)
 {
     const SwsLut3D *lut = params->uop->data.lut3d;
-    out->priv.ptr = (void *) av_refstruct_ref_c(lut);
-    out->free = ff_op_priv_unref;
+    out->ref = out->priv.ptr = (void *) av_refstruct_ref_c(lut);
     return 0;
 }

diff --git a/libswscale/x86/ops.c b/libswscale/x86/ops.c
index 719d19e65b..3c6e783bd6 100644
--- a/libswscale/x86/ops.c
+++ b/libswscale/x86/ops.c
@@ -57,9 +57,8 @@ static int setup_filter_v(const SwsImplParams *params, SwsImplResult *out)
     for (int i = 0; i < filter->num_weights; i++)
         weights[i] = (float) filter->weights[i] / SWS_FILTER_SCALE;

-    out->priv.ptr = weights;
+    out->ref = out->priv.ptr = weights;
     out->priv.uptr[1] = filter->filter_size;
-    out->free = ff_op_priv_unref;
     return 0;
 }

@@ -150,9 +149,8 @@ static int setup_filter_h(const SwsImplParams *params, SwsImplResult *out)
         }
     }

-    out->priv.ptr = weights.ptr;
+    out->ref = out->priv.ptr = weights.ptr;
     out->priv.uptr[1] = aligned_size;
-    out->free = ff_op_priv_unref;

     for (int i = 0; i < 4; i++) {
         if (uop->mask & SWS_COMP(i))
@@ -237,9 +235,8 @@ static int setup_filter_h_4x4(const SwsImplParams *params, SwsImplResult *out)
         }
     }

-    out->priv.ptr = weights.ptr;
+    out->ref = out->priv.ptr = weights.ptr;
     out->priv.uptr[1] = aligned_size * sizeof_weights;
-    out->free = ff_op_priv_unref;

     for (int i = 0; i < 4; i++) {
         if (uop->mask & SWS_COMP(i))
@@ -272,8 +269,7 @@ static int setup_clear(const SwsImplParams *params, SwsImplResult *out)

 static int setup_dither(const SwsImplParams *params, SwsImplResult *out)
 {
-    out->priv.ptr = av_refstruct_ref(params->uop->data.ptr);
-    out->free = ff_op_priv_unref;
+    out->ref = out->priv.ptr = av_refstruct_ref(params->uop->data.ptr);
     return 0;
 }

@@ -298,19 +294,17 @@ static int setup_linear(const SwsImplParams *params, SwsImplResult *out)
 {
     const SwsUOp *uop = params->uop;
     if (uop->type == SWS_PIXEL_F32) {
-        out->priv.ptr = av_refstruct_allocz(sizeof(uop->data.mat4x5));
+        out->ref = out->priv.ptr = av_refstruct_allocz(sizeof(uop->data.mat4x5));
         if (!out->priv.ptr)
             return AVERROR(ENOMEM);
         memcpy(out->priv.ptr, uop->data.mat4x5, sizeof(uop->data.mat4x5));
-        out->free = ff_op_priv_unref;
         return 0;
     }

     uint8_t *mat = av_refstruct_allocz(4 * 5 * 16); /* one lane per component */
     if (!mat)
         return AVERROR(ENOMEM);
-    out->priv.ptr = mat;
-    out->free = ff_op_priv_unref;
+    out->ref = out->priv.ptr = mat;

     for (int i = 0; i < 4; i++) {
         for (int j = 0; j < 5; j++) {