zebra-report: deploy telemetry fingerprint + camera-error breadcrumbs
Pulls in zebra-report 1ed5263: every entry now emits a one-shot
'session: ua=... ctx={...} devs={...} picked={...}' line so the
signal-server log carries browser + audioCtx + device-count + saved-
deviceId-shortprefix context for diagnosing the open tickets
(0001 fedora chrome silence, 0002 fedora firefox cannot share camera).
Camera-open errors now include e.name + the constraints we tried
plus auto-recovery from OverconstrainedError + saved deviceId.
This commit is contained in:
parent
0f36e02e42
commit
8e762fb100
1 changed files with 79 additions and 4 deletions
|
|
@ -4560,7 +4560,36 @@ async function sfuPublishCamera(){
|
|||
* speaker can choose camera-on while still using a different audio
|
||||
* input (monitor source, music mode, etc) */
|
||||
stream = await navigator.mediaDevices.getUserMedia({ video: videoConstraints, audio: false });
|
||||
} catch(e){ logLine('err','camera open cancelled: '+e.message); fsm.send('FAILED', { error: e.message }); fsm.send('DONE'); return; }
|
||||
} catch(e){
|
||||
/* surface e.name + the constraints we tried so signal-server telemetry
|
||||
* can disambiguate NotFoundError (device gone) vs OverconstrainedError
|
||||
* (saved deviceId or 720p/16:9 not supported) vs NotAllowedError (user
|
||||
* declined permission) vs NotReadableError (camera held by another app).
|
||||
* Defect #0002 fedora firefox: e.message alone wasn't enough to pick
|
||||
* the right branch. */
|
||||
const exact = videoConstraints.deviceId && videoConstraints.deviceId.exact;
|
||||
logLine('err',
|
||||
'camera open cancelled: '+(e.name||'Error')+' — '+e.message+
|
||||
' (constraints: 1280x720@30 16:9'+(exact?' deviceId='+String(exact).slice(0,8):'')+')'
|
||||
);
|
||||
/* OverconstrainedError + a saved deviceId is the canonical "saved
|
||||
* device gone or renamed" case — retry once without deviceId so the
|
||||
* user gets default camera rather than a permanent fail. */
|
||||
if (e.name === 'OverconstrainedError' && exact){
|
||||
logLine('', 'camera open retry without saved deviceId');
|
||||
try {
|
||||
const fallbackConstraints = Object.assign({}, videoConstraints);
|
||||
delete fallbackConstraints.deviceId;
|
||||
stream = await navigator.mediaDevices.getUserMedia({ video: fallbackConstraints, audio: false });
|
||||
logLine('', 'camera open recovered with default device');
|
||||
} catch(e2){
|
||||
logLine('err','camera open retry failed: '+(e2.name||'Error')+' — '+e2.message);
|
||||
fsm.send('FAILED', { error: e2.message }); fsm.send('DONE'); return;
|
||||
}
|
||||
} else {
|
||||
fsm.send('FAILED', { error: e.message }); fsm.send('DONE'); return;
|
||||
}
|
||||
}
|
||||
fsm.send('ACQUIRED', { stream });
|
||||
sfuCameraStream = stream;
|
||||
const pc = new RTCPeerConnection(rtcConfig);
|
||||
|
|
@ -6029,6 +6058,52 @@ async function handleSignal(raw){
|
|||
broadcastSpotlight();
|
||||
reorderTiles();
|
||||
logLine('', 'joined as '+myRole+' — uuid '+myUUID);
|
||||
/* one-shot session fingerprint for telemetry (see docs/tickets/
|
||||
* 0001 + 0002): captures the browser + audio/video device state
|
||||
* at entry so a reporter's signal-server log carries enough
|
||||
* context to disambiguate browser-specific defects. Mobile + UA
|
||||
* fields land first because the bug shape often correlates with
|
||||
* a single UA family (fxhp-phone-firefox, fedora-chrome, etc).
|
||||
* Best-effort — wrapped + caught so a hostile UA doesn't break
|
||||
* entry. */
|
||||
(async () => {
|
||||
try {
|
||||
const ua = navigator.userAgent || '';
|
||||
const isMobile = /Mobi|Android|iPhone|iPad|iPod/.test(ua);
|
||||
const browser =
|
||||
/Firefox\//.test(ua) ? 'firefox' :
|
||||
/Edg\//.test(ua) ? 'edge' :
|
||||
/Chrome\//.test(ua) ? 'chrome' :
|
||||
/Safari\//.test(ua) ? 'safari' : 'other';
|
||||
let sr = 'n/a', bl = 'n/a', sinkSupp = false, ctxSink = 'n/a';
|
||||
try {
|
||||
if (audioCtx){
|
||||
sr = audioCtx.sampleRate;
|
||||
bl = (audioCtx.baseLatency != null) ? audioCtx.baseLatency.toFixed(4) : 'n/a';
|
||||
sinkSupp = typeof audioCtx.setSinkId === 'function';
|
||||
ctxSink = (audioCtx.sinkId === undefined) ? 'n/a' : (audioCtx.sinkId || 'default');
|
||||
}
|
||||
} catch(_){}
|
||||
let outCount = 0, inCount = 0, camCount = 0;
|
||||
try {
|
||||
const devs = await navigator.mediaDevices.enumerateDevices();
|
||||
for (const d of devs){
|
||||
if (d.kind === 'audiooutput') outCount++;
|
||||
else if (d.kind === 'audioinput') inCount++;
|
||||
else if (d.kind === 'videoinput') camCount++;
|
||||
}
|
||||
} catch(_){}
|
||||
const pickShort = id => id ? String(id).slice(0,8) : '';
|
||||
logLine('',
|
||||
'session: ua='+browser+(isMobile?'/mobile':'/desktop')+
|
||||
' ctx={sr='+sr+' baseLat='+bl+' sinkSupp='+(sinkSupp?1:0)+' sink='+ctxSink+'}'+
|
||||
' devs={out='+outCount+' in='+inCount+' cam='+camCount+'}'+
|
||||
' picked={mic='+pickShort(micDeviceId)+
|
||||
' cam='+pickShort(cameraDeviceId)+
|
||||
' spk='+pickShort(speakerDeviceId)+'}'
|
||||
);
|
||||
} catch(_){}
|
||||
})();
|
||||
/* dot, leave/mute button visibility, sec-room reveal, entry-row
|
||||
* hide and status text are all driven by applyCallStateUI()
|
||||
* from the 'joined' state. Welcome already dispatched WELCOME
|
||||
|
|
@ -8008,9 +8083,9 @@ logLine('', 'ready — pick a handle, type a rendezvous code, enter the space');
|
|||
</script>
|
||||
|
||||
<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> · built <span class="stamp-date">2026-06-06</span><br>
|
||||
md5 <span class="stamp-md5">43f585be4e478d110d4250eff68b9d68</span><br>
|
||||
sha256 <span class="stamp-sha">6677c5b69c23976a7c9d35104fdc324fe69fa80d24120e1f32aaab23ebdd0606</span><br>
|
||||
<span id="pi-seal" style="color:#777;cursor:default;user-select:none" title="">page integrity</span> · built <span class="stamp-date">2026-06-07</span><br>
|
||||
md5 <span class="stamp-md5">14af06bd5893feb70229dded7e6c5497</span><br>
|
||||
sha256 <span class="stamp-sha">e603642628f264fa919f55be33e6578d77d2d42858566c2d708beefce091aed5</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>
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue