fix(rtc): keep AudioMixer streams that miss the read timeout - #837
RaphaelFakhri wants to merge 2 commits into
Conversation
There was a problem hiding this comment.
Devin Review found 1 potential issue.
1 flag not posted on this PR by your GitHub settings — view it in Devin Review. (Configure)
| for pending in self._pending.values(): | ||
| pending.cancel() | ||
| self._pending.clear() |
There was a problem hiding this comment.
🔴 Mixer shutdown hangs on uncancellable streams
If an input iterator ignores cancellation, aclose waits forever for its read. OutputPlayer.aclose then never reaches output-device cleanup.
Learn more
The mixer accepts arbitrary async iterators as inputs. Cancellation requests do not force an iterator to finish: an iterator can catch CancelledError and continue waiting, or perform cleanup that never completes. The unconditional gather then prevents aclose from returning. OutputPlayer.aclose waits for mixer closure before closing its sounddevice stream.
Example: An iterator catches CancelledError while waiting for an upstream queue that never receives another frame. Calling mixer.aclose() cancels its read but waits forever; closing an OutputPlayer using that mixer also leaves the audio device open.
Recommended fix: Bound the wait for cancelled reads, and detach any non-cooperative reads with appropriate result/exception handling. Ensure AudioMixer.aclose can return even when an input iterator does not finish cancellation.
Was this helpful? React with 👍 or 👎 to provide feedback.
|
Confirmed. Fixed in 8a4db8b. |
Summary
AudioMixerno longer drops an async-generator stream that missesstream_timeout_msonce. The late audio is mixed when it arrives.Problem
_get_contributionread each stream withasyncio.wait_for(stream.__anext__(), timeout). On timeout,wait_forcancels the read. For an async generator, that cancellation is thrown into the generator, which finishes. The next read raisesStopAsyncIteration, so the mixer treats the stream as exhausted and removes it. A source with a slow first chunk (for example, a TTS stream with more than 100 ms of first-byte latency) produced no audio at all, and the only sign was a "timeout, ignoring" warning.Changes
remove_streamandaclose.Testing
pytest tests/rtc/test_mixer.py: the new test fails without the change (no frames with audio) and passes with it. The existing mixer test still passes.