zebra-spaces: audio priority='high', video priority='low' at the RtpSender

When the sender's egress queue gets contended, audio wins. Fox 2026-06-04:
wiggling a terminal window (X11) caused 1-5s audio cutouts on all mesh
peers AND listeners — even with playoutDelayHint=4.0 on subs. Theory
(fox): "i might be the clients video trying to compete with the audio,
the wiggled updates" — exactly that. X11 window wiggling produced a
storm of dirty regions for screen-share; the encoder spiked into a big
keyframe burst; audio packets queued behind that burst arrived late;
listener's playoutDelayHint buffer drained.

Fix: RTCRtpEncodingParameters.priority + networkPriority. Set audio to
'high' (mic + any DJ audio track), video to 'low' (screen, camera).
Both Firefox and Chromium honor these for the local egress queue —
audio packets jump ahead of video bursts.

Applied at:
- setSenderBitrate (mic publisher) — high
- setSenderMaxBitrate (screen audio, camera audio) — high
- setSenderMaxBitrate (screen video, camera video) — low

No new param, no SDP renegotiation needed — setParameters() applies
immediately to the existing PC. Existing call sites all flow through
these two helpers, so the change reaches every publisher.

Pair with publisher-side outbound-rtp telemetry (836a036) — gap on
aud.send during a wiggle is now the metric we measure against.
This commit is contained in:
Russell Ballestrini 2026-06-04 14:38:12 -04:00
parent 836a036cfb
commit 763ba181be
No known key found for this signature in database

View file

@ -3369,17 +3369,33 @@ async function setSenderBitrate(sender){
* for speech. Opus VBR only spends what it needs; idle music stays around
* 200300 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');
<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> &nbsp;·&nbsp; built <span class="stamp-date">2026-06-04</span><br>
md5 <span class="stamp-md5">d0fe5b9f0e4be6dedc517506de9ef174</span><br>
sha256 <span class="stamp-sha">95e89b4620eff5e68a55b443b467f640d3ddc01eb6e5921c9cfe4262cce72543</span><br>
md5 <span class="stamp-md5">a120f5b4723d88b8f6d4518fa647e390</span><br>
sha256 <span class="stamp-sha">82d23c2333f6d6ab0f3c2149725d274a44807ec0a878ebf1ddb59cf07c095197</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>