1 /*
2 * Copyright (c) 2020 Paul B Mahol
3 *
4 * This file is part of FFmpeg.
5 *
6 * FFmpeg is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2.1 of the License, or (at your option) any later version.
10 *
11 * FFmpeg is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
17 * License along with FFmpeg; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
19 */
20
21 #include "libavutil/imgutils.h"
22 #include "libavutil/eval.h"
23 #include "libavutil/opt.h"
24 #include "libavutil/pixfmt.h"
25 #include "avfilter.h"
26 #include "formats.h"
27 #include "internal.h"
28 #include "filters.h"
29 #include "video.h"
30
31 enum XFadeTransitions {
32 CUSTOM = -1,
33 FADE,
34 WIPELEFT,
35 WIPERIGHT,
36 WIPEUP,
37 WIPEDOWN,
38 SLIDELEFT,
39 SLIDERIGHT,
40 SLIDEUP,
41 SLIDEDOWN,
42 CIRCLECROP,
43 RECTCROP,
44 DISTANCE,
45 FADEBLACK,
46 FADEWHITE,
47 RADIAL,
48 SMOOTHLEFT,
49 SMOOTHRIGHT,
50 SMOOTHUP,
51 SMOOTHDOWN,
52 CIRCLEOPEN,
53 CIRCLECLOSE,
54 VERTOPEN,
55 VERTCLOSE,
56 HORZOPEN,
57 HORZCLOSE,
58 DISSOLVE,
59 PIXELIZE,
60 DIAGTL,
61 DIAGTR,
62 DIAGBL,
63 DIAGBR,
64 NB_TRANSITIONS,
65 };
66
67 typedef struct XFadeContext {
68 const AVClass *class;
69
70 int transition;
71 int64_t duration;
72 int64_t offset;
73 char *custom_str;
74
75 int nb_planes;
76 int depth;
77
78 int64_t duration_pts;
79 int64_t offset_pts;
80 int64_t first_pts;
81 int64_t last_pts;
82 int64_t pts;
83 int xfade_is_over;
84 int need_second;
85 int eof[2];
86 AVFrame *xf[2];
87 int max_value;
88 uint16_t black[4];
89 uint16_t white[4];
90
91 void (*transitionf)(AVFilterContext *ctx, const AVFrame *a, const AVFrame *b, AVFrame *out, float progress,
92 int slice_start, int slice_end, int jobnr);
93
94 AVExpr *e;
95 } XFadeContext;
96
97 static const char *const var_names[] = { "X", "Y", "W", "H", "A", "B", "PLANE", "P", NULL };
98 enum { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_A, VAR_B, VAR_PLANE, VAR_PROGRESS, VAR_VARS_NB };
99
100 typedef struct ThreadData {
101 const AVFrame *xf[2];
102 AVFrame *out;
103 float progress;
104 } ThreadData;
105
query_formats(AVFilterContext * ctx)106 static int query_formats(AVFilterContext *ctx)
107 {
108 static const enum AVPixelFormat pix_fmts[] = {
109 AV_PIX_FMT_YUVA444P,
110 AV_PIX_FMT_YUVJ444P,
111 AV_PIX_FMT_YUV444P,
112 AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8,
113 AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_GBRP9,
114 AV_PIX_FMT_YUV444P10,
115 AV_PIX_FMT_YUVA444P10,
116 AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GRAY10,
117 AV_PIX_FMT_YUV444P12,
118 AV_PIX_FMT_YUVA444P12,
119 AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GRAY12,
120 AV_PIX_FMT_YUV444P14, AV_PIX_FMT_GBRP14,
121 AV_PIX_FMT_YUV444P16,
122 AV_PIX_FMT_YUVA444P16,
123 AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY16,
124 AV_PIX_FMT_NONE
125 };
126
127 AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
128 if (!fmts_list)
129 return AVERROR(ENOMEM);
130 return ff_set_common_formats(ctx, fmts_list);
131 }
132
uninit(AVFilterContext * ctx)133 static av_cold void uninit(AVFilterContext *ctx)
134 {
135 XFadeContext *s = ctx->priv;
136
137 av_expr_free(s->e);
138 }
139
140 #define OFFSET(x) offsetof(XFadeContext, x)
141 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
142
143 static const AVOption xfade_options[] = {
144 { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=FADE}, -1, NB_TRANSITIONS-1, FLAGS, "transition" },
145 { "custom", "custom transition", 0, AV_OPT_TYPE_CONST, {.i64=CUSTOM}, 0, 0, FLAGS, "transition" },
146 { "fade", "fade transition", 0, AV_OPT_TYPE_CONST, {.i64=FADE}, 0, 0, FLAGS, "transition" },
147 { "wipeleft", "wipe left transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT}, 0, 0, FLAGS, "transition" },
148 { "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, "transition" },
149 { "wipeup", "wipe up transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEUP}, 0, 0, FLAGS, "transition" },
150 { "wipedown", "wipe down transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN}, 0, 0, FLAGS, "transition" },
151 { "slideleft", "slide left transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT}, 0, 0, FLAGS, "transition" },
152 { "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, "transition" },
153 { "slideup", "slide up transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP}, 0, 0, FLAGS, "transition" },
154 { "slidedown", "slide down transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN}, 0, 0, FLAGS, "transition" },
155 { "circlecrop", "circle crop transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECROP}, 0, 0, FLAGS, "transition" },
156 { "rectcrop", "rect crop transition", 0, AV_OPT_TYPE_CONST, {.i64=RECTCROP}, 0, 0, FLAGS, "transition" },
157 { "distance", "distance transition", 0, AV_OPT_TYPE_CONST, {.i64=DISTANCE}, 0, 0, FLAGS, "transition" },
158 { "fadeblack", "fadeblack transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEBLACK}, 0, 0, FLAGS, "transition" },
159 { "fadewhite", "fadewhite transition", 0, AV_OPT_TYPE_CONST, {.i64=FADEWHITE}, 0, 0, FLAGS, "transition" },
160 { "radial", "radial transition", 0, AV_OPT_TYPE_CONST, {.i64=RADIAL}, 0, 0, FLAGS, "transition" },
161 { "smoothleft", "smoothleft transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHLEFT}, 0, 0, FLAGS, "transition" },
162 { "smoothright","smoothright transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHRIGHT},0, 0, FLAGS, "transition" },
163 { "smoothup", "smoothup transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHUP}, 0, 0, FLAGS, "transition" },
164 { "smoothdown", "smoothdown transition", 0, AV_OPT_TYPE_CONST, {.i64=SMOOTHDOWN}, 0, 0, FLAGS, "transition" },
165 { "circleopen", "circleopen transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLEOPEN}, 0, 0, FLAGS, "transition" },
166 { "circleclose","circleclose transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECLOSE},0, 0, FLAGS, "transition" },
167 { "vertopen", "vert open transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTOPEN}, 0, 0, FLAGS, "transition" },
168 { "vertclose", "vert close transition", 0, AV_OPT_TYPE_CONST, {.i64=VERTCLOSE}, 0, 0, FLAGS, "transition" },
169 { "horzopen", "horz open transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZOPEN}, 0, 0, FLAGS, "transition" },
170 { "horzclose", "horz close transition", 0, AV_OPT_TYPE_CONST, {.i64=HORZCLOSE}, 0, 0, FLAGS, "transition" },
171 { "dissolve", "dissolve transition", 0, AV_OPT_TYPE_CONST, {.i64=DISSOLVE}, 0, 0, FLAGS, "transition" },
172 { "pixelize", "pixelize transition", 0, AV_OPT_TYPE_CONST, {.i64=PIXELIZE}, 0, 0, FLAGS, "transition" },
173 { "diagtl", "diag tl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTL}, 0, 0, FLAGS, "transition" },
174 { "diagtr", "diag tr transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGTR}, 0, 0, FLAGS, "transition" },
175 { "diagbl", "diag bl transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBL}, 0, 0, FLAGS, "transition" },
176 { "diagbr", "diag br transition", 0, AV_OPT_TYPE_CONST, {.i64=DIAGBR}, 0, 0, FLAGS, "transition" },
177 { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS },
178 { "offset", "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, {.i64=0}, INT64_MIN, INT64_MAX, FLAGS },
179 { "expr", "set expression for custom transition", OFFSET(custom_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
180 { NULL }
181 };
182
183 AVFILTER_DEFINE_CLASS(xfade);
184
185 #define CUSTOM_TRANSITION(name, type, div) \
186 static void custom##name##_transition(AVFilterContext *ctx, \
187 const AVFrame *a, const AVFrame *b, AVFrame *out, \
188 float progress, \
189 int slice_start, int slice_end, int jobnr) \
190 { \
191 XFadeContext *s = ctx->priv; \
192 const int height = slice_end - slice_start; \
193 \
194 double values[VAR_VARS_NB]; \
195 values[VAR_W] = out->width; \
196 values[VAR_H] = out->height; \
197 values[VAR_PROGRESS] = progress; \
198 \
199 for (int p = 0; p < s->nb_planes; p++) { \
200 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
201 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
202 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
203 \
204 values[VAR_PLANE] = p; \
205 \
206 for (int y = 0; y < height; y++) { \
207 values[VAR_Y] = slice_start + y; \
208 for (int x = 0; x < out->width; x++) { \
209 values[VAR_X] = x; \
210 values[VAR_A] = xf0[x]; \
211 values[VAR_B] = xf1[x]; \
212 dst[x] = av_expr_eval(s->e, values, s); \
213 } \
214 \
215 dst += out->linesize[p] / div; \
216 xf0 += a->linesize[p] / div; \
217 xf1 += b->linesize[p] / div; \
218 } \
219 } \
220 }
221
222 CUSTOM_TRANSITION(8, uint8_t, 1)
223 CUSTOM_TRANSITION(16, uint16_t, 2)
224
mix(float a,float b,float mix)225 static inline float mix(float a, float b, float mix)
226 {
227 return a * mix + b * (1.f - mix);
228 }
229
smoothstep(float edge0,float edge1,float x)230 static inline float smoothstep(float edge0, float edge1, float x)
231 {
232 float t;
233
234 t = av_clipf((x - edge0) / (edge1 - edge0), 0.f, 1.f);
235
236 return t * t * (3.f - 2.f * t);
237 }
238
239 #define FADE_TRANSITION(name, type, div) \
240 static void fade##name##_transition(AVFilterContext *ctx, \
241 const AVFrame *a, const AVFrame *b, AVFrame *out, \
242 float progress, \
243 int slice_start, int slice_end, int jobnr) \
244 { \
245 XFadeContext *s = ctx->priv; \
246 const int height = slice_end - slice_start; \
247 \
248 for (int p = 0; p < s->nb_planes; p++) { \
249 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
250 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
251 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
252 \
253 for (int y = 0; y < height; y++) { \
254 for (int x = 0; x < out->width; x++) { \
255 dst[x] = mix(xf0[x], xf1[x], progress); \
256 } \
257 \
258 dst += out->linesize[p] / div; \
259 xf0 += a->linesize[p] / div; \
260 xf1 += b->linesize[p] / div; \
261 } \
262 } \
263 }
264
265 FADE_TRANSITION(8, uint8_t, 1)
266 FADE_TRANSITION(16, uint16_t, 2)
267
268 #define WIPELEFT_TRANSITION(name, type, div) \
269 static void wipeleft##name##_transition(AVFilterContext *ctx, \
270 const AVFrame *a, const AVFrame *b, AVFrame *out, \
271 float progress, \
272 int slice_start, int slice_end, int jobnr) \
273 { \
274 XFadeContext *s = ctx->priv; \
275 const int height = slice_end - slice_start; \
276 const int z = out->width * progress; \
277 \
278 for (int p = 0; p < s->nb_planes; p++) { \
279 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
280 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
281 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
282 \
283 for (int y = 0; y < height; y++) { \
284 for (int x = 0; x < out->width; x++) { \
285 dst[x] = x > z ? xf1[x] : xf0[x]; \
286 } \
287 \
288 dst += out->linesize[p] / div; \
289 xf0 += a->linesize[p] / div; \
290 xf1 += b->linesize[p] / div; \
291 } \
292 } \
293 }
294
295 WIPELEFT_TRANSITION(8, uint8_t, 1)
296 WIPELEFT_TRANSITION(16, uint16_t, 2)
297
298 #define WIPERIGHT_TRANSITION(name, type, div) \
299 static void wiperight##name##_transition(AVFilterContext *ctx, \
300 const AVFrame *a, const AVFrame *b, AVFrame *out, \
301 float progress, \
302 int slice_start, int slice_end, int jobnr) \
303 { \
304 XFadeContext *s = ctx->priv; \
305 const int height = slice_end - slice_start; \
306 const int z = out->width * (1.f - progress); \
307 \
308 for (int p = 0; p < s->nb_planes; p++) { \
309 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
310 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
311 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
312 \
313 for (int y = 0; y < height; y++) { \
314 for (int x = 0; x < out->width; x++) { \
315 dst[x] = x > z ? xf0[x] : xf1[x]; \
316 } \
317 \
318 dst += out->linesize[p] / div; \
319 xf0 += a->linesize[p] / div; \
320 xf1 += b->linesize[p] / div; \
321 } \
322 } \
323 }
324
325 WIPERIGHT_TRANSITION(8, uint8_t, 1)
326 WIPERIGHT_TRANSITION(16, uint16_t, 2)
327
328 #define WIPEUP_TRANSITION(name, type, div) \
329 static void wipeup##name##_transition(AVFilterContext *ctx, \
330 const AVFrame *a, const AVFrame *b, AVFrame *out, \
331 float progress, \
332 int slice_start, int slice_end, int jobnr) \
333 { \
334 XFadeContext *s = ctx->priv; \
335 const int height = slice_end - slice_start; \
336 const int z = out->height * progress; \
337 \
338 for (int p = 0; p < s->nb_planes; p++) { \
339 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
340 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
341 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
342 \
343 for (int y = 0; y < height; y++) { \
344 for (int x = 0; x < out->width; x++) { \
345 dst[x] = slice_start + y > z ? xf1[x] : xf0[x]; \
346 } \
347 \
348 dst += out->linesize[p] / div; \
349 xf0 += a->linesize[p] / div; \
350 xf1 += b->linesize[p] / div; \
351 } \
352 } \
353 }
354
355 WIPEUP_TRANSITION(8, uint8_t, 1)
356 WIPEUP_TRANSITION(16, uint16_t, 2)
357
358 #define WIPEDOWN_TRANSITION(name, type, div) \
359 static void wipedown##name##_transition(AVFilterContext *ctx, \
360 const AVFrame *a, const AVFrame *b, AVFrame *out, \
361 float progress, \
362 int slice_start, int slice_end, int jobnr) \
363 { \
364 XFadeContext *s = ctx->priv; \
365 const int height = slice_end - slice_start; \
366 const int z = out->height * (1.f - progress); \
367 \
368 for (int p = 0; p < s->nb_planes; p++) { \
369 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
370 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
371 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
372 \
373 for (int y = 0; y < height; y++) { \
374 for (int x = 0; x < out->width; x++) { \
375 dst[x] = slice_start + y > z ? xf0[x] : xf1[x]; \
376 } \
377 \
378 dst += out->linesize[p] / div; \
379 xf0 += a->linesize[p] / div; \
380 xf1 += b->linesize[p] / div; \
381 } \
382 } \
383 }
384
385 WIPEDOWN_TRANSITION(8, uint8_t, 1)
386 WIPEDOWN_TRANSITION(16, uint16_t, 2)
387
388 #define SLIDELEFT_TRANSITION(name, type, div) \
389 static void slideleft##name##_transition(AVFilterContext *ctx, \
390 const AVFrame *a, const AVFrame *b, AVFrame *out, \
391 float progress, \
392 int slice_start, int slice_end, int jobnr) \
393 { \
394 XFadeContext *s = ctx->priv; \
395 const int height = slice_end - slice_start; \
396 const int width = out->width; \
397 const int z = -progress * width; \
398 \
399 for (int p = 0; p < s->nb_planes; p++) { \
400 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
401 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
402 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
403 \
404 for (int y = 0; y < height; y++) { \
405 for (int x = 0; x < width; x++) { \
406 const int zx = z + x; \
407 const int zz = zx % width + width * (zx < 0); \
408 dst[x] = (zx > 0) && (zx < width) ? xf1[zz] : xf0[zz]; \
409 } \
410 \
411 dst += out->linesize[p] / div; \
412 xf0 += a->linesize[p] / div; \
413 xf1 += b->linesize[p] / div; \
414 } \
415 } \
416 }
417
418 SLIDELEFT_TRANSITION(8, uint8_t, 1)
419 SLIDELEFT_TRANSITION(16, uint16_t, 2)
420
421 #define SLIDERIGHT_TRANSITION(name, type, div) \
422 static void slideright##name##_transition(AVFilterContext *ctx, \
423 const AVFrame *a, const AVFrame *b, AVFrame *out, \
424 float progress, \
425 int slice_start, int slice_end, int jobnr) \
426 { \
427 XFadeContext *s = ctx->priv; \
428 const int height = slice_end - slice_start; \
429 const int width = out->width; \
430 const int z = progress * width; \
431 \
432 for (int p = 0; p < s->nb_planes; p++) { \
433 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
434 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
435 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
436 \
437 for (int y = 0; y < height; y++) { \
438 for (int x = 0; x < out->width; x++) { \
439 const int zx = z + x; \
440 const int zz = zx % width + width * (zx < 0); \
441 dst[x] = (zx > 0) && (zx < width) ? xf1[zz] : xf0[zz]; \
442 } \
443 \
444 dst += out->linesize[p] / div; \
445 xf0 += a->linesize[p] / div; \
446 xf1 += b->linesize[p] / div; \
447 } \
448 } \
449 }
450
451 SLIDERIGHT_TRANSITION(8, uint8_t, 1)
452 SLIDERIGHT_TRANSITION(16, uint16_t, 2)
453
454 #define SLIDEUP_TRANSITION(name, type, div) \
455 static void slideup##name##_transition(AVFilterContext *ctx, \
456 const AVFrame *a, const AVFrame *b, AVFrame *out, \
457 float progress, \
458 int slice_start, int slice_end, int jobnr) \
459 { \
460 XFadeContext *s = ctx->priv; \
461 const int height = out->height; \
462 const int z = -progress * height; \
463 \
464 for (int p = 0; p < s->nb_planes; p++) { \
465 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
466 \
467 for (int y = slice_start; y < slice_end; y++) { \
468 const int zy = z + y; \
469 const int zz = zy % height + height * (zy < 0); \
470 const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \
471 const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \
472 \
473 for (int x = 0; x < out->width; x++) { \
474 dst[x] = (zy > 0) && (zy < height) ? xf1[x] : xf0[x]; \
475 } \
476 \
477 dst += out->linesize[p] / div; \
478 } \
479 } \
480 }
481
482 SLIDEUP_TRANSITION(8, uint8_t, 1)
483 SLIDEUP_TRANSITION(16, uint16_t, 2)
484
485 #define SLIDEDOWN_TRANSITION(name, type, div) \
486 static void slidedown##name##_transition(AVFilterContext *ctx, \
487 const AVFrame *a, const AVFrame *b, AVFrame *out, \
488 float progress, \
489 int slice_start, int slice_end, int jobnr) \
490 { \
491 XFadeContext *s = ctx->priv; \
492 const int height = out->height; \
493 const int z = progress * height; \
494 \
495 for (int p = 0; p < s->nb_planes; p++) { \
496 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
497 \
498 for (int y = slice_start; y < slice_end; y++) { \
499 const int zy = z + y; \
500 const int zz = zy % height + height * (zy < 0); \
501 const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]); \
502 const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]); \
503 \
504 for (int x = 0; x < out->width; x++) { \
505 dst[x] = (zy > 0) && (zy < height) ? xf1[x] : xf0[x]; \
506 } \
507 \
508 dst += out->linesize[p] / div; \
509 } \
510 } \
511 }
512
513 SLIDEDOWN_TRANSITION(8, uint8_t, 1)
514 SLIDEDOWN_TRANSITION(16, uint16_t, 2)
515
516 #define CIRCLECROP_TRANSITION(name, type, div) \
517 static void circlecrop##name##_transition(AVFilterContext *ctx, \
518 const AVFrame *a, const AVFrame *b, AVFrame *out, \
519 float progress, \
520 int slice_start, int slice_end, int jobnr) \
521 { \
522 XFadeContext *s = ctx->priv; \
523 const int width = out->width; \
524 const int height = out->height; \
525 float z = powf(2.f * fabsf(progress - 0.5f), 3.f) * hypotf(width/2, height/2); \
526 \
527 for (int p = 0; p < s->nb_planes; p++) { \
528 const int bg = s->black[p]; \
529 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
530 \
531 for (int y = slice_start; y < slice_end; y++) { \
532 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
533 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
534 \
535 for (int x = 0; x < width; x++) { \
536 float dist = hypotf(x - width / 2, y - height / 2); \
537 int val = progress < 0.5f ? xf1[x] : xf0[x]; \
538 dst[x] = (z < dist) ? bg : val; \
539 } \
540 \
541 dst += out->linesize[p] / div; \
542 } \
543 } \
544 }
545
546 CIRCLECROP_TRANSITION(8, uint8_t, 1)
547 CIRCLECROP_TRANSITION(16, uint16_t, 2)
548
549 #define RECTCROP_TRANSITION(name, type, div) \
550 static void rectcrop##name##_transition(AVFilterContext *ctx, \
551 const AVFrame *a, const AVFrame *b, AVFrame *out, \
552 float progress, \
553 int slice_start, int slice_end, int jobnr) \
554 { \
555 XFadeContext *s = ctx->priv; \
556 const int width = out->width; \
557 const int height = out->height; \
558 int zh = fabsf(progress - 0.5f) * height; \
559 int zw = fabsf(progress - 0.5f) * width; \
560 \
561 for (int p = 0; p < s->nb_planes; p++) { \
562 const int bg = s->black[p]; \
563 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
564 \
565 for (int y = slice_start; y < slice_end; y++) { \
566 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
567 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
568 \
569 for (int x = 0; x < width; x++) { \
570 int dist = FFABS(x - width / 2) < zw && \
571 FFABS(y - height / 2) < zh; \
572 int val = progress < 0.5f ? xf1[x] : xf0[x]; \
573 dst[x] = !dist ? bg : val; \
574 } \
575 \
576 dst += out->linesize[p] / div; \
577 } \
578 } \
579 }
580
581 RECTCROP_TRANSITION(8, uint8_t, 1)
582 RECTCROP_TRANSITION(16, uint16_t, 2)
583
584 #define DISTANCE_TRANSITION(name, type, div) \
585 static void distance##name##_transition(AVFilterContext *ctx, \
586 const AVFrame *a, const AVFrame *b, AVFrame *out, \
587 float progress, \
588 int slice_start, int slice_end, int jobnr) \
589 { \
590 XFadeContext *s = ctx->priv; \
591 const int width = out->width; \
592 const float max = s->max_value; \
593 \
594 for (int y = slice_start; y < slice_end; y++) { \
595 for (int x = 0; x < width; x++) { \
596 float dist = 0.f; \
597 for (int p = 0; p < s->nb_planes; p++) { \
598 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
599 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
600 \
601 dist += (xf0[x] / max - xf1[x] / max) * \
602 (xf0[x] / max - xf1[x] / max); \
603 } \
604 \
605 dist = sqrtf(dist) <= progress; \
606 for (int p = 0; p < s->nb_planes; p++) { \
607 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
608 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
609 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
610 dst[x] = mix(mix(xf0[x], xf1[x], dist), xf1[x], progress); \
611 } \
612 } \
613 } \
614 }
615
616 DISTANCE_TRANSITION(8, uint8_t, 1)
617 DISTANCE_TRANSITION(16, uint16_t, 2)
618
619 #define FADEBLACK_TRANSITION(name, type, div) \
620 static void fadeblack##name##_transition(AVFilterContext *ctx, \
621 const AVFrame *a, const AVFrame *b, AVFrame *out, \
622 float progress, \
623 int slice_start, int slice_end, int jobnr) \
624 { \
625 XFadeContext *s = ctx->priv; \
626 const int height = slice_end - slice_start; \
627 const float phase = 0.2f; \
628 \
629 for (int p = 0; p < s->nb_planes; p++) { \
630 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
631 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
632 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
633 const int bg = s->black[p]; \
634 \
635 for (int y = 0; y < height; y++) { \
636 for (int x = 0; x < out->width; x++) { \
637 dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \
638 mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \
639 progress); \
640 } \
641 \
642 dst += out->linesize[p] / div; \
643 xf0 += a->linesize[p] / div; \
644 xf1 += b->linesize[p] / div; \
645 } \
646 } \
647 }
648
649 FADEBLACK_TRANSITION(8, uint8_t, 1)
650 FADEBLACK_TRANSITION(16, uint16_t, 2)
651
652 #define FADEWHITE_TRANSITION(name, type, div) \
653 static void fadewhite##name##_transition(AVFilterContext *ctx, \
654 const AVFrame *a, const AVFrame *b, AVFrame *out, \
655 float progress, \
656 int slice_start, int slice_end, int jobnr) \
657 { \
658 XFadeContext *s = ctx->priv; \
659 const int height = slice_end - slice_start; \
660 const float phase = 0.2f; \
661 \
662 for (int p = 0; p < s->nb_planes; p++) { \
663 const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
664 const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
665 type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]); \
666 const int bg = s->white[p]; \
667 \
668 for (int y = 0; y < height; y++) { \
669 for (int x = 0; x < out->width; x++) { \
670 dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)), \
671 mix(bg, xf1[x], smoothstep(phase, 1.f, progress)), \
672 progress); \
673 } \
674 \
675 dst += out->linesize[p] / div; \
676 xf0 += a->linesize[p] / div; \
677 xf1 += b->linesize[p] / div; \
678 } \
679 } \
680 }
681
682 FADEWHITE_TRANSITION(8, uint8_t, 1)
683 FADEWHITE_TRANSITION(16, uint16_t, 2)
684
685 #define RADIAL_TRANSITION(name, type, div) \
686 static void radial##name##_transition(AVFilterContext *ctx, \
687 const AVFrame *a, const AVFrame *b, AVFrame *out, \
688 float progress, \
689 int slice_start, int slice_end, int jobnr) \
690 { \
691 XFadeContext *s = ctx->priv; \
692 const int width = out->width; \
693 const int height = out->height; \
694 \
695 for (int y = slice_start; y < slice_end; y++) { \
696 for (int x = 0; x < width; x++) { \
697 const float smooth = atan2f(x - width / 2, y - height / 2) - \
698 (progress - 0.5f) * (M_PI * 2.5f); \
699 for (int p = 0; p < s->nb_planes; p++) { \
700 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
701 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
702 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
703 \
704 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
705 } \
706 } \
707 } \
708 }
709
710 RADIAL_TRANSITION(8, uint8_t, 1)
711 RADIAL_TRANSITION(16, uint16_t, 2)
712
713 #define SMOOTHLEFT_TRANSITION(name, type, div) \
714 static void smoothleft##name##_transition(AVFilterContext *ctx, \
715 const AVFrame *a, const AVFrame *b, AVFrame *out, \
716 float progress, \
717 int slice_start, int slice_end, int jobnr) \
718 { \
719 XFadeContext *s = ctx->priv; \
720 const int width = out->width; \
721 const float w = width; \
722 \
723 for (int y = slice_start; y < slice_end; y++) { \
724 for (int x = 0; x < width; x++) { \
725 const float smooth = 1.f + x / w - progress * 2.f; \
726 \
727 for (int p = 0; p < s->nb_planes; p++) { \
728 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
729 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
730 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
731 \
732 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
733 } \
734 } \
735 } \
736 }
737
738 SMOOTHLEFT_TRANSITION(8, uint8_t, 1)
739 SMOOTHLEFT_TRANSITION(16, uint16_t, 2)
740
741 #define SMOOTHRIGHT_TRANSITION(name, type, div) \
742 static void smoothright##name##_transition(AVFilterContext *ctx, \
743 const AVFrame *a, const AVFrame *b, AVFrame *out, \
744 float progress, \
745 int slice_start, int slice_end, int jobnr) \
746 { \
747 XFadeContext *s = ctx->priv; \
748 const int width = out->width; \
749 const float w = width; \
750 \
751 for (int y = slice_start; y < slice_end; y++) { \
752 for (int x = 0; x < width; x++) { \
753 const float smooth = 1.f + (w - 1 - x) / w - progress * 2.f; \
754 \
755 for (int p = 0; p < s->nb_planes; p++) { \
756 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
757 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
758 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
759 \
760 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
761 } \
762 } \
763 } \
764 }
765
766 SMOOTHRIGHT_TRANSITION(8, uint8_t, 1)
767 SMOOTHRIGHT_TRANSITION(16, uint16_t, 2)
768
769 #define SMOOTHUP_TRANSITION(name, type, div) \
770 static void smoothup##name##_transition(AVFilterContext *ctx, \
771 const AVFrame *a, const AVFrame *b, AVFrame *out, \
772 float progress, \
773 int slice_start, int slice_end, int jobnr) \
774 { \
775 XFadeContext *s = ctx->priv; \
776 const int width = out->width; \
777 const float h = out->height; \
778 \
779 for (int y = slice_start; y < slice_end; y++) { \
780 const float smooth = 1.f + y / h - progress * 2.f; \
781 for (int x = 0; x < width; x++) { \
782 for (int p = 0; p < s->nb_planes; p++) { \
783 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
784 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
785 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
786 \
787 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
788 } \
789 } \
790 } \
791 }
792
793 SMOOTHUP_TRANSITION(8, uint8_t, 1)
794 SMOOTHUP_TRANSITION(16, uint16_t, 2)
795
796 #define SMOOTHDOWN_TRANSITION(name, type, div) \
797 static void smoothdown##name##_transition(AVFilterContext *ctx, \
798 const AVFrame *a, const AVFrame *b, AVFrame *out, \
799 float progress, \
800 int slice_start, int slice_end, int jobnr) \
801 { \
802 XFadeContext *s = ctx->priv; \
803 const int width = out->width; \
804 const float h = out->height; \
805 \
806 for (int y = slice_start; y < slice_end; y++) { \
807 const float smooth = 1.f + (h - 1 - y) / h - progress * 2.f; \
808 for (int x = 0; x < width; x++) { \
809 for (int p = 0; p < s->nb_planes; p++) { \
810 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
811 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
812 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
813 \
814 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
815 } \
816 } \
817 } \
818 }
819
820 SMOOTHDOWN_TRANSITION(8, uint8_t, 1)
821 SMOOTHDOWN_TRANSITION(16, uint16_t, 2)
822
823 #define CIRCLEOPEN_TRANSITION(name, type, div) \
824 static void circleopen##name##_transition(AVFilterContext *ctx, \
825 const AVFrame *a, const AVFrame *b, AVFrame *out, \
826 float progress, \
827 int slice_start, int slice_end, int jobnr) \
828 { \
829 XFadeContext *s = ctx->priv; \
830 const int width = out->width; \
831 const int height = out->height; \
832 const float z = hypotf(width / 2, height / 2); \
833 const float p = (progress - 0.5f) * 3.f; \
834 \
835 for (int y = slice_start; y < slice_end; y++) { \
836 for (int x = 0; x < width; x++) { \
837 const float smooth = hypotf(x - width / 2, y - height / 2) / z + p; \
838 for (int p = 0; p < s->nb_planes; p++) { \
839 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
840 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
841 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
842 \
843 dst[x] = mix(xf0[x], xf1[x], smoothstep(0.f, 1.f, smooth)); \
844 } \
845 } \
846 } \
847 }
848
849 CIRCLEOPEN_TRANSITION(8, uint8_t, 1)
850 CIRCLEOPEN_TRANSITION(16, uint16_t, 2)
851
852 #define CIRCLECLOSE_TRANSITION(name, type, div) \
853 static void circleclose##name##_transition(AVFilterContext *ctx, \
854 const AVFrame *a, const AVFrame *b, AVFrame *out, \
855 float progress, \
856 int slice_start, int slice_end, int jobnr) \
857 { \
858 XFadeContext *s = ctx->priv; \
859 const int width = out->width; \
860 const int height = out->height; \
861 const float z = hypotf(width / 2, height / 2); \
862 const float p = (1.f - progress - 0.5f) * 3.f; \
863 \
864 for (int y = slice_start; y < slice_end; y++) { \
865 for (int x = 0; x < width; x++) { \
866 const float smooth = hypotf(x - width / 2, y - height / 2) / z + p; \
867 for (int p = 0; p < s->nb_planes; p++) { \
868 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
869 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
870 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
871 \
872 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
873 } \
874 } \
875 } \
876 }
877
878 CIRCLECLOSE_TRANSITION(8, uint8_t, 1)
879 CIRCLECLOSE_TRANSITION(16, uint16_t, 2)
880
881 #define VERTOPEN_TRANSITION(name, type, div) \
882 static void vertopen##name##_transition(AVFilterContext *ctx, \
883 const AVFrame *a, const AVFrame *b, AVFrame *out, \
884 float progress, \
885 int slice_start, int slice_end, int jobnr) \
886 { \
887 XFadeContext *s = ctx->priv; \
888 const int width = out->width; \
889 const float w2 = out->width / 2; \
890 \
891 for (int y = slice_start; y < slice_end; y++) { \
892 for (int x = 0; x < width; x++) { \
893 const float smooth = 2.f - fabsf((x - w2) / w2) - progress * 2.f; \
894 for (int p = 0; p < s->nb_planes; p++) { \
895 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
896 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
897 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
898 \
899 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
900 } \
901 } \
902 } \
903 }
904
905 VERTOPEN_TRANSITION(8, uint8_t, 1)
906 VERTOPEN_TRANSITION(16, uint16_t, 2)
907
908 #define VERTCLOSE_TRANSITION(name, type, div) \
909 static void vertclose##name##_transition(AVFilterContext *ctx, \
910 const AVFrame *a, const AVFrame *b, AVFrame *out, \
911 float progress, \
912 int slice_start, int slice_end, int jobnr) \
913 { \
914 XFadeContext *s = ctx->priv; \
915 const int width = out->width; \
916 const float w2 = out->width / 2; \
917 \
918 for (int y = slice_start; y < slice_end; y++) { \
919 for (int x = 0; x < width; x++) { \
920 const float smooth = 1.f + fabsf((x - w2) / w2) - progress * 2.f; \
921 for (int p = 0; p < s->nb_planes; p++) { \
922 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
923 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
924 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
925 \
926 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
927 } \
928 } \
929 } \
930 }
931
932 VERTCLOSE_TRANSITION(8, uint8_t, 1)
933 VERTCLOSE_TRANSITION(16, uint16_t, 2)
934
935 #define HORZOPEN_TRANSITION(name, type, div) \
936 static void horzopen##name##_transition(AVFilterContext *ctx, \
937 const AVFrame *a, const AVFrame *b, AVFrame *out, \
938 float progress, \
939 int slice_start, int slice_end, int jobnr) \
940 { \
941 XFadeContext *s = ctx->priv; \
942 const int width = out->width; \
943 const float h2 = out->height / 2; \
944 \
945 for (int y = slice_start; y < slice_end; y++) { \
946 const float smooth = 2.f - fabsf((y - h2) / h2) - progress * 2.f; \
947 for (int x = 0; x < width; x++) { \
948 for (int p = 0; p < s->nb_planes; p++) { \
949 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
950 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
951 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
952 \
953 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
954 } \
955 } \
956 } \
957 }
958
959 HORZOPEN_TRANSITION(8, uint8_t, 1)
960 HORZOPEN_TRANSITION(16, uint16_t, 2)
961
962 #define HORZCLOSE_TRANSITION(name, type, div) \
963 static void horzclose##name##_transition(AVFilterContext *ctx, \
964 const AVFrame *a, const AVFrame *b, AVFrame *out, \
965 float progress, \
966 int slice_start, int slice_end, int jobnr) \
967 { \
968 XFadeContext *s = ctx->priv; \
969 const int width = out->width; \
970 const float h2 = out->height / 2; \
971 \
972 for (int y = slice_start; y < slice_end; y++) { \
973 const float smooth = 1.f + fabsf((y - h2) / h2) - progress * 2.f; \
974 for (int x = 0; x < width; x++) { \
975 for (int p = 0; p < s->nb_planes; p++) { \
976 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
977 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
978 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
979 \
980 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
981 } \
982 } \
983 } \
984 }
985
986 HORZCLOSE_TRANSITION(8, uint8_t, 1)
987 HORZCLOSE_TRANSITION(16, uint16_t, 2)
988
frand(int x,int y)989 static float frand(int x, int y)
990 {
991 const float r = sinf(x * 12.9898f + y * 78.233f) * 43758.545f;
992
993 return r - floorf(r);
994 }
995
996 #define DISSOLVE_TRANSITION(name, type, div) \
997 static void dissolve##name##_transition(AVFilterContext *ctx, \
998 const AVFrame *a, const AVFrame *b, AVFrame *out, \
999 float progress, \
1000 int slice_start, int slice_end, int jobnr) \
1001 { \
1002 XFadeContext *s = ctx->priv; \
1003 const int width = out->width; \
1004 \
1005 for (int y = slice_start; y < slice_end; y++) { \
1006 for (int x = 0; x < width; x++) { \
1007 const float smooth = frand(x, y) * 2.f + progress * 2.f - 1.5f; \
1008 for (int p = 0; p < s->nb_planes; p++) { \
1009 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1010 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1011 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1012 \
1013 dst[x] = smooth >= 0.5f ? xf0[x] : xf1[x]; \
1014 } \
1015 } \
1016 } \
1017 }
1018
1019 DISSOLVE_TRANSITION(8, uint8_t, 1)
1020 DISSOLVE_TRANSITION(16, uint16_t, 2)
1021
1022 #define PIXELIZE_TRANSITION(name, type, div) \
1023 static void pixelize##name##_transition(AVFilterContext *ctx, \
1024 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1025 float progress, \
1026 int slice_start, int slice_end, int jobnr) \
1027 { \
1028 XFadeContext *s = ctx->priv; \
1029 const int w = out->width; \
1030 const int h = out->height; \
1031 const float d = fminf(progress, 1.f - progress); \
1032 const float dist = ceilf(d * 50.f) / 50.f; \
1033 const float sqx = 2.f * dist * FFMIN(w, h) / 20.f; \
1034 const float sqy = 2.f * dist * FFMIN(w, h) / 20.f; \
1035 \
1036 for (int y = slice_start; y < slice_end; y++) { \
1037 for (int x = 0; x < w; x++) { \
1038 int sx = dist > 0.f ? FFMIN((floorf(x / sqx) + .5f) * sqx, w - 1) : x; \
1039 int sy = dist > 0.f ? FFMIN((floorf(y / sqy) + .5f) * sqy, h - 1) : y; \
1040 for (int p = 0; p < s->nb_planes; p++) { \
1041 const type *xf0 = (const type *)(a->data[p] + sy * a->linesize[p]); \
1042 const type *xf1 = (const type *)(b->data[p] + sy * b->linesize[p]); \
1043 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1044 \
1045 dst[x] = mix(xf0[sx], xf1[sx], progress); \
1046 } \
1047 } \
1048 } \
1049 }
1050
1051 PIXELIZE_TRANSITION(8, uint8_t, 1)
1052 PIXELIZE_TRANSITION(16, uint16_t, 2)
1053
1054 #define DIAGTL_TRANSITION(name, type, div) \
1055 static void diagtl##name##_transition(AVFilterContext *ctx, \
1056 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1057 float progress, \
1058 int slice_start, int slice_end, int jobnr) \
1059 { \
1060 XFadeContext *s = ctx->priv; \
1061 const int width = out->width; \
1062 const float w = width; \
1063 const float h = out->height; \
1064 \
1065 for (int y = slice_start; y < slice_end; y++) { \
1066 for (int x = 0; x < width; x++) { \
1067 const float smooth = 1.f + x / w * y / h - progress * 2.f; \
1068 \
1069 for (int p = 0; p < s->nb_planes; p++) { \
1070 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1071 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1072 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1073 \
1074 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1075 } \
1076 } \
1077 } \
1078 }
1079
1080 DIAGTL_TRANSITION(8, uint8_t, 1)
1081 DIAGTL_TRANSITION(16, uint16_t, 2)
1082
1083 #define DIAGTR_TRANSITION(name, type, div) \
1084 static void diagtr##name##_transition(AVFilterContext *ctx, \
1085 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1086 float progress, \
1087 int slice_start, int slice_end, int jobnr) \
1088 { \
1089 XFadeContext *s = ctx->priv; \
1090 const int width = out->width; \
1091 const float w = width; \
1092 const float h = out->height; \
1093 \
1094 for (int y = slice_start; y < slice_end; y++) { \
1095 for (int x = 0; x < width; x++) { \
1096 const float smooth = 1.f + (w - 1 - x) / w * y / h - progress * 2.f; \
1097 \
1098 for (int p = 0; p < s->nb_planes; p++) { \
1099 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1100 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1101 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1102 \
1103 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1104 } \
1105 } \
1106 } \
1107 }
1108
1109 DIAGTR_TRANSITION(8, uint8_t, 1)
1110 DIAGTR_TRANSITION(16, uint16_t, 2)
1111
1112 #define DIAGBL_TRANSITION(name, type, div) \
1113 static void diagbl##name##_transition(AVFilterContext *ctx, \
1114 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1115 float progress, \
1116 int slice_start, int slice_end, int jobnr) \
1117 { \
1118 XFadeContext *s = ctx->priv; \
1119 const int width = out->width; \
1120 const float w = width; \
1121 const float h = out->height; \
1122 \
1123 for (int y = slice_start; y < slice_end; y++) { \
1124 for (int x = 0; x < width; x++) { \
1125 const float smooth = 1.f + x / w * (h - 1 - y) / h - progress * 2.f; \
1126 \
1127 for (int p = 0; p < s->nb_planes; p++) { \
1128 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1129 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1130 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1131 \
1132 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1133 } \
1134 } \
1135 } \
1136 }
1137
1138 DIAGBL_TRANSITION(8, uint8_t, 1)
1139 DIAGBL_TRANSITION(16, uint16_t, 2)
1140
1141 #define DIAGBR_TRANSITION(name, type, div) \
1142 static void diagbr##name##_transition(AVFilterContext *ctx, \
1143 const AVFrame *a, const AVFrame *b, AVFrame *out, \
1144 float progress, \
1145 int slice_start, int slice_end, int jobnr) \
1146 { \
1147 XFadeContext *s = ctx->priv; \
1148 const int width = out->width; \
1149 const float w = width; \
1150 const float h = out->height; \
1151 \
1152 for (int y = slice_start; y < slice_end; y++) { \
1153 for (int x = 0; x < width; x++) { \
1154 const float smooth = 1.f + (w - 1 - x) / w * (h - 1 - y) / h - \
1155 progress * 2.f; \
1156 \
1157 for (int p = 0; p < s->nb_planes; p++) { \
1158 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]); \
1159 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]); \
1160 type *dst = (type *)(out->data[p] + y * out->linesize[p]); \
1161 \
1162 dst[x] = mix(xf1[x], xf0[x], smoothstep(0.f, 1.f, smooth)); \
1163 } \
1164 } \
1165 } \
1166 }
1167
1168 DIAGBR_TRANSITION(8, uint8_t, 1)
1169 DIAGBR_TRANSITION(16, uint16_t, 2)
1170
getpix(void * priv,double x,double y,int plane,int nb)1171 static inline double getpix(void *priv, double x, double y, int plane, int nb)
1172 {
1173 XFadeContext *s = priv;
1174 AVFrame *in = s->xf[nb];
1175 const uint8_t *src = in->data[FFMIN(plane, s->nb_planes - 1)];
1176 int linesize = in->linesize[FFMIN(plane, s->nb_planes - 1)];
1177 const int w = in->width;
1178 const int h = in->height;
1179
1180 int xi, yi;
1181
1182 xi = av_clipd(x, 0, w - 1);
1183 yi = av_clipd(y, 0, h - 1);
1184
1185 if (s->depth > 8) {
1186 const uint16_t *src16 = (const uint16_t*)src;
1187
1188 linesize /= 2;
1189 return src16[xi + yi * linesize];
1190 } else {
1191 return src[xi + yi * linesize];
1192 }
1193 }
1194
a0(void * priv,double x,double y)1195 static double a0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 0); }
a1(void * priv,double x,double y)1196 static double a1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 0); }
a2(void * priv,double x,double y)1197 static double a2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 0); }
a3(void * priv,double x,double y)1198 static double a3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 0); }
1199
b0(void * priv,double x,double y)1200 static double b0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 1); }
b1(void * priv,double x,double y)1201 static double b1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 1); }
b2(void * priv,double x,double y)1202 static double b2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 1); }
b3(void * priv,double x,double y)1203 static double b3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 1); }
1204
config_output(AVFilterLink * outlink)1205 static int config_output(AVFilterLink *outlink)
1206 {
1207 AVFilterContext *ctx = outlink->src;
1208 AVFilterLink *inlink0 = ctx->inputs[0];
1209 AVFilterLink *inlink1 = ctx->inputs[1];
1210 XFadeContext *s = ctx->priv;
1211 const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink0->format);
1212 int is_rgb;
1213
1214 if (inlink0->format != inlink1->format) {
1215 av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n");
1216 return AVERROR(EINVAL);
1217 }
1218 if (inlink0->w != inlink1->w || inlink0->h != inlink1->h) {
1219 av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
1220 "(size %dx%d) do not match the corresponding "
1221 "second input link %s parameters (size %dx%d)\n",
1222 ctx->input_pads[0].name, inlink0->w, inlink0->h,
1223 ctx->input_pads[1].name, inlink1->w, inlink1->h);
1224 return AVERROR(EINVAL);
1225 }
1226
1227 if (inlink0->time_base.num != inlink1->time_base.num ||
1228 inlink0->time_base.den != inlink1->time_base.den) {
1229 av_log(ctx, AV_LOG_ERROR, "First input link %s timebase "
1230 "(%d/%d) do not match the corresponding "
1231 "second input link %s timebase (%d/%d)\n",
1232 ctx->input_pads[0].name, inlink0->time_base.num, inlink0->time_base.den,
1233 ctx->input_pads[1].name, inlink1->time_base.num, inlink1->time_base.den);
1234 return AVERROR(EINVAL);
1235 }
1236
1237 outlink->w = inlink0->w;
1238 outlink->h = inlink0->h;
1239 outlink->time_base = inlink0->time_base;
1240 outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
1241 outlink->frame_rate = inlink0->frame_rate;
1242
1243 s->depth = pix_desc->comp[0].depth;
1244 is_rgb = !!(pix_desc->flags & AV_PIX_FMT_FLAG_RGB);
1245 s->nb_planes = av_pix_fmt_count_planes(inlink0->format);
1246 s->max_value = (1 << s->depth) - 1;
1247 s->black[0] = 0;
1248 s->black[1] = s->black[2] = is_rgb ? 0 : s->max_value / 2;
1249 s->black[3] = s->max_value;
1250 s->white[0] = s->white[3] = s->max_value;
1251 s->white[1] = s->white[2] = is_rgb ? s->max_value : s->max_value / 2;
1252
1253 s->first_pts = s->last_pts = s->pts = AV_NOPTS_VALUE;
1254
1255 if (s->duration)
1256 s->duration_pts = av_rescale_q(s->duration, AV_TIME_BASE_Q, outlink->time_base);
1257 if (s->offset)
1258 s->offset_pts = av_rescale_q(s->offset, AV_TIME_BASE_Q, outlink->time_base);
1259
1260 switch (s->transition) {
1261 case CUSTOM: s->transitionf = s->depth <= 8 ? custom8_transition : custom16_transition; break;
1262 case FADE: s->transitionf = s->depth <= 8 ? fade8_transition : fade16_transition; break;
1263 case WIPELEFT: s->transitionf = s->depth <= 8 ? wipeleft8_transition : wipeleft16_transition; break;
1264 case WIPERIGHT: s->transitionf = s->depth <= 8 ? wiperight8_transition : wiperight16_transition; break;
1265 case WIPEUP: s->transitionf = s->depth <= 8 ? wipeup8_transition : wipeup16_transition; break;
1266 case WIPEDOWN: s->transitionf = s->depth <= 8 ? wipedown8_transition : wipedown16_transition; break;
1267 case SLIDELEFT: s->transitionf = s->depth <= 8 ? slideleft8_transition : slideleft16_transition; break;
1268 case SLIDERIGHT: s->transitionf = s->depth <= 8 ? slideright8_transition : slideright16_transition; break;
1269 case SLIDEUP: s->transitionf = s->depth <= 8 ? slideup8_transition : slideup16_transition; break;
1270 case SLIDEDOWN: s->transitionf = s->depth <= 8 ? slidedown8_transition : slidedown16_transition; break;
1271 case CIRCLECROP: s->transitionf = s->depth <= 8 ? circlecrop8_transition : circlecrop16_transition; break;
1272 case RECTCROP: s->transitionf = s->depth <= 8 ? rectcrop8_transition : rectcrop16_transition; break;
1273 case DISTANCE: s->transitionf = s->depth <= 8 ? distance8_transition : distance16_transition; break;
1274 case FADEBLACK: s->transitionf = s->depth <= 8 ? fadeblack8_transition : fadeblack16_transition; break;
1275 case FADEWHITE: s->transitionf = s->depth <= 8 ? fadewhite8_transition : fadewhite16_transition; break;
1276 case RADIAL: s->transitionf = s->depth <= 8 ? radial8_transition : radial16_transition; break;
1277 case SMOOTHLEFT: s->transitionf = s->depth <= 8 ? smoothleft8_transition : smoothleft16_transition; break;
1278 case SMOOTHRIGHT:s->transitionf = s->depth <= 8 ? smoothright8_transition: smoothright16_transition;break;
1279 case SMOOTHUP: s->transitionf = s->depth <= 8 ? smoothup8_transition : smoothup16_transition; break;
1280 case SMOOTHDOWN: s->transitionf = s->depth <= 8 ? smoothdown8_transition : smoothdown16_transition; break;
1281 case CIRCLEOPEN: s->transitionf = s->depth <= 8 ? circleopen8_transition : circleopen16_transition; break;
1282 case CIRCLECLOSE:s->transitionf = s->depth <= 8 ? circleclose8_transition: circleclose16_transition;break;
1283 case VERTOPEN: s->transitionf = s->depth <= 8 ? vertopen8_transition : vertopen16_transition; break;
1284 case VERTCLOSE: s->transitionf = s->depth <= 8 ? vertclose8_transition : vertclose16_transition; break;
1285 case HORZOPEN: s->transitionf = s->depth <= 8 ? horzopen8_transition : horzopen16_transition; break;
1286 case HORZCLOSE: s->transitionf = s->depth <= 8 ? horzclose8_transition : horzclose16_transition; break;
1287 case DISSOLVE: s->transitionf = s->depth <= 8 ? dissolve8_transition : dissolve16_transition; break;
1288 case PIXELIZE: s->transitionf = s->depth <= 8 ? pixelize8_transition : pixelize16_transition; break;
1289 case DIAGTL: s->transitionf = s->depth <= 8 ? diagtl8_transition : diagtl16_transition; break;
1290 case DIAGTR: s->transitionf = s->depth <= 8 ? diagtr8_transition : diagtr16_transition; break;
1291 case DIAGBL: s->transitionf = s->depth <= 8 ? diagbl8_transition : diagbl16_transition; break;
1292 case DIAGBR: s->transitionf = s->depth <= 8 ? diagbr8_transition : diagbr16_transition; break;
1293 }
1294
1295 if (s->transition == CUSTOM) {
1296 static const char *const func2_names[] = {
1297 "a0", "a1", "a2", "a3",
1298 "b0", "b1", "b2", "b3",
1299 NULL
1300 };
1301 double (*func2[])(void *, double, double) = {
1302 a0, a1, a2, a3,
1303 b0, b1, b2, b3,
1304 NULL };
1305 int ret;
1306
1307 if (!s->custom_str)
1308 return AVERROR(EINVAL);
1309 ret = av_expr_parse(&s->e, s->custom_str, var_names,
1310 NULL, NULL, func2_names, func2, 0, ctx);
1311 if (ret < 0)
1312 return ret;
1313 }
1314
1315 return 0;
1316 }
1317
xfade_slice(AVFilterContext * ctx,void * arg,int jobnr,int nb_jobs)1318 static int xfade_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
1319 {
1320 XFadeContext *s = ctx->priv;
1321 AVFilterLink *outlink = ctx->outputs[0];
1322 ThreadData *td = arg;
1323 int slice_start = (outlink->h * jobnr ) / nb_jobs;
1324 int slice_end = (outlink->h * (jobnr+1)) / nb_jobs;
1325
1326 s->transitionf(ctx, td->xf[0], td->xf[1], td->out, td->progress, slice_start, slice_end, jobnr);
1327
1328 return 0;
1329 }
1330
xfade_frame(AVFilterContext * ctx,AVFrame * a,AVFrame * b)1331 static int xfade_frame(AVFilterContext *ctx, AVFrame *a, AVFrame *b)
1332 {
1333 XFadeContext *s = ctx->priv;
1334 AVFilterLink *outlink = ctx->outputs[0];
1335 float progress = av_clipf(1.f - ((float)(s->pts - s->first_pts - s->offset_pts) / s->duration_pts), 0.f, 1.f);
1336 ThreadData td;
1337 AVFrame *out;
1338
1339 out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
1340 if (!out)
1341 return AVERROR(ENOMEM);
1342
1343 td.xf[0] = a, td.xf[1] = b, td.out = out, td.progress = progress;
1344 ctx->internal->execute(ctx, xfade_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
1345
1346 out->pts = s->pts;
1347
1348 return ff_filter_frame(outlink, out);
1349 }
1350
xfade_activate(AVFilterContext * ctx)1351 static int xfade_activate(AVFilterContext *ctx)
1352 {
1353 XFadeContext *s = ctx->priv;
1354 AVFilterLink *outlink = ctx->outputs[0];
1355 AVFrame *in = NULL;
1356 int ret = 0, status;
1357 int64_t pts;
1358
1359 FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx);
1360
1361 if (s->xfade_is_over) {
1362 ret = ff_inlink_consume_frame(ctx->inputs[1], &in);
1363 if (ret < 0) {
1364 return ret;
1365 } else if (ret > 0) {
1366 in->pts = (in->pts - s->last_pts) + s->pts;
1367 return ff_filter_frame(outlink, in);
1368 } else if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) {
1369 ff_outlink_set_status(outlink, status, s->pts);
1370 return 0;
1371 } else if (!ret) {
1372 if (ff_outlink_frame_wanted(outlink)) {
1373 ff_inlink_request_frame(ctx->inputs[1]);
1374 return 0;
1375 }
1376 }
1377 }
1378
1379 if (ff_inlink_queued_frames(ctx->inputs[0]) > 0) {
1380 s->xf[0] = ff_inlink_peek_frame(ctx->inputs[0], 0);
1381 if (s->xf[0]) {
1382 if (s->first_pts == AV_NOPTS_VALUE) {
1383 s->first_pts = s->xf[0]->pts;
1384 }
1385 s->pts = s->xf[0]->pts;
1386 if (s->first_pts + s->offset_pts > s->xf[0]->pts) {
1387 s->xf[0] = NULL;
1388 s->need_second = 0;
1389 ff_inlink_consume_frame(ctx->inputs[0], &in);
1390 return ff_filter_frame(outlink, in);
1391 }
1392
1393 s->need_second = 1;
1394 }
1395 }
1396
1397 if (s->xf[0] && ff_inlink_queued_frames(ctx->inputs[1]) > 0) {
1398 ff_inlink_consume_frame(ctx->inputs[0], &s->xf[0]);
1399 ff_inlink_consume_frame(ctx->inputs[1], &s->xf[1]);
1400
1401 s->last_pts = s->xf[1]->pts;
1402 s->pts = s->xf[0]->pts;
1403 if (s->xf[0]->pts - (s->first_pts + s->offset_pts) > s->duration_pts)
1404 s->xfade_is_over = 1;
1405 ret = xfade_frame(ctx, s->xf[0], s->xf[1]);
1406 av_frame_free(&s->xf[0]);
1407 av_frame_free(&s->xf[1]);
1408 return ret;
1409 }
1410
1411 if (ff_inlink_queued_frames(ctx->inputs[0]) > 0 &&
1412 ff_inlink_queued_frames(ctx->inputs[1]) > 0) {
1413 ff_filter_set_ready(ctx, 100);
1414 return 0;
1415 }
1416
1417 if (ff_outlink_frame_wanted(outlink)) {
1418 if (!s->eof[0] && ff_outlink_get_status(ctx->inputs[0])) {
1419 s->eof[0] = 1;
1420 s->xfade_is_over = 1;
1421 }
1422 if (!s->eof[1] && ff_outlink_get_status(ctx->inputs[1])) {
1423 s->eof[1] = 1;
1424 }
1425 if (!s->eof[0] && !s->xf[0])
1426 ff_inlink_request_frame(ctx->inputs[0]);
1427 if (!s->eof[1] && (s->need_second || s->eof[0]))
1428 ff_inlink_request_frame(ctx->inputs[1]);
1429 if (s->eof[0] && s->eof[1] && (
1430 ff_inlink_queued_frames(ctx->inputs[0]) <= 0 ||
1431 ff_inlink_queued_frames(ctx->inputs[1]) <= 0))
1432 ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE);
1433 return 0;
1434 }
1435
1436 return FFERROR_NOT_READY;
1437 }
1438
1439 static const AVFilterPad xfade_inputs[] = {
1440 {
1441 .name = "main",
1442 .type = AVMEDIA_TYPE_VIDEO,
1443 },
1444 {
1445 .name = "xfade",
1446 .type = AVMEDIA_TYPE_VIDEO,
1447 },
1448 { NULL }
1449 };
1450
1451 static const AVFilterPad xfade_outputs[] = {
1452 {
1453 .name = "default",
1454 .type = AVMEDIA_TYPE_VIDEO,
1455 .config_props = config_output,
1456 },
1457 { NULL }
1458 };
1459
1460 AVFilter ff_vf_xfade = {
1461 .name = "xfade",
1462 .description = NULL_IF_CONFIG_SMALL("Cross fade one video with another video."),
1463 .priv_size = sizeof(XFadeContext),
1464 .priv_class = &xfade_class,
1465 .query_formats = query_formats,
1466 .activate = xfade_activate,
1467 .uninit = uninit,
1468 .inputs = xfade_inputs,
1469 .outputs = xfade_outputs,
1470 .flags = AVFILTER_FLAG_SLICE_THREADS,
1471 };
1472