[FFmpeg-devel] [PATCH] swscale: add API to convert AVFrames directly

wm4 nfxjfg at googlemail.com
Sun Sep 29 16:53:02 CEST 2013


See sws_scale_frame() and the example in its doxygen comment.

This makes the libswscale API considerably easier to use. Various
settings are automatically initialized from AVFrame settings, such as
format, size, color space and color range parameters.

To make the API easier to use, also don't require setting the sws_flags
AVOptions, and default to SWS_BILINEAR scaling.
---
 libswscale/options.c          |   2 +-
 libswscale/swscale.h          |  78 +++++++++++++++++++
 libswscale/swscale_internal.h |   7 ++
 libswscale/utils.c            | 174 +++++++++++++++++++++++++++++++++++++++++-
 libswscale/version.h          |   2 +-
 5 files changed, 260 insertions(+), 3 deletions(-)

diff --git a/libswscale/options.c b/libswscale/options.c
index 8985e6b..0f7955a 100644
--- a/libswscale/options.c
+++ b/libswscale/options.c
@@ -34,7 +34,7 @@ static const char *sws_context_to_name(void *ptr)
 #define VE AV_OPT_FLAG_VIDEO_PARAM | AV_OPT_FLAG_ENCODING_PARAM
 
 static const AVOption swscale_options[] = {
-    { "sws_flags",       "scaler flags",                  OFFSET(flags),     AV_OPT_TYPE_FLAGS,  { .i64 = DEFAULT            }, 0,       UINT_MAX,       VE, "sws_flags" },
+    { "sws_flags",       "scaler flags",                  OFFSET(flags),     AV_OPT_TYPE_FLAGS,  { .i64 = SWS_BILINEAR            }, 0,       UINT_MAX,       VE, "sws_flags" },
     { "fast_bilinear",   "fast bilinear",                 0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_FAST_BILINEAR  }, INT_MIN, INT_MAX,        VE, "sws_flags" },
     { "bilinear",        "bilinear",                      0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_BILINEAR       }, INT_MIN, INT_MAX,        VE, "sws_flags" },
     { "bicubic",         "bicubic",                       0,                 AV_OPT_TYPE_CONST,  { .i64  = SWS_BICUBIC        }, INT_MIN, INT_MAX,        VE, "sws_flags" },
diff --git a/libswscale/swscale.h b/libswscale/swscale.h
index 42702b7..60733f0 100644
--- a/libswscale/swscale.h
+++ b/libswscale/swscale.h
@@ -39,6 +39,8 @@
 #include "libavutil/pixfmt.h"
 #include "version.h"
 
+struct AVFrame;
+
 /**
  * Return the LIBSWSCALE_VERSION_INT constant.
  */
@@ -169,6 +171,81 @@ struct SwsContext *sws_alloc_context(void);
 int sws_init_context(struct SwsContext *sws_context, SwsFilter *srcFilter, SwsFilter *dstFilter);
 
 /**
+ * Set source filter. This works only with sws_reinit_cached_context() and
+ * sws_scale_frame().
+ *
+ * @return zero or positive value on success, a negative value on
+ * error
+ */
+int sws_set_src_filter(struct SwsContext *sws_context, SwsFilter *srcFilter);
+
+/**
+ * Set destination filter. This works only with sws_reinit_cached_context() and
+ * sws_scale_frame().
+ *
+ * @return zero or positive value on success, a negative value on
+ * error
+ */
+int sws_set_dst_filter(struct SwsContext *sws_context, SwsFilter *dstFilter);
+
+/**
+ * Check for parameter changes, and reinitialize the swscale context if needed.
+ * You should call this before each sws_scale() call, unless you know for sure
+ * you did not change any swscale parameters since the last sws_scale() call.
+ *
+ * Note that you don't need to call sws_init_context() with this function
+ * (although it is possible).
+ *
+ * @return zero or positive value on success, a negative value on
+ * error
+ */
+int sws_reinit_cached_context(struct SwsContext *sws_context);
+
+/**
+ * Scale and convert image data in src to the dimension and image parameters
+ * in dst. You must initialize dst and allocate image data before calling this
+ * function. In particular, you must set the image format and image dimensions
+ * on dst prior to calling this function.
+ *
+ * Warning: libswscale expects that dst is writable (see av_frame_is_writable()).
+ *          The reason this is not done automatically is that this would not
+ *          allow the user to provide a static destination buffer (as the
+ *          AVFrame API expects
+ *
+ * Warning: this will transparently reinitialize the sws context, and overwrite
+ *          some swscale options according to AVFrame. This includes settings
+ *          like source/destination image dimensions, pixel format, color space,
+ *          color range, chroma position, and possibly more. Should AVFrame be
+ *          extended to carry more image parameters in the future, this
+ *          function might be updated to include them as well. This is usually
+ *          convenient and the right thing to do, but if you do not want
+ *          libswscale to overwrite your settings, use the low-level API and
+ *          not sws_scale_frame().
+ *
+ * Example how to scale a given "src" image to 2x the size and 8 bit 4:2:0 YCbCr:
+ *
+ *      AVFrame *src = ...;
+ *      SwsContext *sws = sws_alloc_context();
+ *      AVFrame *dst = av_frame_alloc();
+ *      if (!dst) goto error;
+ *      dst->format = AV_PIX_FMT_YUV420P;
+ *      dst->width = src->width * 2;
+ *      dst->height = src->height * 2;
+ *      if (av_frame_copy_props(dst, src) < 0) goto error; // don't change anything else
+ *      if (av_frame_get_buffer(dst, 32) < 0) goto error; // allocate image
+ *      if (sws_scale_frame(sws, dst, src) < 0) goto error;
+ *      // dst now contains the scaled image data
+ *
+ * The SwsContext can be reused when converting the next frame. If the AVFrame
+ * parameters are different, libswscale is automatically reinitialized.
+ *
+ * @return zero or positive value on success, a negative value on
+ * error
+ */
+int sws_scale_frame(struct SwsContext *sws_context, struct AVFrame *dst,
+                    struct AVFrame *src);
+
+/**
  * Free the swscaler context swsContext.
  * If swsContext is NULL, then does nothing.
  */
@@ -303,6 +380,7 @@ SwsFilter *sws_getDefaultFilter(float lumaGBlur, float chromaGBlur,
                                 float lumaSharpen, float chromaSharpen,
                                 float chromaHShift, float chromaVShift,
                                 int verbose);
+SwsFilter *sws_cloneFilter(SwsFilter *filter);
 void sws_freeFilter(SwsFilter *filter);
 
 /**
diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h
index 33fdfc2..5c4c9f1 100644
--- a/libswscale/swscale_internal.h
+++ b/libswscale/swscale_internal.h
@@ -271,6 +271,13 @@ typedef struct SwsContext {
     const AVClass *av_class;
 
     /**
+     * All of these are used by sws_reinit_cached_context() only.
+     */
+    struct SwsContext *previous_settings; ///< Used to detect changes using sws_reinit_cached_context()
+    SwsFilter *user_src_filter;
+    SwsFilter *user_dst_filter;
+
+    /**
      * Note that src, dst, srcStride, dstStride will be copied in the
      * sws_scale() wrapper so they can be freely modified here.
      */
diff --git a/libswscale/utils.c b/libswscale/utils.c
index 05ab0c6..5a88f47 100644
--- a/libswscale/utils.c
+++ b/libswscale/utils.c
@@ -46,6 +46,7 @@
 #include "libavutil/mathematics.h"
 #include "libavutil/opt.h"
 #include "libavutil/pixdesc.h"
+#include "libavutil/frame.h"
 #include "libavutil/ppc/cpu.h"
 #include "libavutil/x86/asm.h"
 #include "libavutil/x86/cpu.h"
@@ -72,6 +73,12 @@ const char *swscale_license(void)
     return LICENSE_PREFIX FFMPEG_LICENSE + sizeof(LICENSE_PREFIX) - 1;
 }
 
+// Mark SwsContext in need for reinitialization.
+static void sws_flush_cache(struct SwsContext *sws_context)
+{
+    av_freep(&sws_context->previous_settings);
+}
+
 #define RET 0xC3 // near return opcode for x86
 
 typedef struct FormatEntry {
@@ -960,6 +967,9 @@ int sws_setColorspaceDetails(struct SwsContext *c, const int inv_table[4],
 {
     const AVPixFmtDescriptor *desc_dst;
     const AVPixFmtDescriptor *desc_src;
+
+    sws_flush_cache(c);
+
     memmove(c->srcColorspaceTable, inv_table, sizeof(int) * 4);
     memmove(c->dstColorspaceTable, table, sizeof(int) * 4);
 
@@ -1079,6 +1089,8 @@ SwsContext *sws_alloc_context(void)
     if (c) {
         c->av_class = &sws_context_class;
         av_opt_set_defaults(c);
+        c->contrast = 1 << 16;
+        c->saturation = 1 << 16;
     }
 
     return c;
@@ -1102,6 +1114,8 @@ av_cold int sws_init_context(SwsContext *c, SwsFilter *srcFilter,
     const AVPixFmtDescriptor *desc_src;
     const AVPixFmtDescriptor *desc_dst;
 
+    sws_flush_cache(c);
+
     cpu_flags = av_get_cpu_flags();
     flags     = c->flags;
     emms_c();
@@ -1960,7 +1974,30 @@ void sws_freeFilter(SwsFilter *filter)
     av_free(filter);
 }
 
-void sws_freeContext(SwsContext *c)
+SwsFilter *sws_cloneFilter(SwsFilter *filter)
+{
+    SwsFilter *new_filter;
+    if (!filter)
+        return NULL;
+
+    new_filter = av_malloc(sizeof(SwsFilter));
+    new_filter->lumH = sws_cloneVec(filter->lumH);
+    new_filter->lumV = sws_cloneVec(filter->lumV);
+    new_filter->chrH = sws_cloneVec(filter->chrH);
+    new_filter->chrV = sws_cloneVec(filter->chrV);
+
+    if (!new_filter->lumH || !new_filter->lumV ||
+        !new_filter->chrH || !new_filter->chrV) {
+        sws_freeFilter(new_filter);
+        return NULL;
+    }
+
+    return new_filter;
+}
+
+// Free and reset almost everything in the SwsContext, but do not wipe user
+// settings.
+static void sws_uninit_context(SwsContext *c)
 {
     int i;
     if (!c)
@@ -2024,6 +2061,19 @@ void sws_freeContext(SwsContext *c)
     av_freep(&c->yuvTable);
     av_freep(&c->formatConvBuffer);
 
+    av_freep(&c->previous_settings);
+}
+
+void sws_freeContext(SwsContext *c)
+{
+    if (!c)
+        return;
+
+    sws_uninit_context(c);
+    sws_freeFilter(c->user_src_filter);
+    sws_freeFilter(c->user_dst_filter);
+    c->user_src_filter = NULL;
+    c->user_dst_filter = NULL;
     av_free(c);
 }
 
@@ -2074,3 +2124,125 @@ struct SwsContext *sws_getCachedContext(struct SwsContext *context, int srcW,
     }
     return context;
 }
+
+int sws_set_src_filter(struct SwsContext *sws_context, SwsFilter *srcFilter)
+{
+    SwsFilter *new_filter = sws_cloneFilter(srcFilter);
+    if (srcFilter && !new_filter)
+        return ENOMEM;
+
+    sws_freeFilter(sws_context->user_src_filter);
+    sws_context->user_src_filter = new_filter;
+
+    sws_flush_cache(sws_context);
+    return 0;
+}
+
+int sws_set_dst_filter(struct SwsContext *sws_context, SwsFilter *dstFilter)
+{
+    SwsFilter *new_filter = sws_cloneFilter(dstFilter);
+    if (dstFilter && !new_filter)
+        return ENOMEM;
+
+    sws_freeFilter(sws_context->user_dst_filter);
+    sws_context->user_dst_filter = new_filter;
+
+    sws_flush_cache(sws_context);
+    return 0;
+}
+
+static void sws_set_avframe_colorspace_table(int table[4],
+                                             enum AVColorSpace colorspace)
+{
+    int i;
+    int swscsp = colorspace; // this is libavfilter/vf_scale.c's dirty secret
+    const int *sws_table = sws_getCoefficients(swscsp);
+    for (i = 0; i < 4; i++)
+        table[i] = sws_table[i];
+}
+
+static int sws_compare_csp_table(int table1[4], int table2[4])
+{
+    int i;
+    for (i = 0; i < 4; i++) {
+        if (table1[i] != table2[i])
+            return 0;
+    }
+    return 1;
+}
+
+// Return 1 if nothing changed between new and old, and 0 if it did.
+// This checks user settings only.
+static int sws_can_reuse(struct SwsContext *new, struct SwsContext *old)
+{
+    return new->flags           == old->flags &&         // swscale_options
+           new->srcW            == old->srcW &&
+           new->srcH            == old->srcH &&
+           new->dstW            == old->dstW &&
+           new->dstH            == old->dstH &&
+           new->srcFormat       == old->srcFormat &&
+           new->dstFormat       == old->dstFormat &&
+           new->srcRange        == old->srcRange &&
+           new->dstRange        == old->dstRange &&
+           new->param[0]        == old->param[0] &&
+           new->param[1]        == old->param[1] &&
+           new->src_v_chr_pos   == old->src_v_chr_pos &&
+           new->src_h_chr_pos   == old->src_h_chr_pos &&
+           new->dst_v_chr_pos   == old->dst_v_chr_pos &&
+           new->dst_h_chr_pos   == old->dst_h_chr_pos &&
+           new->dither          == old->dither &&
+           new->brightness      == old->brightness &&    // sws_setColorspaceDetails
+           new->contrast        == old->contrast &&
+           new->saturation      == old->saturation &&
+           sws_compare_csp_table(new->srcColorspaceTable, old->srcColorspaceTable) &&
+           sws_compare_csp_table(new->dstColorspaceTable, old->dstColorspaceTable);
+}
+
+int sws_reinit_cached_context(struct SwsContext *c)
+{
+    int r = 0;
+    if (!c->previous_settings || !sws_can_reuse(c, c->previous_settings)) {
+        sws_uninit_context(c);
+        sws_setColorspaceDetails(c, c->srcColorspaceTable, c->srcRange,
+                                 c->dstColorspaceTable, c->dstRange,
+                                 c->brightness, c->contrast, c->saturation);
+        r = sws_init_context(c, c->user_src_filter, c->user_dst_filter);
+        if (r >= 0) {
+            c->previous_settings = av_malloc(sizeof(struct SwsContext));
+            if (c->previous_settings)
+                *c->previous_settings = *c;
+        }
+    }
+    return r;
+}
+
+static void sws_set_src_frame_options(struct SwsContext *c, struct AVFrame *src)
+{
+    c->srcFormat = src->format;
+    c->srcW      = src->width;
+    c->srcH      = src->height;
+    c->srcRange  = src->color_range;
+    sws_set_avframe_colorspace_table(c->srcColorspaceTable, src->colorspace);
+}
+
+static void sws_set_dst_frame_options(struct SwsContext *c, struct AVFrame *dst)
+{
+    c->dstFormat = dst->format;
+    c->dstW      = dst->width;
+    c->dstH      = dst->height;
+    c->dstRange  = dst->color_range;
+    sws_set_avframe_colorspace_table(c->dstColorspaceTable, dst->colorspace);
+}
+
+int sws_scale_frame(struct SwsContext *sws_context, struct AVFrame *dst,
+                    struct AVFrame *src)
+{
+    int r;
+    sws_set_src_frame_options(sws_context, src);
+    sws_set_dst_frame_options(sws_context, dst);
+    if ((r = sws_reinit_cached_context(sws_context)) < 0)
+        return r;
+    r = sws_scale(sws_context, (const uint8_t *const *)src->data, src->linesize,
+                  0, src->height, dst->data, dst->linesize);
+    return r;
+}
diff --git a/libswscale/version.h b/libswscale/version.h
index 06f119a..72c6074 100644
--- a/libswscale/version.h
+++ b/libswscale/version.h
@@ -27,7 +27,7 @@
 #include "libavutil/avutil.h"
 
 #define LIBSWSCALE_VERSION_MAJOR 2
-#define LIBSWSCALE_VERSION_MINOR 5
+#define LIBSWSCALE_VERSION_MINOR 6
 #define LIBSWSCALE_VERSION_MICRO 100
 
 #define LIBSWSCALE_VERSION_INT  AV_VERSION_INT(LIBSWSCALE_VERSION_MAJOR, \
-- 
1.8.4.rc3



More information about the ffmpeg-devel mailing list