diff --git a/go.mod b/go.mod index 0996d37e..24fc2acf 100644 --- a/go.mod +++ b/go.mod @@ -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 diff --git a/go.sum b/go.sum index ba87d7d5..12a1a550 100644 --- a/go.sum +++ b/go.sum @@ -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= diff --git a/internal/protocols/webrtc/net.go b/internal/protocols/webrtc/net.go index 32dfeef8..7cf3b66d 100644 --- a/internal/protocols/webrtc/net.go +++ b/internal/protocols/webrtc/net.go @@ -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 diff --git a/internal/protocols/webrtc/peer_connection.go b/internal/protocols/webrtc/peer_connection.go index 02b4eff1..98ea2bda 100644 --- a/internal/protocols/webrtc/peer_connection.go +++ b/internal/protocols/webrtc/peer_connection.go @@ -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) { diff --git a/internal/protocols/whip/client.go b/internal/protocols/whip/client.go index e8cbaae8..388d8328 100644 --- a/internal/protocols/whip/client.go +++ b/internal/protocols/whip/client.go @@ -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, diff --git a/internal/servers/webrtc/server.go b/internal/servers/webrtc/server.go index d87ac891..a5c3071e 100644 --- a/internal/servers/webrtc/server.go +++ b/internal/servers/webrtc/server.go @@ -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, diff --git a/internal/servers/webrtc/session.go b/internal/servers/webrtc/session.go index ee348f7d..5bb8a689 100644 --- a/internal/servers/webrtc/session.go +++ b/internal/servers/webrtc/session.go @@ -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,