Speakers, cohosts, and hosts can flip their own row's stream toggle
to listen via the buffered HTTP Ogg/Opus broadcast tap instead of
WebRTC. Mic auto-mutes while in that mode; unmuting hops back to the
live mesh seamlessly. Survives cellular ICE churn that breaks the
WebRTC path.
Hard refresh resumes the last space again — the one-tap friction
buyback didn't actually fix the Firefox Android listener-audio bug
it was supposed to fix.
- DJ self-monitor stream now plays audibly (was muted-start with no
unmute path for non-listeners)
- Screen / camera / game receivers get the same playoutDelayHint as
audio so the picture stays in sync with the voice
Leave was tearing down WebRTC but leaving streamMode/streamAudio/
audioPath stale — re-entry as listener saw "already enrolled" and
never made a fresh /stream request. Now leave wipes all three maps,
removes the <audio> elements, and stops the retry loop. Plus the
primeAudio one-shot gate is gone so every entry click re-primes the
mobile audio session (it can be suspended across tab background).
If the host's mic publish lands after the listener's initial DJ auto-
enrol attempt, the 404 from the SFU was permanent — no event re-
triggered enrol. 4s interval re-checks every speaker and starts streams
that are still missing. Stops on transition out of listener.
Replaces the regressive tap-anywhere-to-resume queue with
primeAudioOnGesture(): the entry-button click handler plays a 1-frame
silent WAV through a hidden <audio>, blessing the page for audio.
Auto-enrolled DJ streams then play() without further user gesture.
No more "tap anywhere" message; no more flip-flop on stalled.
Calls a.load() inside the gesture before a.play() so the retry isn't
no-op'ing on an aborted element. Adds 'click' to the listened events
since Firefox Android only grants gesture-activation on click in some
configs. Logs retry success/failure so we know what's happening.
When mobile Firefox/Safari blocks audio.play() on auto-enrolled DJ
streams, queue the elements and retry on the first pointerdown/
touchstart anywhere on the page. WebRTC stays unmuted as fallback
so the listener isn't silent in the meantime.
Three coupled fixes to the DJ-stream toggle path:
- await setSinkId() before setting .src so it doesn't race + abort
the in-flight media load
- drop the 'stalled' handler that was ping-ponging WebRTC ↔ stream
on every transient mobile-cellular buffer pause
- make startStream idempotent on the same URL so a peer-joined
auto-enrollment race can't reset .src and self-abort
Plus preload="auto" and playsInline on the dynamic <audio> for
mobile compat. Fixes "no music on phone stream stalled" and the
"autoplay blocked: aborted at user's request" log spam from host
self-monitor clicks.
Moves the ◉/○ DJ-stream button out of the bottom mod-actions stack
into the dedicated `strm` grid column on the top row (next to the
mic icon). No more extra sub-row per speaker for the self-monitor or
host-controlled stream toggle — fixes the vertical scroll regression
when many members were in the room.
Swap grid-auto-flow:column to grid-template-columns:repeat(auto-fit,max-content)
so the now-5-button row wraps to a second line on narrow panels instead
of overflowing horizontally.
DJ can preview the ~2s delayed Ogg/Opus stream of their own mic to
confirm what listeners hear. Closed headphones recommended; open
speakers + music mode = feedback (title attr warns).
Each speaker row carries a stream toggle that swaps their audio from
WebRTC subscribe (~700ms latency, glitchy under loss) to a plain
HTTP <audio> pulling Ogg/Opus from the SFU's new /stream endpoint
(~2s latency, browser-managed jitter buffer, bulletproof for DJ
playback). Requires matching proxy.unturf.com SFU build.
Choppy persisted after every other dial-back. The only remaining new
Chrome-publisher params were the fullband pin and the 320k bump. Drop
both. Chrome publisher's SDP now back to the pre-feature shape
(stereo + FEC + 256k, no maxplaybackrate / sprop-maxcapturerate / cbr).
Kept: Firefox SDP regex fix, UA-gated NACK, speaker picker.
Choppy persisted after NACK rollback. Dial back to 320k (transparent
threshold, much less stress on uploads alongside screen+camera). Drop
a=maxptime:120 — receivers + 700ms playout buffer don't handle larger
frames well. Fix the SDP regex that silently no-op'd on Firefox
publishers (Firefox's Opus fmtp lacks minptime=10 — the matcher
required it). Firefox publishers will now actually carry the music
codec params.