[FFmpeg-devel] [PATCH v2 12/19] swscale: expose SwsContext publicly

Niklas Haas ffmpeg at haasn.xyz
Mon Oct 14 16:37:37 EEST 2024


From: Niklas Haas <git at haasn.dev>

Following in the footsteps of the work in the previous commit, it's now
relatively straightforward to expose the options struct publicly as
SwsContext. This is a step towards making this more user friendly, as
well as following API conventions established elsewhere.

Sponsored-by: Sovereign Tech Fund
Signed-off-by: Niklas Haas <git at haasn.dev>
---
 libswscale/options.c          |   3 +-
 libswscale/swscale.c          |   2 +-
 libswscale/swscale.h          | 102 +++++++++++++++++++--
 libswscale/swscale_internal.h |  49 ++--------
 libswscale/utils.c            | 165 +++++++++++++++++-----------------
 libswscale/x86/output.asm     |   2 +-
 tests/checkasm/sw_scale.c     |   8 +-
 7 files changed, 187 insertions(+), 144 deletions(-)

diff --git a/libswscale/options.c b/libswscale/options.c
index 6248e5f4b5..4ce7d8a36a 100644
--- a/libswscale/options.c
+++ b/libswscale/options.c
@@ -27,7 +27,7 @@ static const char *sws_context_to_name(void *ptr)
     return "swscaler";
 }
 
-#define OFFSET(x) offsetof(SwsInternal, opts.x)
+#define OFFSET(x) offsetof(SwsContext, x)
 #define DEFAULT 0
 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 
@@ -91,7 +91,6 @@ const AVClass ff_sws_context_class = {
     .class_name = "SWScaler",
     .item_name  = sws_context_to_name,
     .option     = swscale_options,
-    .parent_log_context_offset = offsetof(SwsInternal, parent),
     .category   = AV_CLASS_CATEGORY_SWSCALER,
     .version    = LIBAVUTIL_VERSION_INT,
 };
diff --git a/libswscale/swscale.c b/libswscale/swscale.c
index ea4c7b00d1..d5be07193d 100644
--- a/libswscale/swscale.c
+++ b/libswscale/swscale.c
@@ -1252,7 +1252,7 @@ int attribute_align_arg sws_scale(SwsContext *sws,
 void ff_sws_slice_worker(void *priv, int jobnr, int threadnr,
                          int nb_jobs, int nb_threads)
 {
-    SwsInternal *parent = priv;
+    SwsInternal *parent = sws_internal(priv);
     SwsContext     *sws = parent->slice_ctx[threadnr];
     SwsInternal      *c = sws_internal(sws);
 
diff --git a/libswscale/swscale.h b/libswscale/swscale.h
index f544387769..20f6b23368 100644
--- a/libswscale/swscale.h
+++ b/libswscale/swscale.h
@@ -42,8 +42,6 @@
 #include "version.h"
 #endif
 
-typedef struct SwsContext SwsContext;
-
 /**
  * @defgroup libsws libswscale
  * Color conversion and scaling library.
@@ -65,17 +63,103 @@ const char *swscale_configuration(void);
 const char *swscale_license(void);
 
 /**
- * Get the AVClass for swsContext. It can be used in combination with
+ * Get the AVClass for SwsContext. It can be used in combination with
  * AV_OPT_SEARCH_FAKE_OBJ for examining options.
  *
  * @see av_opt_find().
  */
 const AVClass *sws_get_class(void);
 
-/**
- * Allocate an empty SwsContext. This must be filled and passed to
- * sws_init_context(). For filling see AVOptions, options.c and
- * sws_setColorspaceDetails().
+/******************************
+ * Flags and quality settings *
+ ******************************/
+
+typedef enum SwsDither {
+    SWS_DITHER_NONE = 0, /* disable dithering */
+    SWS_DITHER_AUTO,     /* auto-select from preset */
+    SWS_DITHER_BAYER,    /* ordered dither matrix */
+    SWS_DITHER_ED,       /* error diffusion */
+    SWS_DITHER_A_DITHER, /* arithmetic addition */
+    SWS_DITHER_X_DITHER, /* arithmetic xor */
+    SWS_DITHER_NB,       /* not part of the ABI */
+} SwsDither;
+
+typedef enum SwsAlphaBlend {
+    SWS_ALPHA_BLEND_NONE = 0,
+    SWS_ALPHA_BLEND_UNIFORM,
+    SWS_ALPHA_BLEND_CHECKERBOARD,
+    SWS_ALPHA_BLEND_NB,  /* not part of the ABI */
+} SwsAlphaBlend;
+
+/***********************************
+ * Context creation and management *
+ ***********************************/
+
+/**
+ * Main external API structure. New fields can be added to the end with
+ * minor version bumps. Removal, reordering and changes to existing fields
+ * require a major version bump. sizeof(SwsContext) is not part of the ABI.
+ */
+typedef struct SwsContext {
+    const AVClass *av_class;
+
+    /**
+     * Private context used for internal data.
+     */
+    struct SwsInternal *internal;
+
+    /**
+     * Private data of the user, can be used to carry app specific stuff.
+     */
+    void *opaque;
+
+    /**
+     * Bitmask of SWS_*.
+     */
+    int64_t flags;
+
+    /**
+     * Extra parameters for fine-tuning certain scalers.
+     */
+    double scaler_params[2];
+
+    /**
+     * How many threads to use for processing, or 0 for automatic selection.
+     */
+    int threads;
+
+    /**
+     * Dither mode.
+     */
+    SwsDither dither;
+
+    /**
+     * Alpha blending mode. See `SwsAlphaBlend` for details.
+     */
+    SwsAlphaBlend alpha_blend;
+
+    /**
+     * Use gamma correct scaling.
+     */
+    int gamma_flag;
+
+    /**
+     * Frame property overrides.
+     */
+    int src_w, src_h;  ///< Width and height of the source frame
+    int dst_w, dst_h;  ///< Width and height of the destination frame
+    int src_format;    ///< Source pixel format
+    int dst_format;    ///< Destination pixel format
+    int src_range;     ///< Source is full range
+    int dst_range;     ///< Destination is full range
+    int src_v_chr_pos; ///< Source vertical chroma position in luma grid / 256
+    int src_h_chr_pos; ///< Source horizontal chroma position
+    int dst_v_chr_pos; ///< Destination vertical chroma position
+    int dst_h_chr_pos; ///< Destination horizontal chroma position
+} SwsContext;
+
+/**
+ * Allocate an empty SwsContext and set its fields to default values.
  */
 SwsContext *sws_alloc_context(void);
 
@@ -255,7 +339,9 @@ int sws_isSupportedOutput(enum AVPixelFormat pix_fmt);
 int sws_isSupportedEndiannessConversion(enum AVPixelFormat pix_fmt);
 
 /**
- * Initialize the swscaler context sws_context.
+ * Initialize the swscaler context sws_context. This function fixes the
+ * values of any options set in the SwsContext; further adjustments will
+ * not affect the scaling process.
  *
  * @return zero or positive value on success, a negative value on
  * error
diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h
index d3359eb837..d3871a9641 100644
--- a/libswscale/swscale_internal.h
+++ b/libswscale/swscale_internal.h
@@ -70,26 +70,9 @@ typedef struct SwsInternal SwsInternal;
 
 static inline SwsInternal *sws_internal(const SwsContext *sws)
 {
-    return (SwsInternal *) sws;
+    return sws ? sws->internal : NULL;
 }
 
-typedef enum SwsDither {
-    SWS_DITHER_NONE = 0,
-    SWS_DITHER_AUTO,
-    SWS_DITHER_BAYER,
-    SWS_DITHER_ED,
-    SWS_DITHER_A_DITHER,
-    SWS_DITHER_X_DITHER,
-    SWS_DITHER_NB,
-} SwsDither;
-
-typedef enum SwsAlphaBlend {
-    SWS_ALPHA_BLEND_NONE  = 0,
-    SWS_ALPHA_BLEND_UNIFORM,
-    SWS_ALPHA_BLEND_CHECKERBOARD,
-    SWS_ALPHA_BLEND_NB,
-} SwsAlphaBlend;
-
 typedef struct Range {
     unsigned int start;
     unsigned int len;
@@ -329,32 +312,10 @@ struct SwsFilterDescriptor;
 
 /* This struct should be aligned on at least a 32-byte boundary. */
 struct SwsInternal {
-    /* Currently active user-facing options. */
-    struct {
-        const AVClass *av_class;
-
-        double scaler_params[2];       ///< Input parameters for scaling algorithms that need them.
-        int flags;                     ///< Flags passed by the user to select scaler algorithm, optimizations, subsampling, etc...
-        int threads;                   ///< Number of threads used for scaling
-
-        int src_w;                     ///< Width  of source      luma/alpha planes.
-        int src_h;                     ///< Height of source      luma/alpha planes.
-        int dst_w;                     ///< Width  of destination luma/alpha planes.
-        int dst_h;                     ///< Height of destination luma/alpha planes.
-        enum AVPixelFormat src_format; ///< Source      pixel format.
-        enum AVPixelFormat dst_format; ///< Destination pixel format.
-        int src_range;                 ///< 0 = MPG YUV range, 1 = JPG YUV range (source      image).
-        int dst_range;                 ///< 0 = MPG YUV range, 1 = JPG YUV range (destination image).
-        int src_h_chr_pos;
-        int dst_h_chr_pos;
-        int src_v_chr_pos;
-        int dst_v_chr_pos;
-        int gamma_flag;
-
-        SwsDither dither;
-        SwsAlphaBlend alpha_blend;
-    } opts;
+    /* Shallow copy of the main scaler context, contains currently active options */
+    SwsContext opts;
 
+    /* Parent context (for cascaded/sliced contexts) */
     SwsContext *parent;
 
     AVSliceThread      *slicethread;
@@ -713,7 +674,7 @@ static_assert(offsetof(SwsInternal, redDither) + DITHER32_INT == offsetof(SwsInt
 #if ARCH_X86
 /* x86 yuv2gbrp uses the SwsInternal for yuv coefficients
    if struct offsets change the asm needs to be updated too */
-static_assert(offsetof(SwsInternal, yuv2rgb_y_offset) == 40316,
+static_assert(offsetof(SwsInternal, yuv2rgb_y_offset) == 40332,
               "yuv2rgb_y_offset must be updated in x86 asm");
 #endif
 
diff --git a/libswscale/utils.c b/libswscale/utils.c
index 95f3abc1d3..6f8e19fef6 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -284,22 +284,20 @@ static SwsContext *alloc_set_opts(int srcW, int srcH, enum AVPixelFormat srcForm
                                   int flags, const double *param)
 {
     SwsContext *sws = sws_alloc_context();
-    SwsInternal *c = sws_internal(sws);
-
-    if (!c)
+    if (!sws)
         return NULL;
 
-    c->opts.flags = flags;
-    c->opts.src_w = srcW;
-    c->opts.src_h = srcH;
-    c->opts.dst_w = dstW;
-    c->opts.dst_h      = dstH;
-    c->opts.src_format = srcFormat;
-    c->opts.dst_format = dstFormat;
+    sws->flags = flags;
+    sws->src_w = srcW;
+    sws->src_h = srcH;
+    sws->dst_w = dstW;
+    sws->dst_h      = dstH;
+    sws->src_format = srcFormat;
+    sws->dst_format = dstFormat;
 
     if (param) {
-        c->opts.scaler_params[0] = param[0];
-        c->opts.scaler_params[1] = param[1];
+        sws->scaler_params[0] = param[0];
+        sws->scaler_params[1] = param[1];
     }
 
     return sws;
@@ -1080,11 +1078,11 @@ int sws_setColorspaceDetails(SwsContext *sws, const int inv_table[4],
 
 
 
-    c->brightness = brightness;
-    c->contrast   = contrast;
-    c->saturation = saturation;
-    c->opts.src_range   = srcRange;
-    c->opts.dst_range   = dstRange;
+    c->brightness     = brightness;
+    c->contrast       = contrast;
+    c->saturation     = saturation;
+    c->opts.src_range = srcRange;
+    c->opts.dst_range = dstRange;
 
     if (need_reinit) {
         ff_sws_init_range_convert(c);
@@ -1153,7 +1151,7 @@ int sws_setColorspaceDetails(SwsContext *sws, const int inv_table[4],
             if (!c->cascaded_context[0])
                 return -1;
 
-            sws_internal(c->cascaded_context[0])->opts.alpha_blend = c->opts.alpha_blend;
+            c->cascaded_context[0]->alpha_blend = c->opts.alpha_blend;
             ret = sws_init_context(c->cascaded_context[0], NULL , NULL);
             if (ret < 0)
                 return ret;
@@ -1168,8 +1166,8 @@ int sws_setColorspaceDetails(SwsContext *sws, const int inv_table[4],
                                                     c->opts.scaler_params);
             if (!c->cascaded_context[1])
                 return -1;
-            sws_internal(c->cascaded_context[1])->opts.src_range = srcRange;
-            sws_internal(c->cascaded_context[1])->opts.dst_range = dstRange;
+            c->cascaded_context[1]->src_range = srcRange;
+            c->cascaded_context[1]->dst_range = dstRange;
             ret = sws_init_context(c->cascaded_context[1], NULL , NULL);
             if (ret < 0)
                 return ret;
@@ -1227,16 +1225,23 @@ int sws_getColorspaceDetails(SwsContext *sws, int **inv_table,
 
 SwsContext *sws_alloc_context(void)
 {
-    SwsInternal *c = av_mallocz(sizeof(SwsInternal));
+    SwsInternal *c;
+    SwsContext *sws = (SwsContext *) av_mallocz(sizeof(SwsContext));
+    if (!sws)
+        return NULL;
 
-    if (c) {
-        c->opts.av_class = &ff_sws_context_class;
-        av_opt_set_defaults(c);
-        atomic_init(&c->stride_unaligned_warned, 0);
-        atomic_init(&c->data_unaligned_warned,   0);
+    c = sws->internal = av_mallocz(sizeof(SwsInternal));
+    if (!c) {
+        av_free(sws);
+        return NULL;
     }
 
-    return (SwsContext *) c;
+    sws->av_class = &ff_sws_context_class;
+    av_opt_set_defaults(sws);
+    atomic_init(&c->stride_unaligned_warned, 0);
+    atomic_init(&c->data_unaligned_warned,   0);
+
+    return sws;
 }
 
 static uint16_t * alloc_gamma_tbl(double e)
@@ -1314,6 +1319,7 @@ static enum AVPixelFormat alphaless_fmt(enum AVPixelFormat fmt)
     }
 }
 
+/* Assumes c->opts is already initialized */
 static av_cold int sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter,
                                            SwsFilter *dstFilter)
 {
@@ -1736,7 +1742,7 @@ static av_cold int sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter
                                                         flags, c->opts.scaler_params);
                 if (!c->cascaded_context[0])
                     return AVERROR(EINVAL);
-                sws_internal(c->cascaded_context[0])->opts.alpha_blend = c->opts.alpha_blend;
+                c->cascaded_context[0]->alpha_blend = c->opts.alpha_blend;
                 ret = sws_init_context(c->cascaded_context[0], NULL , NULL);
                 if (ret < 0)
                     return ret;
@@ -1747,8 +1753,8 @@ static av_cold int sws_init_single_context(SwsContext *sws, SwsFilter *srcFilter
                 if (!c->cascaded_context[1])
                     return AVERROR(EINVAL);
 
-                sws_internal(c->cascaded_context[1])->opts.src_range = c->opts.src_range;
-                sws_internal(c->cascaded_context[1])->opts.dst_range = c->opts.dst_range;
+                c->cascaded_context[1]->src_range = c->opts.src_range;
+                c->cascaded_context[1]->dst_range = c->opts.dst_range;
                 ret = sws_init_context(c->cascaded_context[1], srcFilter , dstFilter);
                 if (ret < 0)
                     return ret;
@@ -2057,15 +2063,11 @@ static int context_init_threaded(SwsContext *sws,
         c->slice_ctx[i] = sws_alloc_context();
         if (!c->slice_ctx[i])
             return AVERROR(ENOMEM);
-        c2 = sws_internal(c->slice_ctx[i]);
-
         c->nb_slice_ctx++;
-        c2->parent = sws;
-
-        ret = av_opt_copy((void*)c->slice_ctx[i], (void*)c);
-        if (ret < 0)
-            return ret;
 
+        c2 = sws_internal(c->slice_ctx[i]);
+        c2->parent = sws;
+        c2->opts = c->opts; /* copy initialized opts directly */
         c2->opts.threads = 1;
 
         ret = sws_init_single_context(c->slice_ctx[i], src_filter, dst_filter);
@@ -2090,6 +2092,11 @@ av_cold int sws_init_context(SwsContext *sws, SwsFilter *srcFilter,
     enum AVPixelFormat src_format, dst_format;
     int ret;
 
+    /* Apply options from main context. Note that this also copies the
+     * AVClass, for logging */
+    c->opts = *sws;
+    c->opts.internal = NULL; /* sanity */
+
     c->frame_src = av_frame_alloc();
     c->frame_dst = av_frame_alloc();
     if (!c->frame_src || !c->frame_dst)
@@ -2520,6 +2527,7 @@ void sws_freeContext(SwsContext *sws)
 
     ff_free_filters(c);
 
+    av_free(c);
     av_free(sws);
 }
 
@@ -2533,7 +2541,7 @@ void sws_free_context(SwsContext **pctx)
     *pctx = NULL;
 }
 
-SwsContext *sws_getCachedContext(SwsContext *sws, int srcW,
+SwsContext *sws_getCachedContext(SwsContext *prev, int srcW,
                                  int srcH, enum AVPixelFormat srcFormat,
                                  int dstW, int dstH,
                                  enum AVPixelFormat dstFormat, int flags,
@@ -2541,59 +2549,48 @@ SwsContext *sws_getCachedContext(SwsContext *sws, int srcW,
                                  SwsFilter *dstFilter,
                                  const double *param)
 {
-    SwsInternal *context;
-
+    SwsContext *sws;
     static const double default_param[2] = { SWS_PARAM_DEFAULT,
                                              SWS_PARAM_DEFAULT };
-    int64_t src_h_chr_pos = -513, dst_h_chr_pos = -513,
-            src_v_chr_pos = -513, dst_v_chr_pos = -513;
 
     if (!param)
         param = default_param;
 
-    if ((context = sws_internal(sws)) &&
-        (context->opts.src_w      != srcW      ||
-         context->opts.src_h      != srcH      ||
-         context->opts.src_format != srcFormat ||
-         context->opts.dst_w      != dstW      ||
-         context->opts.dst_h      != dstH      ||
-         context->opts.dst_format != dstFormat ||
-         context->opts.flags      != flags     ||
-         context->opts.scaler_params[0]  != param[0]  ||
-         context->opts.scaler_params[1]  != param[1])) {
-
-        av_opt_get_int(context, "src_h_chr_pos", 0, &src_h_chr_pos);
-        av_opt_get_int(context, "src_v_chr_pos", 0, &src_v_chr_pos);
-        av_opt_get_int(context, "dst_h_chr_pos", 0, &dst_h_chr_pos);
-        av_opt_get_int(context, "dst_v_chr_pos", 0, &dst_v_chr_pos);
-        sws_freeContext(sws);
-        sws = NULL;
-    }
-
-    if (!sws) {
-        if (!(sws = sws_alloc_context()))
-            return NULL;
-        context            = sws_internal(sws);
-        context->opts.src_w      = srcW;
-        context->opts.src_h      = srcH;
-        context->opts.src_format = srcFormat;
-        context->opts.dst_w      = dstW;
-        context->opts.dst_h      = dstH;
-        context->opts.dst_format = dstFormat;
-        context->opts.flags      = flags;
-        context->opts.scaler_params[0]  = param[0];
-        context->opts.scaler_params[1]  = param[1];
-
-        av_opt_set_int(context, "src_h_chr_pos", src_h_chr_pos, 0);
-        av_opt_set_int(context, "src_v_chr_pos", src_v_chr_pos, 0);
-        av_opt_set_int(context, "dst_h_chr_pos", dst_h_chr_pos, 0);
-        av_opt_set_int(context, "dst_v_chr_pos", dst_v_chr_pos, 0);
-
-        if (sws_init_context(sws, srcFilter, dstFilter) < 0) {
-            sws_freeContext(sws);
-            return NULL;
-        }
+    if (prev && (prev->src_w            == srcW      ||
+                 prev->src_h            == srcH      ||
+                 prev->src_format       == srcFormat ||
+                 prev->dst_w            == dstW      ||
+                 prev->dst_h            == dstH      ||
+                 prev->dst_format       == dstFormat ||
+                 prev->flags            == flags     ||
+                 prev->scaler_params[0] == param[0]  ||
+                 prev->scaler_params[1] == param[1])) {
+        return prev;
     }
+
+    if (!(sws = sws_alloc_context())) {
+        sws_free_context(&prev);
+        return NULL;
+    }
+
+    if (prev) {
+        av_opt_copy(sws, prev);
+        sws_free_context(&prev);
+    }
+
+    sws->src_w            = srcW;
+    sws->src_h            = srcH;
+    sws->src_format       = srcFormat;
+    sws->dst_w            = dstW;
+    sws->dst_h            = dstH;
+    sws->dst_format       = dstFormat;
+    sws->flags            = flags;
+    sws->scaler_params[0] = param[0];
+    sws->scaler_params[1] = param[1];
+
+    if (sws_init_context(sws, srcFilter, dstFilter) < 0)
+        sws_free_context(&sws);
+
     return sws;
 }
 
diff --git a/libswscale/x86/output.asm b/libswscale/x86/output.asm
index dec1d27f9a..7a1e5d9bc1 100644
--- a/libswscale/x86/output.asm
+++ b/libswscale/x86/output.asm
@@ -582,7 +582,7 @@ yuv2nv12cX_fn yuv2nv21
 
 %if ARCH_X86_64
 struc SwsInternal
-    .padding:           resb 40316 ; offsetof(SwsInternal, yuv2rgb_y_offset)
+    .padding:           resb 40332 ; offsetof(SwsInternal, yuv2rgb_y_offset)
     .yuv2rgb_y_offset:  resd 1
     .yuv2rgb_y_coeff:   resd 1
     .yuv2rgb_v2r_coeff: resd 1
diff --git a/tests/checkasm/sw_scale.c b/tests/checkasm/sw_scale.c
index 8951ddce43..119bff96d9 100644
--- a/tests/checkasm/sw_scale.c
+++ b/tests/checkasm/sw_scale.c
@@ -124,12 +124,12 @@ static void check_yuv2yuv1(int accurate)
     randomize_buffers((uint8_t*)dither, 8);
     randomize_buffers((uint8_t*)src_pixels, LARGEST_INPUT_SIZE * sizeof(int16_t));
     sws = sws_alloc_context();
-    c = sws_internal(sws);
     if (accurate)
-        c->opts.flags |= SWS_ACCURATE_RND;
+        sws->flags |= SWS_ACCURATE_RND;
     if (sws_init_context(sws, NULL, NULL) < 0)
         fail();
 
+    c = sws_internal(sws);
     ff_sws_init_scale(c);
     for (isi = 0; isi < INPUT_SIZES; ++isi) {
         dstW = input_sizes[isi];
@@ -192,12 +192,12 @@ static void check_yuv2yuvX(int accurate)
     memset(dither, d_val, LARGEST_INPUT_SIZE);
     randomize_buffers((uint8_t*)src_pixels, LARGEST_FILTER * LARGEST_INPUT_SIZE * sizeof(int16_t));
     sws = sws_alloc_context();
-    c = sws_internal(sws);
     if (accurate)
-        c->opts.flags |= SWS_ACCURATE_RND;
+        sws->flags |= SWS_ACCURATE_RND;
     if (sws_init_context(sws, NULL, NULL) < 0)
         fail();
 
+    c = sws_internal(sws);
     ff_sws_init_scale(c);
     for(isi = 0; isi < INPUT_SIZES; ++isi){
         dstW = input_sizes[isi];
-- 
2.46.1



More information about the ffmpeg-devel mailing list