Skip to content

webrtc: drop stale mux candidates after network change#5782

Open
saeedvft wants to merge 1 commit into
bluenviron:mainfrom
saeedvft:fix/webrtc-stale-mux-candidates
Open

webrtc: drop stale mux candidates after network change#5782
saeedvft wants to merge 1 commit into
bluenviron:mainfrom
saeedvft:fix/webrtc-stale-mux-candidates

Conversation

@saeedvft
Copy link
Copy Markdown

Fixes #5097

Problem

When webrtcLocalUDPAddress is set (the default), a single UDPMuxDefault
is created at server startup. Pion collects local interface addresses once at
that point and caches them inside the mux. When the server's network changes
(e.g. the host switches WiFi networks), the mux keeps advertising the
startup-time IP as a host candidate in every new SDP answer. The new network
IP is never added, so ICE negotiation on the new network fails and the stream
cannot recover without a full server restart.

Fix

removeUnwantedCandidates already has the right place to intercept this.
When ICEUDPMux is set and the caller has not configured IPsFromInterfaces
or AdditionalHosts (both of which have their own filtering logic), call
net.Interfaces() fresh for each new session and drop any host UDP candidate
whose IP is no longer present on a live interface.

The call is cheap — one syscall per session at SDP answer time — and
completely non-fatal: if the query fails, a warning is logged and the filter
is skipped rather than rejecting the session.

Tested

Reproduced the bug by switching WiFi networks while mediamtx was running and
observing the stale IP in the SDP answer. After the fix the stale candidate
is dropped and the new session negotiates successfully on the new network.

Before (stale 192.168.0.9 advertised to client on new network):

a=candidate:... 127.0.0.1 8189 typ host
a=candidate:... 192.168.0.9 8189 typ host
a=candidate:... 172.18.0.1 8189 typ host

After (stale candidate dropped, connection succeeds via 172.18.0.1):

a=candidate:... 127.0.0.1 8189 typ host
a=candidate:... 172.18.0.1 8189 typ host

A unit test covering this code path is included in peer_connection_test.go.

When webrtcLocalUDPAddress is set, mediamtx creates a single
UDPMuxDefault at startup. Pion populates its localAddrsForUnspecified
once at construction time. After a network change (e.g. WiFi switch),
the mux still advertises startup-time IPs as host candidates in SDP
answers, while the new network IP is never generated.

Fix by querying net.Interfaces() fresh in removeUnwantedCandidates for
each new session when ICEUDPMux is set and no explicit AdditionalHosts
or IPsFromInterfaces filter is configured, dropping any host UDP
candidate whose IP no longer appears on a live interface.

Fixes bluenviron#5097
@aler9
Copy link
Copy Markdown
Member

aler9 commented May 19, 2026

Hello, this PR only solves half of the issue: it removes ICE candidates associated with IPs that are not valid anymore, but it doesn't add new IPs in case a certain interface returns online.

Furthermore, the entity that is in charge of caching and updating IPs is pion/ice, not MediaMTX - wouldn't it be better to send a patch to pion/ice instead and fix the problem at the root?

@saeedvft
Copy link
Copy Markdown
Author

Hello, this PR only solves half of the issue: it removes ICE candidates associated with IPs that are not valid anymore, but it doesn't add new IPs in case a certain interface returns online.

Furthermore, the entity that is in charge of caching and updating IPs is pion/ice, not MediaMTX - wouldn't it be better to send a patch to pion/ice instead and fix the problem at the root?

Thank you for the feedback. You're right on both counts.

For point 1, the missing piece is that the new IP needs to be surfaced
as a candidate too — which requires the mux to re-query local interfaces
rather than using its startup-time cache.

For point 2, I agree the root fix belongs in pion/ice — specifically in
UDPMuxDefault, where localAddrsForUnspecified should be refreshed
dynamically rather than populated once at construction. I can send a PR
there instead.

Before I do: would you prefer the pion/ice fix (re-query on each
GetListenAddresses() call), and then mediamtx picks it up when upgrading
the dependency? Or would you also want a mediamtx-side workaround in the
meantime?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

WebRTC stops working when changing the WIFI network

2 participants