@@ -10740,6 +10740,25 @@ The default is @code{round}.
@item input
Pick output frame rate from input if it is available. The default is disabled.
Useful to change input to constant frame rate without needing to know input frame rate.
+
+@item frames
+Frames picking rounding method when duplicating frames.
+
+Possible values are:
+@table @option
+@item zero
+round towards 0
+@item inf
+round away from 0
+@item down
+round towards -infinity
+@item up
+round towards +infinity
+@item near
+round to nearest
+@end table
+The default is @code{down}.
+
@end table
Alternatively, the options can be specified as a flat string:
@@ -49,6 +49,8 @@ typedef struct FPSContext {
AVRational framerate; ///< target framerate
int rounding; ///< AVRounding method for timestamps
+ int frames_rounding; ///< AVRounding method for cloning frames
+
int eof_action; ///< action performed for last frame in FIFO
/* Set during outlink configuration */
@@ -89,6 +91,7 @@ static const AVOption fps_options[] = {
{ "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" },
{ "input", "use input framerate if available", OFFSET(input), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, V|F },
+ { "frames", "set rounding method for frames", OFFSET(frames_rounding), AV_OPT_TYPE_INT, { .i64 = AV_ROUND_DOWN }, 0, 5, V|F, "round" },
{ NULL }
};
@@ -252,13 +255,27 @@ static int write_frame(AVFilterContext *ctx, FPSContext *s, AVFilterLink *outlin
*again = 1;
return 0;
- /* Output a copy of the first buffered frame */
+ /* Output a copy of the buffered frame depending on frames rounding */
} else {
- frame = av_frame_clone(s->frames[0]);
+ int index;
+
+ if (s->frames_count < 2) {
+ index = 0;
+ } else {
+ switch (s->frames_rounding) {
+ case AV_ROUND_ZERO: index = (s->next_pts - s->frames[0]->pts) >= 0 ? 0 : 1; break;
+ case AV_ROUND_INF: index = (s->next_pts - s->frames[0]->pts) <= 0 ? 0 : 1; break;
+ case AV_ROUND_DOWN: index = 0; break;
+ case AV_ROUND_UP: index = (s->next_pts - s->frames[0]->pts) != 0 ? 1 : 0; break;
+ case AV_ROUND_NEAR_INF: index = (s->next_pts - s->frames[0]->pts) > (s->frames[1]->pts - s->frames[0]->pts) / 2 ? 1 : 0; break;
+ }
+ }
+
+ frame = av_frame_clone(s->frames[index]);
if (!frame)
return AVERROR(ENOMEM);
// Make sure Closed Captions will not be duplicated
- av_frame_remove_side_data(s->frames[0], AV_FRAME_DATA_A53_CC);
+ av_frame_remove_side_data(s->frames[index], AV_FRAME_DATA_A53_CC);
frame->pts = s->next_pts++;
av_log(ctx, AV_LOG_DEBUG, "Writing frame with pts %"PRId64" to pts %"PRId64"\n",
Default behaviour is unchanged, and that is always rounding down when picking which frame to clone. Signed-off-by: Paul B Mahol <onemda@gmail.com> --- doc/filters.texi | 19 +++++++++++++++++++ libavfilter/vf_fps.c | 23 ++++++++++++++++++++--- 2 files changed, 39 insertions(+), 3 deletions(-)