LCOV - code coverage report
Current view: top level - libavfilter - vf_fps.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 87 121 71.9 %
Date: 2017-12-17 11:58:42 Functions: 7 7 100.0 %

          Line data    Source code
       1             : /*
       2             :  * Copyright 2007 Bobby Bingham
       3             :  * Copyright 2012 Robert Nagy <ronag89 gmail com>
       4             :  * Copyright 2012 Anton Khirnov <anton khirnov net>
       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 <float.h>
      29             : #include <stdint.h>
      30             : 
      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"
      36             : 
      37             : #define FF_INTERNAL_FIELDS 1
      38             : #include "framequeue.h"
      39             : #include "avfilter.h"
      40             : #include "internal.h"
      41             : #include "video.h"
      42             : 
      43             : enum EOFAction {
      44             :     EOF_ACTION_ROUND,
      45             :     EOF_ACTION_PASS,
      46             :     EOF_ACTION_NB
      47             : };
      48             : 
      49             : typedef struct FPSContext {
      50             :     const AVClass *class;
      51             : 
      52             :     AVFifoBuffer *fifo;     ///< store frames until we get two successive timestamps
      53             : 
      54             :     /* timestamps in input timebase */
      55             :     int64_t first_pts;      ///< pts of the first frame that arrived on this filter
      56             : 
      57             :     double start_time;      ///< pts, in seconds, of the expected first frame
      58             : 
      59             :     AVRational framerate;   ///< target framerate
      60             :     int rounding;           ///< AVRounding method for timestamps
      61             :     int eof_action;         ///< action performed for last frame in FIFO
      62             : 
      63             :     /* statistics */
      64             :     int frames_in;             ///< number of frames on input
      65             :     int frames_out;            ///< number of frames on output
      66             :     int dup;                   ///< number of frames duplicated
      67             :     int drop;                  ///< number of framed dropped
      68             : } FPSContext;
      69             : 
      70             : #define OFFSET(x) offsetof(FPSContext, x)
      71             : #define V AV_OPT_FLAG_VIDEO_PARAM
      72             : #define F AV_OPT_FLAG_FILTERING_PARAM
      73             : static const AVOption fps_options[] = {
      74             :     { "fps", "A string describing desired output framerate", OFFSET(framerate), AV_OPT_TYPE_VIDEO_RATE, { .str = "25" }, 0, INT_MAX, V|F },
      75             :     { "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|F },
      76             :     { "round", "set rounding method for timestamps", OFFSET(rounding), AV_OPT_TYPE_INT, { .i64 = AV_ROUND_NEAR_INF }, 0, 5, V|F, "round" },
      77             :         { "zero", "round towards 0",                 0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_ZERO     }, 0, 0, V|F, "round" },
      78             :         { "inf",  "round away from 0",               0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_INF      }, 0, 0, V|F, "round" },
      79             :         { "down", "round towards -infty",            0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_DOWN     }, 0, 0, V|F, "round" },
      80             :         { "up",   "round towards +infty",            0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_UP       }, 0, 0, V|F, "round" },
      81             :         { "near", "round to nearest",                0, AV_OPT_TYPE_CONST, { .i64 = AV_ROUND_NEAR_INF }, 0, 0, V|F, "round" },
      82             :     { "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" },
      83             :         { "round", "round similar to other frames",  0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_ROUND }, 0, 0, V|F, "eof_action" },
      84             :         { "pass",  "pass through last frame",        0, AV_OPT_TYPE_CONST, { .i64 = EOF_ACTION_PASS  }, 0, 0, V|F, "eof_action" },
      85             :     { NULL }
      86             : };
      87             : 
      88             : AVFILTER_DEFINE_CLASS(fps);
      89             : 
      90           5 : static av_cold int init(AVFilterContext *ctx)
      91             : {
      92           5 :     FPSContext *s = ctx->priv;
      93             : 
      94           5 :     if (!(s->fifo = av_fifo_alloc_array(2, sizeof(AVFrame*))))
      95           0 :         return AVERROR(ENOMEM);
      96             : 
      97           5 :     s->first_pts    = AV_NOPTS_VALUE;
      98             : 
      99           5 :     av_log(ctx, AV_LOG_VERBOSE, "fps=%d/%d\n", s->framerate.num, s->framerate.den);
     100           5 :     return 0;
     101             : }
     102             : 
     103          73 : static void flush_fifo(AVFifoBuffer *fifo)
     104             : {
     105         146 :     while (av_fifo_size(fifo)) {
     106             :         AVFrame *tmp;
     107           0 :         av_fifo_generic_read(fifo, &tmp, sizeof(tmp), NULL);
     108           0 :         av_frame_free(&tmp);
     109             :     }
     110          73 : }
     111             : 
     112           5 : static av_cold void uninit(AVFilterContext *ctx)
     113             : {
     114           5 :     FPSContext *s = ctx->priv;
     115           5 :     if (s->fifo) {
     116           5 :         s->drop += av_fifo_size(s->fifo) / sizeof(AVFrame*);
     117           5 :         flush_fifo(s->fifo);
     118           5 :         av_fifo_freep(&s->fifo);
     119             :     }
     120             : 
     121           5 :     av_log(ctx, AV_LOG_VERBOSE, "%d frames in, %d frames out; %d frames dropped, "
     122             :            "%d frames duplicated.\n", s->frames_in, s->frames_out, s->drop, s->dup);
     123           5 : }
     124             : 
     125           4 : static int config_props(AVFilterLink* link)
     126             : {
     127           4 :     FPSContext   *s = link->src->priv;
     128             : 
     129           4 :     link->time_base = av_inv_q(s->framerate);
     130           4 :     link->frame_rate= s->framerate;
     131           4 :     link->w         = link->src->inputs[0]->w;
     132           4 :     link->h         = link->src->inputs[0]->h;
     133             : 
     134           4 :     return 0;
     135             : }
     136             : 
     137          81 : static int request_frame(AVFilterLink *outlink)
     138             : {
     139          81 :     AVFilterContext *ctx = outlink->src;
     140          81 :     FPSContext        *s = ctx->priv;
     141             :     int ret;
     142             : 
     143          81 :     ret = ff_request_frame(ctx->inputs[0]);
     144             : 
     145             :     /* flush the fifo */
     146          81 :     if (ret == AVERROR_EOF && av_fifo_size(s->fifo)) {
     147             :         int i;
     148          16 :         for (i = 0; av_fifo_size(s->fifo); i++) {
     149             :             AVFrame *buf;
     150             : 
     151           4 :             av_fifo_generic_read(s->fifo, &buf, sizeof(buf), NULL);
     152           4 :             if (av_fifo_size(s->fifo)) {
     153           0 :                 buf->pts = av_rescale_q(s->first_pts, ctx->inputs[0]->time_base,
     154           0 :                                         outlink->time_base) + s->frames_out;
     155             : 
     156           0 :                 if ((ret = ff_filter_frame(outlink, buf)) < 0)
     157           0 :                     return ret;
     158             : 
     159           0 :                 s->frames_out++;
     160             :             } else {
     161             :                 /* This is the last frame, we may have to duplicate it to match
     162             :                  * the last frame duration */
     163             :                 int j;
     164           4 :                 int eof_rounding = (s->eof_action == EOF_ACTION_PASS) ? AV_ROUND_UP : s->rounding;
     165          12 :                 int delta = av_rescale_q_rnd(ctx->inputs[0]->current_pts - s->first_pts,
     166           4 :                                              ctx->inputs[0]->time_base,
     167           4 :                                              outlink->time_base, eof_rounding) - s->frames_out;
     168           4 :                 av_log(ctx, AV_LOG_DEBUG, "EOF frames_out:%d delta:%d\n", s->frames_out, delta);
     169             :                 /* if the delta is equal to 1, it means we just need to output
     170             :                  * the last frame. Greater than 1 means we will need duplicate
     171             :                  * delta-1 frames */
     172           4 :                 if (delta > 0 ) {
     173          14 :                     for (j = 0; j < delta; j++) {
     174          12 :                         AVFrame *dup = av_frame_clone(buf);
     175             : 
     176          12 :                         av_log(ctx, AV_LOG_DEBUG, "Duplicating frame.\n");
     177          24 :                         dup->pts = av_rescale_q(s->first_pts, ctx->inputs[0]->time_base,
     178          12 :                                                 outlink->time_base) + s->frames_out;
     179             : 
     180          12 :                         if ((ret = ff_filter_frame(outlink, dup)) < 0)
     181           0 :                             return ret;
     182             : 
     183          12 :                         s->frames_out++;
     184          12 :                         if (j > 0) s->dup++;
     185             :                     }
     186           2 :                     av_frame_free(&buf);
     187             :                 } else {
     188             :                     /* for delta less or equal to 0, we should drop the frame,
     189             :                      * otherwise, we will have one or more extra frames */
     190           2 :                     av_frame_free(&buf);
     191           2 :                     s->drop++;
     192             :                 }
     193             :             }
     194             :         }
     195           4 :         return 0;
     196             :     }
     197             : 
     198          77 :     return ret;
     199             : }
     200             : 
     201         223 : static int write_to_fifo(AVFifoBuffer *fifo, AVFrame *buf)
     202             : {
     203             :     int ret;
     204             : 
     205         223 :     if (!av_fifo_space(fifo) &&
     206           0 :         (ret = av_fifo_realloc2(fifo, 2*av_fifo_size(fifo)))) {
     207           0 :         av_frame_free(&buf);
     208           0 :         return ret;
     209             :     }
     210             : 
     211         223 :     av_fifo_generic_write(fifo, &buf, sizeof(buf), NULL);
     212         223 :     return 0;
     213             : }
     214             : 
     215          72 : static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     216             : {
     217          72 :     AVFilterContext    *ctx = inlink->dst;
     218          72 :     FPSContext           *s = ctx->priv;
     219          72 :     AVFilterLink   *outlink = ctx->outputs[0];
     220             :     int64_t delta;
     221             :     int i, ret;
     222             : 
     223          72 :     s->frames_in++;
     224             :     /* discard frames until we get the first timestamp */
     225          72 :     if (s->first_pts == AV_NOPTS_VALUE) {
     226           4 :         if (buf->pts != AV_NOPTS_VALUE) {
     227           4 :             ret = write_to_fifo(s->fifo, buf);
     228           4 :             if (ret < 0)
     229           0 :                 return ret;
     230             : 
     231           4 :             if (s->start_time != DBL_MAX && s->start_time != AV_NOPTS_VALUE) {
     232           0 :                 double first_pts = s->start_time * AV_TIME_BASE;
     233           0 :                 first_pts = FFMIN(FFMAX(first_pts, INT64_MIN), INT64_MAX);
     234           0 :                 s->first_pts = av_rescale_q(first_pts, AV_TIME_BASE_Q,
     235             :                                                      inlink->time_base);
     236           0 :                 av_log(ctx, AV_LOG_VERBOSE, "Set first pts to (in:%"PRId64" out:%"PRId64")\n",
     237           0 :                        s->first_pts, av_rescale_q(first_pts, AV_TIME_BASE_Q,
     238             :                                                   outlink->time_base));
     239             :             } else {
     240           4 :                 s->first_pts = buf->pts;
     241             :             }
     242             :         } else {
     243           0 :             av_log(ctx, AV_LOG_WARNING, "Discarding initial frame(s) with no "
     244             :                    "timestamp.\n");
     245           0 :             av_frame_free(&buf);
     246           0 :             s->drop++;
     247             :         }
     248           4 :         return 0;
     249             :     }
     250             : 
     251             :     /* now wait for the next timestamp */
     252          68 :     if (buf->pts == AV_NOPTS_VALUE || av_fifo_size(s->fifo) <= 0) {
     253           0 :         return write_to_fifo(s->fifo, buf);
     254             :     }
     255             : 
     256             :     /* number of output frames */
     257         136 :     delta = av_rescale_q_rnd(buf->pts - s->first_pts, inlink->time_base,
     258         136 :                              outlink->time_base, s->rounding) - s->frames_out ;
     259             : 
     260          68 :     if (delta < 1) {
     261             :         /* drop everything buffered except the last */
     262           0 :         int drop = av_fifo_size(s->fifo)/sizeof(AVFrame*);
     263             : 
     264           0 :         av_log(ctx, AV_LOG_DEBUG, "Dropping %d frame(s).\n", drop);
     265           0 :         s->drop += drop;
     266             : 
     267           0 :         flush_fifo(s->fifo);
     268           0 :         ret = write_to_fifo(s->fifo, buf);
     269             : 
     270           0 :         return ret;
     271             :     }
     272             : 
     273             :     /* can output >= 1 frames */
     274         574 :     for (i = 0; i < delta; i++) {
     275             :         AVFrame *buf_out;
     276         219 :         av_fifo_generic_read(s->fifo, &buf_out, sizeof(buf_out), NULL);
     277             : 
     278             :         /* duplicate the frame if needed */
     279         219 :         if (!av_fifo_size(s->fifo) && i < delta - 1) {
     280         151 :             AVFrame *dup = av_frame_clone(buf_out);
     281             : 
     282         151 :             av_log(ctx, AV_LOG_DEBUG, "Duplicating frame.\n");
     283         151 :             if (dup)
     284         151 :                 ret = write_to_fifo(s->fifo, dup);
     285             :             else
     286           0 :                 ret = AVERROR(ENOMEM);
     287             : 
     288         151 :             if (ret < 0) {
     289           0 :                 av_frame_free(&buf_out);
     290           0 :                 av_frame_free(&buf);
     291           0 :                 return ret;
     292             :             }
     293             : 
     294         151 :             s->dup++;
     295             :         }
     296             : 
     297         657 :         buf_out->pts = av_rescale_q(s->first_pts, inlink->time_base,
     298         438 :                                     outlink->time_base) + s->frames_out;
     299             : 
     300         219 :         if ((ret = ff_filter_frame(outlink, buf_out)) < 0) {
     301           0 :             av_frame_free(&buf);
     302           0 :             return ret;
     303             :         }
     304             : 
     305         219 :         s->frames_out++;
     306             :     }
     307          68 :     flush_fifo(s->fifo);
     308             : 
     309          68 :     ret = write_to_fifo(s->fifo, buf);
     310             : 
     311          68 :     return ret;
     312             : }
     313             : 
     314             : static const AVFilterPad avfilter_vf_fps_inputs[] = {
     315             :     {
     316             :         .name         = "default",
     317             :         .type         = AVMEDIA_TYPE_VIDEO,
     318             :         .filter_frame = filter_frame,
     319             :     },
     320             :     { NULL }
     321             : };
     322             : 
     323             : static const AVFilterPad avfilter_vf_fps_outputs[] = {
     324             :     {
     325             :         .name          = "default",
     326             :         .type          = AVMEDIA_TYPE_VIDEO,
     327             :         .request_frame = request_frame,
     328             :         .config_props  = config_props
     329             :     },
     330             :     { NULL }
     331             : };
     332             : 
     333             : AVFilter ff_vf_fps = {
     334             :     .name        = "fps",
     335             :     .description = NULL_IF_CONFIG_SMALL("Force constant framerate."),
     336             :     .init        = init,
     337             :     .uninit      = uninit,
     338             :     .priv_size   = sizeof(FPSContext),
     339             :     .priv_class  = &fps_class,
     340             :     .inputs      = avfilter_vf_fps_inputs,
     341             :     .outputs     = avfilter_vf_fps_outputs,
     342             : };

Generated by: LCOV version 1.13