zebra-report: stream-toggle reliability fixes
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.
This commit is contained in:
parent
94b981da29
commit
e3363d9cf1
1 changed files with 31 additions and 8 deletions
|
|
@ -4050,23 +4050,47 @@ const streamAudio = new Map(); // uuid -> <audio> pulling stream
|
|||
function streamUrlFor(pubHex){
|
||||
return SFU_BASE + '/stream?room=' + encodeURIComponent(roomID) + '&pub=' + pubHex;
|
||||
}
|
||||
function startStream(uuid, pubHex){
|
||||
async function startStream(uuid, pubHex){
|
||||
/* DON'T mute the WebRTC audio yet — if the fresh <audio>'s autoplay
|
||||
* is blocked (common on mobile after the entry gesture's grace has
|
||||
* passed) we'd be left with zero audio. Mute only after 'playing'
|
||||
* fires so the WebRTC path stays as the live fallback. */
|
||||
const wantUrl = streamUrlFor(pubHex);
|
||||
let a = streamAudio.get(uuid);
|
||||
/* Idempotent: a second toggle-on for the same pubHex while we're
|
||||
* already loading/playing is a no-op. The previous behaviour reset
|
||||
* .src which aborted the in-flight load — Firefox surfaces this as
|
||||
* 'fetching process aborted at user's request' on the play()
|
||||
* promise, which we then mistakenly logged as autoplay-blocked. */
|
||||
if (a && a.src === wantUrl && !a.error) {
|
||||
return;
|
||||
}
|
||||
if (!a){
|
||||
a = document.createElement('audio');
|
||||
a.autoplay = true; a.controls = false;
|
||||
/* preload=auto: encourage the browser to start buffering immediately
|
||||
* instead of waiting for play() — helps slow mobile links fill
|
||||
* the initial buffer before the user-gesture window expires.
|
||||
* playsInline: tell Safari/iOS not to escalate audio playback into
|
||||
* a fullscreen player. No effect on Firefox/Chrome but cheap. */
|
||||
a.preload = 'auto'; a.playsInline = true;
|
||||
document.body.appendChild(a);
|
||||
streamAudio.set(uuid, a);
|
||||
applySinkTo(a);
|
||||
/* setSinkId is async and can re-init the media pipeline. If we
|
||||
* call it AFTER setting src + play(), the re-init aborts the load
|
||||
* with the abort error fox saw. Await it FIRST, before any src
|
||||
* assignment. Safe to await on browsers that don't support it —
|
||||
* applySinkTo early-returns. */
|
||||
await applySinkTo(a);
|
||||
}
|
||||
a.muted = false;
|
||||
a.src = streamUrlFor(pubHex);
|
||||
a.src = wantUrl;
|
||||
/* mute WebRTC the moment the HTTP stream actually plays a frame,
|
||||
* not before. Removed on stop / error so WebRTC takes over again. */
|
||||
* not before. Removed on error / autoplay reject so WebRTC takes
|
||||
* over again. NOTE: 'stalled' is NOT treated as a failure — it
|
||||
* fires constantly during normal startup on mobile cellular and
|
||||
* each fire was causing the audio path to flip-flop. Real failure
|
||||
* shows up as 'error' or as a rejected play() promise. */
|
||||
const muteWebRtcOnPlay = () => {
|
||||
const w = remoteAudio.get(uuid);
|
||||
if (w) try { w.muted = true; } catch(_){}
|
||||
|
|
@ -4078,7 +4102,6 @@ function startStream(uuid, pubHex){
|
|||
};
|
||||
a.addEventListener('playing', muteWebRtcOnPlay, { once: true });
|
||||
a.addEventListener('error', () => unmuteWebRtcOnFail('error'), { once: true });
|
||||
a.addEventListener('stalled', () => unmuteWebRtcOnFail('stalled'));
|
||||
try {
|
||||
const p = a.play();
|
||||
if (p && p.then){
|
||||
|
|
@ -4756,9 +4779,9 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
</script>
|
||||
|
||||
<footer style="margin:2.2rem auto 0;font-size:0.65rem;color:#999;line-height:1.7;word-break:break-all;font-family:monospace">
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-03</span><br>
|
||||
md5 <span class="stamp-md5">14406e1c86dd563732f048cef3cf4cbb</span><br>
|
||||
sha256 <span class="stamp-sha">9b7b9f6b416b367475d3c1bf181acafbb684589ed18188fcb14b29927320c940</span><br>
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-04</span><br>
|
||||
md5 <span class="stamp-md5">a0d7d8646e0dcec5b7563cc20ee69ade</span><br>
|
||||
sha256 <span class="stamp-sha">712548a89b0e0ebfa299b91a360ef0a75128e5f35d17b2ffd7519bf0ff4423fa</span><br>
|
||||
<span style="color:#bbb">hashes are of this page with these two fields zeroed — to verify, blank them and re-hash</span><br>
|
||||
<span style="color:#bbb">one self-contained file — <strong>save a copy</strong> and verify against these hashes; point at your own servers with ?signal= and ?turncred=, or <a href="host-your-own.html" style="color:#999">host your own community</a></span>
|
||||
</footer>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue