]> git.sesse.net Git - ffmpeg/blob - libavfilter/vf_xfade.c
avfilter: add xfade filter
[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 "framesync.h"
28 #include "internal.h"
29 #include "filters.h"
30 #include "video.h"
31
32 enum XFadeTransitions {
33     CUSTOM = -1,
34     FADE,
35     WIPELEFT,
36     WIPERIGHT,
37     WIPEUP,
38     WIPEDOWN,
39     SLIDELEFT,
40     SLIDERIGHT,
41     SLIDEUP,
42     SLIDEDOWN,
43     CIRCLECROP,
44     RECTCROP,
45     DISTANCE,
46     FADEBLACK,
47     FADEWHITE,
48     NB_TRANSITIONS,
49 };
50
51 typedef struct XFadeContext {
52     const AVClass *class;
53
54     int     transition;
55     int64_t duration;
56     int64_t offset;
57     char   *custom_str;
58
59     int nb_planes;
60     int depth;
61
62     int64_t duration_pts;
63     int64_t offset_pts;
64     int64_t first_pts;
65     int64_t last_pts;
66     int64_t pts;
67     int xfade_is_over;
68     int need_second;
69     int eof[2];
70     AVFrame *xf[2];
71     int max_value;
72     uint16_t black[4];
73     uint16_t white[4];
74
75     void (*transitionf)(AVFilterContext *ctx, const AVFrame *a, const AVFrame *b, AVFrame *out, float progress,
76                         int slice_start, int slice_end, int jobnr);
77
78     AVExpr *e;
79 } XFadeContext;
80
81 static const char *const var_names[] = {   "X",   "Y",   "W",   "H",   "A",   "B",   "PLANE",          "P",        NULL };
82 enum                                   { VAR_X, VAR_Y, VAR_W, VAR_H, VAR_A, VAR_B, VAR_PLANE, VAR_PROGRESS, VAR_VARS_NB };
83
84 typedef struct ThreadData {
85     const AVFrame *xf[2];
86     AVFrame *out;
87     float progress;
88 } ThreadData;
89
90 static int query_formats(AVFilterContext *ctx)
91 {
92     static const enum AVPixelFormat pix_fmts[] = {
93         AV_PIX_FMT_YUVA444P,
94         AV_PIX_FMT_YUVJ444P,
95         AV_PIX_FMT_YUV444P,
96         AV_PIX_FMT_GBRP, AV_PIX_FMT_GBRAP, AV_PIX_FMT_GRAY8,
97         AV_PIX_FMT_YUVA444P9, AV_PIX_FMT_GBRP9,
98         AV_PIX_FMT_YUV444P10,
99         AV_PIX_FMT_YUVA444P10,
100         AV_PIX_FMT_GBRP10, AV_PIX_FMT_GBRAP10, AV_PIX_FMT_GRAY10,
101         AV_PIX_FMT_YUV444P12,
102         AV_PIX_FMT_YUVA444P12,
103         AV_PIX_FMT_GBRP12, AV_PIX_FMT_GBRAP12, AV_PIX_FMT_GRAY12,
104         AV_PIX_FMT_YUV444P14, AV_PIX_FMT_GBRP14,
105         AV_PIX_FMT_YUV444P16,
106         AV_PIX_FMT_YUVA444P16,
107         AV_PIX_FMT_GBRP16, AV_PIX_FMT_GBRAP16, AV_PIX_FMT_GRAY16,
108         AV_PIX_FMT_NONE
109     };
110
111     AVFilterFormats *fmts_list = ff_make_format_list(pix_fmts);
112     if (!fmts_list)
113         return AVERROR(ENOMEM);
114     return ff_set_common_formats(ctx, fmts_list);
115 }
116
117 static av_cold void uninit(AVFilterContext *ctx)
118 {
119     XFadeContext *s = ctx->priv;
120
121     av_expr_free(s->e);
122 }
123
124 #define OFFSET(x) offsetof(XFadeContext, x)
125 #define FLAGS (AV_OPT_FLAG_FILTERING_PARAM | AV_OPT_FLAG_VIDEO_PARAM)
126
127 static const AVOption xfade_options[] = {
128     { "transition", "set cross fade transition", OFFSET(transition), AV_OPT_TYPE_INT, {.i64=FADE}, -1, NB_TRANSITIONS-1, FLAGS, "transition" },
129     {   "custom",    "custom transition",     0, AV_OPT_TYPE_CONST, {.i64=CUSTOM},    0, 0, FLAGS, "transition" },
130     {   "fade",      "fade transition",       0, AV_OPT_TYPE_CONST, {.i64=FADE},      0, 0, FLAGS, "transition" },
131     {   "wipeleft",  "wipe left transition",  0, AV_OPT_TYPE_CONST, {.i64=WIPELEFT},  0, 0, FLAGS, "transition" },
132     {   "wiperight", "wipe right transition", 0, AV_OPT_TYPE_CONST, {.i64=WIPERIGHT}, 0, 0, FLAGS, "transition" },
133     {   "wipeup",    "wipe up transition",    0, AV_OPT_TYPE_CONST, {.i64=WIPEUP},    0, 0, FLAGS, "transition" },
134     {   "wipedown",  "wipe down transition",  0, AV_OPT_TYPE_CONST, {.i64=WIPEDOWN},  0, 0, FLAGS, "transition" },
135     {   "slideleft",  "slide left transition",  0, AV_OPT_TYPE_CONST, {.i64=SLIDELEFT},  0, 0, FLAGS, "transition" },
136     {   "slideright", "slide right transition", 0, AV_OPT_TYPE_CONST, {.i64=SLIDERIGHT}, 0, 0, FLAGS, "transition" },
137     {   "slideup",    "slide up transition",    0, AV_OPT_TYPE_CONST, {.i64=SLIDEUP},    0, 0, FLAGS, "transition" },
138     {   "slidedown",  "slide down transition",  0, AV_OPT_TYPE_CONST, {.i64=SLIDEDOWN},  0, 0, FLAGS, "transition" },
139     {   "circlecrop", "circle crop transition", 0, AV_OPT_TYPE_CONST, {.i64=CIRCLECROP}, 0, 0, FLAGS, "transition" },
140     {   "rectcrop",   "rect crop transition",   0, AV_OPT_TYPE_CONST, {.i64=RECTCROP},   0, 0, FLAGS, "transition" },
141     {   "distance",   "distance transition",    0, AV_OPT_TYPE_CONST, {.i64=DISTANCE},   0, 0, FLAGS, "transition" },
142     {   "fadeblack",  "fadeblack transition",   0, AV_OPT_TYPE_CONST, {.i64=FADEBLACK},  0, 0, FLAGS, "transition" },
143     {   "fadewhite",  "fadewhite transition",   0, AV_OPT_TYPE_CONST, {.i64=FADEWHITE},  0, 0, FLAGS, "transition" },
144     { "duration", "set cross fade duration", OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64=1000000}, 0, 60000000, FLAGS },
145     { "offset",   "set cross fade start relative to first input stream", OFFSET(offset), AV_OPT_TYPE_DURATION, {.i64=0}, INT64_MIN, INT64_MAX, FLAGS },
146     { "expr",   "set expression for custom transition", OFFSET(custom_str), AV_OPT_TYPE_STRING, {.str=NULL}, 0, 0, FLAGS },
147     { NULL }
148 };
149
150 AVFILTER_DEFINE_CLASS(xfade);
151
152 #define CUSTOM_TRANSITION(name, type, div)                                           \
153 static void custom##name##_transition(AVFilterContext *ctx,                          \
154                             const AVFrame *a, const AVFrame *b, AVFrame *out,        \
155                             float progress,                                          \
156                             int slice_start, int slice_end, int jobnr)               \
157 {                                                                                    \
158     XFadeContext *s = ctx->priv;                                                     \
159     const int height = slice_end - slice_start;                                      \
160                                                                                      \
161     double values[VAR_VARS_NB];                                                      \
162     values[VAR_W] = out->width;                                                      \
163     values[VAR_H] = out->height;                                                     \
164     values[VAR_PROGRESS] = progress;                                                 \
165                                                                                      \
166     for (int p = 0; p < s->nb_planes; p++) {                                         \
167         const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
168         const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
169         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);         \
170                                                                                      \
171         values[VAR_PLANE] = p;                                                       \
172                                                                                      \
173         for (int y = 0; y < height; y++) {                                           \
174             values[VAR_Y] = slice_start + y;                                         \
175             for (int x = 0; x < out->width; x++) {                                   \
176                 values[VAR_X] = x;                                                   \
177                 values[VAR_A] = xf0[x];                                              \
178                 values[VAR_B] = xf1[x];                                              \
179                 dst[x] = av_expr_eval(s->e, values, s);                              \
180             }                                                                        \
181                                                                                      \
182             dst += out->linesize[p] / div;                                           \
183             xf0 += a->linesize[p] / div;                                             \
184             xf1 += b->linesize[p] / div;                                             \
185         }                                                                            \
186     }                                                                                \
187 }
188
189 CUSTOM_TRANSITION(8, uint8_t, 1)
190 CUSTOM_TRANSITION(16, uint16_t, 2)
191
192 static inline float mix(float a, float b, float mix)
193 {
194     return a * mix + b * (1.f - mix);
195 }
196
197 static inline float smoothstep(float edge0, float edge1, float x)
198 {
199     float t;
200
201     t = av_clipf((x - edge0) / (edge1 - edge0), 0.f, 1.f);
202
203     return t * t * (3.f - 2.f * t);
204 }
205
206 #define FADE_TRANSITION(name, type, div)                                             \
207 static void fade##name##_transition(AVFilterContext *ctx,                            \
208                             const AVFrame *a, const AVFrame *b, AVFrame *out,        \
209                             float progress,                                          \
210                             int slice_start, int slice_end, int jobnr)               \
211 {                                                                                    \
212     XFadeContext *s = ctx->priv;                                                     \
213     const int height = slice_end - slice_start;                                      \
214                                                                                      \
215     for (int p = 0; p < s->nb_planes; p++) {                                         \
216         const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
217         const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
218         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);         \
219                                                                                      \
220         for (int y = 0; y < height; y++) {                                           \
221             for (int x = 0; x < out->width; x++) {                                   \
222                 dst[x] = mix(xf0[x], xf1[x], progress);                              \
223             }                                                                        \
224                                                                                      \
225             dst += out->linesize[p] / div;                                           \
226             xf0 += a->linesize[p] / div;                                             \
227             xf1 += b->linesize[p] / div;                                             \
228         }                                                                            \
229     }                                                                                \
230 }
231
232 FADE_TRANSITION(8, uint8_t, 1)
233 FADE_TRANSITION(16, uint16_t, 2)
234
235 #define WIPELEFT_TRANSITION(name, type, div)                                         \
236 static void wipeleft##name##_transition(AVFilterContext *ctx,                        \
237                                 const AVFrame *a, const AVFrame *b, AVFrame *out,    \
238                                 float progress,                                      \
239                                 int slice_start, int slice_end, int jobnr)           \
240 {                                                                                    \
241     XFadeContext *s = ctx->priv;                                                     \
242     const int height = slice_end - slice_start;                                      \
243     const int z = out->width * progress;                                             \
244                                                                                      \
245     for (int p = 0; p < s->nb_planes; p++) {                                         \
246         const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
247         const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
248         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);         \
249                                                                                      \
250         for (int y = 0; y < height; y++) {                                           \
251             for (int x = 0; x < out->width; x++) {                                   \
252                 dst[x] = x > z ? xf1[x] : xf0[x];                                    \
253             }                                                                        \
254                                                                                      \
255             dst += out->linesize[p] / div;                                           \
256             xf0 += a->linesize[p] / div;                                             \
257             xf1 += b->linesize[p] / div;                                             \
258         }                                                                            \
259     }                                                                                \
260 }
261
262 WIPELEFT_TRANSITION(8, uint8_t, 1)
263 WIPELEFT_TRANSITION(16, uint16_t, 2)
264
265 #define WIPERIGHT_TRANSITION(name, type, div)                                        \
266 static void wiperight##name##_transition(AVFilterContext *ctx,                       \
267                                  const AVFrame *a, const AVFrame *b, AVFrame *out,   \
268                                  float progress,                                     \
269                                  int slice_start, int slice_end, int jobnr)          \
270 {                                                                                    \
271     XFadeContext *s = ctx->priv;                                                     \
272     const int height = slice_end - slice_start;                                      \
273     const int z = out->width * (1.f - progress);                                     \
274                                                                                      \
275     for (int p = 0; p < s->nb_planes; p++) {                                         \
276         const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
277         const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
278         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);         \
279                                                                                      \
280         for (int y = 0; y < height; y++) {                                           \
281             for (int x = 0; x < out->width; x++) {                                   \
282                 dst[x] = x > z ? xf0[x] : xf1[x];                                    \
283             }                                                                        \
284                                                                                      \
285             dst += out->linesize[p] / div;                                           \
286             xf0 += a->linesize[p] / div;                                             \
287             xf1 += b->linesize[p] / div;                                             \
288         }                                                                            \
289     }                                                                                \
290 }
291
292 WIPERIGHT_TRANSITION(8, uint8_t, 1)
293 WIPERIGHT_TRANSITION(16, uint16_t, 2)
294
295 #define WIPEUP_TRANSITION(name, type, div)                                           \
296 static void wipeup##name##_transition(AVFilterContext *ctx,                          \
297                               const AVFrame *a, const AVFrame *b, AVFrame *out,      \
298                               float progress,                                        \
299                               int slice_start, int slice_end, int jobnr)             \
300 {                                                                                    \
301     XFadeContext *s = ctx->priv;                                                     \
302     const int height = slice_end - slice_start;                                      \
303     const int z = out->height * progress;                                            \
304                                                                                      \
305     for (int p = 0; p < s->nb_planes; p++) {                                         \
306         const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
307         const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
308         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);         \
309                                                                                      \
310         for (int y = 0; y < height; y++) {                                           \
311             for (int x = 0; x < out->width; x++) {                                   \
312                 dst[x] = slice_start + y > z ? xf1[x] : xf0[x];                      \
313             }                                                                        \
314                                                                                      \
315             dst += out->linesize[p] / div;                                           \
316             xf0 += a->linesize[p] / div;                                             \
317             xf1 += b->linesize[p] / div;                                             \
318         }                                                                            \
319     }                                                                                \
320 }
321
322 WIPEUP_TRANSITION(8, uint8_t, 1)
323 WIPEUP_TRANSITION(16, uint16_t, 2)
324
325 #define WIPEDOWN_TRANSITION(name, type, div)                                         \
326 static void wipedown##name##_transition(AVFilterContext *ctx,                        \
327                                 const AVFrame *a, const AVFrame *b, AVFrame *out,    \
328                                 float progress,                                      \
329                                 int slice_start, int slice_end, int jobnr)           \
330 {                                                                                    \
331     XFadeContext *s = ctx->priv;                                                     \
332     const int height = slice_end - slice_start;                                      \
333     const int z = out->height * (1.f - progress);                                    \
334                                                                                      \
335     for (int p = 0; p < s->nb_planes; p++) {                                         \
336         const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
337         const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
338         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);         \
339                                                                                      \
340         for (int y = 0; y < height; y++) {                                           \
341             for (int x = 0; x < out->width; x++) {                                   \
342                 dst[x] = slice_start + y > z ? xf0[x] : xf1[x];                      \
343             }                                                                        \
344                                                                                      \
345             dst += out->linesize[p] / div;                                           \
346             xf0 += a->linesize[p] / div;                                             \
347             xf1 += b->linesize[p] / div;                                             \
348         }                                                                            \
349     }                                                                                \
350 }
351
352 WIPEDOWN_TRANSITION(8, uint8_t, 1)
353 WIPEDOWN_TRANSITION(16, uint16_t, 2)
354
355 #define SLIDELEFT_TRANSITION(name, type, div)                                        \
356 static void slideleft##name##_transition(AVFilterContext *ctx,                       \
357                                  const AVFrame *a, const AVFrame *b, AVFrame *out,   \
358                                  float progress,                                     \
359                                  int slice_start, int slice_end, int jobnr)          \
360 {                                                                                    \
361     XFadeContext *s = ctx->priv;                                                     \
362     const int height = slice_end - slice_start;                                      \
363     const int width = out->width;                                                    \
364     const int z = -progress * width;                                                 \
365                                                                                      \
366     for (int p = 0; p < s->nb_planes; p++) {                                         \
367         const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
368         const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
369         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);         \
370                                                                                      \
371         for (int y = 0; y < height; y++) {                                           \
372             for (int x = 0; x < width; x++) {                                        \
373                 const int zx = z + x;                                                \
374                 const int zz = zx % width + width * (zx < 0);                        \
375                 dst[x] = (zx > 0) && (zx < width) ? xf1[zz] : xf0[zz];               \
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 SLIDELEFT_TRANSITION(8, uint8_t, 1)
386 SLIDELEFT_TRANSITION(16, uint16_t, 2)
387
388 #define SLIDERIGHT_TRANSITION(name, type, div)                                       \
389 static void slideright##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 < out->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 SLIDERIGHT_TRANSITION(8, uint8_t, 1)
419 SLIDERIGHT_TRANSITION(16, uint16_t, 2)
420
421 #define SLIDEUP_TRANSITION(name, type, div)                                         \
422 static void slideup##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 = out->height;                                                 \
429     const int z = -progress * height;                                               \
430                                                                                     \
431     for (int p = 0; p < s->nb_planes; p++) {                                        \
432         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);        \
433                                                                                     \
434         for (int y = slice_start; y < slice_end; y++) {                             \
435             const int zy = z + y;                                                   \
436             const int zz = zy % height + height * (zy < 0);                         \
437             const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]);     \
438             const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]);     \
439                                                                                     \
440             for (int x = 0; x < out->width; x++) {                                  \
441                 dst[x] = (zy > 0) && (zy < height) ? xf1[x] : xf0[x];               \
442             }                                                                       \
443                                                                                     \
444             dst += out->linesize[p] / div;                                          \
445         }                                                                           \
446     }                                                                               \
447 }
448
449 SLIDEUP_TRANSITION(8, uint8_t, 1)
450 SLIDEUP_TRANSITION(16, uint16_t, 2)
451
452 #define SLIDEDOWN_TRANSITION(name, type, div)                                       \
453 static void slidedown##name##_transition(AVFilterContext *ctx,                      \
454                                  const AVFrame *a, const AVFrame *b, AVFrame *out,  \
455                                  float progress,                                    \
456                                  int slice_start, int slice_end, int jobnr)         \
457 {                                                                                   \
458     XFadeContext *s = ctx->priv;                                                    \
459     const int height = out->height;                                                 \
460     const int z = progress * height;                                                \
461                                                                                     \
462     for (int p = 0; p < s->nb_planes; p++) {                                        \
463         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);        \
464                                                                                     \
465         for (int y = slice_start; y < slice_end; y++) {                             \
466             const int zy = z + y;                                                   \
467             const int zz = zy % height + height * (zy < 0);                         \
468             const type *xf0 = (const type *)(a->data[p] + zz * a->linesize[p]);     \
469             const type *xf1 = (const type *)(b->data[p] + zz * b->linesize[p]);     \
470                                                                                     \
471             for (int x = 0; x < out->width; x++) {                                  \
472                 dst[x] = (zy > 0) && (zy < height) ? xf1[x] : xf0[x];               \
473             }                                                                       \
474                                                                                     \
475             dst += out->linesize[p] / div;                                          \
476         }                                                                           \
477     }                                                                               \
478 }
479
480 SLIDEDOWN_TRANSITION(8, uint8_t, 1)
481 SLIDEDOWN_TRANSITION(16, uint16_t, 2)
482
483 #define CIRCLECROP_TRANSITION(name, type, div)                                      \
484 static void circlecrop##name##_transition(AVFilterContext *ctx,                     \
485                                  const AVFrame *a, const AVFrame *b, AVFrame *out,  \
486                                  float progress,                                    \
487                                  int slice_start, int slice_end, int jobnr)         \
488 {                                                                                   \
489     XFadeContext *s = ctx->priv;                                                    \
490     const int width = out->width;                                                   \
491     const int height = out->height;                                                 \
492     float z = powf(2.f * fabsf(progress - 0.5f), 3.f) * hypotf(width/2, height/2);  \
493                                                                                     \
494     for (int p = 0; p < s->nb_planes; p++) {                                        \
495         const int bg = s->black[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 type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]);      \
500             const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]);      \
501                                                                                     \
502             for (int x = 0; x < width; x++) {                                       \
503                 float dist = hypotf(x - width / 2, y - height / 2);                 \
504                 int val = progress < 0.5f ? xf1[x] : xf0[x];                        \
505                 dst[x] = (z < dist) ? bg : val;                                     \
506             }                                                                       \
507                                                                                     \
508             dst += out->linesize[p] / div;                                          \
509         }                                                                           \
510     }                                                                               \
511 }
512
513 CIRCLECROP_TRANSITION(8, uint8_t, 1)
514 CIRCLECROP_TRANSITION(16, uint16_t, 2)
515
516 #define RECTCROP_TRANSITION(name, type, div)                                        \
517 static void rectcrop##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     int zh = fabsf(progress - 0.5f) * height;                                       \
526     int zw = fabsf(progress - 0.5f) * width;                                        \
527                                                                                     \
528     for (int p = 0; p < s->nb_planes; p++) {                                        \
529         const int bg = s->black[p];                                                 \
530         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);        \
531                                                                                     \
532         for (int y = slice_start; y < slice_end; y++) {                             \
533             const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]);      \
534             const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]);      \
535                                                                                     \
536             for (int x = 0; x < width; x++) {                                       \
537                 int dist = FFABS(x - width  / 2) < zw &&                            \
538                            FFABS(y - height / 2) < zh;                              \
539                 int val = progress < 0.5f ? xf1[x] : xf0[x];                        \
540                 dst[x] = !dist ? bg : val;                                          \
541             }                                                                       \
542                                                                                     \
543             dst += out->linesize[p] / div;                                          \
544         }                                                                           \
545     }                                                                               \
546 }
547
548 RECTCROP_TRANSITION(8, uint8_t, 1)
549 RECTCROP_TRANSITION(16, uint16_t, 2)
550
551 #define DISTANCE_TRANSITION(name, type, div)                                        \
552 static void distance##name##_transition(AVFilterContext *ctx,                       \
553                                  const AVFrame *a, const AVFrame *b, AVFrame *out,  \
554                                  float progress,                                    \
555                                  int slice_start, int slice_end, int jobnr)         \
556 {                                                                                   \
557     XFadeContext *s = ctx->priv;                                                    \
558     const int width = out->width;                                                   \
559     const float max = s->max_value;                                                 \
560                                                                                     \
561     for (int y = slice_start; y < slice_end; y++) {                                 \
562         for (int x = 0; x < width; x++) {                                           \
563             float dist = 0.f;                                                       \
564             for (int p = 0; p < s->nb_planes; p++) {                                \
565                 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]);  \
566                 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]);  \
567                                                                                     \
568                 dist += (xf0[x] / max - xf1[x] / max) *                             \
569                         (xf0[x] / max - xf1[x] / max);                              \
570             }                                                                       \
571                                                                                     \
572             dist = sqrtf(dist) <= progress;                                         \
573             for (int p = 0; p < s->nb_planes; p++) {                                \
574                 const type *xf0 = (const type *)(a->data[p] + y * a->linesize[p]);  \
575                 const type *xf1 = (const type *)(b->data[p] + y * b->linesize[p]);  \
576                 type *dst = (type *)(out->data[p] + y * out->linesize[p]);          \
577                 dst[x] = mix(mix(xf0[x], xf1[x], dist), xf1[x], progress);          \
578             }                                                                       \
579         }                                                                           \
580     }                                                                               \
581 }
582
583 DISTANCE_TRANSITION(8, uint8_t, 1)
584 DISTANCE_TRANSITION(16, uint16_t, 2)
585
586 #define FADEBLACK_TRANSITION(name, type, div)                                        \
587 static void fadeblack##name##_transition(AVFilterContext *ctx,                       \
588                             const AVFrame *a, const AVFrame *b, AVFrame *out,        \
589                             float progress,                                          \
590                             int slice_start, int slice_end, int jobnr)               \
591 {                                                                                    \
592     XFadeContext *s = ctx->priv;                                                     \
593     const int height = slice_end - slice_start;                                      \
594     const float phase = 0.2f;                                                        \
595                                                                                      \
596     for (int p = 0; p < s->nb_planes; p++) {                                         \
597         const type *xf0 = (const type *)(a->data[p] + slice_start * a->linesize[p]); \
598         const type *xf1 = (const type *)(b->data[p] + slice_start * b->linesize[p]); \
599         type *dst = (type *)(out->data[p] + slice_start * out->linesize[p]);         \
600         const int bg = s->black[p];                                                  \
601                                                                                      \
602         for (int y = 0; y < height; y++) {                                           \
603             for (int x = 0; x < out->width; x++) {                                   \
604                 dst[x] = mix(mix(xf0[x], bg, smoothstep(1.f-phase, 1.f, progress)),  \
605                          mix(bg, xf1[x], smoothstep(phase, 1.f, progress)),          \
606                              progress);                                              \
607             }                                                                        \
608                                                                                      \
609             dst += out->linesize[p] / div;                                           \
610             xf0 += a->linesize[p] / div;                                             \
611             xf1 += b->linesize[p] / div;                                             \
612         }                                                                            \
613     }                                                                                \
614 }
615
616 FADEBLACK_TRANSITION(8, uint8_t, 1)
617 FADEBLACK_TRANSITION(16, uint16_t, 2)
618
619 #define FADEWHITE_TRANSITION(name, type, div)                                        \
620 static void fadewhite##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->white[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 FADEWHITE_TRANSITION(8, uint8_t, 1)
650 FADEWHITE_TRANSITION(16, uint16_t, 2)
651
652 static inline double getpix(void *priv, double x, double y, int plane, int nb)
653 {
654     XFadeContext *s = priv;
655     AVFrame *in = s->xf[nb];
656     const uint8_t *src = in->data[FFMIN(plane, s->nb_planes - 1)];
657     int linesize = in->linesize[FFMIN(plane, s->nb_planes - 1)];
658     const int w = in->width;
659     const int h = in->height;
660
661     int xi, yi;
662
663     xi = av_clipd(x, 0, w - 1);
664     yi = av_clipd(y, 0, h - 1);
665
666     if (s->depth > 8) {
667         const uint16_t *src16 = (const uint16_t*)src;
668
669         linesize /= 2;
670         return src16[xi + yi * linesize];
671     } else {
672         return src[xi + yi * linesize];
673     }
674 }
675
676 static double a0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 0); }
677 static double a1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 0); }
678 static double a2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 0); }
679 static double a3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 0); }
680
681 static double b0(void *priv, double x, double y) { return getpix(priv, x, y, 0, 1); }
682 static double b1(void *priv, double x, double y) { return getpix(priv, x, y, 1, 1); }
683 static double b2(void *priv, double x, double y) { return getpix(priv, x, y, 2, 1); }
684 static double b3(void *priv, double x, double y) { return getpix(priv, x, y, 3, 1); }
685
686 static int config_output(AVFilterLink *outlink)
687 {
688     AVFilterContext *ctx = outlink->src;
689     AVFilterLink *inlink0 = ctx->inputs[0];
690     AVFilterLink *inlink1 = ctx->inputs[1];
691     XFadeContext *s = ctx->priv;
692     const AVPixFmtDescriptor *pix_desc = av_pix_fmt_desc_get(inlink0->format);
693     int is_rgb;
694
695     if (inlink0->format != inlink1->format) {
696         av_log(ctx, AV_LOG_ERROR, "inputs must be of same pixel format\n");
697         return AVERROR(EINVAL);
698     }
699     if (inlink0->w != inlink1->w || inlink0->h != inlink1->h) {
700         av_log(ctx, AV_LOG_ERROR, "First input link %s parameters "
701                "(size %dx%d) do not match the corresponding "
702                "second input link %s parameters (size %dx%d)\n",
703                ctx->input_pads[0].name, inlink0->w, inlink0->h,
704                ctx->input_pads[1].name, inlink1->w, inlink1->h);
705         return AVERROR(EINVAL);
706     }
707
708     if (inlink0->time_base.num != inlink1->time_base.num ||
709         inlink0->time_base.den != inlink1->time_base.den) {
710         av_log(ctx, AV_LOG_ERROR, "First input link %s timebase "
711                "(%d/%d) do not match the corresponding "
712                "second input link %s timebase (%d/%d)\n",
713                ctx->input_pads[0].name, inlink0->time_base.num, inlink0->time_base.den,
714                ctx->input_pads[1].name, inlink1->time_base.num, inlink1->time_base.den);
715         return AVERROR(EINVAL);
716     }
717
718     outlink->w = inlink0->w;
719     outlink->h = inlink0->h;
720     outlink->time_base = inlink0->time_base;
721     outlink->sample_aspect_ratio = inlink0->sample_aspect_ratio;
722     outlink->frame_rate = inlink0->frame_rate;
723
724     s->depth = pix_desc->comp[0].depth;
725     is_rgb = !!(pix_desc->flags & AV_PIX_FMT_FLAG_RGB);
726     s->nb_planes = av_pix_fmt_count_planes(inlink0->format);
727     s->max_value = (1 << s->depth) - 1;
728     s->black[0] = 0;
729     s->black[1] = s->black[2] = is_rgb ? 0 : s->max_value / 2;
730     s->black[3] = s->max_value;
731     s->white[0] = s->white[3] = s->max_value;
732     s->white[1] = s->white[2] = is_rgb ? s->max_value : s->max_value / 2;
733
734     s->first_pts = s->last_pts = s->pts = AV_NOPTS_VALUE;
735
736     if (s->duration)
737         s->duration_pts = av_rescale_q(s->duration, AV_TIME_BASE_Q, outlink->time_base);
738     if (s->offset)
739         s->offset_pts = av_rescale_q(s->offset, AV_TIME_BASE_Q, outlink->time_base);
740
741     switch (s->transition) {
742     case CUSTOM:     s->transitionf = s->depth <= 8 ? custom8_transition     : custom16_transition;     break;
743     case FADE:       s->transitionf = s->depth <= 8 ? fade8_transition       : fade16_transition;       break;
744     case WIPELEFT:   s->transitionf = s->depth <= 8 ? wipeleft8_transition   : wipeleft16_transition;   break;
745     case WIPERIGHT:  s->transitionf = s->depth <= 8 ? wiperight8_transition  : wiperight16_transition;  break;
746     case WIPEUP:     s->transitionf = s->depth <= 8 ? wipeup8_transition     : wipeup16_transition;     break;
747     case WIPEDOWN:   s->transitionf = s->depth <= 8 ? wipedown8_transition   : wipedown16_transition;   break;
748     case SLIDELEFT:  s->transitionf = s->depth <= 8 ? slideleft8_transition  : slideleft16_transition;  break;
749     case SLIDERIGHT: s->transitionf = s->depth <= 8 ? slideright8_transition : slideright16_transition; break;
750     case SLIDEUP:    s->transitionf = s->depth <= 8 ? slideup8_transition    : slideup16_transition;    break;
751     case SLIDEDOWN:  s->transitionf = s->depth <= 8 ? slidedown8_transition  : slidedown16_transition;  break;
752     case CIRCLECROP: s->transitionf = s->depth <= 8 ? circlecrop8_transition : circlecrop16_transition; break;
753     case RECTCROP:   s->transitionf = s->depth <= 8 ? rectcrop8_transition   : rectcrop16_transition;   break;
754     case DISTANCE:   s->transitionf = s->depth <= 8 ? distance8_transition   : distance16_transition;   break;
755     case FADEBLACK:  s->transitionf = s->depth <= 8 ? fadeblack8_transition  : fadeblack16_transition;  break;
756     case FADEWHITE:  s->transitionf = s->depth <= 8 ? fadewhite8_transition  : fadewhite16_transition;  break;
757     }
758
759     if (s->transition == CUSTOM) {
760         static const char *const func2_names[]    = {
761             "a0", "a1", "a2", "a3",
762             "b0", "b1", "b2", "b3",
763             NULL
764         };
765         double (*func2[])(void *, double, double) = {
766             a0, a1, a2, a3,
767             b0, b1, b2, b3,
768             NULL };
769         int ret;
770
771         if (!s->custom_str)
772             return AVERROR(EINVAL);
773         ret = av_expr_parse(&s->e, s->custom_str, var_names,
774                             NULL, NULL, func2_names, func2, 0, ctx);
775         if (ret < 0)
776             return ret;
777     }
778
779     return 0;
780 }
781
782 static int xfade_slice(AVFilterContext *ctx, void *arg, int jobnr, int nb_jobs)
783 {
784     XFadeContext *s = ctx->priv;
785     AVFilterLink *outlink = ctx->outputs[0];
786     ThreadData *td = arg;
787     int slice_start = (outlink->h *  jobnr   ) / nb_jobs;
788     int slice_end   = (outlink->h * (jobnr+1)) / nb_jobs;
789
790     s->transitionf(ctx, td->xf[0], td->xf[1], td->out, td->progress, slice_start, slice_end, jobnr);
791
792     return 0;
793 }
794
795 static int xfade_frame(AVFilterContext *ctx, AVFrame *a, AVFrame *b)
796 {
797     XFadeContext *s = ctx->priv;
798     AVFilterLink *outlink = ctx->outputs[0];
799     float progress = av_clipf(1.f - ((float)(s->pts - s->first_pts - s->offset_pts) / s->duration_pts), 0.f, 1.f);
800     ThreadData td;
801     AVFrame *out;
802
803     out = ff_get_video_buffer(outlink, outlink->w, outlink->h);
804     if (!out)
805         return AVERROR(ENOMEM);
806
807     td.xf[0] = a, td.xf[1] = b, td.out = out, td.progress = progress;
808     ctx->internal->execute(ctx, xfade_slice, &td, NULL, FFMIN(outlink->h, ff_filter_get_nb_threads(ctx)));
809
810     out->pts = s->pts;
811
812     return ff_filter_frame(outlink, out);
813 }
814
815 static int xfade_activate(AVFilterContext *ctx)
816 {
817     XFadeContext *s = ctx->priv;
818     AVFilterLink *outlink = ctx->outputs[0];
819     AVFrame *in = NULL;
820     int ret = 0, status;
821     int64_t pts;
822
823     FF_FILTER_FORWARD_STATUS_BACK_ALL(outlink, ctx);
824
825     if (s->xfade_is_over) {
826         ret = ff_inlink_consume_frame(ctx->inputs[1], &in);
827         if (ret < 0) {
828             return ret;
829         } else if (ff_inlink_acknowledge_status(ctx->inputs[1], &status, &pts)) {
830             ff_outlink_set_status(outlink, status, s->pts);
831             return 0;
832         } else if (!ret) {
833             if (ff_outlink_frame_wanted(outlink)) {
834                 ff_inlink_request_frame(ctx->inputs[1]);
835                 return 0;
836             }
837         } else {
838             in->pts = (in->pts - s->last_pts) + s->pts;
839             return ff_filter_frame(outlink, in);
840         }
841     }
842
843     if (ff_inlink_queued_frames(ctx->inputs[0]) > 0) {
844         s->xf[0] = ff_inlink_peek_frame(ctx->inputs[0], 0);
845         if (s->xf[0]) {
846             if (s->first_pts == AV_NOPTS_VALUE) {
847                 s->first_pts = s->xf[0]->pts;
848             }
849             s->pts = s->xf[0]->pts;
850             if (s->first_pts + s->offset_pts > s->xf[0]->pts) {
851                 s->xf[0] = NULL;
852                 s->need_second = 0;
853                 ff_inlink_consume_frame(ctx->inputs[0], &in);
854                 return ff_filter_frame(outlink, in);
855             }
856
857             s->need_second = 1;
858         }
859     }
860
861     if (s->xf[0] && ff_inlink_queued_frames(ctx->inputs[1]) > 0) {
862         ff_inlink_consume_frame(ctx->inputs[0], &s->xf[0]);
863         ff_inlink_consume_frame(ctx->inputs[1], &s->xf[1]);
864
865         s->last_pts = s->xf[1]->pts;
866         s->pts = s->xf[0]->pts;
867         if (s->xf[0]->pts - (s->first_pts + s->offset_pts) > s->duration_pts)
868             s->xfade_is_over = 1;
869         ret = xfade_frame(ctx, s->xf[0], s->xf[1]);
870         av_frame_free(&s->xf[0]);
871         av_frame_free(&s->xf[1]);
872         return ret;
873     }
874
875     if (ff_inlink_queued_frames(ctx->inputs[0]) > 0 &&
876         ff_inlink_queued_frames(ctx->inputs[1]) > 0) {
877         ff_filter_set_ready(ctx, 100);
878         return 0;
879     }
880
881     if (ff_outlink_frame_wanted(outlink)) {
882         if (!s->eof[0] && ff_outlink_get_status(ctx->inputs[0])) {
883             s->eof[0] = 1;
884             s->xfade_is_over = 1;
885         }
886         if (!s->eof[1] && ff_outlink_get_status(ctx->inputs[1])) {
887             s->eof[1] = 1;
888         }
889         if (!s->eof[0] && !s->xf[0])
890             ff_inlink_request_frame(ctx->inputs[0]);
891         if (!s->eof[1] && (s->need_second || s->eof[0]))
892             ff_inlink_request_frame(ctx->inputs[1]);
893         if (s->eof[0] && s->eof[1] && (
894             ff_inlink_queued_frames(ctx->inputs[0]) <= 0 ||
895             ff_inlink_queued_frames(ctx->inputs[1]) <= 0))
896             ff_outlink_set_status(outlink, AVERROR_EOF, AV_NOPTS_VALUE);
897         return 0;
898     }
899
900     return FFERROR_NOT_READY;
901 }
902
903 static const AVFilterPad xfade_inputs[] = {
904     {
905         .name          = "main",
906         .type          = AVMEDIA_TYPE_VIDEO,
907     },
908     {
909         .name          = "xfade",
910         .type          = AVMEDIA_TYPE_VIDEO,
911     },
912     { NULL }
913 };
914
915 static const AVFilterPad xfade_outputs[] = {
916     {
917         .name          = "default",
918         .type          = AVMEDIA_TYPE_VIDEO,
919         .config_props  = config_output,
920     },
921     { NULL }
922 };
923
924 AVFilter ff_vf_xfade = {
925     .name          = "xfade",
926     .description   = NULL_IF_CONFIG_SMALL("Cross fade one video with another video."),
927     .priv_size     = sizeof(XFadeContext),
928     .priv_class    = &xfade_class,
929     .query_formats = query_formats,
930     .activate      = xfade_activate,
931     .uninit        = uninit,
932     .inputs        = xfade_inputs,
933     .outputs       = xfade_outputs,
934     .flags         = AVFILTER_FLAG_SLICE_THREADS,
935 };