[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