diff --git a/doc/filters.texi b/doc/filters.texi index 2ee5ad556e..52971c76e9 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -1241,6 +1241,14 @@ This filter accepts the following named parameters: @item fps Desired output framerate. +@item start_time +Assume the first PTS should be the given value, in seconds. This allows for +padding/trimming at the start of stream. By default, no assumption is made +about the first frame's expected PTS, so no padding or trimming is done. +For example, this could be set to 0 to pad the beginning with duplicates of +the first frame if a video stream starts after the audio stream or to trim any +frames with a negative PTS. + @end table @anchor{frei0r} diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c index 7f5175248c..4583c89b3b 100644 --- a/libavfilter/vf_fps.c +++ b/libavfilter/vf_fps.c @@ -40,6 +40,8 @@ typedef struct FPSContext { int64_t first_pts; ///< pts of the first frame that arrived on this filter int64_t pts; ///< pts of the first frame currently in the fifo + double start_time; ///< pts, in seconds, of the expected first frame + AVRational framerate; ///< target framerate char *fps; ///< a string describing target framerate @@ -54,6 +56,7 @@ typedef struct FPSContext { #define V AV_OPT_FLAG_VIDEO_PARAM static const AVOption options[] = { { "fps", "A string describing desired output framerate", OFFSET(fps), AV_OPT_TYPE_STRING, { .str = "25" }, .flags = V }, + { "start_time", "Assume the first PTS should be this value.", OFFSET(start_time), AV_OPT_TYPE_DOUBLE, { .dbl = AV_NOPTS_VALUE}, INT64_MIN, INT64_MAX, V }, { NULL }, }; @@ -78,6 +81,7 @@ static av_cold int init(AVFilterContext *ctx) return AVERROR(ENOMEM); s->pts = AV_NOPTS_VALUE; + s->first_pts = AV_NOPTS_VALUE; av_log(ctx, AV_LOG_VERBOSE, "fps=%d/%d\n", s->framerate.num, s->framerate.den); return 0; @@ -177,7 +181,17 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *buf) if (ret < 0) return ret; - s->first_pts = s->pts = buf->pts; + if (s->start_time != AV_NOPTS_VALUE) { + double first_pts = s->start_time * AV_TIME_BASE; + first_pts = FFMIN(FFMAX(first_pts, INT64_MIN), INT64_MAX); + s->first_pts = s->pts = av_rescale_q(first_pts, AV_TIME_BASE_Q, + inlink->time_base); + av_log(ctx, AV_LOG_VERBOSE, "Set first pts to (in:%"PRId64" out:%"PRId64")\n", + s->first_pts, av_rescale_q(first_pts, AV_TIME_BASE_Q, + outlink->time_base)); + } else { + s->first_pts = s->pts = buf->pts; + } } else { av_log(ctx, AV_LOG_WARNING, "Discarding initial frame(s) with no " "timestamp.\n");