Commit d5327166cf for ffmpeg

commit d5327166cf26da2dc46bf06cd3718cf7af654709
Author: Baptiste Coudurier <baptiste.coudurier@gmail.com>
Date:   Thu Nov 12 10:27:30 2026 -0800

    avcodec/opus/dec: do not let the NB SILK resampler hold back samples

    The SILK resampler is primed with 4, 8 or 11 input samples of silence
    for NB, MB and WB, which sets the decoder delay. With a filter size of
    16, swresample needs 8 input samples of lookahead at every rate, which
    the NB priming does not cover: every NB frame came out 24 samples
    short, held back until a mode or rate change or until draining.

    The first frame after the start or a seek was thus 24 samples short,
    the following frames ended 24 samples before the end of their packets,
    and the end trimming of the last packet cut 24 samples too early. The
    held-back samples then came out on draining, as a 24 sample frame past
    the end with the timestamp of the last frame, unless the packet had
    AV_PKT_FLAG_EOS.

    Use a filter size of 8 for NB, the largest whose lookahead fits within
    the priming. The delay does not depend on the filter size, so the
    timing is unchanged, and MB and WB keep a filter size of 16 and decode
    bit-identically. The output gets closer to the RFC 8251 reference
    decodes in testvector02, while priming NB with 8 samples instead,
    which would keep the filter, makes it much further away.

    With a stereo NB stream of 151 20 ms packets, 312 samples of pre-skip
    and 144000 samples, the frames now hold 648, 960, ..., 960 and 312
    samples instead of 624, 960, ..., 960 and 312 + 24 (or 336 with
    AV_PKT_FLAG_EOS), and decoding 1 s after a seek gives 48000 samples
    instead of 47976.

    silk-lbrr-mono is NB and its reference was produced by FFmpeg.

    Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
    Signed-off-by: Baptiste Coudurier <baptiste.coudurier@gmail.com>

diff --git a/libavcodec/opus/dec.c b/libavcodec/opus/dec.c
index 1f80ecd760..29d8113690 100644
--- a/libavcodec/opus/dec.c
+++ b/libavcodec/opus/dec.c
@@ -72,6 +72,13 @@ static const int silk_resample_delay[] = {
     4, 8, 11, 11, 11
 };

+/* filter size of the resampler for each bandwidth, chosen so that the
+ * resampler needs no more input samples of lookahead than the silence
+ * above provides, so it does not hold back output samples */
+static const int silk_resample_filter_size[] = {
+    8, 16, 16, 16, 16
+};
+
 typedef struct OpusStreamContext {
     AVCodecContext *avctx;
     int output_channels;
@@ -197,6 +204,8 @@ static int opus_init_resample(OpusStreamContext *s)
     int ret;

     av_opt_set_int(s->swr, "in_sample_rate", s->silk_samplerate, 0);
+    av_opt_set_int(s->swr, "filter_size",
+                   silk_resample_filter_size[s->packet.bandwidth], 0);
     ret = swr_init(s->swr);
     if (ret < 0) {
         av_log(s->avctx, AV_LOG_ERROR, "Error opening the resampler.\n");
@@ -578,7 +587,6 @@ static av_cold int opus_init_streams(AVCodecContext *avctx,
         av_opt_set_chlayout(s->swr, "in_chlayout",   &layout,            0);
         av_opt_set_chlayout(s->swr, "out_chlayout",  &layout,            0);
         av_opt_set_int(s->swr, "out_sample_rate",    48000,              0);
-        av_opt_set_int(s->swr, "filter_size",        16,                 0);

         ret = ff_silk_init(avctx, &s->silk, s->output_channels);
         if (ret < 0)
diff --git a/tests/fate/opus.mak b/tests/fate/opus.mak
index b07473515a..f107a579fe 100644
--- a/tests/fate/opus.mak
+++ b/tests/fate/opus.mak
@@ -22,7 +22,7 @@ $(FATE_OPUS): CMP = stddev
 $(FATE_OPUS): CMP_UNIT = s16
 $(FATE_OPUS): FUZZ = 3
 fate-opus-testvector01:      CMP_TARGET = 0
-fate-opus-testvector02:      CMP_TARGET = 191
+fate-opus-testvector02:      CMP_TARGET = 190
 fate-opus-testvector03:      CMP_TARGET = 139
 fate-opus-testvector04:      CMP_TARGET = 119
 fate-opus-testvector05:      CMP_TARGET = 108
@@ -34,7 +34,7 @@ fate-opus-testvector10:      CMP_TARGET = 38
 fate-opus-testvector11:      CMP_TARGET = 0
 fate-opus-testvector12:      CMP_TARGET = 160
 fate-opus-silk-lbrr:         CMP_TARGET = 0
-fate-opus-silk-lbrr-mono:    CMP_TARGET = 0
+fate-opus-silk-lbrr-mono:    CMP_TARGET = 37
 fate-opus-tron.6ch.tinypkts: CMP_SHIFT = 1440
 fate-opus-tron.6ch.tinypkts: CMP_TARGET = 0