zebra-report: track mute → debounced tile removal — mirror
This commit is contained in:
parent
f6a89d9276
commit
16007677ef
1 changed files with 39 additions and 4 deletions
|
|
@ -993,6 +993,41 @@ function flushSfuStreams(){
|
|||
}
|
||||
}
|
||||
|
||||
/* When a publisher unshares, the SFU stops its transceiver on every
|
||||
* 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. */
|
||||
function watchVideoTrackForRemoval(track, removeFn){
|
||||
if (!track) return;
|
||||
let timer = null, removed = false;
|
||||
const remove = () => {
|
||||
if (removed) return;
|
||||
removed = true;
|
||||
if (timer) { clearTimeout(timer); timer = null; }
|
||||
try { removeFn(); } catch(_){}
|
||||
};
|
||||
const onMute = () => {
|
||||
if (removed || timer) return;
|
||||
timer = setTimeout(() => {
|
||||
timer = null;
|
||||
if (!removed && track.muted) remove();
|
||||
}, 1500);
|
||||
};
|
||||
const onUnmute = () => {
|
||||
if (timer){ clearTimeout(timer); timer = null; }
|
||||
};
|
||||
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
|
||||
* cameras column (cameras above, screens-thumbs below). When a tile is
|
||||
* spotlit, a SECOND larger tile renders in the middle slot pointing at
|
||||
|
|
@ -1452,13 +1487,13 @@ async function sfuSubscribe(){
|
|||
if (kind === 'screen'){
|
||||
screenStreams.set(pubHex, ev.streams[0]);
|
||||
renderScreenTile(pubHex, ev.streams[0]);
|
||||
ev.track.addEventListener('ended', () => removeScreenTile(pubHex));
|
||||
watchVideoTrackForRemoval(ev.track, () => removeScreenTile(pubHex));
|
||||
return;
|
||||
}
|
||||
if (kind === 'camera'){
|
||||
cameraStreams.set(pubHex, ev.streams[0]);
|
||||
renderCameraTile(pubHex, ev.streams[0]);
|
||||
ev.track.addEventListener('ended', () => removeCameraTile(pubHex));
|
||||
watchVideoTrackForRemoval(ev.track, () => removeCameraTile(pubHex));
|
||||
return;
|
||||
}
|
||||
if (kind !== 'mic'){
|
||||
|
|
@ -2714,8 +2749,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> · built <span class="stamp-date">2026-06-02</span><br>
|
||||
md5 <span class="stamp-md5">dee696071acb10104b0a0c68024e9a68</span><br>
|
||||
sha256 <span class="stamp-sha">7fc20fb17b274f1bf5f667101e705530057cf859984a0dd386d83198ee65439e</span><br>
|
||||
md5 <span class="stamp-md5">2f8a14e9605749db0743c3345d46afab</span><br>
|
||||
sha256 <span class="stamp-sha">124f3e65711264a479b6fa8573b89928825a830c9aeca59649b59e9549e836b3</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