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;