webrtc: fix connectivity after network changes (#5097) (#5818)

This commit is contained in:
Alessandro Ros
2026-06-02 21:40:00 +02:00
committed by GitHub
parent d73066406c
commit 640ddde51b
7 changed files with 100 additions and 40 deletions
+3 -1
View File
@@ -38,6 +38,7 @@ require (
github.com/pion/transport/v4 v4.0.2
github.com/pion/webrtc/v4 v4.2.13
github.com/stretchr/testify v1.11.1
github.com/wlynxg/anet v0.0.5
golang.org/x/crypto v0.52.0
golang.org/x/net v0.55.0
golang.org/x/sys v0.45.0
@@ -92,7 +93,6 @@ require (
github.com/skeema/knownhosts v1.3.1 // indirect
github.com/twitchyliquid64/golang-asm v0.15.1 // indirect
github.com/ugorji/go/codec v1.3.1 // indirect
github.com/wlynxg/anet v0.0.5 // indirect
github.com/xanzy/ssh-agent v0.3.3 // indirect
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
go.mongodb.org/mongo-driver/v2 v2.5.0 // indirect
@@ -103,3 +103,5 @@ require (
gopkg.in/warnings.v0 v0.1.2 // indirect
gopkg.in/yaml.v3 v3.0.1 // indirect
)
replace github.com/pion/ice/v4 => github.com/aler9/ice/v4 v4.2.2-0.20260602093426-86d76c717f99
+2 -2
View File
@@ -23,6 +23,8 @@ github.com/alecthomas/kong v1.15.0 h1:BVJstKbpO73zKpmIu+m/aLRrNmWwxXPIGTNin9VmLV
github.com/alecthomas/kong v1.15.0/go.mod h1:wrlbXem1CWqUV5Vbmss5ISYhsVPkBb1Yo7YKJghju2I=
github.com/alecthomas/repr v0.5.2 h1:SU73FTI9D1P5UNtvseffFSGmdNci/O6RsqzeXJtP0Qs=
github.com/alecthomas/repr v0.5.2/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4=
github.com/aler9/ice/v4 v4.2.2-0.20260602093426-86d76c717f99 h1:qhKzvSXdqLM0Z3YT3DOi1h8kEKhW5HKwxFnS+BVTqTE=
github.com/aler9/ice/v4 v4.2.2-0.20260602093426-86d76c717f99/go.mod h1:tmp90fBKpZhQDkHkp/QJb+Gn8vhMxuAcMq7PfhQWQHE=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be h1:9AeTilPcZAjCFIImctFaOjnTIavg87rW78vTPkQqLI8=
github.com/anmitsu/go-shlex v0.0.0-20200514113438-38f4b401e2be/go.mod h1:ySMOLuWl6zY27l47sB3qLNK6tF2fkHG55UZxx8oIVo4=
github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio=
@@ -165,8 +167,6 @@ github.com/pion/datachannel v1.6.0 h1:XecBlj+cvsxhAMZWFfFcPyUaDZtd7IJvrXqlXD/53i
github.com/pion/datachannel v1.6.0/go.mod h1:ur+wzYF8mWdC+Mkis5Thosk+u/VOL287apDNEbFpsIk=
github.com/pion/dtls/v3 v3.1.2 h1:gqEdOUXLtCGW+afsBLO0LtDD8GnuBBjEy6HRtyofZTc=
github.com/pion/dtls/v3 v3.1.2/go.mod h1:Hw/igcX4pdY69z1Hgv5x7wJFrUkdgHwAn/Q/uo7YHRo=
github.com/pion/ice/v4 v4.2.6 h1:17SKPTupOmumCMAQ4ntf8rv6q6XEfVzuz1gSFrnhzPE=
github.com/pion/ice/v4 v4.2.6/go.mod h1:tmp90fBKpZhQDkHkp/QJb+Gn8vhMxuAcMq7PfhQWQHE=
github.com/pion/interceptor v0.1.45 h1:6PUo/5829bIfRFIPPJQzuDn8EjxRTSB/CSD7QVCOaqo=
github.com/pion/interceptor v0.1.45/go.mod h1:gNDYM/uFKcLe/B3gS2/7+aw6z+RDiMy2qKTnF1LO31w=
github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8=
+73 -17
View File
@@ -1,40 +1,96 @@
package webrtc
import (
"fmt"
"net"
"github.com/bluenviron/gortsplib/v5/pkg/readbuffer"
"github.com/pion/transport/v4"
"github.com/pion/transport/v4/stdnet"
"github.com/wlynxg/anet"
)
type webrtcNet struct {
udpReadBufferSize int
// Net is stdnet.Net with the following changes:
// - Interfaces() is overridden to query the OS directly on every call without caching.
// - ListenUDP() is overridden to apply the configured read buffer size to the returned UDPConn.
type Net struct {
UDPReadBufferSize int
*stdnet.Net
stdnet.Net
}
func (n *webrtcNet) initialize() error {
var err error
n.Net, err = stdnet.NewNet()
if err != nil {
return err
}
return nil
}
func (n *webrtcNet) ListenUDP(network string, laddr *net.UDPAddr) (transport.UDPConn, error) {
conn, err := n.Net.ListenUDP(network, laddr)
// Interfaces returns the current list of network interfaces by querying the OS
// on every call, with no caching.
func (n *Net) Interfaces() ([]*transport.Interface, error) {
oifs, err := anet.Interfaces()
if err != nil {
return nil, err
}
if n.udpReadBufferSize != 0 {
err = readbuffer.SetReadBuffer(conn.(*net.UDPConn), n.udpReadBufferSize)
ifs := make([]*transport.Interface, 0, len(oifs))
for i := range oifs {
ifc := transport.NewInterface(oifs[i])
var addrs []net.Addr
addrs, err = anet.InterfaceAddrsByInterface(&oifs[i])
if err != nil {
return nil, err
}
for _, addr := range addrs {
ifc.AddAddress(addr)
}
ifs = append(ifs, ifc)
}
return ifs, nil
}
// InterfaceByIndex returns the interface specified by index.
func (n *Net) InterfaceByIndex(index int) (*transport.Interface, error) {
ifaces, err := n.Interfaces()
if err != nil {
return nil, err
}
for _, ifc := range ifaces {
if ifc.Index == index {
return ifc, nil
}
}
return nil, fmt.Errorf("%w: index=%d", transport.ErrInterfaceNotFound, index)
}
// InterfaceByName returns the interface specified by name.
func (n *Net) InterfaceByName(name string) (*transport.Interface, error) {
ifaces, err := n.Interfaces()
if err != nil {
return nil, err
}
for _, ifc := range ifaces {
if ifc.Name == name {
return ifc, nil
}
}
return nil, fmt.Errorf("%w: %s", transport.ErrInterfaceNotFound, name)
}
// ListenUDP acts like ListenPacket for UDP networks and applies the configured
// read buffer size.
func (n *Net) ListenUDP(network string, laddr *net.UDPAddr) (transport.UDPConn, error) {
conn, err := net.ListenUDP(network, laddr)
if err != nil {
return nil, err
}
if n.UDPReadBufferSize != 0 {
if err = readbuffer.SetReadBuffer(conn, n.UDPReadBufferSize); err != nil {
return nil, err
}
}
return conn, nil
+8 -14
View File
@@ -16,6 +16,7 @@ import (
"github.com/pion/ice/v4"
"github.com/pion/interceptor"
"github.com/pion/sdp/v3"
"github.com/pion/transport/v4"
"github.com/pion/webrtc/v4"
"github.com/bluenviron/mediamtx/internal/logger"
@@ -151,7 +152,7 @@ type trackRecvPair struct {
// PeerConnection is a wrapper around webrtc.PeerConnection.
type PeerConnection struct {
UDPReadBufferSize uint
Net transport.Net
LocalRandomUDP bool
ICEUDPMux ice.UDPMux
ICETCPMux *TCPMuxWrapper
@@ -218,14 +219,7 @@ func (co *PeerConnection) Start() error {
settingsEngine.SetSTUNGatherTimeout(co.STUNGatherTimeout)
webrtcNet := &webrtcNet{
udpReadBufferSize: int(co.UDPReadBufferSize),
}
err := webrtcNet.initialize()
if err != nil {
return err
}
settingsEngine.SetNet(webrtcNet)
settingsEngine.SetNet(co.Net)
mediaEngine := &webrtc.MediaEngine{}
@@ -259,7 +253,7 @@ func (co *PeerConnection) Start() error {
codecType = webrtc.RTPCodecTypeAudio
}
err = mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{
err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: tr.Caps,
PayloadType: webrtc.PayloadType(96 + i),
}, codecType)
@@ -271,7 +265,7 @@ func (co *PeerConnection) Start() error {
// When video is not used, a track must not be added but a codec has to present.
// Otherwise audio is muted on Firefox and Chrome.
if !videoSetupped {
err = mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{
err := mediaEngine.RegisterCodec(webrtc.RTPCodecParameters{
RTPCodecCapability: webrtc.RTPCodecCapability{
MimeType: webrtc.MimeTypeVP8,
ClockRate: 90000,
@@ -284,14 +278,14 @@ func (co *PeerConnection) Start() error {
}
} else {
for _, codec := range incomingVideoCodecs {
err = mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo)
err := mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeVideo)
if err != nil {
return err
}
}
for _, codec := range incomingAudioCodecs {
err = mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeAudio)
err := mediaEngine.RegisterCodec(codec, webrtc.RTPCodecTypeAudio)
if err != nil {
return err
}
@@ -300,7 +294,7 @@ func (co *PeerConnection) Start() error {
interceptorRegistry := &interceptor.Registry{}
err = registerInterceptors(
err := registerInterceptors(
mediaEngine,
interceptorRegistry,
func(s *statsInterceptor) {
+1 -1
View File
@@ -108,7 +108,7 @@ func (c *Client) Initialize(ctx context.Context) error {
}
c.pc = &webrtc.PeerConnection{
UDPReadBufferSize: c.UDPReadBufferSize,
Net: &webrtc.Net{UDPReadBufferSize: int(c.UDPReadBufferSize)},
LocalRandomUDP: true,
ICEServers: iceServers,
IPsFromInterfaces: true,
+9 -2
View File
@@ -214,6 +214,7 @@ type Server struct {
httpServer *httpServer
udpMuxLn net.PacketConn
tcpMuxLn net.Listener
net *webrtc.Net
iceUDPMux ice.UDPMux
iceTCPMux *webrtc.TCPMuxWrapper
sessions map[*session]struct{}
@@ -268,6 +269,8 @@ func (s *Server) Initialize() error {
return err
}
s.net = &webrtc.Net{UDPReadBufferSize: int(s.UDPReadBufferSize)}
if s.LocalUDPAddress != "" {
s.udpMuxLn, err = net.ListenPacket(restrictnetwork.Restrict("udp", s.LocalUDPAddress))
if err != nil {
@@ -286,7 +289,11 @@ func (s *Server) Initialize() error {
}
}
s.iceUDPMux = pwebrtc.NewICEUDPMux(webrtcNilLogger, s.udpMuxLn)
s.iceUDPMux = ice.NewUDPMuxDefault(ice.UDPMuxParams{
UDPConn: s.udpMuxLn,
Logger: webrtcNilLogger,
Net: s.net,
})
}
if s.LocalTCPAddress != "" {
@@ -356,7 +363,7 @@ outer:
select {
case req := <-s.chNewSession:
sx := &session{
udpReadBufferSize: s.UDPReadBufferSize,
net: s.net,
parentCtx: s.ctx,
ipsFromInterfaces: s.IPsFromInterfaces,
ipsFromInterfacesList: s.IPsFromInterfacesList,
+4 -3
View File
@@ -16,6 +16,7 @@ import (
"github.com/google/uuid"
"github.com/pion/ice/v4"
"github.com/pion/sdp/v3"
"github.com/pion/transport/v4"
pwebrtc "github.com/pion/webrtc/v4"
"github.com/bluenviron/mediamtx/internal/auth"
@@ -235,7 +236,7 @@ type sessionParent interface {
}
type session struct {
udpReadBufferSize uint
net transport.Net
parentCtx context.Context
ipsFromInterfaces bool
ipsFromInterfacesList []string
@@ -362,7 +363,7 @@ func (s *session) runPublish(req *initialRequestReq) (int, error) {
}
pc := &webrtc.PeerConnection{
UDPReadBufferSize: s.udpReadBufferSize,
Net: s.net,
ICEUDPMux: s.iceUDPMux,
ICETCPMux: s.iceTCPMux,
ICEServers: iceServers,
@@ -512,7 +513,7 @@ func (s *session) runRead(req *initialRequestReq) (int, error) {
}
pc := &webrtc.PeerConnection{
UDPReadBufferSize: s.udpReadBufferSize,
Net: s.net,
ICEUDPMux: s.iceUDPMux,
ICETCPMux: s.iceTCPMux,
ICEServers: iceServers,