zebra-spaces: rebuild SFU sub PC on connectionstatechange + visibility resume
Lid-close / suspend recovery. When the OS suspends the browser, the SFU sub PC goes 'disconnected' → 'failed' (or just stops carrying RTP silently). On resume the old PC is dead but JS still holds a reference to it, so we get frozen remote screens until the host hard-reboots. Two recovery paths added: 1. pc.onconnectionstatechange — on 'failed' or 'closed' for the active sub PC, sfuUnsubscribe() then sfuSubscribe() to rebuild. The wantConnected guard prevents fighting a deliberate leave. 2. document visibilitychange → 'visible' — Chromium on Linux can silently keep the PC in 'connected' state through a suspend cycle without firing connectionstatechange. So we also probe on tab/lid wake: if the sub PC's connectionState is anything other than 'connected'/'new'/'connecting', force the rebuild. Both paths converge on the same teardown + re-subscribe so the SFU sends a fresh initial SDP with every current publisher's tracks. 83 FSM tests still green; this is runtime-only and doesn't affect the pure state machines yet.
This commit is contained in:
parent
a2dda2d586
commit
6a72e77518
1 changed files with 38 additions and 2 deletions
|
|
@ -1996,6 +1996,23 @@ async function sfuSubscribe(){
|
|||
});
|
||||
if (!ackRes.ok){ pc.close(); throw new Error('sfu subscribe-answer http '+ackRes.status); }
|
||||
sfuSubPC = pc; sfuSubPeerID = offer.peer_id;
|
||||
/* survive laptop-lid-close / network suspend. When the OS suspends the
|
||||
* browser the SFU sub PC goes 'disconnected' first (transient) then
|
||||
* 'failed'. On 'failed' the existing PC is dead — RTP can never
|
||||
* recover even if we resume — so we tear it down and rebuild via
|
||||
* sfuUnsubscribe + sfuSubscribe. The auto-rejoin code that follows
|
||||
* a hard refresh handles the WS side; this handles the SFU side. */
|
||||
pc.onconnectionstatechange = () => {
|
||||
const s = pc.connectionState;
|
||||
if (s === 'failed' || s === 'closed'){
|
||||
logLine('err', 'sfu sub PC ' + s + ' — rebuilding');
|
||||
if (sfuSubPC === pc){
|
||||
sfuUnsubscribe().then(() => {
|
||||
if (wantConnected && roomID) sfuSubscribe().catch(e => logLine('err','sfu re-subscribe: '+e.message));
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
/* SSE: server pushes renegotiation offers when publisher set changes.
|
||||
* We answer each via POST /answer. ping events are keepalive only.
|
||||
*
|
||||
|
|
@ -3128,6 +3145,25 @@ if (navigator.mediaDevices && navigator.mediaDevices.addEventListener){
|
|||
* sees how many inputs exist. */
|
||||
refreshMicList();
|
||||
|
||||
/* laptop-lid-close / sleep / suspend recovery: when the tab comes back
|
||||
* to visible, check whether our SFU sub PC is still in a healthy state.
|
||||
* Some browsers (Chromium on Linux specifically) don't fire
|
||||
* connectionstatechange on suspend → resume — the PC sits silently in
|
||||
* 'connected' but no RTP flows. If it's in any non-live state, force
|
||||
* the rebuild. */
|
||||
document.addEventListener('visibilitychange', () => {
|
||||
if (document.visibilityState !== 'visible') return;
|
||||
if (!wantConnected || !sfuSubPC) return;
|
||||
const s = sfuSubPC.connectionState;
|
||||
if (s === 'failed' || s === 'closed' || s === 'disconnected'){
|
||||
logLine('err', 'visibility back, sub PC ' + s + ' — rebuilding');
|
||||
const pc = sfuSubPC;
|
||||
sfuUnsubscribe().then(() => {
|
||||
if (wantConnected && roomID) sfuSubscribe().catch(e => logLine('err','sfu re-subscribe: '+e.message));
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
/* ==================================================================
|
||||
* leave / entry buttons
|
||||
* ================================================================== */
|
||||
|
|
@ -3226,8 +3262,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">947c4c514376d2995517d8576ac3e3a4</span><br>
|
||||
sha256 <span class="stamp-sha">583d56867cce3995325c8f35b83388dcbd6fb14584f64dfcac3a7dcb28556f1c</span><br>
|
||||
md5 <span class="stamp-md5">02bfc336d703a55e23a2ce5f1a69adf1</span><br>
|
||||
sha256 <span class="stamp-sha">f554d4c9c6b226f5b79954222c75a48a38f031154d6de8cd6b4df0fd8bd07184</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