mirror of
https://github.com/aler9/rtsp-simple-server
synced 2025-09-26 19:51:26 +08:00
remove custom forks of pion/webrtc and pion/ice (#4861)
this fixes IPv6 reliability issues and allows to receive upstream updates in a more linear way.
This commit is contained in:
12
go.mod
12
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
|
||||
|
16
go.sum
16
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=
|
||||
|
@@ -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 {
|
||||
|
@@ -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
|
||||
|
||||
|
13
internal/protocols/webrtc/tcp_mux_wrapper.go
Normal file
13
internal/protocols/webrtc/tcp_mux_wrapper.go
Normal file
@@ -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
|
||||
}
|
@@ -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)"
|
||||
|
@@ -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
|
||||
|
Reference in New Issue
Block a user