2 * Copyright (c) 2013 Stefano Sabatini
3 * Copyright (c) 2008 Vitor Sessak
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
24 * rotation filter, partially based on the tests/rotozoom.c program
27 #include "libavutil/avstring.h"
28 #include "libavutil/eval.h"
29 #include "libavutil/opt.h"
30 #include "libavutil/intreadwrite.h"
31 #include "libavutil/parseutils.h"
32 #include "libavutil/pixdesc.h"
35 #include "drawutils.h"
41 static const char * const var_names
[] = {
42 "in_w" , "iw", ///< width of the input video
43 "in_h" , "ih", ///< height of the input video
44 "out_w", "ow", ///< width of the input video
45 "out_h", "oh", ///< height of the input video
47 "n", ///< number of frame
48 "t", ///< timestamp expressed in seconds
66 char *angle_expr_str
; ///< expression for the angle
67 AVExpr
*angle_expr
; ///< parsed expression for the angle
68 char *outw_expr_str
, *outh_expr_str
;
70 uint8_t fillcolor
[4]; ///< color expressed either in YUVA or RGBA colorspace for the padding area
77 double var_values
[VAR_VARS_NB
];
82 typedef struct ThreadData
{
92 #define OFFSET(x) offsetof(RotContext, x)
93 #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM
95 static const AVOption rotate_options
[] = {
96 { "angle", "set angle (in radians)", OFFSET(angle_expr_str
), AV_OPT_TYPE_STRING
, {.str
="0"}, CHAR_MIN
, CHAR_MAX
, .flags
=FLAGS
},
97 { "a", "set angle (in radians)", OFFSET(angle_expr_str
), AV_OPT_TYPE_STRING
, {.str
="0"}, CHAR_MIN
, CHAR_MAX
, .flags
=FLAGS
},
98 { "out_w", "set output width expression", OFFSET(outw_expr_str
), AV_OPT_TYPE_STRING
, {.str
="iw"}, CHAR_MIN
, CHAR_MAX
, .flags
=FLAGS
},
99 { "ow", "set output width expression", OFFSET(outw_expr_str
), AV_OPT_TYPE_STRING
, {.str
="iw"}, CHAR_MIN
, CHAR_MAX
, .flags
=FLAGS
},
100 { "out_h", "set output height expression", OFFSET(outh_expr_str
), AV_OPT_TYPE_STRING
, {.str
="ih"}, CHAR_MIN
, CHAR_MAX
, .flags
=FLAGS
},
101 { "oh", "set output height expression", OFFSET(outh_expr_str
), AV_OPT_TYPE_STRING
, {.str
="ih"}, CHAR_MIN
, CHAR_MAX
, .flags
=FLAGS
},
102 { "fillcolor", "set background fill color", OFFSET(fillcolor_str
), AV_OPT_TYPE_STRING
, {.str
="black"}, CHAR_MIN
, CHAR_MAX
, .flags
=FLAGS
},
103 { "c", "set background fill color", OFFSET(fillcolor_str
), AV_OPT_TYPE_STRING
, {.str
="black"}, CHAR_MIN
, CHAR_MAX
, .flags
=FLAGS
},
104 { "bilinear", "use bilinear interpolation", OFFSET(use_bilinear
), AV_OPT_TYPE_INT
, {.i64
=1}, 0, 1, .flags
=FLAGS
},
108 AVFILTER_DEFINE_CLASS(rotate
);
110 static av_cold
int init(AVFilterContext
*ctx
)
112 RotContext
*rot
= ctx
->priv
;
114 if (!strcmp(rot
->fillcolor_str
, "none"))
115 rot
->fillcolor_enable
= 0;
116 else if (av_parse_color(rot
->fillcolor
, rot
->fillcolor_str
, -1, ctx
) >= 0)
117 rot
->fillcolor_enable
= 1;
119 return AVERROR(EINVAL
);
123 static av_cold
void uninit(AVFilterContext
*ctx
)
125 RotContext
*rot
= ctx
->priv
;
127 av_expr_free(rot
->angle_expr
);
128 rot
->angle_expr
= NULL
;
131 static int query_formats(AVFilterContext
*ctx
)
133 static const enum PixelFormat pix_fmts
[] = {
134 AV_PIX_FMT_GBRP
, AV_PIX_FMT_GBRAP
,
135 AV_PIX_FMT_ARGB
, AV_PIX_FMT_RGBA
,
136 AV_PIX_FMT_ABGR
, AV_PIX_FMT_BGRA
,
137 AV_PIX_FMT_0RGB
, AV_PIX_FMT_RGB0
,
138 AV_PIX_FMT_0BGR
, AV_PIX_FMT_BGR0
,
139 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
142 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUVJ444P
,
143 AV_PIX_FMT_YUV420P
, AV_PIX_FMT_YUVJ420P
,
144 AV_PIX_FMT_YUVA444P
, AV_PIX_FMT_YUVA420P
,
148 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
152 static double get_rotated_w(void *opaque
, double angle
)
154 RotContext
*rot
= opaque
;
155 double inw
= rot
->var_values
[VAR_IN_W
];
156 double inh
= rot
->var_values
[VAR_IN_H
];
157 float sinx
= sin(angle
);
158 float cosx
= cos(angle
);
160 return FFMAX(0, inh
* sinx
) + FFMAX(0, -inw
* cosx
) +
161 FFMAX(0, inw
* cosx
) + FFMAX(0, -inh
* sinx
);
164 static double get_rotated_h(void *opaque
, double angle
)
166 RotContext
*rot
= opaque
;
167 double inw
= rot
->var_values
[VAR_IN_W
];
168 double inh
= rot
->var_values
[VAR_IN_H
];
169 float sinx
= sin(angle
);
170 float cosx
= cos(angle
);
172 return FFMAX(0, -inh
* cosx
) + FFMAX(0, -inw
* sinx
) +
173 FFMAX(0, inh
* cosx
) + FFMAX(0, inw
* sinx
);
176 static double (* const func1
[])(void *, double) = {
182 static const char * const func1_names
[] = {
188 static int config_props(AVFilterLink
*outlink
)
190 AVFilterContext
*ctx
= outlink
->src
;
191 RotContext
*rot
= ctx
->priv
;
192 AVFilterLink
*inlink
= ctx
->inputs
[0];
193 const AVPixFmtDescriptor
*pixdesc
= av_pix_fmt_desc_get(inlink
->format
);
198 ff_draw_init(&rot
->draw
, inlink
->format
, 0);
199 ff_draw_color(&rot
->draw
, &rot
->color
, rot
->fillcolor
);
201 rot
->hsub
= pixdesc
->log2_chroma_w
;
202 rot
->vsub
= pixdesc
->log2_chroma_h
;
204 rot
->var_values
[VAR_IN_W
] = rot
->var_values
[VAR_IW
] = inlink
->w
;
205 rot
->var_values
[VAR_IN_H
] = rot
->var_values
[VAR_IH
] = inlink
->h
;
206 rot
->var_values
[VAR_HSUB
] = 1<<rot
->hsub
;
207 rot
->var_values
[VAR_VSUB
] = 1<<rot
->vsub
;
208 rot
->var_values
[VAR_N
] = NAN
;
209 rot
->var_values
[VAR_T
] = NAN
;
210 rot
->var_values
[VAR_OUT_W
] = rot
->var_values
[VAR_OW
] = NAN
;
211 rot
->var_values
[VAR_OUT_H
] = rot
->var_values
[VAR_OH
] = NAN
;
213 av_expr_free(rot
->angle_expr
);
214 rot
->angle_expr
= NULL
;
215 if ((ret
= av_expr_parse(&rot
->angle_expr
, expr
= rot
->angle_expr_str
, var_names
,
216 func1_names
, func1
, NULL
, NULL
, 0, ctx
)) < 0) {
217 av_log(ctx
, AV_LOG_ERROR
,
218 "Error occurred parsing angle expression '%s'\n", rot
->angle_expr_str
);
222 #define SET_SIZE_EXPR(name, opt_name) do { \
223 ret = av_expr_parse_and_eval(&res, expr = rot->name##_expr_str, \
224 var_names, rot->var_values, \
225 func1_names, func1, NULL, NULL, rot, 0, ctx); \
226 if (ret < 0 || isnan(res) || isinf(res) || res <= 0) { \
227 av_log(ctx, AV_LOG_ERROR, \
228 "Error parsing or evaluating expression for option %s: " \
229 "invalid expression '%s' or non-positive or indefinite value %f\n", \
230 opt_name, expr, res); \
235 /* evaluate width and height */
236 av_expr_parse_and_eval(&res
, expr
= rot
->outw_expr_str
, var_names
, rot
->var_values
,
237 func1_names
, func1
, NULL
, NULL
, rot
, 0, ctx
);
238 rot
->var_values
[VAR_OUT_W
] = rot
->var_values
[VAR_OW
] = res
;
239 rot
->outw
= res
+ 0.5;
240 SET_SIZE_EXPR(outh
, "out_w");
241 rot
->var_values
[VAR_OUT_H
] = rot
->var_values
[VAR_OH
] = res
;
242 rot
->outh
= res
+ 0.5;
244 /* evaluate the width again, as it may depend on the evaluated output height */
245 SET_SIZE_EXPR(outw
, "out_h");
246 rot
->var_values
[VAR_OUT_W
] = rot
->var_values
[VAR_OW
] = res
;
247 rot
->outw
= res
+ 0.5;
249 /* compute number of planes */
250 rot
->nb_planes
= av_pix_fmt_count_planes(inlink
->format
);
251 outlink
->w
= rot
->outw
;
252 outlink
->h
= rot
->outh
;
257 #define FIXP2 (1<<20)
258 #define INT_PI 3294199 //(M_PI * FIXP2)
261 * Compute the sin of a using integer values.
262 * Input is scaled by FIXP2 and output values are scaled by FIXP.
264 static int64_t int_sin(int64_t a
)
268 if (a
< 0) a
= INT_PI
-a
; // 0..inf
269 a
%= 2 * INT_PI
; // 0..2PI
271 if (a
>= INT_PI
*3/2) a
-= 2*INT_PI
; // -PI/2 .. 3PI/2
272 if (a
>= INT_PI
/2 ) a
= INT_PI
- a
; // -PI/2 .. PI/2
274 /* compute sin using Taylor series approximated to the fifth term */
276 for (i
= 2; i
< 11; i
+= 2) {
278 a
= -a
*a2
/ (FIXP2
*i
*(i
+1));
284 * Interpolate the color in src at position x and y using bilinear
287 static uint8_t *interpolate_bilinear(uint8_t *dst_color
,
288 const uint8_t *src
, int src_linesize
, int src_linestep
,
289 int x
, int y
, int max_x
, int max_y
)
291 int int_x
= av_clip(x
>>16, 0, max_x
);
292 int int_y
= av_clip(y
>>16, 0, max_y
);
293 int frac_x
= x
&0xFFFF;
294 int frac_y
= y
&0xFFFF;
296 int int_x1
= FFMIN(int_x
+1, max_x
);
297 int int_y1
= FFMIN(int_y
+1, max_y
);
299 for (i
= 0; i
< src_linestep
; i
++) {
300 int s00
= src
[src_linestep
* int_x
+ i
+ src_linesize
* int_y
];
301 int s01
= src
[src_linestep
* int_x1
+ i
+ src_linesize
* int_y
];
302 int s10
= src
[src_linestep
* int_x
+ i
+ src_linesize
* int_y1
];
303 int s11
= src
[src_linestep
* int_x1
+ i
+ src_linesize
* int_y1
];
304 int s0
= (((1<<16) - frac_x
)*s00
+ frac_x
*s01
);
305 int s1
= (((1<<16) - frac_x
)*s10
+ frac_x
*s11
);
307 dst_color
[i
] = ((int64_t)((1<<16) - frac_y
)*s0
+ (int64_t)frac_y
*s1
) >> 32;
313 static av_always_inline
void copy_elem(uint8_t *pout
, const uint8_t *pin
, int elem_size
)
321 *((uint16_t *)pout
) = *((uint16_t *)pin
);
328 *((uint32_t *)pout
) = *((uint32_t *)pin
);
331 memcpy(pout
, pin
, elem_size
);
336 static av_always_inline
void simple_rotate_internal(uint8_t *dst
, const uint8_t *src
, int src_linesize
, int angle
, int elem_size
, int len
)
341 memcpy(dst
, src
, elem_size
* len
);
344 for (i
= 0; i
<len
; i
++)
345 copy_elem(dst
+ i
*elem_size
, src
+ (len
-i
-1)*src_linesize
, elem_size
);
348 for (i
= 0; i
<len
; i
++)
349 copy_elem(dst
+ i
*elem_size
, src
+ (len
-i
-1)*elem_size
, elem_size
);
352 for (i
= 0; i
<len
; i
++)
353 copy_elem(dst
+ i
*elem_size
, src
+ i
*src_linesize
, elem_size
);
358 static av_always_inline
void simple_rotate(uint8_t *dst
, const uint8_t *src
, int src_linesize
, int angle
, int elem_size
, int len
)
361 case 1 : simple_rotate_internal(dst
, src
, src_linesize
, angle
, 1, len
); break;
362 case 2 : simple_rotate_internal(dst
, src
, src_linesize
, angle
, 2, len
); break;
363 case 3 : simple_rotate_internal(dst
, src
, src_linesize
, angle
, 3, len
); break;
364 case 4 : simple_rotate_internal(dst
, src
, src_linesize
, angle
, 4, len
); break;
365 default: simple_rotate_internal(dst
, src
, src_linesize
, angle
, elem_size
, len
); break;
369 #define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb))
371 static int filter_slice(AVFilterContext
*ctx
, void *arg
, int job
, int nb_jobs
)
373 ThreadData
*td
= arg
;
374 AVFrame
*in
= td
->in
;
375 AVFrame
*out
= td
->out
;
376 RotContext
*rot
= ctx
->priv
;
377 const int outw
= td
->outw
, outh
= td
->outh
;
378 const int inw
= td
->inw
, inh
= td
->inh
;
379 const int plane
= td
->plane
;
380 const int xi
= td
->xi
, yi
= td
->yi
;
381 const int c
= td
->c
, s
= td
->s
;
382 const int start
= (outh
* job
) / nb_jobs
;
383 const int end
= (outh
* (job
+1)) / nb_jobs
;
384 int xprime
= td
->xprime
+ start
* s
;
385 int yprime
= td
->yprime
+ start
* c
;
388 for (j
= start
; j
< end
; j
++) {
389 x
= xprime
+ xi
+ FIXP
*(inw
-1)/2;
390 y
= yprime
+ yi
+ FIXP
*(inh
-1)/2;
392 if (fabs(rot
->angle
- 0) < FLT_EPSILON
&& outw
== inw
&& outh
== inh
) {
393 simple_rotate(out
->data
[plane
] + j
* out
->linesize
[plane
],
394 in
->data
[plane
] + j
* in
->linesize
[plane
],
395 in
->linesize
[plane
], 0, rot
->draw
.pixelstep
[plane
], outw
);
396 } else if (fabs(rot
->angle
- M_PI
/2) < FLT_EPSILON
&& outw
== inh
&& outh
== inw
) {
397 simple_rotate(out
->data
[plane
] + j
* out
->linesize
[plane
],
398 in
->data
[plane
] + j
* rot
->draw
.pixelstep
[plane
],
399 in
->linesize
[plane
], 1, rot
->draw
.pixelstep
[plane
], outw
);
400 } else if (fabs(rot
->angle
- M_PI
) < FLT_EPSILON
&& outw
== inw
&& outh
== inh
) {
401 simple_rotate(out
->data
[plane
] + j
* out
->linesize
[plane
],
402 in
->data
[plane
] + (outh
-j
-1) * in
->linesize
[plane
],
403 in
->linesize
[plane
], 2, rot
->draw
.pixelstep
[plane
], outw
);
404 } else if (fabs(rot
->angle
- 3*M_PI
/2) < FLT_EPSILON
&& outw
== inh
&& outh
== inw
) {
405 simple_rotate(out
->data
[plane
] + j
* out
->linesize
[plane
],
406 in
->data
[plane
] + (outh
-j
-1) * rot
->draw
.pixelstep
[plane
],
407 in
->linesize
[plane
], 3, rot
->draw
.pixelstep
[plane
], outw
);
410 for (i
= 0; i
< outw
; i
++) {
417 /* the out-of-range values avoid border artifacts */
418 if (x1
>= -1 && x1
<= inw
&& y1
>= -1 && y1
<= inh
) {
419 uint8_t inp_inv
[4]; /* interpolated input value */
420 pout
= out
->data
[plane
] + j
* out
->linesize
[plane
] + i
* rot
->draw
.pixelstep
[plane
];
421 if (rot
->use_bilinear
) {
422 pin
= interpolate_bilinear(inp_inv
,
423 in
->data
[plane
], in
->linesize
[plane
], rot
->draw
.pixelstep
[plane
],
426 int x2
= av_clip(x1
, 0, inw
-1);
427 int y2
= av_clip(y1
, 0, inh
-1);
428 pin
= in
->data
[plane
] + y2
* in
->linesize
[plane
] + x2
* rot
->draw
.pixelstep
[plane
];
430 switch (rot
->draw
.pixelstep
[plane
]) {
435 *((uint16_t *)pout
) = *((uint16_t *)pin
);
442 *((uint32_t *)pout
) = *((uint32_t *)pin
);
445 memcpy(pout
, pin
, rot
->draw
.pixelstep
[plane
]);
460 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*in
)
462 AVFilterContext
*ctx
= inlink
->dst
;
463 AVFilterLink
*outlink
= ctx
->outputs
[0];
465 RotContext
*rot
= ctx
->priv
;
466 int angle_int
, s
, c
, plane
;
469 out
= ff_get_video_buffer(outlink
, outlink
->w
, outlink
->h
);
472 return AVERROR(ENOMEM
);
474 av_frame_copy_props(out
, in
);
476 rot
->var_values
[VAR_N
] = inlink
->frame_count
;
477 rot
->var_values
[VAR_T
] = TS2T(in
->pts
, inlink
->time_base
);
478 rot
->angle
= res
= av_expr_eval(rot
->angle_expr
, rot
->var_values
, rot
);
480 av_log(ctx
, AV_LOG_DEBUG
, "n:%f time:%f angle:%f/PI\n",
481 rot
->var_values
[VAR_N
], rot
->var_values
[VAR_T
], rot
->angle
/M_PI
);
483 angle_int
= res
* FIXP
* 16;
484 s
= int_sin(angle_int
);
485 c
= int_sin(angle_int
+ INT_PI
/2);
487 /* fill background */
488 if (rot
->fillcolor_enable
)
489 ff_fill_rectangle(&rot
->draw
, &rot
->color
, out
->data
, out
->linesize
,
490 0, 0, outlink
->w
, outlink
->h
);
492 for (plane
= 0; plane
< rot
->nb_planes
; plane
++) {
493 int hsub
= plane
== 1 || plane
== 2 ? rot
->hsub
: 0;
494 int vsub
= plane
== 1 || plane
== 2 ? rot
->vsub
: 0;
495 const int outw
= FF_CEIL_RSHIFT(outlink
->w
, hsub
);
496 const int outh
= FF_CEIL_RSHIFT(outlink
->h
, vsub
);
497 ThreadData td
= { .in
= in
, .out
= out
,
498 .inw
= FF_CEIL_RSHIFT(inlink
->w
, hsub
),
499 .inh
= FF_CEIL_RSHIFT(inlink
->h
, vsub
),
500 .outh
= outh
, .outw
= outw
,
501 .xi
= -(outw
-1) * c
/ 2, .yi
= (outw
-1) * s
/ 2,
502 .xprime
= -(outh
-1) * s
/ 2,
503 .yprime
= -(outh
-1) * c
/ 2,
504 .plane
= plane
, .c
= c
, .s
= s
};
507 ctx
->internal
->execute(ctx
, filter_slice
, &td
, NULL
, FFMIN(outh
, ctx
->graph
->nb_threads
));
511 return ff_filter_frame(outlink
, out
);
514 static int process_command(AVFilterContext
*ctx
, const char *cmd
, const char *args
,
515 char *res
, int res_len
, int flags
)
517 RotContext
*rot
= ctx
->priv
;
520 if (!strcmp(cmd
, "angle") || !strcmp(cmd
, "a")) {
521 AVExpr
*old
= rot
->angle_expr
;
522 ret
= av_expr_parse(&rot
->angle_expr
, args
, var_names
,
523 NULL
, NULL
, NULL
, NULL
, 0, ctx
);
525 av_log(ctx
, AV_LOG_ERROR
,
526 "Error when parsing the expression '%s' for angle command\n", args
);
527 rot
->angle_expr
= old
;
532 ret
= AVERROR(ENOSYS
);
537 static const AVFilterPad rotate_inputs
[] = {
540 .type
= AVMEDIA_TYPE_VIDEO
,
541 .filter_frame
= filter_frame
,
546 static const AVFilterPad rotate_outputs
[] = {
549 .type
= AVMEDIA_TYPE_VIDEO
,
550 .config_props
= config_props
,
555 AVFilter ff_vf_rotate
= {
557 .description
= NULL_IF_CONFIG_SMALL("Rotate the input image."),
558 .priv_size
= sizeof(RotContext
),
561 .query_formats
= query_formats
,
562 .process_command
= process_command
,
563 .inputs
= rotate_inputs
,
564 .outputs
= rotate_outputs
,
565 .priv_class
= &rotate_class
,
566 .flags
= AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC
| AVFILTER_FLAG_SLICE_THREADS
,