chat.html: add reset button + ICE candidate logging + mDNS hint
addresses two issues in the WebRTC same-LAN test:
1. "Cannot set remote answer in state stable" on retry
the offerer's pc enters stable after the first successful accept-
answer; subsequent clicks fail. add a reset-connection button that
close()s the current pc, clears all SDP textareas + status lines,
detaches inbound rx decoders, & restores the disconnected UI.
user can now retry without reloading the page.
2. silent NAT/mDNS diagnostic gap
browsers anonymize host IPs as <uuid>.local mDNS names. routers
with multicast-blocking client isolation (common on guest Wi-Fi)
silently prevent host-candidate pairing on the same LAN. add:
* a details/summary section under the rtc dot documenting the
Firefox + Chromium config knobs to disable host obfuscation;
* pc.onicecandidate logging that prints candidate type
(host/srflx/relay) + an explicit '(mdns-anonymized)' tag
for .local foundations, so the user can see WHAT is being
gathered without opening devtools;
* an err logLine on connectionState=failed pointing at the
reset button.
unchanged: protocol, frame codec, crypto, audio path, signaling
flow. UI surface gains one button + one collapsed details panel.
This commit is contained in:
parent
0dede3d6ca
commit
202dd706f9
1 changed files with 50 additions and 0 deletions
|
|
@ -223,7 +223,24 @@
|
||||||
<div class="row">
|
<div class="row">
|
||||||
<div class="dot warn" id="dot-rtc"></div>
|
<div class="dot warn" id="dot-rtc"></div>
|
||||||
<span id="rtc-status" class="note">disconnected — start audio first, then pick a role below</span>
|
<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>
|
</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">
|
<div class="rtc-role">
|
||||||
<h3>role A · start a new connection</h3>
|
<h3>role A · start a new connection</h3>
|
||||||
|
|
@ -977,6 +994,14 @@ function ensurePC() {
|
||||||
const stream = ev.streams[0] || new MediaStream([ev.track]);
|
const stream = ev.streams[0] || new MediaStream([ev.track]);
|
||||||
attachInboundTrack(stream, ev.track.id);
|
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 = () => {
|
pc.onconnectionstatechange = () => {
|
||||||
const s = pc.connectionState;
|
const s = pc.connectionState;
|
||||||
rtcStatus.textContent = 'connection: ' + s;
|
rtcStatus.textContent = 'connection: ' + s;
|
||||||
|
|
@ -986,6 +1011,7 @@ function ensurePC() {
|
||||||
announceHello().catch(() => {});
|
announceHello().catch(() => {});
|
||||||
} else if (s === 'failed' || s === 'closed' || s === 'disconnected') {
|
} else if (s === 'failed' || s === 'closed' || s === 'disconnected') {
|
||||||
dotRtc.className = 'dot';
|
dotRtc.className = 'dot';
|
||||||
|
logLine('err', `webrtc ${s} — click "reset connection" then retry`);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
pc.oniceconnectionstatechange = () => {
|
pc.oniceconnectionstatechange = () => {
|
||||||
|
|
@ -994,6 +1020,30 @@ function ensurePC() {
|
||||||
return pc;
|
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) {
|
function waitForIceGathering(p) {
|
||||||
return new Promise(resolve => {
|
return new Promise(resolve => {
|
||||||
if (p.iceGatheringState === 'complete') return resolve();
|
if (p.iceGatheringState === 'complete') return resolve();
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue