Repository navigation
fix(stream): never go live without browser audio; end silent connections - #825
Merged
Merged
Conversation
Show 48 was "on air" for half an hour with silence. The browser opened the stream socket but sent no audio, and nothing told the host. - Go Live refuses without a captured input. It stops the socket when the recorder fails to start. - Resume a suspended AudioContext at capture, test, go-live and reconnect. A suspended context sends no audio. - Show an error when the recorder sent no audio 5 s after start. - The test broadcast cannot be passed while 0 chunks were sent.
Pings keep the stream socket open without audio. Then the status says "live" and recording, but Icecast plays silence (show 48). If the first audio chunk does not arrive within 15 s, the backend sends an error frame and ends the stream like a dropped connection. The recording keeps its reconnect grace. The limit is STREAM_FIRST_AUDIO_TIMEOUT_SECS (default 15).
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
anneoneone
marked this pull request as ready for review
October 30, 2026 20:52
This was referenced Oct 30, 2026
This branch was successfully deployed
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.
The host panel no longer goes on air when the browser sends no audio, and the backend ends a stream connection whose first audio chunk does not arrive within 15 s.
Found during show 48 (2026-10-30). The status said "live" and "recording" for half an hour, but
/live.mp3played silence. Every session from robi's browser ended with0 chunks / 0 bytes received. Liquidsoap never got a source, and no recording segment was written.Issue: none filed.
What changes:
STREAM_FIRST_AUDIO_TIMEOUT_SECS). The recording keeps its reconnect grace.Both parts reach hosts only with a backend deploy: the host panel at https://admin.live.moafunk.de is built into the backend image. Prod still runs
7093fe5.Validation:
npm test -- --run186 passed,npm run lintok,npm run typecheckok,npm run buildok.cargo test116 passed, 4 ignored.cargo fmt --checkclean.cargo clippy --all-targetshas no warnings in new code; the existing ones stay.main'suseAudioCapture.ts.connection_without_audio_is_endedfails againstmain'sstream_ws.rs(no frame, timeout).Implementation and review
This is a fix to
main, not architecture-epic work, so the epic fields do not apply.Epic: n/a
Executor: n/a
Lane: n/a
Reviewer: n/a
Leaf IDs: n/a
Evidence from prod (show 48, 19:55–20:23 UTC).
7093fe5. All sessions, test and live, loggedStream ended for user 'robi': 0 chunks / 0 bytes received.EBML header parsing failed(empty stdin)./live.mp3measured −91 dB (mksafe blank).complete_multipart_upload: service error(0-byte artifact).7093fe5, so with the same backend and the same host panel. No code changed on our side.LiveSetupTest) checks that the input track is live and still sent 0 chunks. So the track was live but produced no data. This points to a suspendedAudioContext:MediaRecorderrecords the gain node's destination stream, which gives no audio while the context is suspended. The context was created afterawait getUserMediaand was never resumed.Frontend (
useAudioCapture.ts,FlowOnAir.vue,LiveSetupTest.vue).resumeAudioContext()runs aftersetupAudioProcessing, instartRecording, and on the user clicks for test and reconnect.resume()only works from a user gesture. The countdown auto-start is not one, so the reconnect click is the recovery path.startRecordingrefuses when the device track has ended. It checks the device stream, because the processed stream's track stayslive.NO_AUDIO_DATA_MESSAGEthroughonErrorwhen no non-empty blob was sent. It is cleared on stop and restart.handleGoLivechecksaudioDeviceOkbefore it connects, wiressetOnErrorto the visible error line, and callsstopStream()whenstartRecording()returns false. The order stays connect → record, so the first chunk with the WebM header is not lost.Backend (
stream_ws.rs,config.rs).chunks_received == 0,receiver.next()waits withtimeout_at(first_audio_deadline). On timeout it sendsNO_AUDIO_ERRORand breaks the loop.Config.stream_first_audio_timeout_secs(serde default 15).Configis only built through serde, so the new field breaks no struct literal.Tests.
frontend/tests/useAudioCapture.test.ts: fakeAudioContext(starts suspended) andMediaRecorder. Covers resume, ended track, watchdog fires on empty blobs, stays quiet with data, stays quiet after stop.backend/src/broadcast_tests.rs:connection_without_audio_is_ended,first_audio_chunk_lifts_the_no_audio_deadline. Both use the real router and ffmpeg with a 1 s limit.GitNexus impact (upstream): all LOW.
handle_stream_sockethas 1 caller,startRecording1 (restartRecording),setupAudioProcessing2,handleGoLive1.detect_changeslists only thestream_ws_handlerflows. The index was built ondev/312-interim, so some symbol names in its output are off.Not in this PR.
MediaRecorder. The new ffmpeg probably gets WebM clusters without the EBML header. Suspected, not reproduced.dev/312-interimneeds its own port through the epic process.complete_multipart_upload: service error.