diff --git a/doc/filters.texi b/doc/filters.texi index a4a0893186..1a93309871 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -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 diff --git a/libavfilter/vf_fade.c b/libavfilter/vf_fade.c index 71e0f01208..4dbd4c7d37 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->stop_frame = fade->start_frame + fade->nb_frames; + 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); + } - 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 }, };