Skip to content

Clear a reused channel's stale audio and decoder state - #3969

Open
mcfnord wants to merge 1 commit into
jamulussoftware:mainfrom
mcfnord:fix-3901-reused-slot-audio
Open

mcfnord wants to merge 1 commit into
jamulussoftware:mainfrom
mcfnord:fix-3901-reused-slot-audio

Conversation

@mcfnord

@mcfnord mcfnord commented Oct 5, 2026

Copy link
Copy Markdown
Contributor

🤖 AI: Short description of changes

A new client that takes a reused channel inherits two pieces of the previous client's audio, and with -R both land in the new client's recording. Measured on main at 7ebf8f87: test client A sends a 440 Hz tone and disconnects, and client B joins 100 to 500 ms later sending only silence.

  1. Until B's codec is negotiated, the CT_NONE branch writes nothing to vecvecsData[iChanCnt], which is indexed by position in the connected-channel list. B's pre-identification recording held A's tone in 12 of 13 runs. It was usually one 128-sample frame, but 256, 768 and 896 samples also occurred, each frame a repeat of the same stale buffer.
  2. The per-channel Opus decoders keep the previous client's state. With only the buffer zeroed, B's identified recording still opened with about 128 samples of A's audio, at A's level, in 13 of 13 runs.

This PR zeroes the buffer in the CT_NONE branch, and resets the channel's four decoders in the GS_CHAN_NOW_DISCONNECTED branch, just before FreeChannel(). Both run on the decode path, and neither runs while a client is connected with a negotiated codec. Resetting the decoders in the CT_NONE branch instead missed one case: a new client that never passes through CT_NONE (no pre-identification recording). To force that case, test client B sent its transport properties unrequested right after its first packet.

build B's pre-identification recording first 128 samples of B's identified recording
main A's tone, 12 of 13 not scored
buffer zeroed only silent, 13 of 13 A's audio, 13 of 13
decoders reset in CT_NONE instead, B forced past CT_NONE none, 8 of 10 A's tone in all 8 of those
this PR silent, 190 of 190 RMS 326 or 0, 190 of 190
this PR, B forced past CT_NONE none, 7 of 10 RMS 326, 10 of 10

The RMS 326 is the reset decoder's own start-up output: it is the same when A sends silence. Two statements in #3901 were incomplete: the stale audio is not always one frame, and zeroing the buffer alone does not stop the leak.

CHANGELOG: Server: Fixed the start of a new client's recording containing audio from the previous client in the same channel.

Context: Fixes an issue?

Fixes: #3901

Does this change need documentation? What needs to be documented and how?

No.

Status of this Pull Request

Working implementation.

What is missing until this pull request can be merged?

Review. ThreadSanitizer churn runs, with test clients joining and leaving repeatedly (480 connections in the default mode, 240 with -T), reported no race involving the new lines. clang-format clean; no new compiler warnings.

Checklist

  • I've verified that this Pull Request follows the general code principles
  • I tested my code and it does what I want
  • My code follows the style guide
  • I waited some time after this Pull Request was opened and all GitHub checks completed without errors.
  • I've filled all the content above

🤖 This message was written by AI and reviewed by @mcfnord.

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 jamulussoftware#3901

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@coderabbitai

coderabbitai Bot commented Oct 5, 2026 •

Copy link
Copy Markdown

Review in Change Stack →

Navigate logical layers of code changes, visualize relationships, and explore their blast radius.

🧰 Additional context used
📚 Code guidelines (1)
CONTRIBUTING.md — auto-discovered

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration
  • Configuration used: Repository UI
  • Review profile: QUIET
  • Plan: Advanced
  • Run ID: 084c30e4-7060-4cfa-9158-9978f2d7c28d
📥 Commits

Reviewing files that changed from the base of the PR and between 7ebf8f8 and 090cc44.

📒 Files selected for processing (1)
  • src/server.cpp

Included review availability: This review used your included allowance. Your plan provides up to 4 included reviews per hour; 3 remain after this review.


📝 Walkthrough

Walkthrough

DecodeReceiveData now clears a channel’s audio buffer when no codec is selected. When decoding detects a client disconnect, the server resets the channel’s legacy OPUS and OPUS64 decoder states before freeing the channel.

Changes

Channel audio cleanup

Layer / File(s) Summary
Decode and disconnect cleanup
src/server.cpp
When no codec is selected, DecodeReceiveData clears the channel audio buffer. On disconnect, it resets the channel’s mono and stereo legacy OPUS and OPUS64 decoder states before freeing the channel.

Priority: ➖ Normal

Estimated code review effort: 2 (Simple) | ~8 minutes

Change: Bug fix · Severity of issue fixed: Medium

Suggested reviewers: softins

Merge Risk: ⚪ Minimal · up to 090cc

This change clears audio when no codec is selected and resets decoder state before channel reuse. No actionable merge-blocking risk introduced by this change remains.

Architecture Summary

Architecture risk: 🔵 Low · up to 090cc

The change affects 1 system.

Changed systems: src

Architecture concerns
No architecture-level concerns identified.

Review details

Systems and components

  • observed — src (service) was modified; 1 changed file maps to changed impact.

Before / after behavior

  • observed — Modified behavior in src/server.cpp: When no codec is selected, DecodeReceiveData now clears that client’s audio buffer instead of leaving its previous contents in the buffer.
  • observed — Modified behavior in src/server.cpp: On a disconnect detected during decoding, the server now resets the channel’s mono and stereo decoders for both legacy OPUS and OPUS64 before freeing the channel; previously, it did not reset decoder state.
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 1 functions across 1 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly describes the main change: clearing stale audio and decoder state when a channel is reused.
Description check ✅ Passed The description covers the change, changelog entry, issue context, documentation, implementation status, remaining work, and checklist. The unchecked item about waiting for GitHub checks is non-critic…
Linked Issues check ✅ Passed Issue #3901 requires preventing stale audio from a reused channel from entering the new client’s recording. In DecodeReceiveData, the CT_NONE branch calls vecvecsData[iChanCnt].Reset(0). `CVecto…
Out of Scope Changes check ✅ Passed The diff changes only src/server.cpp. Both the buffer clear and decoder resets directly support issue #3901’s reused-channel recording fix. The decoder resets address the related stale audio that ca…
  • Fix all pre-merge checks with AI
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create a new PR
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Reused channel slot records the previous occupant's audio

1 participant