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.
RTCRtpSender.getCapabilities probe decides whether to advertise NACK
in our Opus offers. Chromium publishes it (responds to retransmits),
Firefox/Safari omit (can't respond). Receivers honor only what the
sender promised — mixed-browser rooms stay clean, same-browser rooms
get the full reactive recovery.
Chrome listeners requested NACK retransmits that Firefox publishers
never replied to (Firefox never implemented the audio NACK responder).
Chrome's jitter buffer waited for recovery that never arrived, then
skipped. useinbandfec + 700ms playout buffer remain.
Codec headroom + loss resilience without DSP filters:
- Opus 256k → 510k (spec ceiling) on mic SFU + mesh + screen + game audio
- Fullband pin: maxplaybackrate / sprop-maxcapturerate = 48000, cbr=0
- Audio NACK feedback on Opus rtpmap (reactive packet recovery)
- a=maxptime:120 advertises tolerance for larger peer frames
Playback chain:
- New speaker-output selector (setSinkId) routes peer audio to a
chosen sink (studio monitors / external DAC). Hidden on Safari.
Hard refresh required for the new speaker-picker row to appear.
Mirror of zebra-report 1dde43f + proxy.unturf.com edb1009. Closes
the 'backgrounded tab squats in room' gap that WS-level pings
can't catch (browsers auto-pong below JS).
Mirror of zebra-report 740ed2b + proxy.unturf.com 0733d61.
- moderation: split single 'boot' into 'kick' (rejoinable) and 'ban'
(blocks pubkey). Server adds matching kick wire-type alongside
renamed boot→ban.
- tab-close lifecycle: pagehide handler fires a 'bye' so closing a
tab triggers a strong-leave (peer-left + SFU evict immediately)
instead of waiting 8s for the hiccup grace. Fix for the case
where Will's phone-tab close left audio leaking until the host
manually kicked him.
Mirror of zebra-report fb7228b. Will refreshed; the supplant ontrack
hit autoplay block on srcObject swap. Fresh <video> dodges the
mid-life play() rejection.
Mirror of zebra-report 3ca9042 + proxy.unturf.com bc04362.
- moderation: boot failures now log to the moderator's page instead
of looking like the button did nothing (stale uuid case).
- diagnostic: watchFirstFrame fires 'still black after 2500ms — no
keyframe?' if a fresh SFU video tile never decodes a first frame.
- proxy-side companion: longer keyframe-burst on subscriber attach
so a dropped PLI no longer leaves the tile permanently black.