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