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

Stefano Sabatini stefasab at gmail.com
Tue Oct 1 13:34:28 CEST 2013


On date Tuesday 2013-10-01 01:10:59 +0200, wm4 encoded:
[...] 
> New patch attached. (I didn't run any extensive tests though - because
> it's a new API and nothing uses it.)

What about using it in doc/examples or in ff*.c code?

> From 0dfefdaf1ccb9687a112ab45bb3a0a2bfff3102a Mon Sep 17 00:00:00 2001
> From: wm4 <nfxjfg at googlemail.com>
> Date: Sun, 29 Sep 2013 19:53:09 +0200
> Subject: [PATCH] swscale: add API to convert AVFrames directly
> 
> 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.
> ---
>  libswscale/swscale.h          |  83 ++++++++++++++++
>  libswscale/swscale_internal.h |  10 ++
>  libswscale/utils.c            | 213 +++++++++++++++++++++++++++++++++++++++++-
>  libswscale/version.h          |   2 +-
>  4 files changed, 306 insertions(+), 2 deletions(-)

Note to commmitter: add missing APIchanges entry

> diff --git a/libswscale/swscale.h b/libswscale/swscale.h
> index 42702b7..d617fcb 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.
>   */
> @@ -157,6 +159,8 @@ int sws_isSupportedEndiannessConversion(enum AVPixelFormat pix_fmt);
>   * Allocate an empty SwsContext. This must be filled and passed to
>   * sws_init_context(). For filling see AVOptions, options.c and
>   * sws_setColorspaceDetails().
> + *
> + * Note that sws_scale_frame() will fill the SwsContext itself.
>   */
>  struct SwsContext *sws_alloc_context(void);
>  
> @@ -169,6 +173,84 @@ 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);

nit: weird mixing between camelStyle and snake_style.

> +
> +/**
> + * 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). sws_setColorspaceDetails() is optional as well.
> + *
> + * Some functions (like sws_setColorspaceDetails() and sws_set_src/dst_filter())
> + * are always considered parameter changes.
> + *
> + * @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 does not consider these writable).
> + *
> + * 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, 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(). (You can
> + *          also just modify the AVFrame fields before passing them to this
> + *          function.)
> + *
> + * 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

You should probably enclose this with @code ... @endcode.

> + * Warning: libswscale expects that dst is writable (see av_frame_is_writable()).

duplicated??

> + *
> + * 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);

const?

> +
> +/**
>   * Free the swscaler context swsContext.
>   * If swsContext is NULL, then does nothing.
>   */
> @@ -303,6 +385,7 @@ SwsFilter *sws_getDefaultFilter(float lumaGBlur, float chromaGBlur,
>                                  float lumaSharpen, float chromaSharpen,
>                                  float chromaHShift, float chromaVShift,
>                                  int verbose);
> +SwsFilter *sws_cloneFilter(SwsFilter *filter);

please docs

>  void sws_freeFilter(SwsFilter *filter);
>  
>  /**
> diff --git a/libswscale/swscale_internal.h b/libswscale/swscale_internal.h
> index 33fdfc2..23f0966 100644
> --- a/libswscale/swscale_internal.h
> +++ b/libswscale/swscale_internal.h
> @@ -271,6 +271,16 @@ typedef struct SwsContext {
>      const AVClass *av_class;
>  
>      /**
> +     * All of these are used by sws_reinit_cached_context() only.
> +     */
> +    int force_reinit;
> +    struct SwsContext *previous_settings;
> +    SwsFilter *user_src_filter;
> +    SwsFilter *user_dst_filter;

> +    int user_srcRange;
> +    int user_dstRange;

nit: self-inconsistent style

> +
> +    /**
>       * 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 a2e3ce1..9a1659b 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)
> +{
> +    sws_context->force_reinit = 1;
> +}

pointless wrapper??

> +
>  #define RET 0xC3 // near return opcode for x86
>  
>  typedef struct FormatEntry {
> @@ -964,6 +971,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);
>  
> @@ -1106,6 +1116,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();
> @@ -1964,7 +1976,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) {

missing check on new_filter, will crash in the unlikely case where
av_malloc() failed.

> +        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)
> @@ -2027,7 +2062,19 @@ void sws_freeContext(SwsContext *c)
>  
>      av_freep(&c->yuvTable);
>      av_freep(&c->formatConvBuffer);
> +}
>  
> +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_freep(&c->previous_settings);
>      av_free(c);
>  }
>  
> @@ -2078,3 +2125,167 @@ 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 AVERROR(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 AVERROR(ENOMEM);
> +
> +    sws_freeFilter(sws_context->user_dst_filter);
> +    sws_context->user_dst_filter = new_filter;
> +
> +    sws_flush_cache(sws_context);
> +    return 0;
> +}

not happy about code duplication, what about a macro or a common
routine?

> +

> +static int compare_csp_table(int table1[4], int table2[4])

nit: csp -> colorspace for readability

> +{
> +    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 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 &&
> +           compare_csp_table(new->srcColorspaceTable, old->srcColorspaceTable) &&
> +           compare_csp_table(new->dstColorspaceTable, old->dstColorspaceTable);
> +}
> +
> +// Does a shallow copy of all fields that are user settings.
> +static void copy_settings(struct SwsContext *dst, struct SwsContext *src)
> +{
> +    dst->user_src_filter = src->user_src_filter;
> +    dst->user_dst_filter = src->user_dst_filter;
> +    dst->flags           = src->flags;
> +    dst->srcW            = src->srcW;
> +    dst->srcH            = src->srcH;
> +    dst->dstW            = src->dstW;
> +    dst->dstH            = src->dstH;
> +    dst->srcFormat       = src->srcFormat;
> +    dst->dstFormat       = src->dstFormat;
> +    dst->srcRange        = src->srcRange;
> +    dst->dstRange        = src->dstRange;
> +    dst->param[0]        = src->param[0];
> +    dst->param[1]        = src->param[1];
> +    dst->src_v_chr_pos   = src->src_v_chr_pos;
> +    dst->src_h_chr_pos   = src->src_h_chr_pos;
> +    dst->dst_v_chr_pos   = src->dst_v_chr_pos;
> +    dst->dst_h_chr_pos   = src->dst_h_chr_pos;
> +    dst->dither          = src->dither;
> +    dst->brightness      = src->brightness;
> +    dst->contrast        = src->contrast;
> +    dst->saturation      = src->saturation;
> +    memcpy(dst->srcColorspaceTable, src->srcColorspaceTable, sizeof(int) * 4);
> +    memcpy(dst->dstColorspaceTable, src->dstColorspaceTable, sizeof(int) * 4);
> +}
> +
> +int sws_reinit_cached_context(struct SwsContext *c)
> +{

> +    int r = 0;

nit+++: ret

> +    if (!c->previous_settings) {
> +        c->force_reinit = 1;
> +        if (!(c->previous_settings = av_malloc(sizeof(struct SwsContext))))
> +            return AVERROR(ENOMEM);
> +    }
> +    if (!c->contrast && !c->saturation) {
> +        c->contrast = 1 << 16;
> +        c->saturation = 1 << 16;
> +    }
> +    if (c->force_reinit || !can_reuse(c, c->previous_settings)) {
> +        struct SwsContext *previous_settings;
> +        previous_settings = c->previous_settings;
> +        *previous_settings = *c;
> +        sws_uninit_context(c);
> +        memset(c, 0, sizeof(*c));
> +        c->av_class = &sws_context_class;
> +        copy_settings(c, previous_settings);
> +        c->previous_settings = previous_settings;
> +        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)
> +            return r;
> +        c->force_reinit = 0;
> +    }
> +    return r;
> +}
> +
> +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);

why the intermediary swscsp variable?

Also I'd prefer to avoid the reference to scale.

> +    for (i = 0; i < 4; i++)
> +        table[i] = sws_table[i];
> +}
> +

> +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 == AVCOL_RANGE_JPEG;
> +    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 == AVCOL_RANGE_JPEG;
> +    sws_set_avframe_colorspace_table(c->dstColorspaceTable, dst->colorspace);
> +}

Could be probably macroized/factored, and merged with sws_set_avframe_colorspace_table().

[...]
-- 
FFmpeg = Frightening and Foolish Minimal Peaceful Extensive God


More information about the ffmpeg-devel mailing list