Skip to content

fix(stream): end a stream socket that sends no audio within 15 s - #832

Draft
anneoneone wants to merge 1 commit into
dev/312-interimfrom
fix/827-no-audio-deadline
Draft

anneoneone wants to merge 1 commit into
dev/312-interimfrom
fix/827-no-audio-deadline

Conversation

@anneoneone

Copy link
Copy Markdown
Collaborator

The backend ends a stream socket whose first audio chunk does not arrive within 15 s, instead of keeping it "on air" with silence. This ports the backend half of #825 (merged to main as 89f5076, in prod as sha-1baf52d) to dev/312-interim. The frontend half is a separate PR on the same issue, because a PR has one lane.

Issue: #827

Validation:

  • cargo fmt --check: clean.
  • cargo test --locked --all-targets: 169 unit and 23 check-config tests pass, 2 of them new. 2 R2 tests are ignored.
  • cargo clippy --locked --all-targets: exit 0. All warnings are in older code; none is in a changed line.
  • connection_without_audio_is_ended fails on interim's old receive loop: no frame, timeout. This is the same failure as on main.
Implementation and review

Epic: #312
Executor: Claude
Lane: backend
Reviewer: Codex
Leaf IDs: B827.1.1

Claim: #827 (comment). Done by hand at Anton's request. Board Status is not set: the issue is not on board 2, so set_status.py refuses.

Cause (show 48, 2026-10-30). The host's Safari 18.3 cannot record WebM. The recorder failed, but the panel kept the stream socket open with pings. The status said "live" and "recording", while Icecast played silence for half an hour. Every session logged 0 chunks / 0 bytes received.

Change.

  • config.rs: stream_first_audio_timeout_secs (env STREAM_FIRST_AUDIO_TIMEOUT_SECS, serde default 15). Same as main.
  • stream_ws.rs: while chunks_received == 0, receiver.next() waits with timeout_at(first_audio_deadline). On timeout it sends NO_AUDIO_ERROR and leaves the loop. After the first chunk the loop is unchanged, so later gaps do not trigger it.

What differs from #825.

  • stream_ws.rs: the same hunk. It applies cleanly onto interim's generation logic from fix(stream): keep a replaced producer from stopping its successor #818. The timeout leaves the loop like a dropped connection, so interim's stop_own_stream path runs unchanged. The stream stops unless another connection replaced it, and the recording gets the 30 s reconnect grace (defer_finalize). A replaced socket that never sent audio also ends after 15 s and leaves the current stream alone.
  • Tests: main's go_live(state, addr, user, query) is connect_producer here, because broadcast_tests.rs already has a go_live. serve_stream_socket gets a serve_stream_socket_with(overrides) variant that adds the overrides on top of the pipe targets.

Tests (backend/src/broadcast_tests.rs, real router and ffmpeg, 1 s limit).

  • connection_without_audio_is_ended: pings only → NO_AUDIO_ERROR, then the stream is not active.
  • first_audio_chunk_lifts_the_no_audio_deadline: one chunk, 1.5 s wait → a pong, no error, the stream stays active.

GitNexus. handle_stream_socket impact (upstream): LOW, one caller (stream_ws_handler). detect_changes against dev/312-interim says MEDIUM. Its index is from 32839db, so line shifts map the hunks onto neighbours (producer_busy, overlay_font_path). It lists only the stream_ws_handler flows.

Not in this PR.

Port of the backend half of
#825 (89f5076) to
dev/312-interim.

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
STREAM_FIRST_AUDIO_TIMEOUT_SECS (default 15), the backend sends an
error frame and ends the stream like a dropped connection. A replaced
socket leaves the current stream alone; the recording keeps its
reconnect grace.

The two new tests use interim's connect_producer and a new
serve_stream_socket_with helper.

Refs: #827
@anneoneone anneoneone added type::backend Layer: Rust/Axum backend (handlers, DB, integrations) project::Stream Area: live audio streaming & stream control executor::claude Executor: the Claude runner does this work labels Nov 2, 2026
@vercel

vercel Bot commented Nov 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
live-moafunk-de Ready Ready Preview Nov 2, 2026 10:51pm UTC

This branch was successfully deployed

1 active deployment
Preview — 68567d02 Deployed Nov 2, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

executor::claude Executor: the Claude runner does this work project::Stream Area: live audio streaming & stream control type::backend Layer: Rust/Axum backend (handlers, DB, integrations)

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant