From c143de40c3bfacc0d6713b16c2305552494fe669 Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Tue, 11 Dec 2012 17:36:09 -0500 Subject: [PATCH 1/2] asyncts: fix the asyncts behavior when using the first_pts option Currently it will do padding, but it does not properly handle start-of-stream trimming as documented. --- libavfilter/af_asyncts.c | 64 ++++++++++++++++++++++++++++++++++------ 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/libavfilter/af_asyncts.c b/libavfilter/af_asyncts.c index 087692e0d6..02dce5b916 100644 --- a/libavfilter/af_asyncts.c +++ b/libavfilter/af_asyncts.c @@ -33,6 +33,8 @@ typedef struct ASyncContext { AVAudioResampleContext *avr; int64_t pts; ///< timestamp in samples of the first sample in fifo int min_delta; ///< pad/trim min threshold in samples + int first_frame; ///< 1 until filter_frame() has processed at least 1 frame with a pts != AV_NOPTS_VALUE + int64_t first_pts; ///< user-specified first expected pts, in samples /* options */ int resample; @@ -50,7 +52,7 @@ static const AVOption options[] = { { "min_delta", "Minimum difference between timestamps and audio data " "(in seconds) to trigger padding/trimmin the data.", OFFSET(min_delta_sec), AV_OPT_TYPE_FLOAT, { .dbl = 0.1 }, 0, INT_MAX, A }, { "max_comp", "Maximum compensation in samples per second.", OFFSET(max_comp), AV_OPT_TYPE_INT, { .i64 = 500 }, 0, INT_MAX, A }, - { "first_pts", "Assume the first pts should be this value.", OFFSET(pts), AV_OPT_TYPE_INT64, { .i64 = AV_NOPTS_VALUE }, INT64_MIN, INT64_MAX, A }, + { "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 }, { NULL }, }; @@ -75,6 +77,9 @@ static int init(AVFilterContext *ctx, const char *args) } av_opt_free(s); + s->pts = AV_NOPTS_VALUE; + s->first_frame = 1; + return 0; } @@ -122,6 +127,20 @@ static int64_t get_delay(ASyncContext *s) return avresample_available(s->avr) + avresample_get_delay(s->avr); } +static void handle_trimming(AVFilterContext *ctx) +{ + ASyncContext *s = ctx->priv; + + if (s->pts < s->first_pts) { + int delta = FFMIN(s->first_pts - s->pts, avresample_available(s->avr)); + av_log(ctx, AV_LOG_VERBOSE, "Trimming %d samples from start\n", + delta); + avresample_read(s->avr, NULL, delta); + s->pts += delta; + } else if (s->first_frame) + s->pts = s->first_pts; +} + static int request_frame(AVFilterLink *link) { AVFilterContext *ctx = link->src; @@ -134,7 +153,11 @@ static int request_frame(AVFilterLink *link) ret = ff_request_frame(ctx->inputs[0]); /* flush the fifo */ - if (ret == AVERROR_EOF && (nb_samples = get_delay(s))) { + if (ret == AVERROR_EOF) { + if (s->first_pts != AV_NOPTS_VALUE) + handle_trimming(ctx); + + if (nb_samples = get_delay(s)) { AVFilterBufferRef *buf = ff_get_audio_buffer(link, AV_PERM_WRITE, nb_samples); if (!buf) @@ -148,6 +171,7 @@ static int request_frame(AVFilterLink *link) buf->pts = s->pts; return ff_filter_frame(link, buf); + } } return ret; @@ -185,12 +209,18 @@ static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf) return write_to_fifo(s, buf); } + if (s->first_pts != AV_NOPTS_VALUE) { + handle_trimming(ctx); + if (!avresample_available(s->avr)) + return write_to_fifo(s, buf); + } + /* when we have two timestamps, compute how many samples would we have * to add/remove to get proper sync between data and timestamps */ delta = pts - s->pts - get_delay(s); out_size = avresample_available(s->avr); - if (labs(delta) > s->min_delta) { + if (labs(delta) > s->min_delta || (s->first_frame && delta)) { av_log(ctx, AV_LOG_VERBOSE, "Discontinuity - %"PRId64" samples.\n", delta); out_size = av_clipl_int32((int64_t)out_size + delta); } else { @@ -210,18 +240,33 @@ static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf) goto fail; } - avresample_read(s->avr, buf_out->extended_data, out_size); - buf_out->pts = s->pts; + if (s->first_frame && delta > 0) { + int ch; - if (delta > 0) { - av_samples_set_silence(buf_out->extended_data, out_size - delta, - delta, nb_channels, buf->format); + av_samples_set_silence(buf_out->extended_data, 0, delta, + nb_channels, buf->format); + + for (ch = 0; ch < nb_channels; ch++) + buf_out->extended_data[ch] += delta; + + avresample_read(s->avr, buf_out->extended_data, out_size); + + for (ch = 0; ch < nb_channels; ch++) + buf_out->extended_data[ch] -= delta; + } else { + avresample_read(s->avr, buf_out->extended_data, out_size); + + if (delta > 0) { + av_samples_set_silence(buf_out->extended_data, out_size - delta, + delta, nb_channels, buf->format); + } } + buf_out->pts = s->pts; ret = ff_filter_frame(outlink, buf_out); if (ret < 0) goto fail; s->got_output = 1; - } else { + } else if (avresample_available(s->avr)) { av_log(ctx, AV_LOG_WARNING, "Non-monotonous timestamps, dropping " "whole buffer.\n"); } @@ -233,6 +278,7 @@ static int filter_frame(AVFilterLink *inlink, AVFilterBufferRef *buf) ret = avresample_convert(s->avr, NULL, 0, 0, buf->extended_data, buf->linesize[0], buf->audio->nb_samples); + s->first_frame = 0; fail: avfilter_unref_buffer(buf); From b35e5d985dd12acf9a0aaa52334134edcf35d68e Mon Sep 17 00:00:00 2001 From: Justin Ruggles Date: Wed, 12 Dec 2012 13:19:16 -0500 Subject: [PATCH 2/2] doc: improve documentation for the asyncts filter first_pts option Note the time base for the option. Add an additional example description. --- doc/filters.texi | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/doc/filters.texi b/doc/filters.texi index 55e4468fd6..34db2f4630 100644 --- a/doc/filters.texi +++ b/doc/filters.texi @@ -250,11 +250,12 @@ Maximum compensation in samples per second. Relevant only with compensate=1. Default value 500. @item first_pts -Assume the first pts should be this value. +Assume the first pts should be this value. The time base is 1 / sample rate. This allows for padding/trimming at the start of stream. By default, no assumption is made about the first frame's expected pts, so no padding or trimming is done. For example, this could be set to 0 to pad the beginning with -silence if an audio stream starts after the video stream. +silence if an audio stream starts after the video stream or to trim any samples +with a negative pts due to encoder delay. @end table