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);