Record every frame of a batched emit and downmix stereo audio - #133
Draft
aviksethia99 wants to merge 1 commit into
Draft
Record every frame of a batched emit and downmix stereo audio#133aviksethia99 wants to merge 1 commit into
aviksethia99 wants to merge 1 commit into
Conversation
A model that emits media in multi-frame batches hands on_chunk a whole generation window at once. The feed queue held four frames and a full queue abandoned the rest of the chunk, so only the head of every burst was recorded: a session recorded as a ~7x time-lapse of itself. The queue now absorbs a two-second burst on the recording grid — the emit thread pays out the same media in real time in the connection pacers right after, which is when the encoder drains it — and an overflowing slot drops alone instead of taking the rest of the chunk with it. Stereo (2, M) audio flattened channel-after-channel into the jitter buffer, so recordings carried alternating blocks of one channel instead of the mix. The buffer now reduces tracks through to_int16_mono, the same reduction the live transport applies. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Signed-off-by: Avik Sethia <aviksethia99@gmail.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Why
A model that emits media in multi-frame batches hands
Recorder.on_chunka whole generation window at once — one call can carry a full second of frames plus its audio. The feed queue between the emit thread and the encoder held four frames, and on the firstqueue.Fullthe loop abandoned the rest of the chunk. Because a Python tight loop out-races the encoder thread for the GIL, exactly the head of every burst survived: a session recorded as a ~7× time-lapse of itself, deterministically. Measured end to end with the real encoder, a 12-second batched session recorded 1.65 seconds of media; the same fix brings it to 12.02 seconds with zero drops. Per-frame emitters never fill the queue and are unaffected (6.02s recorded for a 6s session, before and after).Separately, a stereo
(2, M)audio track was flattened channel-after-channel into the jitter buffer, so recordings carried alternating stretches of the left channel and the right channel instead of the mix. With a constant L=+8000/R=−8000 test signal the decoded recording averaged |s|≈7847 (raw channel blocks); after the fix it averages 0 (the correct mix).What Changed
The queue is sized to absorb a two-second burst on the recording grid (
_FEED_QUEUE_MAX_FRAMES = 2 * RECORDING_FPS). The sizing argument lives where the constant does:on_chunkqueues the entire resampled burst before the emit thread moves on to the connection pacers, and the pacers then pay the same media out in real time — which is exactly the window the encoder has to drain the queue. So the queue never needs to be "large", it needs to hold one burst, and the non-blocking contract ofon_chunk(documented at the call site in the runner) stays intact. An overflowing slot now drops alone (continue) rather than taking the rest of the chunk with it (break); the drop counter and its rate-limited warning are unchanged, and a dropped slot discards its already-pulled audio so the tracks stay aligned._buffer_audionow reduces the track throughto_int16_mono— the reduction the live transport applies on the WebRTC path — instead of a blindreshape(-1). Multi-channel audio mixes down per sample, mono passes through untouched, and float input picks up the correct int16 scaling as a side effect of reusing the helper. Importing atransport.webrtchelper fromrecordingis the one layering wrinkle in the diff;frames.pyis pure NumPy with no libwebrtc dependency, but if the panel prefers, the helper could move to a shared home in a follow-up.Two tests pin the behaviours: a batched emit (24 frames at fps=24 → 30 grid frames) must queue every grid frame with the feed worker parked, and a stereo buffer must yield the per-sample mix rather than a run of one channel.
🤖 Generated with Claude Code