Commit 4ac2da37 authored by Andy Martin's avatar Andy Martin Committed by Clément Bœsch

lavfi/fade: Added ability to do video fade based on timestamp

parent ebea370d
......@@ -3131,6 +3131,17 @@ Default is 25.
@item alpha
If set to 1, fade only alpha channel, if one exists on the input.
Default value is 0.
@item start_time, st
Specify the timestamp (in seconds) of the frame to start to apply the fade
effect. If both start_frame and start_time are specified, the fade will start at
whichever comes last. Default is 0.
@item duration, d
The number of seconds for which the fade effect has to last. At the end of the
fade-in effect the output video will have the same intensity as the input video,
at the end of the fade-out transition the output video will be completely black.
If both duration and nb_frames are specified, duration is used. Default is 0.
@end table
@subsection Examples
......@@ -3171,6 +3182,13 @@ Fade in alpha over first 25 frames of video:
@example
fade=in:0:25:alpha=1
@end example
@item
Make first 5.5 seconds black, then fade in for 0.5 seconds:
@example
fade=t=in:st=5.5:d=0.5
@end example
@end itemize
@section field
......
......@@ -54,13 +54,14 @@ typedef struct {
int type;
int factor, fade_per_frame;
int start_frame, nb_frames;
unsigned int frame_index, stop_frame;
unsigned int frame_index;
int hsub, vsub, bpp;
unsigned int black_level, black_level_scaled;
uint8_t is_packed_rgb;
uint8_t rgba_map[4];
int alpha;
uint64_t start_time, duration;
enum {VF_FADE_WAITING=0, VF_FADE_FADING, VF_FADE_DONE} fade_state;
} FadeContext;
static av_cold int init(AVFilterContext *ctx)
......@@ -68,18 +69,27 @@ static av_cold int init(AVFilterContext *ctx)
FadeContext *fade = ctx->priv;
fade->fade_per_frame = (1 << 16) / fade->nb_frames;
if (fade->type == FADE_IN) {
fade->factor = 0;
} else if (fade->type == FADE_OUT) {
fade->fade_per_frame = -fade->fade_per_frame;
fade->factor = (1 << 16);
fade->fade_state = VF_FADE_WAITING;
if (fade->duration != 0) {
// If duration (seconds) is non-zero, assume that we are not fading based on frames
fade->nb_frames = 0; // Mostly to clean up logging
}
// Choose what to log. If both time-based and frame-based options, both lines will be in the log
if (fade->start_frame || fade->nb_frames) {
av_log(ctx, AV_LOG_VERBOSE,
"type:%s start_frame:%d nb_frames:%d alpha:%d\n",
fade->type == FADE_IN ? "in" : "out", fade->start_frame,
fade->nb_frames,fade->alpha);
}
if (fade->start_time || fade->duration) {
av_log(ctx, AV_LOG_VERBOSE,
"type:%s start_time:%f duration:%f alpha:%d\n",
fade->type == FADE_IN ? "in" : "out", (fade->start_time / (double)AV_TIME_BASE),
(fade->duration / (double)AV_TIME_BASE),fade->alpha);
}
fade->stop_frame = fade->start_frame + fade->nb_frames;
av_log(ctx, AV_LOG_VERBOSE,
"type:%s start_frame:%d nb_frames:%d alpha:%d\n",
fade->type == FADE_IN ? "in" : "out", fade->start_frame,
fade->nb_frames, fade->alpha);
return 0;
}
......@@ -153,6 +163,55 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
FadeContext *fade = inlink->dst->priv;
uint8_t *p;
int i, j, plane;
double frame_timestamp = frame->pts == AV_NOPTS_VALUE ? -1 : frame->pts * av_q2d(inlink->time_base);
// Calculate Fade assuming this is a Fade In
if (fade->fade_state == VF_FADE_WAITING) {
fade->factor=0;
if ((frame_timestamp >= (fade->start_time/(double)AV_TIME_BASE))
&& (fade->frame_index >= fade->start_frame)) {
// Time to start fading
fade->fade_state = VF_FADE_FADING;
// Save start time in case we are starting based on frames and fading based on time
if ((fade->start_time == 0) && (fade->start_frame != 0)) {
fade->start_time = frame_timestamp*(double)AV_TIME_BASE;
}
// Save start frame in case we are starting based on time and fading based on frames
if ((fade->start_time != 0) && (fade->start_frame == 0)) {
fade->start_frame = fade->frame_index;
}
}
}
if (fade->fade_state == VF_FADE_FADING) {
if (fade->duration == 0) {
// Fading based on frame count
fade->factor = (fade->frame_index - fade->start_frame) * fade->fade_per_frame;
if (fade->frame_index > (fade->start_frame + fade->nb_frames)) {
fade->fade_state = VF_FADE_DONE;
}
} else {
// Fading based on duration
fade->factor = (frame_timestamp - (fade->start_time/(double)AV_TIME_BASE))
* (float) UINT16_MAX / (fade->duration/(double)AV_TIME_BASE);
if (frame_timestamp > ((fade->start_time/(double)AV_TIME_BASE)
+ (fade->duration/(double)AV_TIME_BASE))) {
fade->fade_state = VF_FADE_DONE;
}
}
}
if (fade->fade_state == VF_FADE_DONE) {
fade->factor=UINT16_MAX;
}
fade->factor = av_clip_uint16(fade->factor);
// Invert fade_factor if Fading Out
if (fade->type == 1) {
fade->factor=UINT16_MAX-fade->factor;
}
if (fade->factor < UINT16_MAX) {
if (fade->alpha) {
......@@ -188,10 +247,6 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
}
}
if (fade->frame_index >= fade->start_frame &&
fade->frame_index <= fade->stop_frame)
fade->factor += fade->fade_per_frame;
fade->factor = av_clip_uint16(fade->factor);
fade->frame_index++;
return ff_filter_frame(inlink->dst->outputs[0], frame);
......@@ -215,6 +270,14 @@ static const AVOption fade_options[] = {
{ "n", "Number of frames to which the effect should be applied.",
OFFSET(nb_frames), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, FLAGS },
{ "alpha", "fade alpha if it is available on the input", OFFSET(alpha), AV_OPT_TYPE_INT, {.i64 = 0 }, 0, 1, FLAGS },
{ "start_time", "Number of seconds of the beginning of the effect.",
OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "st", "Number of seconds of the beginning of the effect.",
OFFSET(start_time), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "duration", "Duration of the effect in seconds.",
OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ "d", "Duration of the effect in seconds.",
OFFSET(duration), AV_OPT_TYPE_DURATION, {.i64 = 0. }, 0, INT32_MAX, FLAGS },
{ NULL },
};
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment