Subversion Repositories Kolibri OS

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
4349 Serge 1
/*
2
 * Copyright 2007 Bobby Bingham
3
 * Copyright 2012 Robert Nagy 
4
 * Copyright 2012 Anton Khirnov 
5
 *
6
 * This file is part of FFmpeg.
7
 *
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.
12
 *
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.
17
 *
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
21
 */
22
 
23
/**
24
 * @file
25
 * a filter enforcing given constant framerate
26
 */
27
 
28
#include 
29
 
30
#include "libavutil/common.h"
31
#include "libavutil/fifo.h"
32
#include "libavutil/mathematics.h"
33
#include "libavutil/opt.h"
34
#include "libavutil/parseutils.h"
35
 
36
#include "avfilter.h"
37
#include "internal.h"
38
#include "video.h"
39
 
40
typedef struct FPSContext {
41
    const AVClass *class;
42
 
43
    AVFifoBuffer *fifo;     ///< store frames until we get two successive timestamps
44
 
45
    /* timestamps in input timebase */
46
    int64_t first_pts;      ///< pts of the first frame that arrived on this filter
47
    int64_t pts;            ///< pts of the first frame currently in the fifo
48
 
49
    double start_time;      ///< pts, in seconds, of the expected first frame
50
 
51
    AVRational framerate;   ///< target framerate
52
    int rounding;           ///< AVRounding method for timestamps
53
 
54
    /* statistics */
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
59
} FPSContext;
60
 
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" },
73
    { NULL }
74
};
75
 
76
AVFILTER_DEFINE_CLASS(fps);
77
 
78
static av_cold int init(AVFilterContext *ctx)
79
{
80
    FPSContext *s = ctx->priv;
81
 
82
    if (!(s->fifo = av_fifo_alloc(2*sizeof(AVFrame*))))
83
        return AVERROR(ENOMEM);
84
 
85
    s->pts          = AV_NOPTS_VALUE;
86
    s->first_pts    = AV_NOPTS_VALUE;
87
 
88
    av_log(ctx, AV_LOG_VERBOSE, "fps=%d/%d\n", s->framerate.num, s->framerate.den);
89
    return 0;
90
}
91
 
92
static void flush_fifo(AVFifoBuffer *fifo)
93
{
94
    while (av_fifo_size(fifo)) {
95
        AVFrame *tmp;
96
        av_fifo_generic_read(fifo, &tmp, sizeof(tmp), NULL);
97
        av_frame_free(&tmp);
98
    }
99
}
100
 
101
static av_cold void uninit(AVFilterContext *ctx)
102
{
103
    FPSContext *s = ctx->priv;
104
    if (s->fifo) {
105
        s->drop += av_fifo_size(s->fifo) / sizeof(AVFrame*);
106
        flush_fifo(s->fifo);
107
        av_fifo_free(s->fifo);
108
    }
109
 
110
    av_log(ctx, AV_LOG_VERBOSE, "%d frames in, %d frames out; %d frames dropped, "
111
           "%d frames duplicated.\n", s->frames_in, s->frames_out, s->drop, s->dup);
112
}
113
 
114
static int config_props(AVFilterLink* link)
115
{
116
    FPSContext   *s = link->src->priv;
117
 
118
    link->time_base = av_inv_q(s->framerate);
119
    link->frame_rate= s->framerate;
120
    link->w         = link->src->inputs[0]->w;
121
    link->h         = link->src->inputs[0]->h;
122
 
123
    return 0;
124
}
125
 
126
static int request_frame(AVFilterLink *outlink)
127
{
128
    AVFilterContext *ctx = outlink->src;
129
    FPSContext        *s = ctx->priv;
130
    int frames_out = s->frames_out;
131
    int ret = 0;
132
 
133
    while (ret >= 0 && s->frames_out == frames_out)
134
        ret = ff_request_frame(ctx->inputs[0]);
135
 
136
    /* flush the fifo */
137
    if (ret == AVERROR_EOF && av_fifo_size(s->fifo)) {
138
        int i;
139
        for (i = 0; av_fifo_size(s->fifo); i++) {
140
            AVFrame *buf;
141
 
142
            av_fifo_generic_read(s->fifo, &buf, sizeof(buf), NULL);
143
            buf->pts = av_rescale_q(s->first_pts, ctx->inputs[0]->time_base,
144
                                    outlink->time_base) + s->frames_out;
145
 
146
            if ((ret = ff_filter_frame(outlink, buf)) < 0)
147
                return ret;
148
 
149
            s->frames_out++;
150
        }
151
        return 0;
152
    }
153
 
154
    return ret;
155
}
156
 
157
static int write_to_fifo(AVFifoBuffer *fifo, AVFrame *buf)
158
{
159
    int ret;
160
 
161
    if (!av_fifo_space(fifo) &&
162
        (ret = av_fifo_realloc2(fifo, 2*av_fifo_size(fifo)))) {
163
        av_frame_free(&buf);
164
        return ret;
165
    }
166
 
167
    av_fifo_generic_write(fifo, &buf, sizeof(buf), NULL);
168
    return 0;
169
}
170
 
171
static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
172
{
173
    AVFilterContext    *ctx = inlink->dst;
174
    FPSContext           *s = ctx->priv;
175
    AVFilterLink   *outlink = ctx->outputs[0];
176
    int64_t delta;
177
    int i, ret;
178
 
179
    s->frames_in++;
180
    /* discard frames until we get the first timestamp */
181
    if (s->pts == AV_NOPTS_VALUE) {
182
        if (buf->pts != AV_NOPTS_VALUE) {
183
            ret = write_to_fifo(s->fifo, buf);
184
            if (ret < 0)
185
                return ret;
186
 
187
            if (s->start_time != DBL_MAX && s->start_time != AV_NOPTS_VALUE) {
188
                double first_pts = s->start_time * AV_TIME_BASE;
189
                first_pts = FFMIN(FFMAX(first_pts, INT64_MIN), INT64_MAX);
190
                s->first_pts = s->pts = av_rescale_q(first_pts, AV_TIME_BASE_Q,
191
                                                     inlink->time_base);
192
                av_log(ctx, AV_LOG_VERBOSE, "Set first pts to (in:%"PRId64" out:%"PRId64")\n",
193
                       s->first_pts, av_rescale_q(first_pts, AV_TIME_BASE_Q,
194
                                                  outlink->time_base));
195
            } else {
196
                s->first_pts = s->pts = buf->pts;
197
            }
198
        } else {
199
            av_log(ctx, AV_LOG_WARNING, "Discarding initial frame(s) with no "
200
                   "timestamp.\n");
201
            av_frame_free(&buf);
202
            s->drop++;
203
        }
204
        return 0;
205
    }
206
 
207
    /* now wait for the next timestamp */
208
    if (buf->pts == AV_NOPTS_VALUE || av_fifo_size(s->fifo) <= 0) {
209
        return write_to_fifo(s->fifo, buf);
210
    }
211
 
212
    /* number of output frames */
213
    delta = av_rescale_q_rnd(buf->pts - s->pts, inlink->time_base,
214
                             outlink->time_base, s->rounding);
215
 
216
    if (delta < 1) {
217
        /* drop the frame and everything buffered except the first */
218
        AVFrame *tmp;
219
        int drop = av_fifo_size(s->fifo)/sizeof(AVFrame*);
220
 
221
        av_log(ctx, AV_LOG_DEBUG, "Dropping %d frame(s).\n", drop);
222
        s->drop += drop;
223
 
224
        av_fifo_generic_read(s->fifo, &tmp, sizeof(tmp), NULL);
225
        flush_fifo(s->fifo);
226
        ret = write_to_fifo(s->fifo, tmp);
227
 
228
        av_frame_free(&buf);
229
        return ret;
230
    }
231
 
232
    /* can output >= 1 frames */
233
    for (i = 0; i < delta; i++) {
234
        AVFrame *buf_out;
235
        av_fifo_generic_read(s->fifo, &buf_out, sizeof(buf_out), NULL);
236
 
237
        /* duplicate the frame if needed */
238
        if (!av_fifo_size(s->fifo) && i < delta - 1) {
239
            AVFrame *dup = av_frame_clone(buf_out);
240
 
241
            av_log(ctx, AV_LOG_DEBUG, "Duplicating frame.\n");
242
            if (dup)
243
                ret = write_to_fifo(s->fifo, dup);
244
            else
245
                ret = AVERROR(ENOMEM);
246
 
247
            if (ret < 0) {
248
                av_frame_free(&buf_out);
249
                av_frame_free(&buf);
250
                return ret;
251
            }
252
 
253
            s->dup++;
254
        }
255
 
256
        buf_out->pts = av_rescale_q(s->first_pts, inlink->time_base,
257
                                    outlink->time_base) + s->frames_out;
258
 
259
        if ((ret = ff_filter_frame(outlink, buf_out)) < 0) {
260
            av_frame_free(&buf);
261
            return ret;
262
        }
263
 
264
        s->frames_out++;
265
    }
266
    flush_fifo(s->fifo);
267
 
268
    ret = write_to_fifo(s->fifo, buf);
269
    s->pts = s->first_pts + av_rescale_q(s->frames_out, outlink->time_base, inlink->time_base);
270
 
271
    return ret;
272
}
273
 
274
static const AVFilterPad avfilter_vf_fps_inputs[] = {
275
    {
276
        .name         = "default",
277
        .type         = AVMEDIA_TYPE_VIDEO,
278
        .filter_frame = filter_frame,
279
    },
280
    { NULL }
281
};
282
 
283
static const AVFilterPad avfilter_vf_fps_outputs[] = {
284
    {
285
        .name          = "default",
286
        .type          = AVMEDIA_TYPE_VIDEO,
287
        .request_frame = request_frame,
288
        .config_props  = config_props
289
    },
290
    { NULL }
291
};
292
 
293
AVFilter avfilter_vf_fps = {
294
    .name        = "fps",
295
    .description = NULL_IF_CONFIG_SMALL("Force constant framerate."),
296
    .init        = init,
297
    .uninit      = uninit,
298
    .priv_size   = sizeof(FPSContext),
299
    .priv_class  = &fps_class,
300
    .inputs      = avfilter_vf_fps_inputs,
301
    .outputs     = avfilter_vf_fps_outputs,
302
};