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