[FFmpeg-devel] [PATCH] rotate filter

Clément Bœsch ubitux at gmail.com
Wed Jun 12 09:56:36 CEST 2013


On Tue, Jun 11, 2013 at 03:12:31PM +0200, Stefano Sabatini wrote:
[..]
> From e92156a05861c55699e4460fc1b9df1204dc7171 Mon Sep 17 00:00:00 2001
> From: Stefano Sabatini <stefasab at gmail.com>
> Date: Tue, 11 Jun 2013 10:31:59 +0200
> Subject: [PATCH] lavfi: add rotate filter
> 
> Based on the libavfilter SOC filter by Vitor Sessak, with the following additions:
> * integer arithmetic
> * bilinear interpolation
> * RGB path
> * configurable parametric angle, output width and height
> 
> Address trac issue #1500.
> ---
>  doc/filters.texi         |  111 +++++++++++
>  libavfilter/Makefile     |    1 +
>  libavfilter/allfilters.c |    1 +
>  libavfilter/vf_rotate.c  |  456 ++++++++++++++++++++++++++++++++++++++++++++++
>  4 files changed, 569 insertions(+)
>  create mode 100644 libavfilter/vf_rotate.c
> 
> diff --git a/doc/filters.texi b/doc/filters.texi
> index 4cb6710..7e8fff9 100644
> --- a/doc/filters.texi
> +++ b/doc/filters.texi
> @@ -5741,6 +5741,117 @@ pp=hb|y/vb|a
>  @end example
>  @end itemize
>  
> + at section rotate
> +

> +Rotate video by a chosen amount in degrees. By default, 45 degrees.

chosen → arbitrary?

"chosen" sounds like a selection between a number of choice; at first I
thought it was a transpose like filter.

> +
> +The filter accepts the following options:
> +
> +A description of the optional parameters follows.
> + at table @option
> + at item angle, a
> +Set an expression for the angle by which to rotate the input video
> +clockwise, expressed in radians degrees.

radians or degrees?

>                                           A negative value will result in
> +a counter-clockwise rotation. By default it is set to "0".
> +

> +This expression is evaluated for each frame.

This is going to be fun... You spin me right round baby right round~

> +
> + at item out_w, ow
> +Set the output width expression, default value is "rotw(a)".
> +This expression is evaluated just once during configuration.
> +
> + at item out_h, oh
> +Set the output height expression, default value is "roth(a)".
> +This expression is evaluated just once during configuration.
> +
> + at item bilinear
> +Enable bilinear interpolation if set to 1, a value of 0 disables
> +it. Default value is 1.
> +
> + at item fillcolor, c
> +Set the color used to fill the output area not covered by the rotated
> +image. If the special value "none" is selected then no background is
> +printed (useful for example if the background is never shown). Default
> +value is "black".
> + at end table
> +
> +The expressions for the angle and the output size can contain the
> +following constants and functions:
> +
> + at table @option
> + at item n
> +sequential number of the input frame, starting from 0
> +
> + at item t
> +time in seconds of the input frame, it is set to 0 when the filter is
> +configured
> +
> + at item hsub
> + at item vsub
> +horizontal and vertical chroma subsample values. For example for the
> +pixel format "yuv422p" @var{hsub} is 2 and @var{vsub} is 1.
> +
> + at item in_w, iw
> + at tiem in_h, ih
> +the input video width and heigth
> +
> + at item out_w, ow
> + at item out_h, oh
> +the output width and heigth, that is the size of the padded area as
> +specified by the @var{width} and @var{height} expressions
> +
> + at item rotw(a)
> +the minimal width required for completely containing the input video
> +rotated by @var{a} degress
> +
> + at item roth(a)
> +the minimal height required for completely containing the input video
> +rotated by @var{a} degress
> + at end table
> +
> + at section Examples
> +
> + at itemize
> + at item
> +Rotate the input by PI/6 degrees clockwise:
> + at example
> +rotate=PI/6
> + at end example
> +
> + at item
> +Rotate the input by PI/6 degrees counter-clockwise:
> + at example
> +rotate=-PI/6
> + at end example
> +
> + at item

> +Apply a constant rotation with period $var{T}, starting from an angle of PI/3:

$ → @

Make sure the rest of the HTML output is OK.

> + at example
> +rotate=PI/3+2*PI*t/@var{T}
> + at end example
> +
> + at item
> +Make the input video rotation oscillating with a period of @var{T}
> +seconds and an amplitude of @var{A} degrees:
> + at example
> +rotate=@var{A}*sin(2*PI/@var{T}*t)
> + at end example
> +
> + at item
> +Rotate the video, output size is choosen so that the whole rotating
> +input video is always completely contained in the output:
> + at example

> +rotate=2*PI*t:ow=sqrt(iw*iw+ih*ih):oh=ow

hypot()

> + at end example
> +
> + at item
> +Rotate the video, reduce the output size so that no background is ever
> +shown:
> + at example
> +rotate=2*PI*t:ow='min(iw,ih)/sqrt(2)':oh=ow
> + at end example
> + at end itemize
> +
>  @section removelogo
>  
>  Suppress a TV station logo, using an image file to determine which
> diff --git a/libavfilter/Makefile b/libavfilter/Makefile
> index 2d2ea45..9746bbb 100644
> --- a/libavfilter/Makefile
> +++ b/libavfilter/Makefile
> @@ -168,6 +168,7 @@ OBJS-$(CONFIG_PERMS_FILTER)                  += f_perms.o
>  OBJS-$(CONFIG_PIXDESCTEST_FILTER)            += vf_pixdesctest.o
>  OBJS-$(CONFIG_PP_FILTER)                     += vf_pp.o
>  OBJS-$(CONFIG_REMOVELOGO_FILTER)             += bbox.o lswsutils.o lavfutils.o vf_removelogo.o
> +OBJS-$(CONFIG_ROTATE_FILTER)                 += vf_rotate.o
>  OBJS-$(CONFIG_SEPARATEFIELDS_FILTER)         += vf_separatefields.o
>  OBJS-$(CONFIG_SAB_FILTER)                    += vf_sab.o
>  OBJS-$(CONFIG_SCALE_FILTER)                  += vf_scale.o
> diff --git a/libavfilter/allfilters.c b/libavfilter/allfilters.c
> index f9d9391..e802601 100644
> --- a/libavfilter/allfilters.c
> +++ b/libavfilter/allfilters.c
> @@ -163,6 +163,7 @@ void avfilter_register_all(void)
>      REGISTER_FILTER(PIXDESCTEST,    pixdesctest,    vf);
>      REGISTER_FILTER(PP,             pp,             vf);
>      REGISTER_FILTER(REMOVELOGO,     removelogo,     vf);
> +    REGISTER_FILTER(ROTATE,         rotate,         vf);
>      REGISTER_FILTER(SAB,            sab,            vf);
>      REGISTER_FILTER(SCALE,          scale,          vf);
>      REGISTER_FILTER(SELECT,         select,         vf);
> diff --git a/libavfilter/vf_rotate.c b/libavfilter/vf_rotate.c
> new file mode 100644
> index 0000000..6a2c33f
> --- /dev/null
> +++ b/libavfilter/vf_rotate.c
> @@ -0,0 +1,456 @@
> +/*
> + * Copyright (c) 2013 Stefano Sabatini
> + * Copyright (c) 2008 Vitor Sessak
> + *
> + * This file is part of FFmpeg.
> + *
> + * FFmpeg is free software; you can redistribute it and/or
> + * modify it under the terms of the GNU Lesser General Public
> + * License as published by the Free Software Foundation; either
> + * version 2.1 of the License, or (at your option) any later version.
> + *
> + * FFmpeg is distributed in the hope that it will be useful,
> + * but WITHOUT ANY WARRANTY; without even the implied warranty of
> + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
> + * Lesser General Public License for more details.
> + *
> + * You should have received a copy of the GNU Lesser General Public
> + * License along with FFmpeg; if not, write to the Free Software
> + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
> + */
> +
> +/**
> + * @file
> + * rotation filter, partially based on the tests/rotozoom.c program
> +*/
> +
> +#include "libavutil/avstring.h"
> +#include "libavutil/eval.h"
> +#include "libavutil/opt.h"
> +#include "libavutil/intreadwrite.h"
> +#include "libavutil/parseutils.h"
> +#include "libavutil/pixdesc.h"
> +
> +#include "avfilter.h"
> +#include "drawutils.h"
> +#include "internal.h"
> +#include "video.h"
> +
> +static const char *var_names[] = {
> +    "in_w" , "iw",  ///< width   of the input video
> +    "in_h" , "ih",  ///< height  of the input video
                                 ^^^^^^
> +    "out_w", "ow",  ///< width of the input video
> +    "out_h", "oh",  ///< height of the input video
                                 ^^^^^^
                          You should be consistent
                          with the spacing

> +    "hsub", "vsub",
> +    "a",
> +    "n",            ///< number of frame
> +    "t",            ///< timestamp expressed in seconds
> +    NULL
> +};
> +
> +enum var_name {
> +    VAR_IN_W , VAR_IW,
> +    VAR_IN_H , VAR_IH,
> +    VAR_OUT_W, VAR_OW,
> +    VAR_OUT_H, VAR_OH,
> +    VAR_HSUB, VAR_VSUB,
> +    VAR_A,
> +    VAR_N,
> +    VAR_T,
> +    VAR_VARS_NB
> +};
> +
> +typedef struct {
> +    const AVClass *class;
> +    float angle;
> +    char *angle_expr_str;   ///< expression for the angle
> +    AVExpr *angle_expr;     ///< parsed expression for the angle
> +    char *outw_expr_str, *outh_expr_str;
> +    int outh, outw;
> +    uint8_t fillcolor[4];   ///< color expressed either in YUVA or RGBA colorspace for the padding area
> +    char *fillcolor_str;
> +    int fillcolor_enable;
> +    int hsub, vsub;
> +    int nb_planes;
> +    int use_bilinear;
> +    uint8_t *line[4];
> +    int linestep[4];
> +    float sinx, cosx;
> +    double var_values[VAR_VARS_NB];
> +} RotContext;
> +
> +#define OFFSET(x) offsetof(RotContext, x)
> +#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
> +
> +static const AVOption rotate_options[] = {
> +    { "angle",     "set angle (in radians)",       OFFSET(angle_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS },
> +    { "a",         "set angle (in radians)",       OFFSET(angle_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS },
> +    { "out_w",     "set output width expression",  OFFSET(outw_expr_str), AV_OPT_TYPE_STRING, {.str="rotw(a)"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS },
> +    { "ow",        "set output width expression",  OFFSET(outw_expr_str), AV_OPT_TYPE_STRING, {.str="rotw(a)"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS },
> +    { "out_h",     "set output height expression", OFFSET(outh_expr_str), AV_OPT_TYPE_STRING, {.str="roth(a)"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS },
> +    { "oh",        "set output width expression",  OFFSET(outh_expr_str), AV_OPT_TYPE_STRING, {.str="roth(a)"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS },
> +    { "fillcolor", "set background fill color",    OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS },
> +    { "c",         "set background fill color",    OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS },
> +    { "bilinear",  "use bilinear interpolation",   OFFSET(use_bilinear),  AV_OPT_TYPE_INT, {.i64=1}, 0, 1, .flags=FLAGS },
> +    { NULL }
> +};
> +
> +AVFILTER_DEFINE_CLASS(rotate);
> +
> +static av_cold int init(AVFilterContext *ctx)
> +{
> +    RotContext *rot = ctx->priv;
> +
> +    if (!strcmp(rot->fillcolor_str, "none"))
> +        rot->fillcolor_enable = 0;
> +    else if (av_parse_color(rot->fillcolor, rot->fillcolor_str, -1, ctx) >= 0)
> +        rot->fillcolor_enable = 1;
> +    else
> +        return AVERROR(EINVAL);
> +    return 0;
> +}
> +
> +static av_cold void uninit(AVFilterContext *ctx)
> +{
> +    RotContext *rot = ctx->priv;
> +    int i;
> +
> +    for (i = 0; i < 4; i++) {
> +        av_freep(&rot->line[i]);
> +        rot->linestep[i] = 0;
> +    }
> +
> +    av_expr_free(rot->angle_expr);
> +    rot->angle_expr = NULL;
> +}
> +
> +static int query_formats(AVFilterContext *ctx)
> +{
> +    static enum PixelFormat pix_fmts[] = {
> +        PIX_FMT_ARGB,      PIX_FMT_RGBA,
> +        PIX_FMT_ABGR,      PIX_FMT_BGRA,
> +        PIX_FMT_RGB24,     PIX_FMT_BGR24,
> +        PIX_FMT_GRAY8,
> +        PIX_FMT_YUV444P,   PIX_FMT_YUVJ444P,
> +        PIX_FMT_YUV420P,   PIX_FMT_YUVJ444P,
> +        PIX_FMT_YUVA420P,  PIX_FMT_YUVJ444P,
> +        PIX_FMT_YUVJ420P,
> +        PIX_FMT_NONE
> +    };
> +
> +    ff_set_common_formats(ctx, ff_make_format_list(pix_fmts));
> +    return 0;
> +}
> +
> +static double get_rotated_w(void *opaque, double angle)
> +{
> +    RotContext *rot = opaque;
> +    double inw = rot->var_values[VAR_IN_W];
> +    double inh = rot->var_values[VAR_IN_H];
> +    float sinx = sin(angle);
> +    float cosx = cos(angle);
> +
> +    return FFMAX(0, inh * sinx) + FFMAX(0, -inw * cosx) +
> +           FFMAX(0, inw * cosx) + FFMAX(0, -inh * sinx);
> +}
> +
> +static double get_rotated_h(void *opaque, double angle)
> +{
> +    RotContext *rot = opaque;
> +    double inw = rot->var_values[VAR_IN_W];
> +    double inh = rot->var_values[VAR_IN_H];
> +    float sinx = sin(angle);
> +    float cosx = cos(angle);
> +
> +    return FFMAX(0, -inh * cosx) + FFMAX(0, -inw * sinx) +
> +           FFMAX(0,  inh * cosx) + FFMAX(0,  inw * sinx);
> +}
> +
> +static double (* const func1[])(void *, double) = {

> +    (void *)get_rotated_w,
> +    (void *)get_rotated_h,

Unless I'm missing something, the cast should not be needed, or there is
something wrong.

> +    NULL
> +};
> +
> +static const char * const func1_names[] = {
> +    "rotw",
> +    "roth",
> +    NULL
> +};
> +
> +static int config_props(AVFilterLink *outlink)
> +{
> +    AVFilterContext *ctx = outlink->src;
> +    RotContext *rot = ctx->priv;
> +    AVFilterLink *inlink = ctx->inputs[0];
> +    const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(inlink->format);
> +    uint8_t rgba_color[4];
> +    int i, is_packed_rgba, ret;
> +    double res;
> +    char *expr;
> +
> +    rot->hsub = pixdesc->log2_chroma_w;
> +    rot->vsub = pixdesc->log2_chroma_h;
> +
> +    rot->var_values[VAR_IN_W]  = rot->var_values[VAR_IW] = inlink->w;
> +    rot->var_values[VAR_IN_H]  = rot->var_values[VAR_IH] = inlink->h;
> +    rot->var_values[VAR_HSUB]  = 1<<rot->hsub;
> +    rot->var_values[VAR_VSUB]  = 1<<rot->vsub;
> +    rot->var_values[VAR_N]     = 0;
> +    rot->var_values[VAR_T]     = 0;
> +    rot->var_values[VAR_A]     = NAN;
> +    rot->var_values[VAR_OUT_W] = rot->var_values[VAR_OW] = NAN;
> +    rot->var_values[VAR_OUT_H] = rot->var_values[VAR_OH] = NAN;
> +
> +    if ((ret = av_expr_parse(&rot->angle_expr, rot->angle_expr_str, var_names,

(expr = rot->angle_expr_str) and goto eval_fail?

> +                             func1_names, func1, NULL, NULL, 0, ctx)) < 0) {
> +        av_log(ctx, AV_LOG_ERROR,
> +               "Error occurred parsing expression '%s'\n", rot->angle_expr_str);
> +        return ret;
> +    }
> +
> +    res = av_expr_eval(rot->angle_expr, rot->var_values, NULL);
> +    if (isnan(res)) {
> +        av_log(ctx, AV_LOG_ERROR,

> +               "Invalid angle expression '%s', evaluates to nan.\n",

nit: NaN

> +               rot->angle_expr_str);
> +        return AVERROR(EINVAL);
> +    }
> +    rot->angle = rot->var_values[VAR_A] = res;
> +
> +    /* evaluate width and height */
> +    av_expr_parse_and_eval(&res, (expr = rot->outw_expr_str),
> +                           var_names, rot->var_values,
> +                           func1_names, func1, NULL, NULL, rot, 0, ctx);
> +    rot->var_values[VAR_OUT_W] = rot->var_values[VAR_OW] = res;
> +    rot->outw = res + 0.5;
> +    if ((ret = av_expr_parse_and_eval(&res, (expr = rot->outh_expr_str),
> +                                      var_names, rot->var_values,
> +                                      func1_names, func1, NULL, NULL, rot, 0, ctx)) < 0)
> +        goto eval_fail;
> +    rot->var_values[VAR_OUT_H] = rot->var_values[VAR_OH] = res;
> +    rot->outh = res + 0.5;
> +    /* evaluate the width again, as it may depend on the evaluated output height */
> +    if ((ret = av_expr_parse_and_eval(&res, (expr = rot->outw_expr_str),
> +                                      var_names, rot->var_values,
> +                                      func1_names, func1, NULL, NULL, rot, 0, ctx)) < 0)
> +        goto eval_fail;
> +    rot->var_values[VAR_OUT_W] = rot->var_values[VAR_OW] = res;
> +    rot->outw = res + 0.5;
> +
> +    /* compute number of planes */
> +    rot->nb_planes = 0;
> +    for (i = 0; i < 4; i++) {
> +        const AVComponentDescriptor *comp = &(pixdesc->comp[i]);

nit: pointless ( )

> +        rot->nb_planes = FFMAX(rot->nb_planes, comp->plane);
> +    }
> +    rot->nb_planes++;
> +

> +    rot->outw &= ~((1 << rot->hsub) - 1);
> +    rot->outh &= ~((1 << rot->vsub) - 1);

Can't you use on of the available macro for this?

> +    outlink->w = rot->outw;
> +    outlink->h = rot->outh;
> +
> +    memcpy(rgba_color, rot->fillcolor, sizeof(rgba_color));
> +    ff_fill_line_with_color(rot->line, rot->linestep, outlink->w, rot->fillcolor,
> +                            outlink->format, rgba_color, &is_packed_rgba, NULL);
> +    av_log(ctx, AV_LOG_INFO,
> +           "angle:%f/PI w:%d h:%d -> w:%d h:%d bgcolor:0x%02X%02X%02X%02X[%s]\n",
> +           rot->angle/M_PI, inlink->w, inlink->h, outlink->w, outlink->h,
> +           rot->fillcolor[0], rot->fillcolor[1], rot->fillcolor[2], rot->fillcolor[3],
> +           is_packed_rgba ? "rgba" : "yuva");
> +
> +    return 0;
> +
> +eval_fail:
> +    av_log(NULL, AV_LOG_ERROR,
> +           "Error when evaluating the expression '%s'\n", expr);
> +    return ret;
> +}
> +
> +#define FIXP (1<<16)
> +#define INT_PI 205887 //(M_PI * FIXP)
> +
> +/**
> + * Compute the sin of a using integer values.
> + * Input and output values are scaled by FIXP.
> + */
> +static int64_t int_sin(int64_t a)
> +{
> +    int64_t a2, res = 0;
> +    int i;
> +    if (a < 0) a = INT_PI-a; // 0..inf
> +    a %= 2 * INT_PI;         // 0..2PI
> +
> +    if (a >= INT_PI*3/2) a -= 2*INT_PI;  // -PI/2 .. 3PI/2
> +    if (a >= INT_PI/2  ) a = INT_PI - a; // -PI/2 ..  PI/2
> +
> +    /* compute sin using Taylor series approximated to the third term */
> +    a2 = (a*a)/FIXP;
> +    for (i = 2; i < 7; i += 2) {
> +        res += a;
> +        a = -a*a2 / (FIXP*i*(i+1));
> +    }
> +    return res;
> +}
> +
> +/**
> + * Interpolate the color in src at position x and y using bilinear
> + * interpolation.
> + */
> +static uint8_t *interpolate_bilinear(uint8_t *dst_color,
> +                                     const uint8_t *src, int src_linesize, int src_linestep,
> +                                     int x, int y, int max_x, int max_y)
> +{
> +    int int_x = av_clip(x>>16, 0, max_x);
> +    int int_y = av_clip(y>>16, 0, max_y);
> +    int frac_x = x&0xFFFF;
> +    int frac_y = y&0xFFFF;
> +    int i;
> +    int int_x1 = FFMIN(int_x+1, max_x);
> +    int int_y1 = FFMIN(int_y+1, max_y);
> +
> +    for (i = 0; i < src_linestep; i++) {
> +        int s00 = src[src_linestep * int_x  + i + src_linesize * int_y ];
> +        int s01 = src[src_linestep * int_x1 + i + src_linesize * int_y ];
> +        int s10 = src[src_linestep * int_x  + i + src_linesize * int_y1];
> +        int s11 = src[src_linestep * int_x1 + i + src_linesize * int_y1];
> +        int s0 = (((1<<16) - frac_x)*s00 + frac_x*s01);
> +        int s1 = (((1<<16) - frac_x)*s10 + frac_x*s11);
> +
> +        dst_color[i] = ((int64_t)((1<<16) - frac_y)*s0 + (int64_t)frac_y*s1) >> 32;
> +    }
> +
> +    return dst_color;
> +}
> +
> +static int filter_frame(AVFilterLink *inlink, AVFrame *in)
> +{
> +    AVFilterContext *ctx = inlink->dst;
> +    AVFilterLink *outlink = ctx->outputs[0];
> +    AVFrame *out;
> +    RotContext *rot = ctx->priv;
> +    int angle_int, s, c, plane;
> +    double res;
> +
> +    out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
> +    if (!out) {
> +        av_frame_free(&in);
> +        return AVERROR(ENOMEM);
> +    }
> +    av_frame_copy_props(out, in);
> +
> +    rot->var_values[VAR_N] = inlink->frame_count;
> +    rot->var_values[VAR_T] = in->pts * av_q2d(inlink->time_base);
> +    res = av_expr_eval(rot->angle_expr, rot->var_values, rot);
> +    av_log(ctx, AV_LOG_DEBUG, "n:%f time:%f angle:%f/PI\n",
> +           rot->var_values[VAR_N], rot->var_values[VAR_T], res/M_PI);
> +
> +    angle_int = res * FIXP;
> +    s = int_sin(angle_int);
> +    c = int_sin(angle_int + INT_PI/2);
> +
> +    /* fill background */
> +    if (rot->fillcolor_enable)
> +        ff_draw_rectangle(out->data, out->linesize,
> +                          rot->line, rot->linestep, rot->hsub, rot->vsub,
> +                          0, 0, outlink->w, outlink->h);
> +
> +    for (plane = 0; plane < rot->nb_planes; plane++) {
> +        int hsub = plane == 1 || plane == 2 ? rot->hsub : 0;
> +        int vsub = plane == 1 || plane == 2 ? rot->vsub : 0;
> +        int inw  = FF_CEIL_RSHIFT(inlink->w, hsub);
> +        int inh  = FF_CEIL_RSHIFT(inlink->h, vsub);
> +        int outw = FF_CEIL_RSHIFT(outlink->w, hsub);
> +        int outh = FF_CEIL_RSHIFT(outlink->h, hsub);
> +
> +        const int xi = -outw/2 * c;
> +        const int yi =  outw/2 * s;
> +        int xprime = -outh/2 * s;
> +        int yprime = -outh/2 * c;
> +        int i, j, x, y;
> +
> +        for (j = 0; j < outh; j++) {
> +            x = xprime + xi + FIXP*inw/2;
> +            y = yprime + yi + FIXP*inh/2;
> +
> +            for (i = 0; i < outw; i++) {
> +                int32_t v;
> +                int x1, y1;
> +                uint8_t *pin, *pout;
> +                x += c;
> +                y -= s;
> +                x1 = x>>16;
> +                y1 = y>>16;
> +
> +                /* the out-of-range values avoid border artifacts */
> +                if (x1 >= -1 && x1 <= inw && y1 >= -1 && y1 <= inh) {
> +                    uint8_t inp_inv[4]; /* interpolated input value */
> +                    pout = out->data[plane] + j * out->linesize[plane] + i * rot->linestep[plane];
> +                    if (rot->use_bilinear) {
> +                        pin = interpolate_bilinear(inp_inv,
> +                                                   in->data[plane], in->linesize[plane], rot->linestep[plane],
> +                                                   x, y, inw-1, inh-1);
> +                    } else {
> +                        int x2 = av_clip(x1, 0, inw-1);
> +                        int y2 = av_clip(y1, 0, inh-1);
> +                        pin = in->data[plane] + y2 * in->linesize[plane] + x2 * rot->linestep[plane];
> +                    }
> +                    switch (rot->linestep[plane]) {
> +                    case 1:
> +                        *pout = *pin;
> +                        break;
> +                    case 2:
> +                        *((uint16_t *)pout) = *((uint16_t *)pin);
> +                        break;
> +                    case 3:
> +                        v = AV_RB24(pin);
> +                        AV_WB24(pout, v);
> +                        break;
> +                    case 4:
> +                        *((uint32_t *)pout) = *((uint32_t *)pin);
> +                        break;
> +                    default:
> +                        memcpy(pout, pin, rot->linestep[plane]);
> +                        break;
> +                    }
> +                }
> +            }
> +            xprime += s;
> +            yprime += c;
> +        }
> +    }
> +
> +    av_frame_free(&in);
> +    return ff_filter_frame(outlink, out);
> +}
> +
> +static const AVFilterPad rotate_inputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_VIDEO,
> +        .filter_frame = filter_frame,
> +    },
> +    { NULL }
> +};
> +
> +static const AVFilterPad rotate_outputs[] = {
> +    {
> +        .name         = "default",
> +        .type         = AVMEDIA_TYPE_VIDEO,
> +        .config_props = config_props,
> +    },
> +    { NULL }
> +};
> +
> +AVFilter avfilter_vf_rotate = {
> +    .name        = "rotate",
> +    .description = NULL_IF_CONFIG_SMALL("Rotate the input image."),
> +    .priv_size   = sizeof(RotContext),
> +    .init        = init,
> +    .uninit      = uninit,

> +    .query_formats = query_formats,

nit++: use this one for align basis.

> +    .inputs      = rotate_inputs,
> +    .outputs     = rotate_outputs,
> +    .priv_class  = &rotate_class,
> +};

I didn't find anything obviously wrong, but I didn't look at the math
involved. Patch LGTM.

-- 
Clément B.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 490 bytes
Desc: not available
URL: <http://ffmpeg.org/pipermail/ffmpeg-devel/attachments/20130612/323c7c30/attachment.asc>


More information about the ffmpeg-devel mailing list