fix(call): answer a far end's new session on a fresh connection - #200
Merged
Merged
Conversation
A far end that rebuilds its RTCPeerConnection and offers from the new one reached this side's old connection: the roster says nothing (Android publishes no page session, and a web client that rebuilt keeps its own), so the old peer was kept. Chromium refused every copy of that offer on the old connection, and where the m-lines happened to line up the answer came from senders that believed their tracks were negotiated already. Either way that person went unheard while they still heard us. A profile-1 peer now remembers the o= session id and ICE ufrag of the last remote description it applied. An offer where both have moved is a new far-end session, judged before glare handling; the connection's own m-line refusal is the fallback when the session id alone changed. The peer hands the offer to the mesh, which closes it and builds a fresh one the way a downgrade does: tracks first, then the offer, so the answer carries this side's media. Once per peer; retransmissions are answered by the replacement from store, and a late copy from the retired session is dropped. Candidates naming credentials not yet applied are held rather than refused, and ones that overtook the new offer go with it. An ICE restart (ufrag alone) stays on the existing connection. The fake connection now mints a session id and ufrag per connection and writes m-lines in negotiated order. Claude-Session: https://claude.ai/code/session_01CG4pPCsd8pdySNvBpt8fTk
Merged
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Web half of kithmoot-android #107 (in Android 0.6.23). On the owner's 5-person call, people went silent to one device while still hearing it, after a far end rebuilt its connection.
Root cause: the mesh rebuilds only when a roster
sidchanges and both old and new are known; Android publishes none and a page that rebuilt its own connection keeps its sid, so the far end's new offer went to the old RTCPeerConnection. Chromium refused every copy (setRemoteDescriptionm-line order); the rebuilding side stayed unheard until the old connection timed out and ICE-restarted.#senderRefusedwas not involved.src/sdp-shape.ts: reado=session id and ufrag; detect a new far-end session; recognise the m-line refusal.src/peer.ts: both changed = new session, checked before glare handling; handed to the mesh once with any early candidates (onRemoteRestart); fallback on the refusal when the session id differs; ufrag alone = ICE restart, stays.src/mesh.ts#replaceForRemoteSession: close old peer, build new one like#downgradePeer(tracks first, then offer and carried candidates); remembers retired session ids so a late copy of an old offer causes no second rebuild. Forwarder excluded; profile-2 unchanged.Tests: unit (new-session answered with tracks, impolite glare, refusal fallback, ICE restart not rebuilt, retransmissions = one rebuild, early candidates carried, web↔web); new e2e in
call-stability.specfails without the fix;npm test2,639 pass; typecheck; Playwright chromium 32/32 (call-stability incl. Firefox receiver, media, speaking, volume). Not yet tested live against Android 0.6.23.🤖 Generated with Claude Code
https://claude.ai/code/session_01CG4pPCsd8pdySNvBpt8fTk