zebra-report: deploy sink-reapply on attach + per-tick ctx.sink telemetry

Continues ticket 0001 fix. Fox 2026-06-07 second telemetry pass showed
blanka-chrome's audioCtx sink reverted to 'default' after a leave/rejoin
even though the user had a picked device, because applySinkToAudioCtx
only ran inside the `if (!audioCtx)` branch and a persistent ctx with a
drifted sinkId stayed default forever.

Attach now re-applies sink when audioCtx exists + speakerDeviceId is set
+ ctx.sinkId differs. Per-tick telemetry exposes ctx.sink= (and want= on
mismatch) so the next session can verify sink stays on the picked device
across leave/rejoin.

Source commit: 4cedd85 in zebra-report.git
This commit is contained in:
russell@unturf.com 2026-06-07 17:43:00 -04:00
parent c40240167b
commit faa52b4871
No known key found for this signature in database

View file

@ -3281,6 +3281,19 @@ function attachAudioStreamViaWorklet(uuid, stream, targetSeconds){
applySinkToAudioCtx();
}
catch(e){ logLine('err','audioCtx create: '+e.message); return false; }
} else if (speakerDeviceId
&& typeof audioCtx.setSinkId === 'function'
&& audioCtx.sinkId !== speakerDeviceId){
/* audioCtx survived a leave→re-enter but its sinkId drifted back
* to '' (system default). Re-apply the picked sink so the worklet
* keeps emitting to the device the user picked in the dropdown.
* Fox 2026-06-07 blanka-chrome telemetry: first attach
* `sink=c38572ec…`, a leave/rejoin later, next attach
* `sink=default` and no `audioCtx sink → …` success line —
* applySinkToAudioCtx only ran inside the `!audioCtx` branch above,
* so a persistent audioCtx with reset sinkId stayed default
* forever. Idempotent: same-sink call is a noop. */
applySinkToAudioCtx();
}
if (audioCtx.state === 'suspended'){
audioCtx.resume().catch(()=>{});
@ -5830,6 +5843,23 @@ async function dumpTelemetry(){
/* reset per-tick whisper counters */
_whisperTick.sent = 0; _whisperTick.dropped = 0;
_whisperTick.emitted = 0; _whisperTick.totalLatencyMs = 0;
/* audioCtx sink visibility on EVERY tick — the one-shot 'sink=…' in
* the attach log only captures the moment of attach; if the sink
* drifts later (Chrome's audioCtx.sinkId getter can revert to '' on
* device-changes, leave/rejoin, suspend/resume), this surfaces it
* every 5s instead of needing to grep for the rare attach event.
* Fox 2026-06-07 blanka-chrome: per-attach sink looked fine but the
* silent-listener state persisted because the GETTER value drifted
* between attaches. */
if (audioCtx){
const cs = (audioCtx.sinkId === undefined) ? 'n/a'
: (audioCtx.sinkId || 'default');
const want = speakerDeviceId || 'default';
parts.push('ctx.sink=' + (cs.length > 12 ? cs.slice(0,8) : cs)
+ (cs !== want && audioCtx.sinkId !== undefined
? ' want=' + (want.length > 12 ? want.slice(0,8) : want)
: ''));
}
/* receiver stats from sfuSubPC (the main listener path) */
if (sfuSubPC && typeof sfuSubPC.getStats === 'function'){
try {
@ -8183,8 +8213,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-07</span><br>
md5 <span class="stamp-md5">bb5c7dea19fcb19f88c479d1cb26f24f</span><br>
sha256 <span class="stamp-sha">03e08e3499cf18fac3aec4b5dbeb808e6b3b6bceb5222ca560026c6d65864d38</span><br>
md5 <span class="stamp-md5">1e77ac2d270d5b6b8697257a6d7f43cc</span><br>
sha256 <span class="stamp-sha">6b76bd2486a0b366599cc8e22c1e11d51072e3bf09df3afed6f0208e5af8b0a9</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>