From 090cc44480e3694e8045ac166dbae55918b1dc01 Mon Sep 17 00:00:00 2001 From: mcfnord Date: Mon, 5 Oct 2026 17:56:46 +0000 Subject: [PATCH] Clear a reused channel's stale audio and decoder state While a channel has no negotiated codec (CT_NONE), DecodeReceiveData() skipped both decode branches and left vecvecsData[iChanCnt] unwritten. That buffer is indexed by position in the connected-channel list, so with -R a new client in a reused channel was recorded with the audio last decoded at that position. Separately, the per-channel Opus decoders kept the previous client's state, so the new client's recording opened with the end of the previous client's audio. Zero the buffer in the CT_NONE branch, and reset the channel's four decoders when the channel is freed. Both run on the decode path. Fixes #3901 Co-Authored-By: Claude Opus 5.5 --- src/server.cpp | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/src/server.cpp b/src/server.cpp index d24e5a142d..bf9f4f8290 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -894,6 +894,10 @@ void CServer::DecodeReceiveData ( const int iChanCnt, const int iNumClients ) else { CurOpusDecoder = nullptr; + + // no codec yet (e.g. a new client in a reused channel): nothing writes this buffer, and it + // still holds the audio last decoded at this position in the connected-channel list + vecvecsData[iChanCnt].Reset ( 0 ); } // get gains and pannings of all connected channels, compacted to the @@ -940,6 +944,13 @@ void CServer::DecodeReceiveData ( const int iChanCnt, const int iNumClients ) { emit ClientDisconnected ( iCurChanID ); // TODO do this outside the mutex lock? + // the decoders belong to the channel, not the client, so drop this client's state + // before the next client in this channel is decoded + opus_custom_decoder_ctl ( OpusDecoderMono[iCurChanID], OPUS_RESET_STATE ); + opus_custom_decoder_ctl ( OpusDecoderStereo[iCurChanID], OPUS_RESET_STATE ); + opus_custom_decoder_ctl ( Opus64DecoderMono[iCurChanID], OPUS_RESET_STATE ); + opus_custom_decoder_ctl ( Opus64DecoderStereo[iCurChanID], OPUS_RESET_STATE ); + FreeChannel ( iCurChanID ); // note that the channel is now not in use // note that no mutex is needed for this shared resource since it is a