1/* 2 * Copyright (c) 2013 Stefano Sabatini 3 * Copyright (c) 2008 Vitor Sessak 4 * 5 * This file is part of FFmpeg. 6 * 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. 11 * 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. 16 * 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 20 */ 21 22/** 23 * @file 24 * rotation filter, partially based on the tests/rotozoom.c program 25*/ 26 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" 33 34#include "avfilter.h" 35#include "drawutils.h" 36#include "internal.h" 37#include "video.h" 38 39#include <float.h> 40 41static const char *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 46 "hsub", "vsub", 47 "n", ///< number of frame 48 "t", ///< timestamp expressed in seconds 49 NULL 50}; 51 52enum var_name { 53 VAR_IN_W , VAR_IW, 54 VAR_IN_H , VAR_IH, 55 VAR_OUT_W, VAR_OW, 56 VAR_OUT_H, VAR_OH, 57 VAR_HSUB, VAR_VSUB, 58 VAR_N, 59 VAR_T, 60 VAR_VARS_NB 61}; 62 63typedef struct { 64 const AVClass *class; 65 double angle; 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; 69 int outh, outw; 70 uint8_t fillcolor[4]; ///< color expressed either in YUVA or RGBA colorspace for the padding area 71 char *fillcolor_str; 72 int fillcolor_enable; 73 int hsub, vsub; 74 int nb_planes; 75 int use_bilinear; 76 float sinx, cosx; 77 double var_values[VAR_VARS_NB]; 78 FFDrawContext draw; 79 FFDrawColor color; 80} RotContext; 81 82typedef struct ThreadData { 83 AVFrame *in, *out; 84 int inw, inh; 85 int outw, outh; 86 int plane; 87 int xi, yi; 88 int xprime, yprime; 89 int c, s; 90} ThreadData; 91 92#define OFFSET(x) offsetof(RotContext, x) 93#define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM 94 95static 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 }, 105 { NULL } 106}; 107 108AVFILTER_DEFINE_CLASS(rotate); 109 110static av_cold int init(AVFilterContext *ctx) 111{ 112 RotContext *rot = ctx->priv; 113 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; 118 else 119 return AVERROR(EINVAL); 120 return 0; 121} 122 123static av_cold void uninit(AVFilterContext *ctx) 124{ 125 RotContext *rot = ctx->priv; 126 127 av_expr_free(rot->angle_expr); 128 rot->angle_expr = NULL; 129} 130 131static int query_formats(AVFilterContext *ctx) 132{ 133 static 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, 140 AV_PIX_FMT_GRAY8, 141 AV_PIX_FMT_YUV410P, 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, 145 AV_PIX_FMT_NONE 146 }; 147 148 ff_set_common_formats(ctx, ff_make_format_list(pix_fmts)); 149 return 0; 150} 151 152static double get_rotated_w(void *opaque, double angle) 153{ 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); 159 160 return FFMAX(0, inh * sinx) + FFMAX(0, -inw * cosx) + 161 FFMAX(0, inw * cosx) + FFMAX(0, -inh * sinx); 162} 163 164static double get_rotated_h(void *opaque, double angle) 165{ 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); 171 172 return FFMAX(0, -inh * cosx) + FFMAX(0, -inw * sinx) + 173 FFMAX(0, inh * cosx) + FFMAX(0, inw * sinx); 174} 175 176static double (* const func1[])(void *, double) = { 177 get_rotated_w, 178 get_rotated_h, 179 NULL 180}; 181 182static const char * const func1_names[] = { 183 "rotw", 184 "roth", 185 NULL 186}; 187 188static int config_props(AVFilterLink *outlink) 189{ 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); 194 int ret; 195 double res; 196 char *expr; 197 198 ff_draw_init(&rot->draw, inlink->format, 0); 199 ff_draw_color(&rot->draw, &rot->color, rot->fillcolor); 200 201 rot->hsub = pixdesc->log2_chroma_w; 202 rot->vsub = pixdesc->log2_chroma_h; 203 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; 212 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); 219 return ret; 220 } 221 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); \ 231 return ret; \ 232 } \ 233} while (0) 234 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; 243 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; 248 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; 253 return 0; 254} 255 256#define FIXP (1<<16) 257#define FIXP2 (1<<20) 258#define INT_PI 3294199 //(M_PI * FIXP2) 259 260/** 261 * Compute the sin of a using integer values. 262 * Input is scaled by FIXP2 and output values are scaled by FIXP. 263 */ 264static int64_t int_sin(int64_t a) 265{ 266 int64_t a2, res = 0; 267 int i; 268 if (a < 0) a = INT_PI-a; // 0..inf 269 a %= 2 * INT_PI; // 0..2PI 270 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 273 274 /* compute sin using Taylor series approximated to the fifth term */ 275 a2 = (a*a)/(FIXP2); 276 for (i = 2; i < 11; i += 2) { 277 res += a; 278 a = -a*a2 / (FIXP2*i*(i+1)); 279 } 280 return (res + 8)>>4; 281} 282 283/** 284 * Interpolate the color in src at position x and y using bilinear 285 * interpolation. 286 */ 287static 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) 290{ 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; 295 int i; 296 int int_x1 = FFMIN(int_x+1, max_x); 297 int int_y1 = FFMIN(int_y+1, max_y); 298 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); 306 307 dst_color[i] = ((int64_t)((1<<16) - frac_y)*s0 + (int64_t)frac_y*s1) >> 32; 308 } 309 310 return dst_color; 311} 312 313static av_always_inline void copy_elem(uint8_t *pout, const uint8_t *pin, int elem_size) 314{ 315 int v; 316 switch (elem_size) { 317 case 1: 318 *pout = *pin; 319 break; 320 case 2: 321 *((uint16_t *)pout) = *((uint16_t *)pin); 322 break; 323 case 3: 324 v = AV_RB24(pin); 325 AV_WB24(pout, v); 326 break; 327 case 4: 328 *((uint32_t *)pout) = *((uint32_t *)pin); 329 break; 330 default: 331 memcpy(pout, pin, elem_size); 332 break; 333 } 334} 335 336static av_always_inline void simple_rotate_internal(uint8_t *dst, const uint8_t *src, int src_linesize, int angle, int elem_size, int len) 337{ 338 int i; 339 switch(angle) { 340 case 0: 341 memcpy(dst, src, elem_size * len); 342 break; 343 case 1: 344 for (i = 0; i<len; i++) 345 copy_elem(dst + i*elem_size, src + (len-i-1)*src_linesize, elem_size); 346 break; 347 case 2: 348 for (i = 0; i<len; i++) 349 copy_elem(dst + i*elem_size, src + (len-i-1)*elem_size, elem_size); 350 break; 351 case 3: 352 for (i = 0; i<len; i++) 353 copy_elem(dst + i*elem_size, src + i*src_linesize, elem_size); 354 break; 355 } 356} 357 358static av_always_inline void simple_rotate(uint8_t *dst, const uint8_t *src, int src_linesize, int angle, int elem_size, int len) 359{ 360 switch(elem_size) { 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; 366 } 367} 368 369#define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb)) 370 371static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) 372{ 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; 386 int i, j, x, y; 387 388 for (j = start; j < end; j++) { 389 x = xprime + xi + FIXP*(inw-1)/2; 390 y = yprime + yi + FIXP*(inh-1)/2; 391 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); 408 } else { 409 410 for (i = 0; i < outw; i++) { 411 int32_t v; 412 int x1, y1; 413 uint8_t *pin, *pout; 414 x1 = x>>16; 415 y1 = y>>16; 416 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], 424 x, y, inw-1, inh-1); 425 } else { 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]; 429 } 430 switch (rot->draw.pixelstep[plane]) { 431 case 1: 432 *pout = *pin; 433 break; 434 case 2: 435 *((uint16_t *)pout) = *((uint16_t *)pin); 436 break; 437 case 3: 438 v = AV_RB24(pin); 439 AV_WB24(pout, v); 440 break; 441 case 4: 442 *((uint32_t *)pout) = *((uint32_t *)pin); 443 break; 444 default: 445 memcpy(pout, pin, rot->draw.pixelstep[plane]); 446 break; 447 } 448 } 449 x += c; 450 y -= s; 451 } 452 } 453 xprime += s; 454 yprime += c; 455 } 456 457 return 0; 458} 459 460static int filter_frame(AVFilterLink *inlink, AVFrame *in) 461{ 462 AVFilterContext *ctx = inlink->dst; 463 AVFilterLink *outlink = ctx->outputs[0]; 464 AVFrame *out; 465 RotContext *rot = ctx->priv; 466 int angle_int, s, c, plane; 467 double res; 468 469 out = ff_get_video_buffer(outlink, outlink->w, outlink->h); 470 if (!out) { 471 av_frame_free(&in); 472 return AVERROR(ENOMEM); 473 } 474 av_frame_copy_props(out, in); 475 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); 479 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); 482 483 angle_int = res * FIXP * 16; 484 s = int_sin(angle_int); 485 c = int_sin(angle_int + INT_PI/2); 486 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); 491 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 }; 505 506 507 ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outh, ctx->graph->nb_threads)); 508 } 509 510 av_frame_free(&in); 511 return ff_filter_frame(outlink, out); 512} 513 514static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, 515 char *res, int res_len, int flags) 516{ 517 RotContext *rot = ctx->priv; 518 int ret; 519 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); 524 if (ret < 0) { 525 av_log(ctx, AV_LOG_ERROR, 526 "Error when parsing the expression '%s' for angle command\n", args); 527 rot->angle_expr = old; 528 return ret; 529 } 530 av_expr_free(old); 531 } else 532 ret = AVERROR(ENOSYS); 533 534 return ret; 535} 536 537static const AVFilterPad rotate_inputs[] = { 538 { 539 .name = "default", 540 .type = AVMEDIA_TYPE_VIDEO, 541 .filter_frame = filter_frame, 542 }, 543 { NULL } 544}; 545 546static const AVFilterPad rotate_outputs[] = { 547 { 548 .name = "default", 549 .type = AVMEDIA_TYPE_VIDEO, 550 .config_props = config_props, 551 }, 552 { NULL } 553}; 554 555AVFilter ff_vf_rotate = { 556 .name = "rotate", 557 .description = NULL_IF_CONFIG_SMALL("Rotate the input image."), 558 .priv_size = sizeof(RotContext), 559 .init = init, 560 .uninit = uninit, 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, 567}; 568