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