zebra-spaces: mesh.recv telemetry — surface mesh PC audio decoder stats

Fox 2026-06-07 follow-up to ticket 0001: with the caa0548 fix live,
SFU sub PC receivers still show level=0 + jbuf=? on blanka-chrome.
That's EXPECTED when mesh has taken the swap (SFU receiver becomes
orphaned by design), but without a corresponding stat from the mesh
PC's own receiver we can't distinguish "mesh decoder is alive and
producing samples; SFU orphan is fine" from "mesh decoder is also
silent — both paths dead, listener hears nothing".

Add per-peer mesh.recv lines to the telemetry tick, tagged with the
short uuid prefix:

  mesh.recv u=8afc pkt=23900 lost=8 bytes=339884 jitter=0.0080
            level=0.025 jbuf=0.48s lp=0.1s

Same fields as aud.recv (the SFU sub PC line) so direct comparison
is easy. We skip peers whose connectionState !== 'connected' so a
failed mesh PC doesn't poison the line. Failure on one peer's
getStats is swallowed independently — multi-peer rooms remain
readable even if one mesh PC's stats reporter throws.

Reveals (per ticket 0001 next narrowing):
  - mesh decoder level + jbuf — proves or rules out the mesh decoder
    being orphaned at the worklet swap point
  - per-peer breakdown when mesh=N > 1, so we can tell which peer's
    mesh path is healthy and which is stalled
  - same lastPacketReceived 'lp' so a mesh RTP stall is visible at
    a glance
This commit is contained in:
Russell Ballestrini 2026-06-07 12:24:22 -04:00
parent caa0548113
commit b9e254172b
No known key found for this signature in database

View file

@ -5877,6 +5877,41 @@ async function dumpTelemetry(){
}); });
} catch(e){ parts.push('stats.err=' + e.message); } } catch(e){ parts.push('stats.err=' + e.message); }
} }
/* mesh receiver stats — one line per peer's mesh PC inbound audio.
* Lets us tell whether the worklet is being fed by a live mesh
* decoder vs an orphaned one. For ticket 0001 we needed to answer:
* when handleRemoteSfuTrack hands off to mesh and the SFU receiver
* reports level=0+jbuf=? (expected — orphaned), is the MESH receiver
* actually decoding (level>0+jbuf>0) or also silent? fox 2026-06-07.
*
* Each peer's PC has one audio inbound-rtp; tag with the short uuid
* so a multi-peer room is readable. Failure on one peer's getStats
* doesn't stop the others. */
for (const [uuid, pc] of peers){
if (!pc || typeof pc.getStats !== 'function') continue;
if (pc.connectionState !== 'connected') continue; /* skip non-live PCs */
try {
const stats = await pc.getStats(null);
const nowMs = Date.now();
stats.forEach(r => {
if (r.type !== 'inbound-rtp' || r.kind !== 'audio') return;
const lp = r.lastPacketReceivedTimestamp
? ((nowMs - r.lastPacketReceivedTimestamp) / 1000).toFixed(1) + 's'
: '?';
const jbuf = (r.jitterBufferEmittedCount > 0)
? (r.jitterBufferDelay / r.jitterBufferEmittedCount).toFixed(2) + 's'
: '?';
parts.push('mesh.recv u=' + uuid.slice(0,4) +
' pkt=' + (r.packetsReceived|0) +
' lost=' + (r.packetsLost|0) +
' bytes=' + (r.bytesReceived|0) +
' jitter=' + (r.jitter || 0).toFixed(4) +
' level=' + (r.audioLevel || 0).toFixed(3) +
' jbuf=' + jbuf +
' lp=' + lp);
});
} catch(_){}
}
/* publisher-side stats — what we are actually emitting on the wire. /* publisher-side stats — what we are actually emitting on the wire.
* One line per kind so screen-audio shows up independently of * One line per kind so screen-audio shows up independently of
* screen-video (earlier collapse hid scr.send.aud entirely under * screen-video (earlier collapse hid scr.send.aud entirely under
@ -8148,8 +8183,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"> <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-07</span><br> <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-07</span><br>
md5 <span class="stamp-md5">59cd5e54142e3880cfe784df09204e52</span><br> md5 <span class="stamp-md5">bb5c7dea19fcb19f88c479d1cb26f24f</span><br>
sha256 <span class="stamp-sha">9ba89654f5a18538ee2e4ed6d614b8901e1088c4892fcbfc41d0b5a991692517</span><br> sha256 <span class="stamp-sha">03e08e3499cf18fac3aec4b5dbeb808e6b3b6bceb5222ca560026c6d65864d38</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">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> <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> </footer>