diff --git a/doc/filters.texi b/doc/filters.texi index 624e5cd01b..57189c77b0 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -8695,6 +8695,18 @@ round to nearest @end table The default is @code{near}. +@item eof_action +Action performed when reading the last frame. + +Possible values are: +@table @option +@item round +Use same timestamp rounding method as used for other frames. +@item pass +Pass through last frame if input duration has not been reached yet. +@end table +The default is @code{round}. + @end table Alternatively, the options can be specified as a flat string: diff --git a/libavfilter/version.h b/libavfilter/version.h index fb382d4e25..8191c59a15 100644 --- a/libavfilter/version.h +++ b/libavfilter/version.h @@ -31,7 +31,7 @@ #define LIBAVFILTER_VERSION_MAJOR 6 #define LIBAVFILTER_VERSION_MINOR 106 -#define LIBAVFILTER_VERSION_MICRO 100 +#define LIBAVFILTER_VERSION_MICRO 101 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \ LIBAVFILTER_VERSION_MINOR, \ diff --git a/libavfilter/vf_fps.c b/libavfilter/vf_fps.c index a5e51c3bbb..dbafd2c35a 100644 --- a/libavfilter/vf_fps.c +++ b/libavfilter/vf_fps.c @@ -40,6 +40,12 @@ #include "internal.h" #include "video.h" +enum EOFAction { + EOF_ACTION_ROUND, + EOF_ACTION_PASS, + EOF_ACTION_NB +}; + typedef struct FPSContext { const AVClass *class; @@ -52,6 +58,7 @@ typedef struct FPSContext { AVRational framerate; ///< target framerate int rounding; ///< AVRounding method for timestamps + int eof_action; ///< action performed for last frame in FIFO /* statistics */ int frames_in; ///< number of frames on input @@ -72,6 +79,9 @@ static const AVOption fps_options[] = { { "down", "round towards -infty", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_DOWN }, 0, 0, V|F, "round" }, { "up", "round towards +infty", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_UP }, 0, 0, V|F, "round" }, { "near", "round to nearest", 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_NEAR_INF }, 0, 0, V|F, "round" }, + { "eof_action", "action performed for last frame", OFFSET(eof_action), AV_OPT_TYPE_INT, { .i64 = EOF_ACTION_ROUND }, 0, EOF_ACTION_NB-1, V|F, "eof_action" }, + { "round", "round similar to other frames", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ROUND }, 0, 0, V|F, "eof_action" }, + { "pass", "pass through last frame", 0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS }, 0, 0, V|F, "eof_action" }, { NULL } }; @@ -151,9 +161,11 @@ static int request_frame(AVFilterLink *outlink) /* This is the last frame, we may have to duplicate it to match * the last frame duration */ int j; + int eof_rounding = (s->eof_action == EOF_ACTION_PASS) ? AV_ROUND_UP : s->rounding; int delta = av_rescale_q_rnd(ctx->inputs[0]->current_pts - s->first_pts, ctx->inputs[0]->time_base, - outlink->time_base, s->rounding) - s->frames_out ; + outlink->time_base, eof_rounding) - s->frames_out; + av_log(ctx, AV_LOG_DEBUG, "EOF frames_out:%d delta:%d\n", s->frames_out, delta); /* if the delta is equal to 1, it means we just need to output * the last frame. Greater than 1 means we will need duplicate * delta-1 frames */