blob: 371ff7f722ba4ae04fd5d2d8f5c674f65d673e60
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 | |
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 |
46 | "hsub", "vsub", |
47 | "n", ///< number of frame |
48 | "t", ///< timestamp expressed in seconds |
49 | NULL |
50 | }; |
51 | |
52 | enum 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 | |
63 | typedef struct RotContext { |
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 | uint8_t *(*interpolate_bilinear)(uint8_t *dst_color, |
81 | const uint8_t *src, int src_linesize, int src_linestep, |
82 | int x, int y, int max_x, int max_y); |
83 | } RotContext; |
84 | |
85 | typedef struct ThreadData { |
86 | AVFrame *in, *out; |
87 | int inw, inh; |
88 | int outw, outh; |
89 | int plane; |
90 | int xi, yi; |
91 | int xprime, yprime; |
92 | int c, s; |
93 | } ThreadData; |
94 | |
95 | #define OFFSET(x) offsetof(RotContext, x) |
96 | #define FLAGS AV_OPT_FLAG_FILTERING_PARAM|AV_OPT_FLAG_VIDEO_PARAM |
97 | |
98 | static const AVOption rotate_options[] = { |
99 | { "angle", "set angle (in radians)", OFFSET(angle_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, |
100 | { "a", "set angle (in radians)", OFFSET(angle_expr_str), AV_OPT_TYPE_STRING, {.str="0"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, |
101 | { "out_w", "set output width expression", OFFSET(outw_expr_str), AV_OPT_TYPE_STRING, {.str="iw"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, |
102 | { "ow", "set output width expression", OFFSET(outw_expr_str), AV_OPT_TYPE_STRING, {.str="iw"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, |
103 | { "out_h", "set output height expression", OFFSET(outh_expr_str), AV_OPT_TYPE_STRING, {.str="ih"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, |
104 | { "oh", "set output height expression", OFFSET(outh_expr_str), AV_OPT_TYPE_STRING, {.str="ih"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, |
105 | { "fillcolor", "set background fill color", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, |
106 | { "c", "set background fill color", OFFSET(fillcolor_str), AV_OPT_TYPE_STRING, {.str="black"}, CHAR_MIN, CHAR_MAX, .flags=FLAGS }, |
107 | { "bilinear", "use bilinear interpolation", OFFSET(use_bilinear), AV_OPT_TYPE_BOOL, {.i64=1}, 0, 1, .flags=FLAGS }, |
108 | { NULL } |
109 | }; |
110 | |
111 | AVFILTER_DEFINE_CLASS(rotate); |
112 | |
113 | static av_cold int init(AVFilterContext *ctx) |
114 | { |
115 | RotContext *rot = ctx->priv; |
116 | |
117 | if (!strcmp(rot->fillcolor_str, "none")) |
118 | rot->fillcolor_enable = 0; |
119 | else if (av_parse_color(rot->fillcolor, rot->fillcolor_str, -1, ctx) >= 0) |
120 | rot->fillcolor_enable = 1; |
121 | else |
122 | return AVERROR(EINVAL); |
123 | return 0; |
124 | } |
125 | |
126 | static av_cold void uninit(AVFilterContext *ctx) |
127 | { |
128 | RotContext *rot = ctx->priv; |
129 | |
130 | av_expr_free(rot->angle_expr); |
131 | rot->angle_expr = NULL; |
132 | } |
133 | |
134 | static int query_formats(AVFilterContext *ctx) |
135 | { |
136 | static const enum AVPixelFormat pix_fmts[] = { |
137 | AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, |
138 | AV_PIX_FMT_ARGB, AV_PIX_FMT_RGBA, |
139 | AV_PIX_FMT_ABGR, AV_PIX_FMT_BGRA, |
140 | AV_PIX_FMT_0RGB, AV_PIX_FMT_RGB0, |
141 | AV_PIX_FMT_0BGR, AV_PIX_FMT_BGR0, |
142 | AV_PIX_FMT_RGB24, AV_PIX_FMT_BGR24, |
143 | AV_PIX_FMT_GRAY8, |
144 | AV_PIX_FMT_YUV410P, |
145 | AV_PIX_FMT_YUV444P, AV_PIX_FMT_YUVJ444P, |
146 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUVJ420P, |
147 | AV_PIX_FMT_YUVA444P, AV_PIX_FMT_YUVA420P, |
148 | AV_PIX_FMT_YUV420P10LE, AV_PIX_FMT_YUVA420P10LE, |
149 | AV_PIX_FMT_YUV444P10LE, AV_PIX_FMT_YUVA444P10LE, |
150 | AV_PIX_FMT_YUV420P12LE, |
151 | AV_PIX_FMT_YUV444P12LE, |
152 | AV_PIX_FMT_YUV444P16LE, AV_PIX_FMT_YUVA444P16LE, |
153 | AV_PIX_FMT_YUV420P16LE, AV_PIX_FMT_YUVA420P16LE, |
154 | AV_PIX_FMT_YUV444P9LE, AV_PIX_FMT_YUVA444P9LE, |
155 | AV_PIX_FMT_YUV420P9LE, AV_PIX_FMT_YUVA420P9LE, |
156 | AV_PIX_FMT_NONE |
157 | }; |
158 | |
159 | AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts); |
160 | if (!fmts_list) |
161 | return AVERROR(ENOMEM); |
162 | return ff_set_common_formats(ctx, fmts_list); |
163 | } |
164 | |
165 | static double get_rotated_w(void *opaque, double angle) |
166 | { |
167 | RotContext *rot = opaque; |
168 | double inw = rot->var_values[VAR_IN_W]; |
169 | double inh = rot->var_values[VAR_IN_H]; |
170 | float sinx = sin(angle); |
171 | float cosx = cos(angle); |
172 | |
173 | return FFMAX(0, inh * sinx) + FFMAX(0, -inw * cosx) + |
174 | FFMAX(0, inw * cosx) + FFMAX(0, -inh * sinx); |
175 | } |
176 | |
177 | static double get_rotated_h(void *opaque, double angle) |
178 | { |
179 | RotContext *rot = opaque; |
180 | double inw = rot->var_values[VAR_IN_W]; |
181 | double inh = rot->var_values[VAR_IN_H]; |
182 | float sinx = sin(angle); |
183 | float cosx = cos(angle); |
184 | |
185 | return FFMAX(0, -inh * cosx) + FFMAX(0, -inw * sinx) + |
186 | FFMAX(0, inh * cosx) + FFMAX(0, inw * sinx); |
187 | } |
188 | |
189 | static double (* const func1[])(void *, double) = { |
190 | get_rotated_w, |
191 | get_rotated_h, |
192 | NULL |
193 | }; |
194 | |
195 | static const char * const func1_names[] = { |
196 | "rotw", |
197 | "roth", |
198 | NULL |
199 | }; |
200 | |
201 | #define FIXP (1<<16) |
202 | #define FIXP2 (1<<20) |
203 | #define INT_PI 3294199 //(M_PI * FIXP2) |
204 | |
205 | /** |
206 | * Compute the sin of a using integer values. |
207 | * Input is scaled by FIXP2 and output values are scaled by FIXP. |
208 | */ |
209 | static int64_t int_sin(int64_t a) |
210 | { |
211 | int64_t a2, res = 0; |
212 | int i; |
213 | if (a < 0) a = INT_PI-a; // 0..inf |
214 | a %= 2 * INT_PI; // 0..2PI |
215 | |
216 | if (a >= INT_PI*3/2) a -= 2*INT_PI; // -PI/2 .. 3PI/2 |
217 | if (a >= INT_PI/2 ) a = INT_PI - a; // -PI/2 .. PI/2 |
218 | |
219 | /* compute sin using Taylor series approximated to the fifth term */ |
220 | a2 = (a*a)/(FIXP2); |
221 | for (i = 2; i < 11; i += 2) { |
222 | res += a; |
223 | a = -a*a2 / (FIXP2*i*(i+1)); |
224 | } |
225 | return (res + 8)>>4; |
226 | } |
227 | |
228 | /** |
229 | * Interpolate the color in src at position x and y using bilinear |
230 | * interpolation. |
231 | */ |
232 | static uint8_t *interpolate_bilinear8(uint8_t *dst_color, |
233 | const uint8_t *src, int src_linesize, int src_linestep, |
234 | int x, int y, int max_x, int max_y) |
235 | { |
236 | int int_x = av_clip(x>>16, 0, max_x); |
237 | int int_y = av_clip(y>>16, 0, max_y); |
238 | int frac_x = x&0xFFFF; |
239 | int frac_y = y&0xFFFF; |
240 | int i; |
241 | int int_x1 = FFMIN(int_x+1, max_x); |
242 | int int_y1 = FFMIN(int_y+1, max_y); |
243 | |
244 | for (i = 0; i < src_linestep; i++) { |
245 | int s00 = src[src_linestep * int_x + i + src_linesize * int_y ]; |
246 | int s01 = src[src_linestep * int_x1 + i + src_linesize * int_y ]; |
247 | int s10 = src[src_linestep * int_x + i + src_linesize * int_y1]; |
248 | int s11 = src[src_linestep * int_x1 + i + src_linesize * int_y1]; |
249 | int s0 = (((1<<16) - frac_x)*s00 + frac_x*s01); |
250 | int s1 = (((1<<16) - frac_x)*s10 + frac_x*s11); |
251 | |
252 | dst_color[i] = ((int64_t)((1<<16) - frac_y)*s0 + (int64_t)frac_y*s1) >> 32; |
253 | } |
254 | |
255 | return dst_color; |
256 | } |
257 | |
258 | /** |
259 | * Interpolate the color in src at position x and y using bilinear |
260 | * interpolation. |
261 | */ |
262 | static uint8_t *interpolate_bilinear16(uint8_t *dst_color, |
263 | const uint8_t *src, int src_linesize, int src_linestep, |
264 | int x, int y, int max_x, int max_y) |
265 | { |
266 | int int_x = av_clip(x>>16, 0, max_x); |
267 | int int_y = av_clip(y>>16, 0, max_y); |
268 | int frac_x = x&0xFFFF; |
269 | int frac_y = y&0xFFFF; |
270 | int i; |
271 | int int_x1 = FFMIN(int_x+1, max_x); |
272 | int int_y1 = FFMIN(int_y+1, max_y); |
273 | |
274 | for (i = 0; i < src_linestep; i+=2) { |
275 | int s00 = AV_RL16(&src[src_linestep * int_x + i + src_linesize * int_y ]); |
276 | int s01 = AV_RL16(&src[src_linestep * int_x1 + i + src_linesize * int_y ]); |
277 | int s10 = AV_RL16(&src[src_linestep * int_x + i + src_linesize * int_y1]); |
278 | int s11 = AV_RL16(&src[src_linestep * int_x1 + i + src_linesize * int_y1]); |
279 | int s0 = (((1<<16) - frac_x)*s00 + frac_x*s01); |
280 | int s1 = (((1<<16) - frac_x)*s10 + frac_x*s11); |
281 | |
282 | AV_WL16(&dst_color[i], ((int64_t)((1<<16) - frac_y)*s0 + (int64_t)frac_y*s1) >> 32); |
283 | } |
284 | |
285 | return dst_color; |
286 | } |
287 | |
288 | static int config_props(AVFilterLink *outlink) |
289 | { |
290 | AVFilterContext *ctx = outlink->src; |
291 | RotContext *rot = ctx->priv; |
292 | AVFilterLink *inlink = ctx->inputs[0]; |
293 | const AVPixFmtDescriptor *pixdesc = av_pix_fmt_desc_get(inlink->format); |
294 | int ret; |
295 | double res; |
296 | char *expr; |
297 | |
298 | ff_draw_init(&rot->draw, inlink->format, 0); |
299 | ff_draw_color(&rot->draw, &rot->color, rot->fillcolor); |
300 | |
301 | rot->hsub = pixdesc->log2_chroma_w; |
302 | rot->vsub = pixdesc->log2_chroma_h; |
303 | |
304 | if (pixdesc->comp[0].depth == 8) |
305 | rot->interpolate_bilinear = interpolate_bilinear8; |
306 | else |
307 | rot->interpolate_bilinear = interpolate_bilinear16; |
308 | |
309 | rot->var_values[VAR_IN_W] = rot->var_values[VAR_IW] = inlink->w; |
310 | rot->var_values[VAR_IN_H] = rot->var_values[VAR_IH] = inlink->h; |
311 | rot->var_values[VAR_HSUB] = 1<<rot->hsub; |
312 | rot->var_values[VAR_VSUB] = 1<<rot->vsub; |
313 | rot->var_values[VAR_N] = NAN; |
314 | rot->var_values[VAR_T] = NAN; |
315 | rot->var_values[VAR_OUT_W] = rot->var_values[VAR_OW] = NAN; |
316 | rot->var_values[VAR_OUT_H] = rot->var_values[VAR_OH] = NAN; |
317 | |
318 | av_expr_free(rot->angle_expr); |
319 | rot->angle_expr = NULL; |
320 | if ((ret = av_expr_parse(&rot->angle_expr, expr = rot->angle_expr_str, var_names, |
321 | func1_names, func1, NULL, NULL, 0, ctx)) < 0) { |
322 | av_log(ctx, AV_LOG_ERROR, |
323 | "Error occurred parsing angle expression '%s'\n", rot->angle_expr_str); |
324 | return ret; |
325 | } |
326 | |
327 | #define SET_SIZE_EXPR(name, opt_name) do { \ |
328 | ret = av_expr_parse_and_eval(&res, expr = rot->name##_expr_str, \ |
329 | var_names, rot->var_values, \ |
330 | func1_names, func1, NULL, NULL, rot, 0, ctx); \ |
331 | if (ret < 0 || isnan(res) || isinf(res) || res <= 0) { \ |
332 | av_log(ctx, AV_LOG_ERROR, \ |
333 | "Error parsing or evaluating expression for option %s: " \ |
334 | "invalid expression '%s' or non-positive or indefinite value %f\n", \ |
335 | opt_name, expr, res); \ |
336 | return ret; \ |
337 | } \ |
338 | } while (0) |
339 | |
340 | /* evaluate width and height */ |
341 | av_expr_parse_and_eval(&res, expr = rot->outw_expr_str, var_names, rot->var_values, |
342 | func1_names, func1, NULL, NULL, rot, 0, ctx); |
343 | rot->var_values[VAR_OUT_W] = rot->var_values[VAR_OW] = res; |
344 | rot->outw = res + 0.5; |
345 | SET_SIZE_EXPR(outh, "out_h"); |
346 | rot->var_values[VAR_OUT_H] = rot->var_values[VAR_OH] = res; |
347 | rot->outh = res + 0.5; |
348 | |
349 | /* evaluate the width again, as it may depend on the evaluated output height */ |
350 | SET_SIZE_EXPR(outw, "out_w"); |
351 | rot->var_values[VAR_OUT_W] = rot->var_values[VAR_OW] = res; |
352 | rot->outw = res + 0.5; |
353 | |
354 | /* compute number of planes */ |
355 | rot->nb_planes = av_pix_fmt_count_planes(inlink->format); |
356 | outlink->w = rot->outw; |
357 | outlink->h = rot->outh; |
358 | return 0; |
359 | } |
360 | |
361 | static av_always_inline void copy_elem(uint8_t *pout, const uint8_t *pin, int elem_size) |
362 | { |
363 | int v; |
364 | switch (elem_size) { |
365 | case 1: |
366 | *pout = *pin; |
367 | break; |
368 | case 2: |
369 | *((uint16_t *)pout) = *((uint16_t *)pin); |
370 | break; |
371 | case 3: |
372 | v = AV_RB24(pin); |
373 | AV_WB24(pout, v); |
374 | break; |
375 | case 4: |
376 | *((uint32_t *)pout) = *((uint32_t *)pin); |
377 | break; |
378 | default: |
379 | memcpy(pout, pin, elem_size); |
380 | break; |
381 | } |
382 | } |
383 | |
384 | 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) |
385 | { |
386 | int i; |
387 | switch(angle) { |
388 | case 0: |
389 | memcpy(dst, src, elem_size * len); |
390 | break; |
391 | case 1: |
392 | for (i = 0; i<len; i++) |
393 | copy_elem(dst + i*elem_size, src + (len-i-1)*src_linesize, elem_size); |
394 | break; |
395 | case 2: |
396 | for (i = 0; i<len; i++) |
397 | copy_elem(dst + i*elem_size, src + (len-i-1)*elem_size, elem_size); |
398 | break; |
399 | case 3: |
400 | for (i = 0; i<len; i++) |
401 | copy_elem(dst + i*elem_size, src + i*src_linesize, elem_size); |
402 | break; |
403 | } |
404 | } |
405 | |
406 | static av_always_inline void simple_rotate(uint8_t *dst, const uint8_t *src, int src_linesize, int angle, int elem_size, int len) |
407 | { |
408 | switch(elem_size) { |
409 | case 1 : simple_rotate_internal(dst, src, src_linesize, angle, 1, len); break; |
410 | case 2 : simple_rotate_internal(dst, src, src_linesize, angle, 2, len); break; |
411 | case 3 : simple_rotate_internal(dst, src, src_linesize, angle, 3, len); break; |
412 | case 4 : simple_rotate_internal(dst, src, src_linesize, angle, 4, len); break; |
413 | default: simple_rotate_internal(dst, src, src_linesize, angle, elem_size, len); break; |
414 | } |
415 | } |
416 | |
417 | #define TS2T(ts, tb) ((ts) == AV_NOPTS_VALUE ? NAN : (double)(ts)*av_q2d(tb)) |
418 | |
419 | static int filter_slice(AVFilterContext *ctx, void *arg, int job, int nb_jobs) |
420 | { |
421 | ThreadData *td = arg; |
422 | AVFrame *in = td->in; |
423 | AVFrame *out = td->out; |
424 | RotContext *rot = ctx->priv; |
425 | const int outw = td->outw, outh = td->outh; |
426 | const int inw = td->inw, inh = td->inh; |
427 | const int plane = td->plane; |
428 | const int xi = td->xi, yi = td->yi; |
429 | const int c = td->c, s = td->s; |
430 | const int start = (outh * job ) / nb_jobs; |
431 | const int end = (outh * (job+1)) / nb_jobs; |
432 | int xprime = td->xprime + start * s; |
433 | int yprime = td->yprime + start * c; |
434 | int i, j, x, y; |
435 | |
436 | for (j = start; j < end; j++) { |
437 | x = xprime + xi + FIXP*(inw-1)/2; |
438 | y = yprime + yi + FIXP*(inh-1)/2; |
439 | |
440 | if (fabs(rot->angle - 0) < FLT_EPSILON && outw == inw && outh == inh) { |
441 | simple_rotate(out->data[plane] + j * out->linesize[plane], |
442 | in->data[plane] + j * in->linesize[plane], |
443 | in->linesize[plane], 0, rot->draw.pixelstep[plane], outw); |
444 | } else if (fabs(rot->angle - M_PI/2) < FLT_EPSILON && outw == inh && outh == inw) { |
445 | simple_rotate(out->data[plane] + j * out->linesize[plane], |
446 | in->data[plane] + j * rot->draw.pixelstep[plane], |
447 | in->linesize[plane], 1, rot->draw.pixelstep[plane], outw); |
448 | } else if (fabs(rot->angle - M_PI) < FLT_EPSILON && outw == inw && outh == inh) { |
449 | simple_rotate(out->data[plane] + j * out->linesize[plane], |
450 | in->data[plane] + (outh-j-1) * in->linesize[plane], |
451 | in->linesize[plane], 2, rot->draw.pixelstep[plane], outw); |
452 | } else if (fabs(rot->angle - 3*M_PI/2) < FLT_EPSILON && outw == inh && outh == inw) { |
453 | simple_rotate(out->data[plane] + j * out->linesize[plane], |
454 | in->data[plane] + (outh-j-1) * rot->draw.pixelstep[plane], |
455 | in->linesize[plane], 3, rot->draw.pixelstep[plane], outw); |
456 | } else { |
457 | |
458 | for (i = 0; i < outw; i++) { |
459 | int32_t v; |
460 | int x1, y1; |
461 | uint8_t *pin, *pout; |
462 | x1 = x>>16; |
463 | y1 = y>>16; |
464 | |
465 | /* the out-of-range values avoid border artifacts */ |
466 | if (x1 >= -1 && x1 <= inw && y1 >= -1 && y1 <= inh) { |
467 | uint8_t inp_inv[4]; /* interpolated input value */ |
468 | pout = out->data[plane] + j * out->linesize[plane] + i * rot->draw.pixelstep[plane]; |
469 | if (rot->use_bilinear) { |
470 | pin = rot->interpolate_bilinear(inp_inv, |
471 | in->data[plane], in->linesize[plane], rot->draw.pixelstep[plane], |
472 | x, y, inw-1, inh-1); |
473 | } else { |
474 | int x2 = av_clip(x1, 0, inw-1); |
475 | int y2 = av_clip(y1, 0, inh-1); |
476 | pin = in->data[plane] + y2 * in->linesize[plane] + x2 * rot->draw.pixelstep[plane]; |
477 | } |
478 | switch (rot->draw.pixelstep[plane]) { |
479 | case 1: |
480 | *pout = *pin; |
481 | break; |
482 | case 2: |
483 | v = AV_RL16(pin); |
484 | AV_WL16(pout, v); |
485 | break; |
486 | case 3: |
487 | v = AV_RB24(pin); |
488 | AV_WB24(pout, v); |
489 | break; |
490 | case 4: |
491 | *((uint32_t *)pout) = *((uint32_t *)pin); |
492 | break; |
493 | default: |
494 | memcpy(pout, pin, rot->draw.pixelstep[plane]); |
495 | break; |
496 | } |
497 | } |
498 | x += c; |
499 | y -= s; |
500 | } |
501 | } |
502 | xprime += s; |
503 | yprime += c; |
504 | } |
505 | |
506 | return 0; |
507 | } |
508 | |
509 | static int filter_frame(AVFilterLink *inlink, AVFrame *in) |
510 | { |
511 | AVFilterContext *ctx = inlink->dst; |
512 | AVFilterLink *outlink = ctx->outputs[0]; |
513 | AVFrame *out; |
514 | RotContext *rot = ctx->priv; |
515 | int angle_int, s, c, plane; |
516 | double res; |
517 | |
518 | out = ff_get_video_buffer(outlink, outlink->w, outlink->h); |
519 | if (!out) { |
520 | av_frame_free(&in); |
521 | return AVERROR(ENOMEM); |
522 | } |
523 | av_frame_copy_props(out, in); |
524 | |
525 | rot->var_values[VAR_N] = inlink->frame_count_out; |
526 | rot->var_values[VAR_T] = TS2T(in->pts, inlink->time_base); |
527 | rot->angle = res = av_expr_eval(rot->angle_expr, rot->var_values, rot); |
528 | |
529 | av_log(ctx, AV_LOG_DEBUG, "n:%f time:%f angle:%f/PI\n", |
530 | rot->var_values[VAR_N], rot->var_values[VAR_T], rot->angle/M_PI); |
531 | |
532 | angle_int = res * FIXP * 16; |
533 | s = int_sin(angle_int); |
534 | c = int_sin(angle_int + INT_PI/2); |
535 | |
536 | /* fill background */ |
537 | if (rot->fillcolor_enable) |
538 | ff_fill_rectangle(&rot->draw, &rot->color, out->data, out->linesize, |
539 | 0, 0, outlink->w, outlink->h); |
540 | |
541 | for (plane = 0; plane < rot->nb_planes; plane++) { |
542 | int hsub = plane == 1 || plane == 2 ? rot->hsub : 0; |
543 | int vsub = plane == 1 || plane == 2 ? rot->vsub : 0; |
544 | const int outw = AV_CEIL_RSHIFT(outlink->w, hsub); |
545 | const int outh = AV_CEIL_RSHIFT(outlink->h, vsub); |
546 | ThreadData td = { .in = in, .out = out, |
547 | .inw = AV_CEIL_RSHIFT(inlink->w, hsub), |
548 | .inh = AV_CEIL_RSHIFT(inlink->h, vsub), |
549 | .outh = outh, .outw = outw, |
550 | .xi = -(outw-1) * c / 2, .yi = (outw-1) * s / 2, |
551 | .xprime = -(outh-1) * s / 2, |
552 | .yprime = -(outh-1) * c / 2, |
553 | .plane = plane, .c = c, .s = s }; |
554 | |
555 | |
556 | ctx->internal->execute(ctx, filter_slice, &td, NULL, FFMIN(outh, ff_filter_get_nb_threads(ctx))); |
557 | } |
558 | |
559 | av_frame_free(&in); |
560 | return ff_filter_frame(outlink, out); |
561 | } |
562 | |
563 | static int process_command(AVFilterContext *ctx, const char *cmd, const char *args, |
564 | char *res, int res_len, int flags) |
565 | { |
566 | RotContext *rot = ctx->priv; |
567 | int ret; |
568 | |
569 | if (!strcmp(cmd, "angle") || !strcmp(cmd, "a")) { |
570 | AVExpr *old = rot->angle_expr; |
571 | ret = av_expr_parse(&rot->angle_expr, args, var_names, |
572 | NULL, NULL, NULL, NULL, 0, ctx); |
573 | if (ret < 0) { |
574 | av_log(ctx, AV_LOG_ERROR, |
575 | "Error when parsing the expression '%s' for angle command\n", args); |
576 | rot->angle_expr = old; |
577 | return ret; |
578 | } |
579 | av_expr_free(old); |
580 | } else |
581 | ret = AVERROR(ENOSYS); |
582 | |
583 | return ret; |
584 | } |
585 | |
586 | static const AVFilterPad rotate_inputs[] = { |
587 | { |
588 | .name = "default", |
589 | .type = AVMEDIA_TYPE_VIDEO, |
590 | .filter_frame = filter_frame, |
591 | }, |
592 | { NULL } |
593 | }; |
594 | |
595 | static const AVFilterPad rotate_outputs[] = { |
596 | { |
597 | .name = "default", |
598 | .type = AVMEDIA_TYPE_VIDEO, |
599 | .config_props = config_props, |
600 | }, |
601 | { NULL } |
602 | }; |
603 | |
604 | AVFilter ff_vf_rotate = { |
605 | .name = "rotate", |
606 | .description = NULL_IF_CONFIG_SMALL("Rotate the input image."), |
607 | .priv_size = sizeof(RotContext), |
608 | .init = init, |
609 | .uninit = uninit, |
610 | .query_formats = query_formats, |
611 | .process_command = process_command, |
612 | .inputs = rotate_inputs, |
613 | .outputs = rotate_outputs, |
614 | .priv_class = &rotate_class, |
615 | .flags = AVFILTER_FLAG_SUPPORT_TIMELINE_GENERIC | AVFILTER_FLAG_SLICE_THREADS, |
616 | }; |
617 |