diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index 688f280..110a54d 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -1459,30 +1459,33 @@ function flushSfuStreams(){ * (transient network blip) we cancel the removal. */ function watchVideoTrackForRemoval(track, removeFn){ if (!track) return; - let timer = null, removed = false; + let timer = null, removed = false, hasFlowed = false; const remove = () => { if (removed) return; removed = true; if (timer) { clearTimeout(timer); timer = null; } try { removeFn(); } catch(_){} }; + const onUnmute = () => { + /* RTP arrived; future mute events are meaningful (a flow that + * existed then stopped — publisher unshared or net dropped) */ + hasFlowed = true; + if (timer){ clearTimeout(timer); timer = null; } + }; const onMute = () => { if (removed || timer) return; + /* fresh remote tracks ALWAYS start muted until the first RTP packet + * arrives. If we've never seen data flow on this track, the mute is + * its initial state, not a publisher unshare — don't prune. */ + if (!hasFlowed) return; timer = setTimeout(() => { timer = null; if (!removed && track.muted) remove(); - }, 1500); - }; - const onUnmute = () => { - if (timer){ clearTimeout(timer); timer = null; } + }, 3000); }; track.addEventListener('ended', remove); track.addEventListener('mute', onMute); track.addEventListener('unmute', onUnmute); - /* defensive: if the track is already muted at attach time (the - * 'mute' event may have fired before we got here), schedule the - * pruning right away */ - if (track.muted) onMute(); } /* Spotlight model — every tile keeps its thumbnail permanently in the @@ -3360,8 +3363,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');