zebra-report: serialise SFU sub renegotiation + skip own mic echo — mirror
This commit is contained in:
parent
f858869b3e
commit
6e4d58ffab
1 changed files with 31 additions and 14 deletions
|
|
@ -1254,6 +1254,9 @@ async function sfuSubscribe(){
|
|||
* delay which is well below noticeable. */
|
||||
try { ev.receiver.playoutDelayHint = 0.4; } catch(_){}
|
||||
const pubHex = sid;
|
||||
/* never play our own mic back to ourselves — the SFU echoes our
|
||||
* publish to every sub including our own (we always subscribe now) */
|
||||
if (myKeys && pubHex === myKeys.pubHex) return;
|
||||
/* cache stream by publisher pubkey so it survives the member's session
|
||||
* uuid changing across leave/rejoin — see flushSfuStreams */
|
||||
sfuStreamsByPubHex.set(pubHex, ev.streams[0]);
|
||||
|
|
@ -1296,21 +1299,35 @@ async function sfuSubscribe(){
|
|||
if (!ackRes.ok){ pc.close(); throw new Error('sfu subscribe-answer http '+ackRes.status); }
|
||||
sfuSubPC = pc; sfuSubPeerID = offer.peer_id;
|
||||
/* SSE: server pushes renegotiation offers when publisher set changes.
|
||||
* We answer each via POST /answer. ping events are keepalive only. */
|
||||
* We answer each via POST /answer. ping events are keepalive only.
|
||||
*
|
||||
* Renegotiation must be SERIALISED. Each offer transitions the PC
|
||||
* through have-remote-offer → stable, and if we kick off the next
|
||||
* setRemoteDescription before the previous has applied its answer,
|
||||
* the second one throws ('failed to set remote offer sdp: Called in
|
||||
* wrong state'). When a speaker publishes mic + screen + camera in
|
||||
* quick succession the SFU fires three offers in a row — without
|
||||
* serialisation we drop the later ones, browser-side track set goes
|
||||
* out of sync with the SFU, and existing screen/camera tiles can
|
||||
* stop receiving RTP. Chain through a single promise queue. */
|
||||
let renegQueue = Promise.resolve();
|
||||
sfuSubEvents = new EventSource(SFU_BASE + '/events?room=' + encodeURIComponent(roomID) + '&peer=' + sfuSubPeerID);
|
||||
sfuSubEvents.onmessage = async (ev) => {
|
||||
sfuSubEvents.onmessage = (ev) => {
|
||||
let m; try { m = JSON.parse(ev.data); } catch(_){ return; }
|
||||
if (m.type !== 'offer' || !sfuSubPC) return;
|
||||
try {
|
||||
await sfuSubPC.setRemoteDescription({ type:'offer', sdp: m.sdp });
|
||||
const ans = await sfuSubPC.createAnswer();
|
||||
await sfuSubPC.setLocalDescription(ans);
|
||||
await waitForIceGathering(sfuSubPC);
|
||||
await fetch(SFU_BASE + '/answer?room=' + encodeURIComponent(roomID) + '&peer=' + sfuSubPeerID, {
|
||||
method:'POST', headers:{'Content-Type':'application/json'},
|
||||
body: JSON.stringify({ sdp: sfuSubPC.localDescription.sdp })
|
||||
});
|
||||
} catch(e){ logLine('err','sfu renegotiate: '+e.message); }
|
||||
renegQueue = renegQueue.then(async () => {
|
||||
if (!sfuSubPC) return;
|
||||
try {
|
||||
await sfuSubPC.setRemoteDescription({ type:'offer', sdp: m.sdp });
|
||||
const ans = await sfuSubPC.createAnswer();
|
||||
await sfuSubPC.setLocalDescription(ans);
|
||||
await waitForIceGathering(sfuSubPC);
|
||||
await fetch(SFU_BASE + '/answer?room=' + encodeURIComponent(roomID) + '&peer=' + sfuSubPeerID, {
|
||||
method:'POST', headers:{'Content-Type':'application/json'},
|
||||
body: JSON.stringify({ sdp: sfuSubPC.localDescription.sdp })
|
||||
});
|
||||
} catch(e){ logLine('err','sfu renegotiate: '+e.message); }
|
||||
});
|
||||
};
|
||||
sfuSubEvents.onerror = () => { /* EventSource auto-reconnects */ };
|
||||
logLine('', 'sfu: subscribed as '+shortHex(sfuSubPeerID));
|
||||
|
|
@ -2415,8 +2432,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">e1c393db6371901f9dd2bc5f30195cf1</span><br>
|
||||
sha256 <span class="stamp-sha">b7c89d66d504eb2765bac6c128f31d07df03bc7f4309e4f6173811212510bb71</span><br>
|
||||
md5 <span class="stamp-md5">4d956692f61f60657077d2e04b576b71</span><br>
|
||||
sha256 <span class="stamp-sha">eb02187ba33a4618b347444a41e25d6d2f58ff6c52c7f7ff0e3437a020dbfa25</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