zebra-report/docs/tickets/0001-fedora-chrome-cannot-hear-speakers.md
Russell Ballestrini e321a7cee1
docs: add tickets dir + first two open defects
zebra-report didn't have a defect tracker before; until now everything
lived in commit-message archaeology and signal-server log greps. Two
known defects make this the right moment to start one:

  0001: fedora chrome cannot hear speakers (P0, in-progress)
    P0 because every invariant (RTP arriving, worklet started,
    ctxState=running) is green but the user hears nothing — silent
    regression is trust-eroding. Primary hypothesis is that
    AudioContext.destination always routed to system-default while
    the speaker-output picker only setSinkId'd <audio> elements.
    Fix landed in 467146a (applySinkToAudioCtx), awaiting fox retest
    in production.

  0002: fedora firefox cannot share camera (High, open)
    Camera publish fails on firefox; chrome same machine works.
    No telemetry yet; ticket enumerates the candidates to narrow
    on the next attempt.

README.md spells out the format (status / priority / surface / first
seen, then symptom / telemetry / hypothesis / fix / status notes)
and the resolution loop (telemetry → failing test → fix → stamp →
deploy → update ticket).
2026-06-07 11:24:17 -04:00

4.3 KiB

0001: fedora chrome cannot hear speakers

Status: in-progress Priority: P0 (silent regression — every invariant green except audible output) Surface: fedora chrome — observed under both listener and speaker roles First seen: 2026-06-04 (or earlier — "for days") Reporter: fox (blanka-chrome identity)

Symptom

On fedora chrome, no audio from any speaker in the room is audible. Affects every role chrome takes: as listener, no mic audio plays. As speaker, no peer mic audio plays through the mesh+SFU path. Same chrome can publish mic + camera + screen fine — only the receive-side audio is silent.

Other browsers on the same machine (firefox) hear the room correctly once the same code is loaded — so the codebase is not uniformly broken.

Telemetry / Repro

Signal-server log (/var/log/zebra-spaces-signal.log, CLIENT_LOG actor_handle="blanka-chrome") on 2026-06-06 ~23:24 UTC:

· role=speaker sub=connected/connected pub=connected/connected
  mesh=2 sListen=0 streamMode=0 muted=1 xcr=off
  aud.recv pkt=308 lost=0 bytes=197428 jitter=0.0080 level=0.000 jbuf=? lp=0.2s
  aud.recv pkt=22075 lost=3 bytes=44147 jitter=0.3210 level=0.000 jbuf=? lp=0.9s
  aud.recv pkt=44242 lost=4 bytes=751664 jitter=0.0260 level=0.000 jbuf=? lp=0.3s
  • RTP arriving at ~50 pps on the active speaker (real Opus voice traffic)
  • level=0.000 on EVERY receiver
  • mesh=2 (both mesh peers connected)
  • sub=connected and pub=connected (both SFU PCs healthy)

Earlier in the session:

audio attach uuid=2d4a target=0.5s (current pool=3)
audio via AudioContext 2d4a target=0.5s ctxState=running
jitter-buffer installed uuid=2d4a target=0.5s
jitter-buffer started uuid=2d4a target=0.5s
mesh stream swapped into worklet for 2d4a — buffer cushion now applies to mesh too

Every attach invariant green. Worklet started event fired. ctxState=running. Yet the user hears nothing.

User report: chrome tab indicator shows NO audio playing.

Hypothesis

Primary (deployed 2026-06-06, awaiting retest): the speaker-output dropdown in the page calls setSinkId on <audio> elements only. The worklet path (every listener AND every speaker monitor since c3ff58c routed mesh through the worklet) runs through audioCtx.destination, which always emits to the system default sink. If the user picks a non-default sink in the dropdown, the worklet keeps emitting to the wrong device — silence on the device they're monitoring.

Fox 2026-06-06 confirmed picking a non-default sink on fedora chrome. Firefox defaults to system default which happens to match the right speaker for him, which is why firefox music started working without needing this fix.

Secondary (not yet ruled out):

  • AudioContext.setSinkId() may not be honored on this chrome version / flag set → silent fallthrough to default
  • PulseAudio per-app default routing for chrome may override the picked sink at the OS layer
  • The worklet's started=true may flip back to false on a network blip and never re-arm if the queue can't refill to target (rearm guard fires)

Fix

Landed: 467146a 2026-06-06 — applySinkToAudioCtx() calls audioCtx.setSinkId(speakerDeviceId) on creation and in the dropdown's fan-out. Diagnostic log line now includes sink=... so the next telemetry session shows which sink the worklet is routed to.

Deployed: 0f36e02 to www.unturf.com — blanka-chrome needs page reload (it's a code-shell change, not SDP-level).

Pinned by tests:

  • audioCtx.setSinkId is called on creation so the worklet routes to the user-picked sink (not just system default)
  • audioCtx without setSinkId support (older browser) does not throw — silent fallback to default

Status notes

  • 2026-06-07 (open): awaiting fox retest after reload. Next telemetry should show sink=<device-id> (not default) on the audio via AudioContext line. If chrome still silent with sink=<correct-id>, hypothesis shifts to PulseAudio per-app routing or chrome's WebAudio sink negotiation.
  • 2026-06-06: the Jun 5 cascade + flushSfuStreams prefix-match fix (0ce1339) helped firefox but not chrome. Telemetry above ruled out the FSM / attach defects.
  • 2026-06-04 → 2026-06-05: earlier cascade commits (2e74b92, f479878, 144dd15, 61b21a6) tightened the audio attach path but did not address this defect (they targeted double-audio on fxhp-android-firefox).