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

This commit is contained in:
Andy Martin 2013-04-16 21:35:02 -04:00 committed by Clément Bœsch
parent ebea370dc3
commit 4ac2da3793
2 changed files with 98 additions and 17 deletions

View File

@ -3131,6 +3131,17 @@ Default is 25.
@item alpha @item alpha
If set to 1, fade only alpha channel, if one exists on the input. If set to 1, fade only alpha channel, if one exists on the input.
Default value is 0. 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 @end table
@subsection Examples @subsection Examples
@ -3171,6 +3182,13 @@ Fade in alpha over first 25 frames of video:
@example @example
fade=in:0:25:alpha=1 fade=in:0:25:alpha=1
@end example @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 @end itemize
@section field @section field

View File

@ -54,13 +54,14 @@ typedef struct {
int type; int type;
int factor, fade_per_frame; int factor, fade_per_frame;
int start_frame, nb_frames; int start_frame, nb_frames;
unsigned int frame_index, stop_frame; unsigned int frame_index;
int hsub, vsub, bpp; int hsub, vsub, bpp;
unsigned int black_level, black_level_scaled; unsigned int black_level, black_level_scaled;
uint8_t is_packed_rgb; uint8_t is_packed_rgb;
uint8_t rgba_map[4]; uint8_t rgba_map[4];
int alpha; int alpha;
uint64_t start_time, duration;
enum {VF_FADE_WAITING=0, VF_FADE_FADING, VF_FADE_DONE} fade_state;
} FadeContext; } FadeContext;
static av_cold int init(AVFilterContext *ctx) static av_cold int init(AVFilterContext *ctx)
@ -68,18 +69,27 @@ static av_cold int init(AVFilterContext *ctx)
FadeContext *fade = ctx->priv; FadeContext *fade = ctx->priv;
fade->fade_per_frame = (1 << 16) / fade->nb_frames; fade->fade_per_frame = (1 << 16) / fade->nb_frames;
if (fade->type == FADE_IN) { fade->fade_state = VF_FADE_WAITING;
fade->factor = 0;
} else if (fade->type == FADE_OUT) { if (fade->duration != 0) {
fade->fade_per_frame = -fade->fade_per_frame; // If duration (seconds) is non-zero, assume that we are not fading based on frames
fade->factor = (1 << 16); fade->nb_frames = 0; // Mostly to clean up logging
} }
fade->stop_frame = fade->start_frame + fade->nb_frames;
// 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);
}
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; return 0;
} }
@ -153,6 +163,55 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *frame)
FadeContext *fade = inlink->dst->priv; FadeContext *fade = inlink->dst->priv;
uint8_t *p; uint8_t *p;
int i, j, plane; 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->factor < UINT16_MAX) {
if (fade->alpha) { 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++; fade->frame_index++;
return ff_filter_frame(inlink->dst->outputs[0], frame); 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.", { "n", "Number of frames to which the effect should be applied.",
OFFSET(nb_frames), AV_OPT_TYPE_INT, { .i64 = 25 }, 0, INT_MAX, FLAGS }, 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 }, { "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 }, { NULL },
}; };