Commit 111393a78b for ffmpeg

commit 111393a78b3af7b66c717288bed0ac047bec07bb
Author: Peter Krefting <peter@softwolves.pp.se>
Date:   Mon Oct 19 09:07:17 2026 +0100

    avcodec/aac: Reset SBR context (HE-AAC) when flushing AAC decoder

    When calling avcodec_flush_buffers() on the AAC decoder, decoding an
    HE-AAC stream, the SBR state would not be cleared properly.

    See: https://ffmpeg.org/pipermail/ffmpeg-devel/2020-January/255176.html
    Assisted-by: OpenCode Big Pickle

diff --git a/libavcodec/aac/aacdec.c b/libavcodec/aac/aacdec.c
index d66c363b58..ba167eaab7 100644
--- a/libavcodec/aac/aacdec.c
+++ b/libavcodec/aac/aacdec.c
@@ -566,6 +566,7 @@ static av_cold void flush(AVCodecContext *avctx)
                 for (j = 0; j <= 1; j++) {
                     memset(che->ch[j].saved, 0, sizeof(che->ch[j].saved));
                 }
+                ac->proc.sbr_ctx_reset(ac, che);
             }
         }
     }
diff --git a/libavcodec/aac/aacdec.h b/libavcodec/aac/aacdec.h
index 18412fb97a..1fb89ace61 100644
--- a/libavcodec/aac/aacdec.h
+++ b/libavcodec/aac/aacdec.h
@@ -458,6 +458,7 @@ typedef struct AACDecProc {
     void (*sbr_apply)(AACDecContext *ac, ChannelElement *che, int id_aac, int fl960,
                       void /* INTFLOAT */ *L, void /* INTFLOAT */ *R);
     void (*sbr_ctx_close)(ChannelElement *che);
+    void (*sbr_ctx_reset)(AACDecContext *ac, ChannelElement *che);
 } AACDecProc;

 /**
diff --git a/libavcodec/aac/aacdec_proc_template.c b/libavcodec/aac/aacdec_proc_template.c
index 327f3117b5..b56302a93f 100644
--- a/libavcodec/aac/aacdec_proc_template.c
+++ b/libavcodec/aac/aacdec_proc_template.c
@@ -444,5 +444,6 @@ static av_cold void AAC_RENAME(aac_proc_init)(AACDecProc *aac_proc)
     SET(sbr_decode_extension);
     SET(sbr_apply);
     SET(sbr_ctx_close);
+    SET(sbr_ctx_reset);
 #undef SET
 }
diff --git a/libavcodec/aacsbr.h b/libavcodec/aacsbr.h
index afc8ad188a..c330a13183 100644
--- a/libavcodec/aacsbr.h
+++ b/libavcodec/aacsbr.h
@@ -82,6 +82,11 @@ int ff_aac_sbr_ctx_alloc_init_fixed(AACDecContext *ac, ChannelElement **che, int
 void ff_aac_sbr_ctx_close(ChannelElement *che);
 void ff_aac_sbr_ctx_close_fixed(ChannelElement *che);

+/** Reset the SBR context contained in a ChannelElement, discarding all
+ *  inter-frame state while keeping the allocated transform contexts. */
+void ff_aac_sbr_ctx_reset(AACDecContext *ac, ChannelElement *che);
+void ff_aac_sbr_ctx_reset_fixed(AACDecContext *ac, ChannelElement *che);
+
 /** Decode one SBR element. */
 int ff_aac_sbr_decode_extension(AACDecContext *ac, ChannelElement *che,
                                 GetBitContext *gb, int crc, int cnt, int id_aac,
diff --git a/libavcodec/aacsbr_template.c b/libavcodec/aacsbr_template.c
index f398d79a61..b084b0b82e 100644
--- a/libavcodec/aacsbr_template.c
+++ b/libavcodec/aacsbr_template.c
@@ -123,6 +123,24 @@ av_cold void AAC_RENAME(ff_aac_sbr_ctx_close)(ChannelElement *che)
     av_tx_uninit(&sbr->mdct_ana);
 }

+av_cold void AAC_RENAME(ff_aac_sbr_ctx_reset)(AACDecContext *ac, ChannelElement *che)
+{
+    SpectralBandReplication *sbr = get_sbr(che);
+    int id_aac = sbr->id_aac;
+
+    /* Discard all inter-frame state. The transform contexts (mdct_ana,
+     * mdct) and the function pointer tables (dsp, c) at the end of the
+     * struct are set up once in ff_aac_sbr_ctx_alloc_init() and hold no
+     * stream state, so they are deliberately left untouched. */
+    memset(sbr, 0, offsetof(SpectralBandReplication, mdct_ana));
+
+    sbr->id_aac = id_aac;
+    sbr_turnoff(sbr);
+    sbr->data[0].synthesis_filterbank_samples_offset = SBR_SYNTHESIS_BUF_SIZE - (1280 - 128);
+    sbr->data[1].synthesis_filterbank_samples_offset = SBR_SYNTHESIS_BUF_SIZE - (1280 - 128);
+    AAC_RENAME(ff_ps_ctx_init)(&sbr->ps);
+}
+
 static int qsort_comparison_function_int16(const void *a, const void *b)
 {
     return *(const int16_t *)a - *(const int16_t *)b;