2 * Copyright (c) 2010 Brandon Mintern
3 * Copyright (c) 2007 Bobby Bingham
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 * based heavily on vf_negate.c by Bobby Bingham
28 #include "libavutil/avassert.h"
29 #include "libavutil/avstring.h"
30 #include "libavutil/common.h"
31 #include "libavutil/eval.h"
32 #include "libavutil/opt.h"
33 #include "libavutil/pixdesc.h"
35 #include "drawutils.h"
52 typedef struct FadeContext
{
55 int factor
, fade_per_frame
;
56 int start_frame
, nb_frames
;
58 unsigned int black_level
, black_level_scaled
;
59 uint8_t is_packed_rgb
;
62 uint64_t start_time
, duration
;
63 enum {VF_FADE_WAITING
=0, VF_FADE_FADING
, VF_FADE_DONE
} fade_state
;
64 uint8_t color_rgba
[4]; ///< fade color
65 int black_fade
; ///< if color_rgba is black
68 static av_cold
int init(AVFilterContext
*ctx
)
70 FadeContext
*s
= ctx
->priv
;
72 s
->fade_per_frame
= (1 << 16) / s
->nb_frames
;
73 s
->fade_state
= VF_FADE_WAITING
;
75 if (s
->duration
!= 0) {
76 // If duration (seconds) is non-zero, assume that we are not fading based on frames
77 s
->nb_frames
= 0; // Mostly to clean up logging
80 // Choose what to log. If both time-based and frame-based options, both lines will be in the log
81 if (s
->start_frame
|| s
->nb_frames
) {
82 av_log(ctx
, AV_LOG_VERBOSE
,
83 "type:%s start_frame:%d nb_frames:%d alpha:%d\n",
84 s
->type
== FADE_IN
? "in" : "out", s
->start_frame
,
85 s
->nb_frames
,s
->alpha
);
87 if (s
->start_time
|| s
->duration
) {
88 av_log(ctx
, AV_LOG_VERBOSE
,
89 "type:%s start_time:%f duration:%f alpha:%d\n",
90 s
->type
== FADE_IN
? "in" : "out", (s
->start_time
/ (double)AV_TIME_BASE
),
91 (s
->duration
/ (double)AV_TIME_BASE
),s
->alpha
);
94 s
->black_fade
= !memcmp(s
->color_rgba
, "\x00\x00\x00\xff", 4);
98 static int query_formats(AVFilterContext
*ctx
)
100 const FadeContext
*s
= ctx
->priv
;
101 static const enum AVPixelFormat pix_fmts
[] = {
102 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV420P
,
103 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
104 AV_PIX_FMT_YUVJ444P
, AV_PIX_FMT_YUVJ422P
, AV_PIX_FMT_YUVJ420P
,
105 AV_PIX_FMT_YUV440P
, AV_PIX_FMT_YUVJ440P
,
106 AV_PIX_FMT_YUVA420P
, AV_PIX_FMT_YUVA422P
, AV_PIX_FMT_YUVA444P
,
107 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
108 AV_PIX_FMT_ARGB
, AV_PIX_FMT_ABGR
,
109 AV_PIX_FMT_RGBA
, AV_PIX_FMT_BGRA
,
112 static const enum AVPixelFormat pix_fmts_rgb
[] = {
113 AV_PIX_FMT_RGB24
, AV_PIX_FMT_BGR24
,
114 AV_PIX_FMT_ARGB
, AV_PIX_FMT_ABGR
,
115 AV_PIX_FMT_RGBA
, AV_PIX_FMT_BGRA
,
120 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts
));
122 ff_set_common_formats(ctx
, ff_make_format_list(pix_fmts_rgb
));
126 const static enum AVPixelFormat studio_level_pix_fmts
[] = {
127 AV_PIX_FMT_YUV444P
, AV_PIX_FMT_YUV422P
, AV_PIX_FMT_YUV420P
,
128 AV_PIX_FMT_YUV411P
, AV_PIX_FMT_YUV410P
,
133 static int config_props(AVFilterLink
*inlink
)
135 FadeContext
*s
= inlink
->dst
->priv
;
136 const AVPixFmtDescriptor
*pixdesc
= av_pix_fmt_desc_get(inlink
->format
);
138 s
->hsub
= pixdesc
->log2_chroma_w
;
139 s
->vsub
= pixdesc
->log2_chroma_h
;
141 s
->bpp
= av_get_bits_per_pixel(pixdesc
) >> 3;
142 s
->alpha
&= !!(pixdesc
->flags
& AV_PIX_FMT_FLAG_ALPHA
);
143 s
->is_packed_rgb
= ff_fill_rgba_map(s
->rgba_map
, inlink
->format
) >= 0;
145 /* use CCIR601/709 black level for studio-level pixel non-alpha components */
147 ff_fmt_is_in(inlink
->format
, studio_level_pix_fmts
) && !s
->alpha
? 16 : 0;
148 /* 32768 = 1 << 15, it is an integer representation
149 * of 0.5 and is for rounding. */
150 s
->black_level_scaled
= (s
->black_level
<< 16) + 32768;
154 static av_always_inline
void filter_rgb(FadeContext
*s
, const AVFrame
*frame
,
155 int slice_start
, int slice_end
,
156 int do_alpha
, int step
)
159 const uint8_t r_idx
= s
->rgba_map
[R
];
160 const uint8_t g_idx
= s
->rgba_map
[G
];
161 const uint8_t b_idx
= s
->rgba_map
[B
];
162 const uint8_t a_idx
= s
->rgba_map
[A
];
163 const uint8_t *c
= s
->color_rgba
;
165 for (i
= slice_start
; i
< slice_end
; i
++) {
166 uint8_t *p
= frame
->data
[0] + i
* frame
->linesize
[0];
167 for (j
= 0; j
< frame
->width
; j
++) {
168 #define INTERP(c_name, c_idx) av_clip_uint8(((c[c_idx]<<16) + ((int)p[c_name] - (int)c[c_idx]) * s->factor + (1<<15)) >> 16)
169 p
[r_idx
] = INTERP(r_idx
, 0);
170 p
[g_idx
] = INTERP(g_idx
, 1);
171 p
[b_idx
] = INTERP(b_idx
, 2);
173 p
[a_idx
] = INTERP(a_idx
, 3);
179 static int filter_slice_rgb(AVFilterContext
*ctx
, void *arg
, int jobnr
,
182 FadeContext
*s
= ctx
->priv
;
183 AVFrame
*frame
= arg
;
184 int slice_start
= (frame
->height
* jobnr
) / nb_jobs
;
185 int slice_end
= (frame
->height
* (jobnr
+1)) / nb_jobs
;
187 if (s
->alpha
) filter_rgb(s
, frame
, slice_start
, slice_end
, 1, 4);
188 else if (s
->bpp
== 3) filter_rgb(s
, frame
, slice_start
, slice_end
, 0, 3);
189 else if (s
->bpp
== 4) filter_rgb(s
, frame
, slice_start
, slice_end
, 0, 4);
195 static int filter_slice_luma(AVFilterContext
*ctx
, void *arg
, int jobnr
,
198 FadeContext
*s
= ctx
->priv
;
199 AVFrame
*frame
= arg
;
200 int slice_start
= (frame
->height
* jobnr
) / nb_jobs
;
201 int slice_end
= (frame
->height
* (jobnr
+1)) / nb_jobs
;
204 for (i
= slice_start
; i
< slice_end
; i
++) {
205 uint8_t *p
= frame
->data
[0] + i
* frame
->linesize
[0];
206 for (j
= 0; j
< frame
->width
* s
->bpp
; j
++) {
207 /* s->factor is using 16 lower-order bits for decimal
208 * places. 32768 = 1 << 15, it is an integer representation
209 * of 0.5 and is for rounding. */
210 *p
= ((*p
- s
->black_level
) * s
->factor
+ s
->black_level_scaled
) >> 16;
218 static int filter_slice_chroma(AVFilterContext
*ctx
, void *arg
, int jobnr
,
221 FadeContext
*s
= ctx
->priv
;
222 AVFrame
*frame
= arg
;
224 const int width
= FF_CEIL_RSHIFT(frame
->width
, s
->hsub
);
225 const int height
= FF_CEIL_RSHIFT(frame
->height
, s
->vsub
);
226 int slice_start
= (height
* jobnr
) / nb_jobs
;
227 int slice_end
= (height
* (jobnr
+1)) / nb_jobs
;
229 for (plane
= 1; plane
< 3; plane
++) {
230 for (i
= slice_start
; i
< slice_end
; i
++) {
231 uint8_t *p
= frame
->data
[plane
] + i
* frame
->linesize
[plane
];
232 for (j
= 0; j
< width
; j
++) {
233 /* 8421367 = ((128 << 1) + 1) << 15. It is an integer
234 * representation of 128.5. The .5 is for rounding
236 *p
= ((*p
- 128) * s
->factor
+ 8421367) >> 16;
245 static int filter_slice_alpha(AVFilterContext
*ctx
, void *arg
, int jobnr
,
248 FadeContext
*s
= ctx
->priv
;
249 AVFrame
*frame
= arg
;
250 int plane
= s
->is_packed_rgb
? 0 : A
;
251 int slice_start
= (frame
->height
* jobnr
) / nb_jobs
;
252 int slice_end
= (frame
->height
* (jobnr
+1)) / nb_jobs
;
255 for (i
= slice_start
; i
< slice_end
; i
++) {
256 uint8_t *p
= frame
->data
[plane
] + i
* frame
->linesize
[plane
] + s
->is_packed_rgb
*s
->rgba_map
[A
];
257 int step
= s
->is_packed_rgb
? 4 : 1;
258 for (j
= 0; j
< frame
->width
; j
++) {
259 /* s->factor is using 16 lower-order bits for decimal
260 * places. 32768 = 1 << 15, it is an integer representation
261 * of 0.5 and is for rounding. */
262 *p
= ((*p
- s
->black_level
) * s
->factor
+ s
->black_level_scaled
) >> 16;
270 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*frame
)
272 AVFilterContext
*ctx
= inlink
->dst
;
273 FadeContext
*s
= ctx
->priv
;
274 double frame_timestamp
= frame
->pts
== AV_NOPTS_VALUE
? -1 : frame
->pts
* av_q2d(inlink
->time_base
);
276 // Calculate Fade assuming this is a Fade In
277 if (s
->fade_state
== VF_FADE_WAITING
) {
279 if (frame_timestamp
>= s
->start_time
/(double)AV_TIME_BASE
280 && inlink
->frame_count
>= s
->start_frame
) {
281 // Time to start fading
282 s
->fade_state
= VF_FADE_FADING
;
284 // Save start time in case we are starting based on frames and fading based on time
285 if (s
->start_time
== 0 && s
->start_frame
!= 0) {
286 s
->start_time
= frame_timestamp
*(double)AV_TIME_BASE
;
289 // Save start frame in case we are starting based on time and fading based on frames
290 if (s
->start_time
!= 0 && s
->start_frame
== 0) {
291 s
->start_frame
= inlink
->frame_count
;
295 if (s
->fade_state
== VF_FADE_FADING
) {
296 if (s
->duration
== 0) {
297 // Fading based on frame count
298 s
->factor
= (inlink
->frame_count
- s
->start_frame
) * s
->fade_per_frame
;
299 if (inlink
->frame_count
> s
->start_frame
+ s
->nb_frames
) {
300 s
->fade_state
= VF_FADE_DONE
;
304 // Fading based on duration
305 s
->factor
= (frame_timestamp
- s
->start_time
/(double)AV_TIME_BASE
)
306 * (float) UINT16_MAX
/ (s
->duration
/(double)AV_TIME_BASE
);
307 if (frame_timestamp
> s
->start_time
/(double)AV_TIME_BASE
308 + s
->duration
/(double)AV_TIME_BASE
) {
309 s
->fade_state
= VF_FADE_DONE
;
313 if (s
->fade_state
== VF_FADE_DONE
) {
314 s
->factor
=UINT16_MAX
;
317 s
->factor
= av_clip_uint16(s
->factor
);
319 // Invert fade_factor if Fading Out
320 if (s
->type
== FADE_OUT
) {
321 s
->factor
=UINT16_MAX
-s
->factor
;
324 if (s
->factor
< UINT16_MAX
) {
326 ctx
->internal
->execute(ctx
, filter_slice_alpha
, frame
, NULL
,
327 FFMIN(frame
->height
, ctx
->graph
->nb_threads
));
328 } else if (s
->is_packed_rgb
&& !s
->black_fade
) {
329 ctx
->internal
->execute(ctx
, filter_slice_rgb
, frame
, NULL
,
330 FFMIN(frame
->height
, ctx
->graph
->nb_threads
));
332 /* luma, or rgb plane in case of black */
333 ctx
->internal
->execute(ctx
, filter_slice_luma
, frame
, NULL
,
334 FFMIN(frame
->height
, ctx
->graph
->nb_threads
));
336 if (frame
->data
[1] && frame
->data
[2]) {
338 ctx
->internal
->execute(ctx
, filter_slice_chroma
, frame
, NULL
,
339 FFMIN(frame
->height
, ctx
->graph
->nb_threads
));
344 return ff_filter_frame(inlink
->dst
->outputs
[0], frame
);
348 #define OFFSET(x) offsetof(FadeContext, x)
349 #define FLAGS AV_OPT_FLAG_VIDEO_PARAM|AV_OPT_FLAG_FILTERING_PARAM
351 static const AVOption fade_options
[] = {
352 { "type", "'in' or 'out' for fade-in/fade-out", OFFSET(type
), AV_OPT_TYPE_INT
, { .i64
= FADE_IN
}, FADE_IN
, FADE_OUT
, FLAGS
, "type" },
353 { "t", "'in' or 'out' for fade-in/fade-out", OFFSET(type
), AV_OPT_TYPE_INT
, { .i64
= FADE_IN
}, FADE_IN
, FADE_OUT
, FLAGS
, "type" },
354 { "in", "fade-in", 0, AV_OPT_TYPE_CONST
, { .i64
= FADE_IN
}, .unit
= "type" },
355 { "out", "fade-out", 0, AV_OPT_TYPE_CONST
, { .i64
= FADE_OUT
}, .unit
= "type" },
356 { "start_frame", "Number of the first frame to which to apply the effect.",
357 OFFSET(start_frame
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, FLAGS
},
358 { "s", "Number of the first frame to which to apply the effect.",
359 OFFSET(start_frame
), AV_OPT_TYPE_INT
, { .i64
= 0 }, 0, INT_MAX
, FLAGS
},
360 { "nb_frames", "Number of frames to which the effect should be applied.",
361 OFFSET(nb_frames
), AV_OPT_TYPE_INT
, { .i64
= 25 }, 0, INT_MAX
, FLAGS
},
362 { "n", "Number of frames to which the effect should be applied.",
363 OFFSET(nb_frames
), AV_OPT_TYPE_INT
, { .i64
= 25 }, 0, INT_MAX
, FLAGS
},
364 { "alpha", "fade alpha if it is available on the input", OFFSET(alpha
), AV_OPT_TYPE_INT
, {.i64
= 0 }, 0, 1, FLAGS
},
365 { "start_time", "Number of seconds of the beginning of the effect.",
366 OFFSET(start_time
), AV_OPT_TYPE_DURATION
, {.i64
= 0. }, 0, INT32_MAX
, FLAGS
},
367 { "st", "Number of seconds of the beginning of the effect.",
368 OFFSET(start_time
), AV_OPT_TYPE_DURATION
, {.i64
= 0. }, 0, INT32_MAX
, FLAGS
},
369 { "duration", "Duration of the effect in seconds.",
370 OFFSET(duration
), AV_OPT_TYPE_DURATION
, {.i64
= 0. }, 0, INT32_MAX
, FLAGS
},
371 { "d", "Duration of the effect in seconds.",
372 OFFSET(duration
), AV_OPT_TYPE_DURATION
, {.i64
= 0. }, 0, INT32_MAX
, FLAGS
},
373 { "color", "set color", OFFSET(color_rgba
), AV_OPT_TYPE_COLOR
, {.str
= "black"}, CHAR_MIN
, CHAR_MAX
, FLAGS
},
374 { "c", "set color", OFFSET(color_rgba
), AV_OPT_TYPE_COLOR
, {.str
= "black"}, CHAR_MIN
, CHAR_MAX
, FLAGS
},
378 AVFILTER_DEFINE_CLASS(fade
);
380 static const AVFilterPad avfilter_vf_fade_inputs
[] = {
383 .type
= AVMEDIA_TYPE_VIDEO
,
384 .config_props
= config_props
,
385 .filter_frame
= filter_frame
,
391 static const AVFilterPad avfilter_vf_fade_outputs
[] = {
394 .type
= AVMEDIA_TYPE_VIDEO
,
399 AVFilter ff_vf_fade
= {
401 .description
= NULL_IF_CONFIG_SMALL("Fade in/out input video."),
403 .priv_size
= sizeof(FadeContext
),
404 .priv_class
= &fade_class
,
405 .query_formats
= query_formats
,
406 .inputs
= avfilter_vf_fade_inputs
,
407 .outputs
= avfilter_vf_fade_outputs
,
408 .flags
= AVFILTER_FLAG_SLICE_THREADS
,