I don't use pulseaudio, so I can't test the exact conditions that led to bug 753, but basic conversion from Vorbis to Opus fails for me without the attached patch (producing an initial page with a granule position 1 too small to be valid), so this is at least a step in the right direction.

I also notice that the flag AVFMT_TS_NEGATIVE is set for all muxer classes in libavformat/oggenc.c, but I don't believe this is actually true for any of them (the granule position value -1 is reserved as invalid, but granule positions are otherwise treated as unsigned, leaving no way to represent a negative timestamp). I didn't change anything there, however, as I don't really understand all the implications, and I don't want to paper over other problems like this one.
>From c54239a28993847fc52094f654be46bc64186422 Mon Sep 17 00:00:00 2001
From: "Timothy B. Terriberry" <[email protected]>
Date: Mon, 13 Oct 2014 17:46:00 -0700
Subject: [PATCH] resample: Avoid off-by-1 errors in PTS calcs.

The rounding used in the PTS calculations in filter_frame() does
not actually match the number of samples output by the resampler.
This leads to off-by-1 errors in the timestamps indicating gaps and
underruns, even when the input timestamps are all contiguous.

Bug-Id: 753
---
 libavfilter/af_resample.c | 10 +++++++++-
 1 file changed, 9 insertions(+), 1 deletion(-)

diff --git a/libavfilter/af_resample.c b/libavfilter/af_resample.c
index bc8fd8a..a59e6f8 100644
--- a/libavfilter/af_resample.c
+++ b/libavfilter/af_resample.c
@@ -37,16 +37,17 @@
 #include "internal.h"
 
 typedef struct ResampleContext {
     const AVClass *class;
     AVAudioResampleContext *avr;
     AVDictionary *options;
 
     int64_t next_pts;
+    int64_t next_in_pts;
 
     /* set by filter_frame() to signal an output frame to request_frame() */
     int got_output;
 } ResampleContext;
 
 static av_cold int init(AVFilterContext *ctx, AVDictionary **opts)
 {
     ResampleContext *s = ctx->priv;
@@ -149,16 +150,17 @@ static int config_output(AVFilterLink *outlink)
     av_opt_set_int(s->avr,  "in_sample_rate",    inlink ->sample_rate,    0);
     av_opt_set_int(s->avr, "out_sample_rate",    outlink->sample_rate,    0);
 
     if ((ret = avresample_open(s->avr)) < 0)
         return ret;
 
     outlink->time_base = (AVRational){ 1, outlink->sample_rate };
     s->next_pts        = AV_NOPTS_VALUE;
+    s->next_in_pts     = AV_NOPTS_VALUE;
 
     av_get_channel_layout_string(buf1, sizeof(buf1),
                                  -1, inlink ->channel_layout);
     av_get_channel_layout_string(buf2, sizeof(buf2),
                                  -1, outlink->channel_layout);
     av_log(ctx, AV_LOG_VERBOSE,
            "fmt:%s srate:%d cl:%s -> fmt:%s srate:%d cl:%s\n",
            av_get_sample_fmt_name(inlink ->format), inlink ->sample_rate, buf1,
@@ -250,25 +252,31 @@ static int filter_frame(AVFilterLink *inlink, AVFrame *in)
 
             ret = av_frame_copy_props(out, in);
             if (ret < 0) {
                 av_frame_free(&out);
                 goto fail;
             }
 
             out->sample_rate = outlink->sample_rate;
-            if (in->pts != AV_NOPTS_VALUE) {
+            /* Only convert in->pts if there is a discontinuous jump.
+               This ensures that out->pts tracks the number of samples actually
+               output by the resampler in the absence of such a jump.
+               Otherwise, the rounding in av_rescale_q() and av_rescale()
+               causes off-by-1 errors. */
+            if (in->pts != AV_NOPTS_VALUE && in->pts != s->next_in_pts) {
                 out->pts = av_rescale_q(in->pts, inlink->time_base,
                                             outlink->time_base) -
                                av_rescale(delay, outlink->sample_rate,
                                           inlink->sample_rate);
             } else
                 out->pts = s->next_pts;
 
             s->next_pts = out->pts + out->nb_samples;
+            s->next_in_pts = in->pts + in->nb_samples;
 
             ret = ff_filter_frame(outlink, out);
             s->got_output = 1;
         }
 
 fail:
         av_frame_free(&in);
     } else {
-- 
1.8.3.2

_______________________________________________
libav-devel mailing list
[email protected]
https://lists.libav.org/mailman/listinfo/libav-devel

Reply via email to