diff --git a/go.mod b/go.mod index 9e8fe6b3..d6ebec66 100644 --- a/go.mod +++ b/go.mod @@ -24,13 +24,13 @@ require ( github.com/gorilla/websocket v1.5.3 github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 github.com/matthewhartstonge/argon2 v1.3.4 - github.com/pion/ice/v4 v4.0.7 + github.com/pion/ice/v4 v4.0.10 github.com/pion/interceptor v0.1.40 github.com/pion/logging v0.2.4 github.com/pion/rtcp v1.2.15 github.com/pion/rtp v1.8.21 github.com/pion/sdp/v3 v3.0.15 - github.com/pion/webrtc/v4 v4.0.7 + github.com/pion/webrtc/v4 v4.1.3 github.com/stretchr/testify v1.10.0 golang.org/x/crypto v0.41.0 golang.org/x/sys v0.35.0 @@ -69,10 +69,10 @@ require ( github.com/modern-go/reflect2 v1.0.2 // indirect github.com/pelletier/go-toml/v2 v2.2.3 // indirect github.com/pion/datachannel v1.5.10 // indirect - github.com/pion/dtls/v3 v3.0.4 // indirect + github.com/pion/dtls/v3 v3.0.6 // indirect github.com/pion/mdns/v2 v2.0.7 // indirect github.com/pion/randutil v0.1.0 // indirect - github.com/pion/sctp v1.8.36 // indirect + github.com/pion/sctp v1.8.39 // indirect github.com/pion/srtp/v3 v3.0.6 // indirect github.com/pion/stun/v3 v3.0.0 // indirect github.com/pion/transport/v3 v3.0.7 // indirect @@ -94,7 +94,3 @@ 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.0.0-20250301104324-b2df7db9f75d - -replace github.com/pion/webrtc/v4 => github.com/aler9/webrtc/v4 v4.0.0-20250228091429-18796cd12b4f diff --git a/go.sum b/go.sum index bf8046d7..0be1104e 100644 --- a/go.sum +++ b/go.sum @@ -19,10 +19,6 @@ github.com/alecthomas/kong v1.12.1 h1:iq6aMJDcFYP9uFrLdsiZQ2ZMmcshduyGv4Pek0MQPW github.com/alecthomas/kong v1.12.1/go.mod h1:p2vqieVMeTAnaC83txKtXe8FLke2X07aruPWXyMPQrU= github.com/alecthomas/repr v0.4.0 h1:GhI2A8MACjfegCPVq9f1FLvIBS+DrQ2KQBFZP1iFzXc= github.com/alecthomas/repr v0.4.0/go.mod h1:Fr0507jx4eOXV7AlPV6AVZLYrLIuIeSOWtW57eE/O/4= -github.com/aler9/ice/v4 v4.0.0-20250301104324-b2df7db9f75d h1:x28XXs2QllDNMOEjurxOMKEcg5150lQuzyjNFiHlvkM= -github.com/aler9/ice/v4 v4.0.0-20250301104324-b2df7db9f75d/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= -github.com/aler9/webrtc/v4 v4.0.0-20250228091429-18796cd12b4f h1:QM/Wq0tWsInWN9rtxdMNnCBWg5o3XJl6LdWAS61h2yc= -github.com/aler9/webrtc/v4 v4.0.0-20250228091429-18796cd12b4f/go.mod h1:C+5JA7KiyLyoKyGh7hVFD/HCAon3IB/tfniocpZ9JoU= 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= @@ -155,8 +151,10 @@ github.com/pelletier/go-toml/v2 v2.2.3 h1:YmeHyLY8mFWbdkNWwpr+qIL2bEqT0o95WSdkNH github.com/pelletier/go-toml/v2 v2.2.3/go.mod h1:MfCQTFTvCcUyyvvwm1+G6H/jORL20Xlb6rzQu9GuUkc= github.com/pion/datachannel v1.5.10 h1:ly0Q26K1i6ZkGf42W7D4hQYR90pZwzFOjTq5AuCKk4o= github.com/pion/datachannel v1.5.10/go.mod h1:p/jJfC9arb29W7WrxyKbepTU20CFgyx5oLo8Rs4Py/M= -github.com/pion/dtls/v3 v3.0.4 h1:44CZekewMzfrn9pmGrj5BNnTMDCFwr+6sLH+cCuLM7U= -github.com/pion/dtls/v3 v3.0.4/go.mod h1:R373CsjxWqNPf6MEkfdy3aSe9niZvL/JaKlGeFphtMg= +github.com/pion/dtls/v3 v3.0.6 h1:7Hkd8WhAJNbRgq9RgdNh1aaWlZlGpYTzdqjy9x9sK2E= +github.com/pion/dtls/v3 v3.0.6/go.mod h1:iJxNQ3Uhn1NZWOMWlLxEEHAN5yX7GyPvvKw04v9bzYU= +github.com/pion/ice/v4 v4.0.10 h1:P59w1iauC/wPk9PdY8Vjl4fOFL5B+USq1+xbDcN6gT4= +github.com/pion/ice/v4 v4.0.10/go.mod h1:y3M18aPhIxLlcO/4dn9X8LzLLSma84cx6emMSu14FGw= github.com/pion/interceptor v0.1.40 h1:e0BjnPcGpr2CFQgKhrQisBU7V3GXK6wrfYrGYaU6Jq4= github.com/pion/interceptor v0.1.40/go.mod h1:Z6kqH7M/FYirg3frjGJ21VLSRJGBXB/KqaTIrdqnOic= github.com/pion/logging v0.2.4 h1:tTew+7cmQ+Mc1pTBLKH2puKsOvhm32dROumOZ655zB8= @@ -169,8 +167,8 @@ github.com/pion/rtcp v1.2.15 h1:LZQi2JbdipLOj4eBjK4wlVoQWfrZbh3Q6eHtWtJBZBo= github.com/pion/rtcp v1.2.15/go.mod h1:jlGuAjHMEXwMUHK78RgX0UmEJFV4zUKOFHR7OP+D3D0= github.com/pion/rtp v1.8.21 h1:3yrOwmZFyUpcIosNcWRpQaU+UXIJ6yxLuJ8Bx0mw37Y= github.com/pion/rtp v1.8.21/go.mod h1:bAu2UFKScgzyFqvUKmbvzSdPr+NGbZtv6UB2hesqXBk= -github.com/pion/sctp v1.8.36 h1:owNudmnz1xmhfYje5L/FCav3V9wpPRePHle3Zi+P+M0= -github.com/pion/sctp v1.8.36/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= +github.com/pion/sctp v1.8.39 h1:PJma40vRHa3UTO3C4MyeJDQ+KIobVYRZQZ0Nt7SjQnE= +github.com/pion/sctp v1.8.39/go.mod h1:cNiLdchXra8fHQwmIoqw0MbLLMs+f7uQ+dGMG2gWebE= github.com/pion/sdp/v3 v3.0.15 h1:F0I1zds+K/+37ZrzdADmx2Q44OFDOPRLhPnNTaUX9hk= github.com/pion/sdp/v3 v3.0.15/go.mod h1:88GMahN5xnScv1hIMTqLdu/cOcUkj6a9ytbncwMCq2E= github.com/pion/srtp/v3 v3.0.6 h1:E2gyj1f5X10sB/qILUGIkL4C2CqK269Xq167PbGCc/4= @@ -181,6 +179,8 @@ github.com/pion/transport/v3 v3.0.7 h1:iRbMH05BzSNwhILHoBoAPxoB9xQgOaJk+591KC9P1 github.com/pion/transport/v3 v3.0.7/go.mod h1:YleKiTZ4vqNxVwh77Z0zytYi7rXHl7j6uPLGhhz9rwo= github.com/pion/turn/v4 v4.0.0 h1:qxplo3Rxa9Yg1xXDxxH8xaqcyGUtbHYw4QSCvmFWvhM= github.com/pion/turn/v4 v4.0.0/go.mod h1:MuPDkm15nYSklKpN8vWJ9W2M0PlyQZqYt1McGuxG7mA= +github.com/pion/webrtc/v4 v4.1.3 h1:YZ67Boj9X/hk190jJZ8+HFGQ6DqSZ/fYP3sLAZv7c3c= +github.com/pion/webrtc/v4 v4.1.3/go.mod h1:rsq+zQ82ryfR9vbb0L1umPJ6Ogq7zm8mcn9fcGnxomM= github.com/pjbgf/sha1cd v0.3.2 h1:a9wb0bp1oC2TGwStyn0Umc/IGKQnEgF0vVaZ8QF8eo4= github.com/pjbgf/sha1cd v0.3.2/go.mod h1:zQWigSxVmsHEZow5qaLtPYxpcKMMQpa09ixqBxuCS6A= github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= diff --git a/internal/protocols/webrtc/peer_connection.go b/internal/protocols/webrtc/peer_connection.go index 31e73766..d1a49614 100644 --- a/internal/protocols/webrtc/peer_connection.go +++ b/internal/protocols/webrtc/peer_connection.go @@ -5,8 +5,10 @@ import ( "context" "errors" "fmt" + "net" "slices" "strconv" + "strings" "sync" "sync/atomic" "time" @@ -24,6 +26,40 @@ const ( webrtcStreamID = "mediamtx" ) +func interfaceIPs(interfaceList []string) ([]string, error) { + intfs, err := net.Interfaces() + if err != nil { + return nil, err + } + + var ips []string + + for _, intf := range intfs { + if len(interfaceList) == 0 || slices.Contains(interfaceList, intf.Name) { + var addrs []net.Addr + addrs, err = intf.Addrs() + if err == nil { + for _, addr := range addrs { + var ip net.IP + + switch v := addr.(type) { + case *net.IPNet: + ip = v.IP + case *net.IPAddr: + ip = v.IP + } + + if ip != nil { + ips = append(ips, ip.String()) + } + } + } + } + } + + return ips, nil +} + // * skip ConfigureRTCPReports // * add statsInterceptor func registerInterceptors( @@ -98,7 +134,7 @@ type trackRecvPair struct { type PeerConnection struct { LocalRandomUDP bool ICEUDPMux ice.UDPMux - ICETCPMux ice.TCPMux + ICETCPMux *TCPMuxWrapper ICEServers []webrtc.ICEServer IPsFromInterfaces bool IPsFromInterfacesList []string @@ -135,31 +171,24 @@ func (co *PeerConnection) Start() error { settingsEngine.SetIncludeLoopbackCandidate(true) - settingsEngine.SetInterfaceFilter(func(iface string) bool { - return co.IPsFromInterfaces && (len(co.IPsFromInterfacesList) == 0 || - slices.Contains(co.IPsFromInterfacesList, iface)) - }) - - settingsEngine.SetAdditionalHosts(co.AdditionalHosts) - - // always enable all networks since we might be the client of a remote TCP listener - settingsEngine.SetNetworkTypes([]webrtc.NetworkType{ - webrtc.NetworkTypeUDP4, + // always enable TCP since we might be the client of a remote TCP listener + networkTypes := []webrtc.NetworkType{ webrtc.NetworkTypeTCP4, - webrtc.NetworkTypeUDP6, webrtc.NetworkTypeTCP6, - }) + } + + if co.LocalRandomUDP || co.ICEUDPMux != nil || len(co.ICEServers) != 0 { + networkTypes = append(networkTypes, webrtc.NetworkTypeUDP4, webrtc.NetworkTypeUDP6) + } + + settingsEngine.SetNetworkTypes(networkTypes) if co.ICEUDPMux != nil { settingsEngine.SetICEUDPMux(co.ICEUDPMux) } if co.ICETCPMux != nil { - settingsEngine.SetICETCPMux(co.ICETCPMux) - } - - if co.LocalRandomUDP { - settingsEngine.SetLocalRandomUDP(true) + settingsEngine.SetICETCPMux(co.ICETCPMux.Mux) } settingsEngine.SetSTUNGatherTimeout(time.Duration(co.STUNGatherTimeout)) @@ -388,19 +417,139 @@ func (co *PeerConnection) Close() { <-co.closed } +func (co *PeerConnection) removeUnwantedCandidates(firstMedia *sdp.MediaDescription) error { + var allowedIPs []string + if co.IPsFromInterfaces { + var err error + allowedIPs, err = interfaceIPs(co.IPsFromInterfacesList) + if err != nil { + return err + } + } + + var newAttributes []sdp.Attribute //nolint:prealloc + + for _, attr := range firstMedia.Attributes { + if attr.Key == "candidate" { + parts := strings.Split(attr.Value, " ") + + // hide random UDP candidates + if !co.LocalRandomUDP && co.ICEUDPMux == nil && parts[2] == "udp" && parts[7] == "host" { + continue + } + + // hide disallowed IPs + if parts[7] == "host" && !slices.Contains(allowedIPs, parts[4]) { + continue + } + } + + newAttributes = append(newAttributes, attr) + } + + firstMedia.Attributes = newAttributes + + return nil +} + +func (co *PeerConnection) addAdditionalCandidates(firstMedia *sdp.MediaDescription) error { + i := 0 + for _, attr := range firstMedia.Attributes { + if attr.Key == "end-of-candidates" { + break + } + i++ + } + + for _, host := range co.AdditionalHosts { + newAttrs := append([]sdp.Attribute(nil), firstMedia.Attributes[:i]...) + + if co.ICEUDPMux != nil { + port := strconv.FormatInt(int64(co.ICEUDPMux.GetListenAddresses()[0].(*net.UDPAddr).Port), 10) + + tmp, err := randUint32() + if err != nil { + return err + } + id := strconv.FormatInt(int64(tmp), 10) + + newAttrs = append(newAttrs, sdp.Attribute{ + Key: "candidate", + Value: id + " 1 udp 2130706431 " + host + " " + port + " typ host", + }) + newAttrs = append(newAttrs, sdp.Attribute{ + Key: "candidate", + Value: id + " 2 udp 2130706431 " + host + " " + port + " typ host", + }) + } + + if co.ICETCPMux != nil { + port := strconv.FormatInt(int64(co.ICETCPMux.Ln.Addr().(*net.TCPAddr).Port), 10) + + tmp, err := randUint32() + if err != nil { + return err + } + id := strconv.FormatInt(int64(tmp), 10) + + newAttrs = append(newAttrs, sdp.Attribute{ + Key: "candidate", + Value: id + " 1 tcp 1671430143 " + host + " " + port + " typ host tcptype passive", + }) + newAttrs = append(newAttrs, sdp.Attribute{ + Key: "candidate", + Value: id + " 2 tcp 1671430143 " + host + " " + port + " typ host tcptype passive", + }) + } + + newAttrs = append(newAttrs, firstMedia.Attributes[i:]...) + firstMedia.Attributes = newAttrs + } + + return nil +} + +func (co *PeerConnection) filterLocalDescription(desc *webrtc.SessionDescription) (*webrtc.SessionDescription, error) { + var psdp sdp.SessionDescription + psdp.Unmarshal([]byte(desc.SDP)) //nolint:errcheck + + firstMedia := psdp.MediaDescriptions[0] + + err := co.removeUnwantedCandidates(firstMedia) + if err != nil { + return nil, err + } + + err = co.addAdditionalCandidates(firstMedia) + if err != nil { + return nil, err + } + + out, _ := psdp.Marshal() + desc.SDP = string(out) + + return desc, nil +} + // CreatePartialOffer creates a partial offer. func (co *PeerConnection) CreatePartialOffer() (*webrtc.SessionDescription, error) { - offer, err := co.wr.CreateOffer(nil) + tmp, err := co.wr.CreateOffer(nil) + if err != nil { + return nil, err + } + offer := &tmp + + err = co.wr.SetLocalDescription(*offer) if err != nil { return nil, err } - err = co.wr.SetLocalDescription(offer) + offer, err = co.filterLocalDescription(offer) if err != nil { return nil, err } - return &offer, nil + return offer, nil } // SetAnswer sets the answer. @@ -423,15 +572,16 @@ func (co *PeerConnection) CreateFullAnswer( return nil, err } - answer, err := co.wr.CreateAnswer(nil) + tmp, err := co.wr.CreateAnswer(nil) if err != nil { if errors.Is(err, webrtc.ErrSenderWithNoCodecs) { return nil, fmt.Errorf("codecs not supported by client") } return nil, err } + answer := &tmp - err = co.wr.SetLocalDescription(answer) + err = co.wr.SetLocalDescription(*answer) if err != nil { return nil, err } @@ -441,7 +591,14 @@ func (co *PeerConnection) CreateFullAnswer( return nil, err } - return co.wr.LocalDescription(), nil + answer = co.wr.LocalDescription() + + answer, err = co.filterLocalDescription(answer) + if err != nil { + return nil, err + } + + return answer, nil } func (co *PeerConnection) waitGatheringDone(ctx context.Context) error { diff --git a/internal/protocols/webrtc/peer_connection_test.go b/internal/protocols/webrtc/peer_connection_test.go index 005c6bdb..65de7c8d 100644 --- a/internal/protocols/webrtc/peer_connection_test.go +++ b/internal/protocols/webrtc/peer_connection_test.go @@ -3,6 +3,7 @@ package webrtc import ( "context" "net" + "regexp" "sort" "strings" "testing" @@ -60,12 +61,59 @@ func TestPeerConnectionCloseImmediately(t *testing.T) { func TestPeerConnectionCandidates(t *testing.T) { for _, ca := range []string{ + "udp random", "udp", + "tcp", "stun", "udp+stun", + "udp random+stun", } { t.Run(ca, func(t *testing.T) { + pc2, err := webrtc.NewPeerConnection(webrtc.Configuration{}) + require.NoError(t, err) + defer pc2.Close() //nolint:errcheck + + track, err := webrtc.NewTrackLocalStaticRTP( + webrtc.RTPCodecCapability{ + MimeType: webrtc.MimeTypeVP8, + ClockRate: 90000, + }, + "video", + "publisher", + ) + require.NoError(t, err) + + _, err = pc2.AddTrack(track) + require.NoError(t, err) + + offer, err := pc2.CreateOffer(nil) + require.NoError(t, err) + + var udpMux ice.UDPMux + if ca == "udp" || ca == "udp+stun" { + var ln net.PacketConn + ln, err = net.ListenPacket("udp", ":3454") + require.NoError(t, err) + defer ln.Close() + udpMux = webrtc.NewICEUDPMux(webrtcNilLogger, ln) + } + + var tcpMux *TCPMuxWrapper + if ca == "tcp" { + var ln net.Listener + ln, err = net.Listen("tcp", ":3454") + require.NoError(t, err) + defer ln.Close() + tcpMux = &TCPMuxWrapper{ + Mux: webrtc.NewICETCPMux(webrtcNilLogger, ln, 8), + Ln: ln, + } + } + pc := &PeerConnection{ + LocalRandomUDP: (ca == "udp random" || ca == "udp random+stun"), + ICEUDPMux: udpMux, + ICETCPMux: tcpMux, IPsFromInterfaces: true, IPsFromInterfacesList: []string{"lo"}, HandshakeTimeout: conf.Duration(10 * time.Second), @@ -73,33 +121,38 @@ func TestPeerConnectionCandidates(t *testing.T) { Log: test.NilLogger, } - if ca == "udp" || ca == "udp+stun" { - pc.LocalRandomUDP = true - } - if ca == "stun" || ca == "udp+stun" { + if ca == "stun" || ca == "udp+stun" || ca == "udp random+stun" { pc.ICEServers = []webrtc.ICEServer{{ URLs: []string{"stun:stun.l.google.com:19302"}, }} } - err := pc.Start() + err = pc.Start() require.NoError(t, err) defer pc.Close() - _, err = pc.CreatePartialOffer() + answer, err := pc.CreateFullAnswer(context.Background(), &offer) require.NoError(t, err) - // convert partial offer into full offer - err = pc.waitGatheringDone(context.Background()) - require.NoError(t, err) - - offer := pc.wr.LocalDescription() - - if ca == "udp" || ca == "udp+stun" { - require.Equal(t, 2, strings.Count(offer.SDP, "typ host")) + n := len(regexp.MustCompile("(?m)^a=candidate:.+? udp .+? typ host").FindAllString(answer.SDP, -1)) + if ca == "udp" || ca == "udp random" || ca == "udp+stun" || ca == "udp random+stun" { + require.Equal(t, 2, n) + } else { + require.Equal(t, 0, n) } - if ca == "stun" || ca == "udp+stun" { - require.Equal(t, 2, strings.Count(offer.SDP, "typ srflx")) + + n = len(regexp.MustCompile("(?m)^a=candidate:.+? tcp .+? typ host tcptype passive").FindAllString(answer.SDP, -1)) + if ca == "tcp" { + require.Equal(t, 2, n) + } else { + require.Equal(t, 0, n) + } + + n = len(regexp.MustCompile("(?m)^a=candidate:.+? udp .+? typ srflx").FindAllString(answer.SDP, -1)) + if ca == "stun" || ca == "udp+stun" || ca == "udp random+stun" { + require.Equal(t, 2, n) + } else { + require.Equal(t, 0, n) } }) } @@ -117,7 +170,7 @@ func TestPeerConnectionConnectivity(t *testing.T) { "additional hosts", } { // LocalRandomUDP doesn't work with AdditionalHosts - // we do not care since currently we are not using them together + // we don't care since we are not currently using them together if mode == "active udp" && ip == "additional hosts" { continue } @@ -145,7 +198,7 @@ func TestPeerConnectionConnectivity(t *testing.T) { defer clientPC.Close() var udpMux ice.UDPMux - var tcpMux ice.TCPMux + var tcpMux *TCPMuxWrapper switch mode { case "passive udp": @@ -160,7 +213,10 @@ func TestPeerConnectionConnectivity(t *testing.T) { ln, err = net.Listen("tcp4", ":4458") require.NoError(t, err) defer ln.Close() - tcpMux = webrtc.NewICETCPMux(webrtcNilLogger, ln, 8) + tcpMux = &TCPMuxWrapper{ + Mux: webrtc.NewICETCPMux(webrtcNilLogger, ln, 8), + Ln: ln, + } } serverPC := &PeerConnection{ @@ -184,21 +240,17 @@ func TestPeerConnectionConnectivity(t *testing.T) { serverPC.IPsFromInterfaces = true serverPC.IPsFromInterfacesList = []string{"lo"} } else { - serverPC.AdditionalHosts = []string{"127.0.0.2"} + serverPC.AdditionalHosts = []string{"127.0.0.1"} } err = serverPC.Start() require.NoError(t, err) defer serverPC.Close() - _, err = clientPC.CreatePartialOffer() + offer, err := clientPC.CreatePartialOffer() require.NoError(t, err) - // convert partial offer into full offer - err = clientPC.waitGatheringDone(context.Background()) - require.NoError(t, err) - - answer, err := serverPC.CreateFullAnswer(context.Background(), clientPC.wr.LocalDescription()) + answer, err := serverPC.CreateFullAnswer(context.Background(), offer) require.NoError(t, err) require.Equal(t, 2, strings.Count(answer.SDP, "a=candidate:")) @@ -206,43 +258,28 @@ func TestPeerConnectionConnectivity(t *testing.T) { err = clientPC.SetAnswer(answer) require.NoError(t, err) + go func() { + for { + select { + case cd := <-clientPC.NewLocalCandidate(): + err2 := serverPC.AddRemoteCandidate(cd) + require.NoError(t, err2) + + case <-clientPC.Failed(): + return + } + } + }() + err = serverPC.WaitUntilConnected(context.Background()) require.NoError(t, err) - - switch mode { - case "passive udp": - if ip == "from interfaces" { - require.Regexp(t, "^host/udp/127\\.0\\.0\\.1/4458$", serverPC.LocalCandidate()) - } else { - require.Regexp(t, "^host/udp/127\\.0\\.0\\.2/4458$", serverPC.LocalCandidate()) - } - - case "passive tcp": - if ip == "from interfaces" { - require.Regexp(t, "^host/tcp/127\\.0\\.0\\.1/4458$", serverPC.LocalCandidate()) - } else { - require.Regexp(t, "^host/tcp/127\\.0\\.0\\.2/4458$", serverPC.LocalCandidate()) - } - - case "active udp": - require.Regexp(t, "^host/udp/127\\.0\\.0\\.1", serverPC.LocalCandidate()) - - case "active udp + stun": - require.Regexp(t, "^srflx/udp/", serverPC.LocalCandidate()) - } }) } } } func TestPeerConnectionRead(t *testing.T) { - settingsEngine := webrtc.SettingEngine{} - settingsEngine.SetLocalRandomUDP(true) - - api := webrtc.NewAPI( - webrtc.WithSettingEngine(settingsEngine)) - - pub, err := api.NewPeerConnection(webrtc.Configuration{}) + pub, err := webrtc.NewPeerConnection(webrtc.Configuration{}) require.NoError(t, err) defer pub.Close() //nolint:errcheck diff --git a/internal/protocols/webrtc/tcp_mux_wrapper.go b/internal/protocols/webrtc/tcp_mux_wrapper.go new file mode 100644 index 00000000..df8d6452 --- /dev/null +++ b/internal/protocols/webrtc/tcp_mux_wrapper.go @@ -0,0 +1,13 @@ +package webrtc + +import ( + "net" + + "github.com/pion/ice/v4" +) + +// TCPMuxWrapper is a wrapper around ice.TCPMux. +type TCPMuxWrapper struct { + Mux ice.TCPMux + Ln net.Listener +} diff --git a/internal/servers/webrtc/server.go b/internal/servers/webrtc/server.go index 090851d4..928640c4 100644 --- a/internal/servers/webrtc/server.go +++ b/internal/servers/webrtc/server.go @@ -26,6 +26,7 @@ import ( "github.com/bluenviron/mediamtx/internal/defs" "github.com/bluenviron/mediamtx/internal/externalcmd" "github.com/bluenviron/mediamtx/internal/logger" + "github.com/bluenviron/mediamtx/internal/protocols/webrtc" "github.com/bluenviron/mediamtx/internal/restrictnetwork" "github.com/bluenviron/mediamtx/internal/stream" ) @@ -211,7 +212,7 @@ type Server struct { udpMuxLn net.PacketConn tcpMuxLn net.Listener iceUDPMux ice.UDPMux - iceTCPMux ice.TCPMux + iceTCPMux *webrtc.TCPMuxWrapper sessions map[*session]struct{} sessionsBySecret map[uuid.UUID]*session @@ -282,7 +283,10 @@ func (s *Server) Initialize() error { ctxCancel() return err } - s.iceTCPMux = pwebrtc.NewICETCPMux(webrtcNilLogger, s.tcpMuxLn, 8) + s.iceTCPMux = &webrtc.TCPMuxWrapper{ + Mux: pwebrtc.NewICETCPMux(webrtcNilLogger, s.tcpMuxLn, 8), + Ln: s.tcpMuxLn, + } } str := "listener opened on " + s.Address + " (HTTP)" diff --git a/internal/servers/webrtc/session.go b/internal/servers/webrtc/session.go index df3e9a29..12fd6217 100644 --- a/internal/servers/webrtc/session.go +++ b/internal/servers/webrtc/session.go @@ -46,7 +46,7 @@ type session struct { ipsFromInterfacesList []string additionalHosts []string iceUDPMux ice.UDPMux - iceTCPMux ice.TCPMux + iceTCPMux *webrtc.TCPMuxWrapper handshakeTimeout conf.Duration trackGatherTimeout conf.Duration stunGatherTimeout conf.Duration