zebra-report: deploy — 15s mute-removal window

This commit is contained in:
russell@unturf.com 2026-06-03 10:11:24 -04:00
parent 17a62cfee6
commit 27a5319d81
No known key found for this signature in database

View file

@ -1601,9 +1601,19 @@ function flushSfuStreams(){
* subscriber's pc and renegotiates. Browsers don't reliably fire
* 'ended' on remote tracks under this path (Chromium half-fires,
* Firefox stays silent). They DO fire 'mute' when RTP stops arriving.
* Watch both: a sustained mute for >1.5s = the publisher is gone and
* we should remove the tile. If 'unmute' fires within the window
* (transient network blip) we cancel the removal. */
* Watch both: a sustained mute for >MUTE_WINDOW ms = the publisher is
* (probably) gone and we remove the tile. If 'unmute' fires within
* the window — transient network blip, NACK retransmission gap, brief
* CPU pressure on publisher, mobile network handoff — we cancel the
* removal.
*
* Window history:
* - 3s: too aggressive — transient blips killed live tiles, the
* 'host shares screen, other speaker loses it' regression.
* - 15s: enough to weather Wi-Fi stalls and mobile network handoffs
* without leaving frozen tiles around for ages. A genuine unshare
* still removes the tile within 15s. */
const VIDEO_REMOVE_MUTE_WINDOW_MS = 15000;
function watchVideoTrackForRemoval(track, removeFn){
if (!track) return;
let timer = null, removed = false, hasFlowed = false;
@ -1617,7 +1627,10 @@ function watchVideoTrackForRemoval(track, removeFn){
/* 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; }
if (timer){
clearTimeout(timer); timer = null;
logLine('', 'video track: RTP resumed before timeout — keeping tile');
}
};
const onMute = () => {
if (removed || timer) return;
@ -1627,8 +1640,11 @@ function watchVideoTrackForRemoval(track, removeFn){
if (!hasFlowed) return;
timer = setTimeout(() => {
timer = null;
if (!removed && track.muted) remove();
}, 3000);
if (!removed && track.muted){
logLine('', 'video track muted >'+(VIDEO_REMOVE_MUTE_WINDOW_MS/1000)+'s — removing tile');
remove();
}
}, VIDEO_REMOVE_MUTE_WINDOW_MS);
};
track.addEventListener('ended', remove);
track.addEventListener('mute', onMute);
@ -3813,8 +3829,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
<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> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-03</span><br>
md5 <span class="stamp-md5">39ee0b8db4e04da59859e19bee0ea41a</span><br>
sha256 <span class="stamp-sha">ba28e894d869cb11ccea4cdbbeedf22cf67a25f11d29450c4de27a1ecffa9396</span><br>
md5 <span class="stamp-md5">202400eb71552609f46039301779f8ed</span><br>
sha256 <span class="stamp-sha">684fb8eb60e8a4a30f7a9781f51a3dba088401b874262a84f735f332269f5758</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>