diff --git a/zebra-report/zebra-spaces.html b/zebra-report/zebra-spaces.html
index 95ad5f1..b185773 100644
--- a/zebra-report/zebra-spaces.html
+++ b/zebra-report/zebra-spaces.html
@@ -1640,6 +1640,7 @@ const sfuStreamsByPubHex = new Map(); // pubHex -> MediaStream
function attachSfuTrack(uuid, stream){
let a = remoteAudio.get(uuid);
+ const fresh = !a;
if (!a){
/* lease from the pre-blessed audio pool so Firefox Android's
* per-element autoplay grant carries over from the entry-button
@@ -1650,12 +1651,26 @@ function attachSfuTrack(uuid, stream){
a = leaseAudioElement();
remoteAudio.set(uuid, a);
applySinkTo(a);
+ /* one-shot breadcrumbs so we can see whether the new path lights
+ * up at all, vs. the silent-but-state=connected pattern. */
+ a.addEventListener('playing', () => logLine('', 'rtc playing '+uuid.slice(0,4)+' ct='+a.currentTime.toFixed(2)), { once: true });
+ a.addEventListener('pause', () => logLine('err','rtc paused '+uuid.slice(0,4)+' ct='+a.currentTime.toFixed(2)));
+ a.addEventListener('ended', () => logLine('err','rtc ended '+uuid.slice(0,4)));
+ a.addEventListener('stalled', () => logLine('err','rtc stalled '+uuid.slice(0,4)));
+ a.addEventListener('error', () => logLine('err','rtc error '+uuid.slice(0,4)+' code='+(a.error?a.error.code:'?')));
}
a.srcObject = stream;
+ const tracks = stream && stream.getAudioTracks ? stream.getAudioTracks() : [];
+ const t0 = tracks[0];
+ logLine('', 'sfu attach '+uuid.slice(0,4)+' fresh='+(fresh?1:0)+
+ ' tracks='+tracks.length+
+ (t0 ? ' tr0={en='+t0.enabled+' mu='+t0.muted+' rs='+t0.readyState+'}' : ''));
try {
const p = a.play();
- if (p && p.catch) p.catch(e => logLine('err','rtc autoplay '+uuid+': '+e.message));
- } catch(_){}
+ if (p && p.catch) p.catch(e => logLine('err','rtc autoplay '+uuid.slice(0,4)+': '+e.message));
+ } catch(e){
+ logLine('err','rtc play threw '+uuid.slice(0,4)+': '+e.message);
+ }
stopMeter(uuid); startMeter(uuid, stream);
logLine('', 'sfu: receiving '+((members.get(uuid)||{}).handle || uuid));
}
@@ -3355,6 +3370,88 @@ function stopAlivePings(){
clearInterval(aliveTimer); aliveTimer = null;
}
+/* Telemetry tick — fired every TELEMETRY_INTERVAL_MS while in a room.
+ * Dumps PC states + receiver RTP counters +