Browse code

lavfi: Add fps filter.

Partially based on a patch by Robert Nagy <ronag89@gmail.com>

Anton Khirnov authored on 2012/05/09 21:08:21
Showing 6 changed files
... ...
@@ -18,6 +18,7 @@ version <next>:
18 18
 - drop support for avconv without libavfilter
19 19
 - add libavresample audio conversion library
20 20
 - audio filters support in libavfilter and avconv
21
+- add fps filter
21 22
 
22 23
 
23 24
 version 0.8:
... ...
@@ -814,6 +814,19 @@ format=yuv420p
814 814
 format=yuv420p:yuv444p:yuv410p
815 815
 @end example
816 816
 
817
+@section fps
818
+
819
+Convert the video to specified constant framerate by duplicating or dropping
820
+frames as necessary.
821
+
822
+This filter accepts the following named parameters:
823
+@table @option
824
+
825
+@item fps
826
+Desired output framerate.
827
+
828
+@end table
829
+
817 830
 @anchor{frei0r}
818 831
 @section frei0r
819 832
 
... ...
@@ -46,6 +46,7 @@ OBJS-$(CONFIG_FADE_FILTER)                   += vf_fade.o
46 46
 OBJS-$(CONFIG_FIELDORDER_FILTER)             += vf_fieldorder.o
47 47
 OBJS-$(CONFIG_FIFO_FILTER)                   += vf_fifo.o
48 48
 OBJS-$(CONFIG_FORMAT_FILTER)                 += vf_format.o
49
+OBJS-$(CONFIG_FPS_FILTER)                    += vf_fps.o
49 50
 OBJS-$(CONFIG_FREI0R_FILTER)                 += vf_frei0r.o
50 51
 OBJS-$(CONFIG_GRADFUN_FILTER)                += vf_gradfun.o
51 52
 OBJS-$(CONFIG_HFLIP_FILTER)                  += vf_hflip.o
... ...
@@ -55,6 +55,7 @@ void avfilter_register_all(void)
55 55
     REGISTER_FILTER (FIELDORDER,  fieldorder,  vf);
56 56
     REGISTER_FILTER (FIFO,        fifo,        vf);
57 57
     REGISTER_FILTER (FORMAT,      format,      vf);
58
+    REGISTER_FILTER (FPS,         fps,         vf);
58 59
     REGISTER_FILTER (FREI0R,      frei0r,      vf);
59 60
     REGISTER_FILTER (GRADFUN,     gradfun,     vf);
60 61
     REGISTER_FILTER (HFLIP,       hflip,       vf);
... ...
@@ -29,7 +29,7 @@
29 29
 #include "libavutil/avutil.h"
30 30
 
31 31
 #define LIBAVFILTER_VERSION_MAJOR  2
32
-#define LIBAVFILTER_VERSION_MINOR  17
32
+#define LIBAVFILTER_VERSION_MINOR  18
33 33
 #define LIBAVFILTER_VERSION_MICRO  0
34 34
 
35 35
 #define LIBAVFILTER_VERSION_INT AV_VERSION_INT(LIBAVFILTER_VERSION_MAJOR, \
36 36
new file mode 100644
... ...
@@ -0,0 +1,271 @@
0
+/*
1
+ * This file is part of Libav.
2
+ *
3
+ * Libav is free software; you can redistribute it and/or
4
+ * modify it under the terms of the GNU Lesser General Public
5
+ * License as published by the Free Software Foundation; either
6
+ * version 2.1 of the License, or (at your option) any later version.
7
+ *
8
+ * Libav is distributed in the hope that it will be useful,
9
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
10
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11
+ * Lesser General Public License for more details.
12
+ *
13
+ * You should have received a copy of the GNU Lesser General Public
14
+ * License along with Libav; if not, write to the Free Software
15
+ * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
16
+ */
17
+
18
+/**
19
+ * @file
20
+ * a filter enforcing given constant framerate
21
+ */
22
+
23
+#include "libavutil/fifo.h"
24
+#include "libavutil/mathematics.h"
25
+#include "libavutil/opt.h"
26
+#include "libavutil/parseutils.h"
27
+
28
+#include "avfilter.h"
29
+
30
+typedef struct FPSContext {
31
+    const AVClass *class;
32
+
33
+    AVFifoBuffer *fifo;     ///< store frames until we get two successive timestamps
34
+
35
+    /* timestamps in input timebase */
36
+    int64_t first_pts;      ///< pts of the first frame that arrived on this filter
37
+    int64_t pts;            ///< pts of the first frame currently in the fifo
38
+
39
+    AVRational framerate;   ///< target framerate
40
+    char *fps;              ///< a string describing target framerate
41
+
42
+    /* statistics */
43
+    int frames_in;             ///< number of frames on input
44
+    int frames_out;            ///< number of frames on output
45
+    int dup;                   ///< number of frames duplicated
46
+    int drop;                  ///< number of framed dropped
47
+} FPSContext;
48
+
49
+#define OFFSET(x) offsetof(FPSContext, x)
50
+#define V AV_OPT_FLAG_VIDEO_PARAM
51
+static const AVOption options[] = {
52
+    { "fps", "A string describing desired output framerate", OFFSET(fps), AV_OPT_TYPE_STRING, { .str = "25" }, .flags = V },
53
+    { NULL },
54
+};
55
+
56
+static const AVClass class = {
57
+    .class_name = "FPS filter",
58
+    .item_name  = av_default_item_name,
59
+    .option     = options,
60
+    .version    = LIBAVUTIL_VERSION_INT,
61
+};
62
+
63
+static av_cold int init(AVFilterContext *ctx, const char *args, void *opaque)
64
+{
65
+    FPSContext *s = ctx->priv;
66
+    int ret;
67
+
68
+    s->class = &class;
69
+    av_opt_set_defaults(s);
70
+
71
+    if ((ret = av_set_options_string(s, args, "=", ":")) < 0) {
72
+        av_log(ctx, AV_LOG_ERROR, "Error parsing the options string %s.\n",
73
+               args);
74
+        return ret;
75
+    }
76
+
77
+    if ((ret = av_parse_video_rate(&s->framerate, s->fps)) < 0) {
78
+        av_log(ctx, AV_LOG_ERROR, "Error parsing framerate %s.\n", s->fps);
79
+        return ret;
80
+    }
81
+    av_opt_free(s);
82
+
83
+    if (!(s->fifo = av_fifo_alloc(2*sizeof(AVFilterBufferRef*))))
84
+        return AVERROR(ENOMEM);
85
+
86
+    av_log(ctx, AV_LOG_VERBOSE, "fps=%d/%d\n", s->framerate.num, s->framerate.den);
87
+    return 0;
88
+}
89
+
90
+static void flush_fifo(AVFifoBuffer *fifo)
91
+{
92
+    while (av_fifo_size(fifo)) {
93
+        AVFilterBufferRef *tmp;
94
+        av_fifo_generic_read(fifo, &tmp, sizeof(tmp), NULL);
95
+        avfilter_unref_buffer(tmp);
96
+    }
97
+}
98
+
99
+static av_cold void uninit(AVFilterContext *ctx)
100
+{
101
+    FPSContext *s = ctx->priv;
102
+    if (s->fifo) {
103
+        flush_fifo(s->fifo);
104
+        av_fifo_free(s->fifo);
105
+    }
106
+
107
+    av_log(ctx, AV_LOG_VERBOSE, "%d frames in, %d frames out; %d frames dropped, "
108
+           "%d frames duplicated.\n", s->frames_in, s->frames_out, s->drop, s->dup);
109
+}
110
+
111
+static int config_props(AVFilterLink* link)
112
+{
113
+    FPSContext   *s = link->src->priv;
114
+
115
+    link->time_base = (AVRational){ s->framerate.den, s->framerate.num };
116
+    link->w         = link->src->inputs[0]->w;
117
+    link->h         = link->src->inputs[0]->h;
118
+    s->pts          = AV_NOPTS_VALUE;
119
+
120
+    return 0;
121
+}
122
+
123
+static int request_frame(AVFilterLink *outlink)
124
+{
125
+    AVFilterContext *ctx = outlink->src;
126
+    FPSContext        *s = ctx->priv;
127
+    int frames_out = s->frames_out;
128
+    int ret = 0;
129
+
130
+    while (ret >= 0 && s->frames_out == frames_out)
131
+        ret = avfilter_request_frame(ctx->inputs[0]);
132
+
133
+    /* flush the fifo */
134
+    if (ret == AVERROR_EOF && av_fifo_size(s->fifo)) {
135
+        int i;
136
+        for (i = 0; av_fifo_size(s->fifo); i++) {
137
+            AVFilterBufferRef *buf;
138
+
139
+            av_fifo_generic_read(s->fifo, &buf, sizeof(buf), NULL);
140
+            buf->pts = av_rescale_q(s->first_pts, ctx->inputs[0]->time_base,
141
+                                    outlink->time_base) + s->frames_out;
142
+
143
+            avfilter_start_frame(outlink, buf);
144
+            avfilter_draw_slice(outlink, 0, outlink->h, 1);
145
+            avfilter_end_frame(outlink);
146
+            s->frames_out++;
147
+        }
148
+        return 0;
149
+    }
150
+
151
+    return ret;
152
+}
153
+
154
+static int write_to_fifo(AVFifoBuffer *fifo, AVFilterBufferRef *buf)
155
+{
156
+    int ret;
157
+
158
+    if (!av_fifo_space(fifo) &&
159
+        (ret = av_fifo_realloc2(fifo, 2*av_fifo_size(fifo))))
160
+        return ret;
161
+
162
+    av_fifo_generic_write(fifo, &buf, sizeof(buf), NULL);
163
+    return 0;
164
+}
165
+
166
+static void end_frame(AVFilterLink *inlink)
167
+{
168
+    AVFilterContext    *ctx = inlink->dst;
169
+    FPSContext           *s = ctx->priv;
170
+    AVFilterLink   *outlink = ctx->outputs[0];
171
+    AVFilterBufferRef  *buf = inlink->cur_buf;
172
+    int64_t delta;
173
+    int i;
174
+
175
+    s->frames_in++;
176
+    /* discard frames until we get the first timestamp */
177
+    if (s->pts == AV_NOPTS_VALUE) {
178
+        if (buf->pts != AV_NOPTS_VALUE) {
179
+            write_to_fifo(s->fifo, buf);
180
+            s->first_pts = s->pts = buf->pts;
181
+        } else {
182
+            av_log(ctx, AV_LOG_WARNING, "Discarding initial frame(s) with no "
183
+                   "timestamp.\n");
184
+            avfilter_unref_buffer(buf);
185
+            s->drop++;
186
+        }
187
+        return;
188
+    }
189
+
190
+    /* now wait for the next timestamp */
191
+    if (buf->pts == AV_NOPTS_VALUE) {
192
+        write_to_fifo(s->fifo, buf);
193
+        return;
194
+    }
195
+
196
+    /* number of output frames */
197
+    delta = av_rescale_q(buf->pts - s->pts, inlink->time_base,
198
+                         outlink->time_base);
199
+
200
+    if (delta < 1) {
201
+        /* drop the frame and everything buffered except the first */
202
+        AVFilterBufferRef *tmp;
203
+        int drop = av_fifo_size(s->fifo)/sizeof(AVFilterBufferRef*);
204
+
205
+        av_log(ctx, AV_LOG_DEBUG, "Dropping %d frame(s).\n", drop);
206
+        s->drop += drop;
207
+
208
+        av_fifo_generic_read(s->fifo, &tmp, sizeof(tmp), NULL);
209
+        flush_fifo(s->fifo);
210
+        write_to_fifo(s->fifo, tmp);
211
+
212
+        avfilter_unref_buffer(buf);
213
+        return;
214
+    }
215
+
216
+    /* can output >= 1 frames */
217
+    for (i = 0; i < delta; i++) {
218
+        AVFilterBufferRef *buf_out;
219
+        av_fifo_generic_read(s->fifo, &buf_out, sizeof(buf_out), NULL);
220
+
221
+        /* duplicate the frame if needed */
222
+        if (!av_fifo_size(s->fifo) && i < delta - 1) {
223
+            av_log(ctx, AV_LOG_DEBUG, "Duplicating frame.\n");
224
+            write_to_fifo(s->fifo, avfilter_ref_buffer(buf_out, AV_PERM_READ));
225
+            s->dup++;
226
+        }
227
+
228
+        buf_out->pts = av_rescale_q(s->first_pts, inlink->time_base,
229
+                                    outlink->time_base) + s->frames_out;
230
+
231
+        avfilter_start_frame(outlink, buf_out);
232
+        avfilter_draw_slice(outlink, 0, outlink->h, 1);
233
+        avfilter_end_frame(outlink);
234
+        s->frames_out++;
235
+    }
236
+    flush_fifo(s->fifo);
237
+
238
+    write_to_fifo(s->fifo, buf);
239
+    s->pts = s->first_pts + av_rescale_q(s->frames_out, outlink->time_base, inlink->time_base);
240
+}
241
+
242
+static void null_start_frame(AVFilterLink *link, AVFilterBufferRef *buf)
243
+{
244
+}
245
+
246
+static void null_draw_slice(AVFilterLink *link, int y, int h, int slice_dir)
247
+{
248
+}
249
+
250
+AVFilter avfilter_vf_fps = {
251
+    .name        = "fps",
252
+    .description = NULL_IF_CONFIG_SMALL("Force constant framerate"),
253
+
254
+    .init      = init,
255
+    .uninit    = uninit,
256
+
257
+    .priv_size = sizeof(FPSContext),
258
+
259
+    .inputs    = (AVFilterPad[]) {{ .name            = "default",
260
+                                    .type            = AVMEDIA_TYPE_VIDEO,
261
+                                    .start_frame     = null_start_frame,
262
+                                    .draw_slice      = null_draw_slice,
263
+                                    .end_frame       = end_frame, },
264
+                                  { .name = NULL}},
265
+    .outputs   = (AVFilterPad[]) {{ .name            = "default",
266
+                                    .type            = AVMEDIA_TYPE_VIDEO,
267
+                                    .request_frame   = request_frame,
268
+                                    .config_props    = config_props},
269
+                                  { .name = NULL}},
270
+};