[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