2 * Copyright 2007 Bobby Bingham
3 * Copyright 2012 Robert Nagy <ronag89 gmail com>
4 * Copyright 2012 Anton Khirnov <anton khirnov net>
6 * This file is part of FFmpeg.
8 * FFmpeg is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2.1 of the License, or (at your option) any later version.
13 * FFmpeg is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with FFmpeg; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
25 * a filter enforcing given constant framerate
31 #include "libavutil/common.h"
32 #include "libavutil/fifo.h"
33 #include "libavutil/mathematics.h"
34 #include "libavutil/opt.h"
35 #include "libavutil/parseutils.h"
41 typedef struct FPSContext
{
44 AVFifoBuffer
*fifo
; ///< store frames until we get two successive timestamps
46 /* timestamps in input timebase */
47 int64_t first_pts
; ///< pts of the first frame that arrived on this filter
49 double start_time
; ///< pts, in seconds, of the expected first frame
51 AVRational framerate
; ///< target framerate
52 int rounding
; ///< AVRounding method for timestamps
55 int frames_in
; ///< number of frames on input
56 int frames_out
; ///< number of frames on output
57 int dup
; ///< number of frames duplicated
58 int drop
; ///< number of framed dropped
61 #define OFFSET(x) offsetof(FPSContext, x)
62 #define V AV_OPT_FLAG_VIDEO_PARAM
63 #define F AV_OPT_FLAG_FILTERING_PARAM
64 static const AVOption fps_options
[] = {
65 { "fps", "A string describing desired output framerate", OFFSET(framerate
), AV_OPT_TYPE_VIDEO_RATE
, { .str
= "25" }, .flags
= V
|F
},
66 { "start_time", "Assume the first PTS should be this value.", OFFSET(start_time
), AV_OPT_TYPE_DOUBLE
, { .dbl
= DBL_MAX
}, -DBL_MAX
, DBL_MAX
, V
},
67 { "round", "set rounding method for timestamps", OFFSET(rounding
), AV_OPT_TYPE_INT
, { .i64
= AV_ROUND_NEAR_INF
}, 0, 5, V
|F
, "round" },
68 { "zero", "round towards 0", OFFSET(rounding
), AV_OPT_TYPE_CONST
, { .i64
= AV_ROUND_ZERO
}, 0, 5, V
|F
, "round" },
69 { "inf", "round away from 0", OFFSET(rounding
), AV_OPT_TYPE_CONST
, { .i64
= AV_ROUND_INF
}, 0, 5, V
|F
, "round" },
70 { "down", "round towards -infty", OFFSET(rounding
), AV_OPT_TYPE_CONST
, { .i64
= AV_ROUND_DOWN
}, 0, 5, V
|F
, "round" },
71 { "up", "round towards +infty", OFFSET(rounding
), AV_OPT_TYPE_CONST
, { .i64
= AV_ROUND_UP
}, 0, 5, V
|F
, "round" },
72 { "near", "round to nearest", OFFSET(rounding
), AV_OPT_TYPE_CONST
, { .i64
= AV_ROUND_NEAR_INF
}, 0, 5, V
|F
, "round" },
76 AVFILTER_DEFINE_CLASS(fps
);
78 static av_cold
int init(AVFilterContext
*ctx
)
80 FPSContext
*s
= ctx
->priv
;
82 if (!(s
->fifo
= av_fifo_alloc_array(2, sizeof(AVFrame
*))))
83 return AVERROR(ENOMEM
);
85 s
->first_pts
= AV_NOPTS_VALUE
;
87 av_log(ctx
, AV_LOG_VERBOSE
, "fps=%d/%d\n", s
->framerate
.num
, s
->framerate
.den
);
91 static void flush_fifo(AVFifoBuffer
*fifo
)
93 while (av_fifo_size(fifo
)) {
95 av_fifo_generic_read(fifo
, &tmp
, sizeof(tmp
), NULL
);
100 static av_cold
void uninit(AVFilterContext
*ctx
)
102 FPSContext
*s
= ctx
->priv
;
104 s
->drop
+= av_fifo_size(s
->fifo
) / sizeof(AVFrame
*);
106 av_fifo_freep(&s
->fifo
);
109 av_log(ctx
, AV_LOG_VERBOSE
, "%d frames in, %d frames out; %d frames dropped, "
110 "%d frames duplicated.\n", s
->frames_in
, s
->frames_out
, s
->drop
, s
->dup
);
113 static int config_props(AVFilterLink
* link
)
115 FPSContext
*s
= link
->src
->priv
;
117 link
->time_base
= av_inv_q(s
->framerate
);
118 link
->frame_rate
= s
->framerate
;
119 link
->w
= link
->src
->inputs
[0]->w
;
120 link
->h
= link
->src
->inputs
[0]->h
;
125 static int request_frame(AVFilterLink
*outlink
)
127 AVFilterContext
*ctx
= outlink
->src
;
128 FPSContext
*s
= ctx
->priv
;
129 int frames_out
= s
->frames_out
;
132 while (ret
>= 0 && s
->frames_out
== frames_out
)
133 ret
= ff_request_frame(ctx
->inputs
[0]);
136 if (ret
== AVERROR_EOF
&& av_fifo_size(s
->fifo
)) {
138 for (i
= 0; av_fifo_size(s
->fifo
); i
++) {
141 av_fifo_generic_read(s
->fifo
, &buf
, sizeof(buf
), NULL
);
142 buf
->pts
= av_rescale_q(s
->first_pts
, ctx
->inputs
[0]->time_base
,
143 outlink
->time_base
) + s
->frames_out
;
145 if ((ret
= ff_filter_frame(outlink
, buf
)) < 0)
156 static int write_to_fifo(AVFifoBuffer
*fifo
, AVFrame
*buf
)
160 if (!av_fifo_space(fifo
) &&
161 (ret
= av_fifo_realloc2(fifo
, 2*av_fifo_size(fifo
)))) {
166 av_fifo_generic_write(fifo
, &buf
, sizeof(buf
), NULL
);
170 static int filter_frame(AVFilterLink
*inlink
, AVFrame
*buf
)
172 AVFilterContext
*ctx
= inlink
->dst
;
173 FPSContext
*s
= ctx
->priv
;
174 AVFilterLink
*outlink
= ctx
->outputs
[0];
179 /* discard frames until we get the first timestamp */
180 if (s
->first_pts
== AV_NOPTS_VALUE
) {
181 if (buf
->pts
!= AV_NOPTS_VALUE
) {
182 ret
= write_to_fifo(s
->fifo
, buf
);
186 if (s
->start_time
!= DBL_MAX
&& s
->start_time
!= AV_NOPTS_VALUE
) {
187 double first_pts
= s
->start_time
* AV_TIME_BASE
;
188 first_pts
= FFMIN(FFMAX(first_pts
, INT64_MIN
), INT64_MAX
);
189 s
->first_pts
= av_rescale_q(first_pts
, AV_TIME_BASE_Q
,
191 av_log(ctx
, AV_LOG_VERBOSE
, "Set first pts to (in:%"PRId64
" out:%"PRId64
")\n",
192 s
->first_pts
, av_rescale_q(first_pts
, AV_TIME_BASE_Q
,
193 outlink
->time_base
));
195 s
->first_pts
= buf
->pts
;
198 av_log(ctx
, AV_LOG_WARNING
, "Discarding initial frame(s) with no "
206 /* now wait for the next timestamp */
207 if (buf
->pts
== AV_NOPTS_VALUE
|| av_fifo_size(s
->fifo
) <= 0) {
208 return write_to_fifo(s
->fifo
, buf
);
211 /* number of output frames */
212 delta
= av_rescale_q_rnd(buf
->pts
- s
->first_pts
, inlink
->time_base
,
213 outlink
->time_base
, s
->rounding
) - s
->frames_out
;
216 /* drop the frame and everything buffered except the first */
218 int drop
= av_fifo_size(s
->fifo
)/sizeof(AVFrame
*);
220 av_log(ctx
, AV_LOG_DEBUG
, "Dropping %d frame(s).\n", drop
);
223 av_fifo_generic_read(s
->fifo
, &tmp
, sizeof(tmp
), NULL
);
225 ret
= write_to_fifo(s
->fifo
, tmp
);
231 /* can output >= 1 frames */
232 for (i
= 0; i
< delta
; i
++) {
234 av_fifo_generic_read(s
->fifo
, &buf_out
, sizeof(buf_out
), NULL
);
236 /* duplicate the frame if needed */
237 if (!av_fifo_size(s
->fifo
) && i
< delta
- 1) {
238 AVFrame
*dup
= av_frame_clone(buf_out
);
240 av_log(ctx
, AV_LOG_DEBUG
, "Duplicating frame.\n");
242 ret
= write_to_fifo(s
->fifo
, dup
);
244 ret
= AVERROR(ENOMEM
);
247 av_frame_free(&buf_out
);
255 buf_out
->pts
= av_rescale_q(s
->first_pts
, inlink
->time_base
,
256 outlink
->time_base
) + s
->frames_out
;
258 if ((ret
= ff_filter_frame(outlink
, buf_out
)) < 0) {
267 ret
= write_to_fifo(s
->fifo
, buf
);
272 static const AVFilterPad avfilter_vf_fps_inputs
[] = {
275 .type
= AVMEDIA_TYPE_VIDEO
,
276 .filter_frame
= filter_frame
,
281 static const AVFilterPad avfilter_vf_fps_outputs
[] = {
284 .type
= AVMEDIA_TYPE_VIDEO
,
285 .request_frame
= request_frame
,
286 .config_props
= config_props
291 AVFilter ff_vf_fps
= {
293 .description
= NULL_IF_CONFIG_SMALL("Force constant framerate."),
296 .priv_size
= sizeof(FPSContext
),
297 .priv_class
= &fps_class
,
298 .inputs
= avfilter_vf_fps_inputs
,
299 .outputs
= avfilter_vf_fps_outputs
,