[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