diff --git a/zebra-report/zebra-spaces.html b/zebra-report/zebra-spaces.html index 59112b3..9fd2b84 100644 --- a/zebra-report/zebra-spaces.html +++ b/zebra-report/zebra-spaces.html @@ -3369,17 +3369,33 @@ async function setSenderBitrate(sender){ * for speech. Opus VBR only spends what it needs; idle music stays around * 200–300 kbps and only peaks claim the ceiling. */ p.encodings[0].maxBitrate = musicMode ? 256000 : 40000; + /* Audio rides ahead of video in the sender's egress queue. Fox + * 2026-06-04: wiggling a terminal window caused 1-5s cutouts on + * listeners even with a 4s playoutDelayHint buffer. Cause: the + * wiggle triggered a big screen-share keyframe burst that + * monopolized the egress queue; audio packets queued behind it + * arrived too late and the buffer drained. Setting priority='high' + * + networkPriority='high' on audio tells the browser to drain + * audio first when its outgoing queue is contended. Video keeps + * the default ('low' on the screen/camera senders below). */ + p.encodings[0].priority = 'high'; + p.encodings[0].networkPriority = 'high'; await sender.setParameters(p); } catch(_){} } /* explicit bitrate setter for non-mic senders (screen video, screen audio). - * setSenderBitrate above is locked to the mic's musicMode value. */ + * setSenderBitrate above is locked to the mic's musicMode value. Audio kind + * gets high priority like the mic; video kind gets low priority so audio + * wins the egress queue under contention. */ async function setSenderMaxBitrate(sender, bps){ if (!sender) return; try { const p = sender.getParameters(); if (!p.encodings || !p.encodings.length) p.encodings = [{}]; p.encodings[0].maxBitrate = bps; + const isVideo = sender.track && sender.track.kind === 'video'; + p.encodings[0].priority = isVideo ? 'low' : 'high'; + p.encodings[0].networkPriority = isVideo ? 'low' : 'high'; await sender.setParameters(p); } catch(_){} } @@ -5925,8 +5941,8 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');