[FFmpeg-devel] [PATCH] lavfi/fade: Add ability to do video fade based on time
Andy n Deanna
andyndeanna at gmail.com
Sat Apr 20 18:59:04 CEST 2013
Clément,
I have implemented all of your suggestions, with the exception of adding
a test to FATE. There is a subtle different between how the fade is
calculated that makes some of the output frames slightly different when
using the equivalent parameters. The frame-based fade has a fixed
difference between frames, so all of the rounding error happens at the
end, whereas the time-based fade calculates the fade every frame, so the
rounding error is distributed throughout the fade. Not sure which is
technically correct, but calculating at every frame seemed to be simpler
for the time-based fade.
So to add a test to FATE, we need to add a new file to compare the
output to. That is currently outside of my abilities. I can do it, but
I need some guidance as to how to do it.
Summary of changes from last patch:
- Conditional logging for frame-based vs time-based.
- Fixed indentation and trailing whitespace (oops)
- Switch to AV_OPT_TYPE_DURATION
- Moved new options to the end
Thanks!
Andy
-------------- next part --------------
>From 77c9528f1b736ef0f37402f41700ade0b2cf55e5 Mon Sep 17 00:00:00 2001
From: Andy Martin <amartin at ubuntu.(none)>
Date: Tue, 16 Apr 2013 21:35:02 -0400
Subject: [PATCH] lavfi/fade: Added ability to do video fade based on
timestamp
---
doc/filters.texi | 18 ++++++++++
libavfilter/vf_fade.c | 95 ++++++++++++++++++++++++++++++++++++++++---------
2 files changed, 97 insertions(+), 16 deletions(-)
diff --git a/doc/filters.texi b/doc/filters.texi
index 4c702a9..9ea7a29 100644
--- a/doc/filters.texi
+++ b/doc/filters.texi
@@ -3119,6 +3119,17 @@ Default is 25.
@item alpha
If set to 1, fade only alpha channel, if one exists on the input.
Default value is 0.
+
+ at 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.
+
+ at 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
@@ -3159,6 +3170,13 @@ Fade in alpha over first 25 frames of video:
@example
fade=in:0:25:alpha=1
@end example
+
+ at item
+Make first 5.5 seconds black, then fade in for 0.5 seconds:
+ at example
+fade=t=in:st=5.5:d=0.5
+ at end example
+
@end itemize
@section field
diff --git a/libavfilter/vf_fade.c b/libavfilter/vf_fade.c
index 71e0f01..4dbd4c7 100644
--- a/libavfilter/vf_fade.c
+++ b/libavfilter/vf_fade.c
@@ -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 },
};
--
1.7.10.4
More information about the ffmpeg-devel
mailing list