zebra-report: reset button + ICE candidate logging + mDNS hint

This commit is contained in:
russell@unturf.com 2026-05-27 16:39:26 -04:00
parent 3826d818a2
commit 172d140868

View file

@ -223,7 +223,24 @@
<div class="row">
<div class="dot warn" id="dot-rtc"></div>
<span id="rtc-status" class="note">disconnected — start audio first, then pick a role below</span>
<button id="btn-reset-rtc" style="margin-left:auto">reset connection</button>
</div>
<details>
<summary>stuck on the same LAN? host candidates as <code>.local</code>?</summary>
<p class="note">
modern browsers anonymize host IPs as <code>.local</code> mDNS names for
privacy. if a router blocks multicast (common on guest Wi-Fi, sometimes
consumer defaults), peers cannot resolve each other's <code>.local</code>
addresses & host candidates fail. workaround: turn off the obfuscation.
</p>
<p class="note" style="margin-top:0.3rem">
Firefox: <code>about:config</code> → set
<code>media.peerconnection.ice.obfuscate_host_addresses</code> to <code>false</code>.<br>
Chromium: <code>chrome://flags/#enable-webrtc-hide-local-ips-with-mdns</code>
→ set to Disabled.<br>
reload the page after toggling, then retry.
</p>
</details>
<div class="rtc-role">
<h3>role A · start a new connection</h3>
@ -977,6 +994,14 @@ function ensurePC() {
const stream = ev.streams[0] || new MediaStream([ev.track]);
attachInboundTrack(stream, ev.track.id);
};
pc.onicecandidate = (ev) => {
if (!ev.candidate) return;
const c = ev.candidate.candidate;
const tm = c.match(/typ\s+(\S+)/);
const typ = tm ? tm[1] : '?';
const mdns = c.includes('.local') ? ' (mdns-anonymized)' : '';
logLine('sys', `ice candidate: ${typ}${mdns}`);
};
pc.onconnectionstatechange = () => {
const s = pc.connectionState;
rtcStatus.textContent = 'connection: ' + s;
@ -986,6 +1011,7 @@ function ensurePC() {
announceHello().catch(() => {});
} else if (s === 'failed' || s === 'closed' || s === 'disconnected') {
dotRtc.className = 'dot';
logLine('err', `webrtc ${s} — click "reset connection" then retry`);
}
};
pc.oniceconnectionstatechange = () => {
@ -994,6 +1020,30 @@ function ensurePC() {
return pc;
}
function resetConnection() {
if (pc) {
try { pc.close(); } catch (_) {}
pc = null;
}
for (const [, d] of rxDecoders) {
try { d.node.disconnect(); } catch (_) {}
try { d.audio.pause(); d.audio.srcObject = null; } catch (_) {}
}
rxDecoders.clear();
$('local-offer').value = '';
$('local-answer').value = '';
$('remote-offer').value = '';
$('remote-answer').value = '';
for (const id of ['offer-status','answer-status','ans-status']) {
$(id).textContent = ''; $(id).className = 'status-line';
}
rtcStatus.textContent = 'disconnected — pick a role below';
dotRtc.className = 'dot warn';
logLine('sys', 'connection reset — ready to retry');
}
$('btn-reset-rtc').addEventListener('click', resetConnection);
function waitForIceGathering(p) {
return new Promise(resolve => {
if (p.iceGatheringState === 'complete') return resolve();