LCOV - code coverage report
Current view: top level - src/libavfilter - af_asyncts.c (source / functions) Hit Total Coverage
Test: coverage.info Lines: 92 137 67.2 %
Date: 2017-01-24 04:42:20 Functions: 7 8 87.5 %

          Line data    Source code
       1             : /*
       2             :  * This file is part of FFmpeg.
       3             :  *
       4             :  * FFmpeg is free software; you can redistribute it and/or
       5             :  * modify it under the terms of the GNU Lesser General Public
       6             :  * License as published by the Free Software Foundation; either
       7             :  * version 2.1 of the License, or (at your option) any later version.
       8             :  *
       9             :  * FFmpeg is distributed in the hope that it will be useful,
      10             :  * but WITHOUT ANY WARRANTY; without even the implied warranty of
      11             :  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
      12             :  * Lesser General Public License for more details.
      13             :  *
      14             :  * You should have received a copy of the GNU Lesser General Public
      15             :  * License along with FFmpeg; if not, write to the Free Software
      16             :  * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
      17             :  */
      18             : 
      19             : #include <stdint.h>
      20             : 
      21             : #include "libavresample/avresample.h"
      22             : #include "libavutil/attributes.h"
      23             : #include "libavutil/audio_fifo.h"
      24             : #include "libavutil/common.h"
      25             : #include "libavutil/mathematics.h"
      26             : #include "libavutil/opt.h"
      27             : #include "libavutil/samplefmt.h"
      28             : 
      29             : #include "audio.h"
      30             : #include "avfilter.h"
      31             : #include "internal.h"
      32             : 
      33             : typedef struct ASyncContext {
      34             :     const AVClass *class;
      35             : 
      36             :     AVAudioResampleContext *avr;
      37             :     int64_t pts;            ///< timestamp in samples of the first sample in fifo
      38             :     int min_delta;          ///< pad/trim min threshold in samples
      39             :     int first_frame;        ///< 1 until filter_frame() has processed at least 1 frame with a pts != AV_NOPTS_VALUE
      40             :     int64_t first_pts;      ///< user-specified first expected pts, in samples
      41             :     int comp;               ///< current resample compensation
      42             : 
      43             :     /* options */
      44             :     int resample;
      45             :     float min_delta_sec;
      46             :     int max_comp;
      47             : 
      48             :     /* set by filter_frame() to signal an output frame to request_frame() */
      49             :     int got_output;
      50             : } ASyncContext;
      51             : 
      52             : #define OFFSET(x) offsetof(ASyncContext, x)
      53             : #define A AV_OPT_FLAG_AUDIO_PARAM
      54             : #define F AV_OPT_FLAG_FILTERING_PARAM
      55             : static const AVOption asyncts_options[] = {
      56             :     { "compensate", "Stretch/squeeze the data to make it match the timestamps", OFFSET(resample),      AV_OPT_TYPE_BOOL,  { .i64 = 0 },   0, 1,       A|F },
      57             :     { "min_delta",  "Minimum difference between timestamps and audio data "
      58             :                     "(in seconds) to trigger padding/trimmin the data.",        OFFSET(min_delta_sec), AV_OPT_TYPE_FLOAT, { .dbl = 0.1 }, 0, INT_MAX, A|F },
      59             :     { "max_comp",   "Maximum compensation in samples per second.",              OFFSET(max_comp),      AV_OPT_TYPE_INT,   { .i64 = 500 }, 0, INT_MAX, A|F },
      60             :     { "first_pts",  "Assume the first pts should be this value.",               OFFSET(first_pts),     AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, A|F },
      61             :     { NULL }
      62             : };
      63             : 
      64             : AVFILTER_DEFINE_CLASS(asyncts);
      65             : 
      66           1 : static av_cold int init(AVFilterContext *ctx)
      67             : {
      68           1 :     ASyncContext *s = ctx->priv;
      69             : 
      70           1 :     s->pts         = AV_NOPTS_VALUE;
      71           1 :     s->first_frame = 1;
      72             : 
      73           1 :     return 0;
      74             : }
      75             : 
      76           1 : static av_cold void uninit(AVFilterContext *ctx)
      77             : {
      78           1 :     ASyncContext *s = ctx->priv;
      79             : 
      80           1 :     if (s->avr) {
      81           1 :         avresample_close(s->avr);
      82           1 :         avresample_free(&s->avr);
      83             :     }
      84           1 : }
      85             : 
      86           1 : static int config_props(AVFilterLink *link)
      87             : {
      88           1 :     ASyncContext *s = link->src->priv;
      89             :     int ret;
      90             : 
      91           1 :     s->min_delta = s->min_delta_sec * link->sample_rate;
      92           1 :     link->time_base = (AVRational){1, link->sample_rate};
      93             : 
      94           1 :     s->avr = avresample_alloc_context();
      95           1 :     if (!s->avr)
      96           0 :         return AVERROR(ENOMEM);
      97             : 
      98           1 :     av_opt_set_int(s->avr,  "in_channel_layout", link->channel_layout, 0);
      99           1 :     av_opt_set_int(s->avr, "out_channel_layout", link->channel_layout, 0);
     100           1 :     av_opt_set_int(s->avr,  "in_sample_fmt",     link->format,         0);
     101           1 :     av_opt_set_int(s->avr, "out_sample_fmt",     link->format,         0);
     102           1 :     av_opt_set_int(s->avr,  "in_sample_rate",    link->sample_rate,    0);
     103           1 :     av_opt_set_int(s->avr, "out_sample_rate",    link->sample_rate,    0);
     104             : 
     105           1 :     if (s->resample)
     106           0 :         av_opt_set_int(s->avr, "force_resampling", 1, 0);
     107             : 
     108           1 :     if ((ret = avresample_open(s->avr)) < 0)
     109           0 :         return ret;
     110             : 
     111           1 :     return 0;
     112             : }
     113             : 
     114             : /* get amount of data currently buffered, in samples */
     115         957 : static int64_t get_delay(ASyncContext *s)
     116             : {
     117         957 :     return avresample_available(s->avr) + avresample_get_delay(s->avr);
     118             : }
     119             : 
     120           0 : static void handle_trimming(AVFilterContext *ctx)
     121             : {
     122           0 :     ASyncContext *s = ctx->priv;
     123             : 
     124           0 :     if (s->pts < s->first_pts) {
     125           0 :         int delta = FFMIN(s->first_pts - s->pts, avresample_available(s->avr));
     126           0 :         av_log(ctx, AV_LOG_VERBOSE, "Trimming %d samples from start\n",
     127             :                delta);
     128           0 :         avresample_read(s->avr, NULL, delta);
     129           0 :         s->pts += delta;
     130           0 :     } else if (s->first_frame)
     131           0 :         s->pts = s->first_pts;
     132           0 : }
     133             : 
     134         958 : static int request_frame(AVFilterLink *link)
     135             : {
     136         958 :     AVFilterContext *ctx = link->src;
     137         958 :     ASyncContext      *s = ctx->priv;
     138         958 :     int ret = 0;
     139             :     int nb_samples;
     140             : 
     141         958 :     s->got_output = 0;
     142         958 :     ret = ff_request_frame(ctx->inputs[0]);
     143             : 
     144             :     /* flush the fifo */
     145         958 :     if (ret == AVERROR_EOF) {
     146           2 :         if (s->first_pts != AV_NOPTS_VALUE)
     147           0 :             handle_trimming(ctx);
     148             : 
     149           2 :         if (nb_samples = get_delay(s)) {
     150           1 :             AVFrame *buf = ff_get_audio_buffer(link, nb_samples);
     151           1 :             if (!buf)
     152           0 :                 return AVERROR(ENOMEM);
     153           1 :             ret = avresample_convert(s->avr, buf->extended_data,
     154           1 :                                      buf->linesize[0], nb_samples, NULL, 0, 0);
     155           1 :             if (ret <= 0) {
     156           0 :                 av_frame_free(&buf);
     157           0 :                 return (ret < 0) ? ret : AVERROR_EOF;
     158             :             }
     159             : 
     160           1 :             buf->pts = s->pts;
     161           1 :             return ff_filter_frame(link, buf);
     162             :         }
     163             :     }
     164             : 
     165         957 :     return ret;
     166             : }
     167             : 
     168           1 : static int write_to_fifo(ASyncContext *s, AVFrame *buf)
     169             : {
     170           2 :     int ret = avresample_convert(s->avr, NULL, 0, 0, buf->extended_data,
     171           2 :                                  buf->linesize[0], buf->nb_samples);
     172           1 :     av_frame_free(&buf);
     173           1 :     return ret;
     174             : }
     175             : 
     176         955 : static int filter_frame(AVFilterLink *inlink, AVFrame *buf)
     177             : {
     178         955 :     AVFilterContext  *ctx = inlink->dst;
     179         955 :     ASyncContext       *s = ctx->priv;
     180         955 :     AVFilterLink *outlink = ctx->outputs[0];
     181         955 :     int nb_channels = av_get_channel_layout_nb_channels(buf->channel_layout);
     182        1910 :     int64_t pts = (buf->pts == AV_NOPTS_VALUE) ? buf->pts :
     183         955 :                   av_rescale_q(buf->pts, inlink->time_base, outlink->time_base);
     184             :     int out_size, ret;
     185             :     int64_t delta;
     186             :     int64_t new_pts;
     187             : 
     188             :     /* buffer data until we get the next timestamp */
     189         955 :     if (s->pts == AV_NOPTS_VALUE || pts == AV_NOPTS_VALUE) {
     190           1 :         if (pts != AV_NOPTS_VALUE) {
     191           1 :             s->pts = pts - get_delay(s);
     192             :         }
     193           1 :         return write_to_fifo(s, buf);
     194             :     }
     195             : 
     196         954 :     if (s->first_pts != AV_NOPTS_VALUE) {
     197           0 :         handle_trimming(ctx);
     198           0 :         if (!avresample_available(s->avr))
     199           0 :             return write_to_fifo(s, buf);
     200             :     }
     201             : 
     202             :     /* when we have two timestamps, compute how many samples would we have
     203             :      * to add/remove to get proper sync between data and timestamps */
     204         954 :     delta    = pts - s->pts - get_delay(s);
     205         954 :     out_size = avresample_available(s->avr);
     206             : 
     207        1907 :     if (llabs(delta) > s->min_delta ||
     208         954 :         (s->first_frame && delta && s->first_pts != AV_NOPTS_VALUE)) {
     209           1 :         av_log(ctx, AV_LOG_VERBOSE, "Discontinuity - %"PRId64" samples.\n", delta);
     210           1 :         out_size = av_clipl_int32((int64_t)out_size + delta);
     211             :     } else {
     212         953 :         if (s->resample) {
     213             :             // adjust the compensation if delta is non-zero
     214           0 :             int delay = get_delay(s);
     215           0 :             int comp = s->comp + av_clip(delta * inlink->sample_rate / delay,
     216           0 :                                          -s->max_comp, s->max_comp);
     217           0 :             if (comp != s->comp) {
     218           0 :                 av_log(ctx, AV_LOG_VERBOSE, "Compensating %d samples per second.\n", comp);
     219           0 :                 if (avresample_set_compensation(s->avr, comp, inlink->sample_rate) == 0) {
     220           0 :                     s->comp = comp;
     221             :                 }
     222             :             }
     223             :         }
     224             :         // adjust PTS to avoid monotonicity errors with input PTS jitter
     225         953 :         pts -= delta;
     226         953 :         delta = 0;
     227             :     }
     228             : 
     229         954 :     if (out_size > 0) {
     230         954 :         AVFrame *buf_out = ff_get_audio_buffer(outlink, out_size);
     231         954 :         if (!buf_out) {
     232           0 :             ret = AVERROR(ENOMEM);
     233           0 :             goto fail;
     234             :         }
     235             : 
     236         954 :         if (s->first_frame && delta > 0) {
     237           0 :             int planar = av_sample_fmt_is_planar(buf_out->format);
     238           0 :             int planes = planar ?  nb_channels : 1;
     239           0 :             int block_size = av_get_bytes_per_sample(buf_out->format) *
     240           0 :                              (planar ? 1 : nb_channels);
     241             : 
     242             :             int ch;
     243             : 
     244           0 :             av_samples_set_silence(buf_out->extended_data, 0, delta,
     245           0 :                                    nb_channels, buf->format);
     246             : 
     247           0 :             for (ch = 0; ch < planes; ch++)
     248           0 :                 buf_out->extended_data[ch] += delta * block_size;
     249             : 
     250           0 :             avresample_read(s->avr, buf_out->extended_data, out_size);
     251             : 
     252           0 :             for (ch = 0; ch < planes; ch++)
     253           0 :                 buf_out->extended_data[ch] -= delta * block_size;
     254             :         } else {
     255         954 :             avresample_read(s->avr, buf_out->extended_data, out_size);
     256             : 
     257         954 :             if (delta > 0) {
     258           1 :                 av_samples_set_silence(buf_out->extended_data, out_size - delta,
     259           1 :                                        delta, nb_channels, buf->format);
     260             :             }
     261             :         }
     262         954 :         buf_out->pts = s->pts;
     263         954 :         ret = ff_filter_frame(outlink, buf_out);
     264         954 :         if (ret < 0)
     265           0 :             goto fail;
     266         954 :         s->got_output = 1;
     267           0 :     } else if (avresample_available(s->avr)) {
     268           0 :         av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping "
     269             :                "whole buffer.\n");
     270             :     }
     271             : 
     272             :     /* drain any remaining buffered data */
     273         954 :     avresample_read(s->avr, NULL, avresample_available(s->avr));
     274             : 
     275         954 :     new_pts = pts - avresample_get_delay(s->avr);
     276             :     /* check for s->pts monotonicity */
     277         954 :     if (new_pts > s->pts) {
     278         954 :         s->pts = new_pts;
     279        1908 :         ret = avresample_convert(s->avr, NULL, 0, 0, buf->extended_data,
     280        1908 :                                  buf->linesize[0], buf->nb_samples);
     281             :     } else {
     282           0 :         av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping "
     283             :                "whole buffer.\n");
     284           0 :         ret = 0;
     285             :     }
     286             : 
     287         954 :     s->first_frame = 0;
     288             : fail:
     289         954 :     av_frame_free(&buf);
     290             : 
     291         954 :     return ret;
     292             : }
     293             : 
     294             : static const AVFilterPad avfilter_af_asyncts_inputs[] = {
     295             :     {
     296             :         .name          = "default",
     297             :         .type          = AVMEDIA_TYPE_AUDIO,
     298             :         .filter_frame  = filter_frame
     299             :     },
     300             :     { NULL }
     301             : };
     302             : 
     303             : static const AVFilterPad avfilter_af_asyncts_outputs[] = {
     304             :     {
     305             :         .name          = "default",
     306             :         .type          = AVMEDIA_TYPE_AUDIO,
     307             :         .config_props  = config_props,
     308             :         .request_frame = request_frame
     309             :     },
     310             :     { NULL }
     311             : };
     312             : 
     313             : AVFilter ff_af_asyncts = {
     314             :     .name        = "asyncts",
     315             :     .description = NULL_IF_CONFIG_SMALL("Sync audio data to timestamps."),
     316             :     .init        = init,
     317             :     .uninit      = uninit,
     318             :     .priv_size   = sizeof(ASyncContext),
     319             :     .priv_class  = &asyncts_class,
     320             :     .query_formats = ff_query_formats_all_layouts,
     321             :     .inputs      = avfilter_af_asyncts_inputs,
     322             :     .outputs     = avfilter_af_asyncts_outputs,
     323             : };

Generated by: LCOV version 1.12