diff --git a/web/zebra-spaces.html b/web/zebra-spaces.html index b952f7e..84ef5c1 100644 --- a/web/zebra-spaces.html +++ b/web/zebra-spaces.html @@ -1260,6 +1260,11 @@ async function sfuSubscribe(){ for (const [uuid, mm] of members){ try { if (mm.pubkey && hex(unb64(mm.pubkey)) === pubHex){ + /* if we're a speaker and already have a mesh peer with this + * member, mesh carries their audio with lower latency — skip + * the duplicate SFU mic. Screens + cameras still come through + * because they're handled above. */ + if (canSpeak(myRole) && peers.has(uuid)) return; attachSfuTrack(uuid, ev.streams[0]); return; } @@ -1796,14 +1801,17 @@ function applyState(state){ async function onRoleEntered(){ if (canSpeak(myRole)) await ensureMicAndUI(); else updateRoleUI(); + /* Everyone subscribes to the SFU. Listeners use it for mic+screen+camera. + * Speakers use it for screen+camera (and as a backup audio path); the + * SFU mic track is suppressed by the ontrack handler when we already + * have a mesh peer for that pubkey, so we don't get double audio. */ + sfuSubscribe().catch(e => logLine('err','sfu subscribe: '+e.message)); if (canSpeak(myRole)){ for (const [uuid, mm] of members){ if (uuid === myUUID) continue; if (canSpeak(mm.role)) connectToPeer(uuid, myUUID < uuid); } sfuPublish().catch(e => logLine('err','sfu publish: '+e.message)); - } else { - sfuSubscribe().catch(e => logLine('err','sfu subscribe: '+e.message)); } } async function onRoleChanged(prev, next){ @@ -1813,13 +1821,14 @@ async function onRoleChanged(prev, next){ if (uuid === myUUID) continue; if (canSpeak(mm.role)) connectToPeer(uuid, myUUID < uuid); } - await sfuUnsubscribe(); + /* keep sfuSub alive — screen + camera tracks still ride it. The + * ontrack handler suppresses SFU mic when mesh is also up. */ sfuPublish().catch(e => logLine('err','sfu publish: '+e.message)); } else if (canSpeak(prev) && !canSpeak(next)){ for (const u of [...peers.keys()]) tearPeer(u); dropMic(); muted = false; await sfuUnpublish(); - sfuSubscribe().catch(e => logLine('err','sfu subscribe: '+e.message)); + /* still subscribed — listener role needs the same incoming streams */ } updateRoleUI(); } @@ -2406,8 +2415,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');