diff --git a/apidocs/openapi.yaml b/apidocs/openapi.yaml index dbfe4047..3b676e0f 100644 --- a/apidocs/openapi.yaml +++ b/apidocs/openapi.yaml @@ -151,6 +151,8 @@ components: type: array items: type: string + webrtcICEUDPMuxAddress: + type: string webrtcICETCPMuxAddress: type: string diff --git a/internal/conf/conf.go b/internal/conf/conf.go index 1674dcde..faec7de4 100644 --- a/internal/conf/conf.go +++ b/internal/conf/conf.go @@ -233,6 +233,7 @@ type Conf struct { WebRTCTrustedProxies IPsOrCIDRs `json:"webrtcTrustedProxies"` WebRTCICEServers []string `json:"webrtcICEServers"` WebRTCICEHostNAT1To1IPs []string `json:"webrtcICEHostNAT1To1IPs"` + WebRTCICEUDPMuxAddress string `json:"webrtcICEUDPMuxAddress"` WebRTCICETCPMuxAddress string `json:"webrtcICETCPMuxAddress"` // paths diff --git a/internal/core/core.go b/internal/core/core.go index 21dfdd25..11f1109b 100644 --- a/internal/core/core.go +++ b/internal/core/core.go @@ -414,6 +414,7 @@ func (p *Core) createResources(initial bool) error { p.metrics, p, p.conf.WebRTCICEHostNAT1To1IPs, + p.conf.WebRTCICEUDPMuxAddress, p.conf.WebRTCICETCPMuxAddress, ) if err != nil { @@ -578,6 +579,7 @@ func (p *Core) closeResources(newConf *conf.Conf, calledByAPI bool) { closeMetrics || closePathManager || !reflect.DeepEqual(newConf.WebRTCICEHostNAT1To1IPs, p.conf.WebRTCICEHostNAT1To1IPs) || + newConf.WebRTCICEUDPMuxAddress != p.conf.WebRTCICEUDPMuxAddress || newConf.WebRTCICETCPMuxAddress != p.conf.WebRTCICETCPMuxAddress closeAPI := newConf == nil || diff --git a/internal/core/webrtc_conn.go b/internal/core/webrtc_conn.go index 188f0975..fe6b65b7 100644 --- a/internal/core/webrtc_conn.go +++ b/internal/core/webrtc_conn.go @@ -126,6 +126,7 @@ type webRTCConn struct { wg *sync.WaitGroup pathManager webRTCConnPathManager parent webRTCConnParent + iceUDPMux ice.UDPMux iceTCPMux ice.TCPMux iceHostNAT1To1IPs []string @@ -146,8 +147,9 @@ func newWebRTCConn( wg *sync.WaitGroup, pathManager webRTCConnPathManager, parent webRTCConnParent, - iceTCPMux ice.TCPMux, iceHostNAT1To1IPs []string, + iceUDPMux ice.UDPMux, + iceTCPMux ice.TCPMux, ) *webRTCConn { ctx, ctxCancel := context.WithCancel(parentCtx) @@ -163,6 +165,7 @@ func newWebRTCConn( ctxCancel: ctxCancel, uuid: uuid.New(), created: time.Now(), + iceUDPMux: iceUDPMux, iceTCPMux: iceTCPMux, iceHostNAT1To1IPs: iceHostNAT1To1IPs, } @@ -289,15 +292,19 @@ func (c *webRTCConn) runInner(ctx context.Context) error { configuration := webrtc.Configuration{ICEServers: c.genICEServers()} settingsEngine := webrtc.SettingEngine{} + if len(c.iceHostNAT1To1IPs) != 0 { + settingsEngine.SetNAT1To1IPs(c.iceHostNAT1To1IPs, webrtc.ICECandidateTypeHost) + } + + if c.iceUDPMux != nil { + settingsEngine.SetICEUDPMux(c.iceUDPMux) + } + if c.iceTCPMux != nil { settingsEngine.SetICETCPMux(c.iceTCPMux) settingsEngine.SetNetworkTypes([]webrtc.NetworkType{webrtc.NetworkTypeTCP4}) } - if len(c.iceHostNAT1To1IPs) != 0 { - settingsEngine.SetNAT1To1IPs(c.iceHostNAT1To1IPs, webrtc.ICECandidateTypeHost) - } - pc, err := newPeerConnection(configuration, webrtc.WithSettingEngine(settingsEngine)) if err != nil { return err diff --git a/internal/core/webrtc_server.go b/internal/core/webrtc_server.go index e1eaa526..177fe2da 100644 --- a/internal/core/webrtc_server.go +++ b/internal/core/webrtc_server.go @@ -79,16 +79,17 @@ type webRTCServer struct { metrics *metrics parent webRTCServerParent - ctx context.Context - ctxCancel func() - wg sync.WaitGroup - ln net.Listener - tcpMuxLn net.Listener - tlsConfig *tls.Config - conns map[*webRTCConn]struct{} - - iceTCPMux ice.TCPMux + ctx context.Context + ctxCancel func() + wg sync.WaitGroup + ln net.Listener + udpMuxLn net.PacketConn + tcpMuxLn net.Listener + tlsConfig *tls.Config + conns map[*webRTCConn]struct{} iceHostNAT1To1IPs []string + iceUDPMux ice.UDPMux + iceTCPMux ice.TCPMux // in connNew chan webRTCConnNewReq @@ -112,6 +113,7 @@ func newWebRTCServer( metrics *metrics, parent webRTCServerParent, iceHostNAT1To1IPs []string, + iceUDPMuxAddress string, iceTCPMuxAddress string, ) (*webRTCServer, error) { ln, err := net.Listen("tcp", address) @@ -132,6 +134,16 @@ func newWebRTCServer( } } + var iceUDPMux ice.UDPMux + var udpMuxLn net.PacketConn + if iceUDPMuxAddress != "" { + udpMuxLn, err = net.ListenPacket("udp", iceUDPMuxAddress) + if err != nil { + return nil, err + } + iceUDPMux = webrtc.NewICEUDPMux(nil, udpMuxLn) + } + var iceTCPMux ice.TCPMux var tcpMuxLn net.Listener if iceTCPMuxAddress != "" { @@ -139,7 +151,6 @@ func newWebRTCServer( if err != nil { return nil, err } - iceTCPMux = webrtc.NewICETCPMux(nil, tcpMuxLn, 8) } @@ -157,8 +168,10 @@ func newWebRTCServer( ctx: ctx, ctxCancel: ctxCancel, ln: ln, + udpMuxLn: udpMuxLn, tcpMuxLn: tcpMuxLn, tlsConfig: tlsConfig, + iceUDPMux: iceUDPMux, iceTCPMux: iceTCPMux, iceHostNAT1To1IPs: iceHostNAT1To1IPs, conns: make(map[*webRTCConn]struct{}), @@ -168,11 +181,14 @@ func newWebRTCServer( chAPIConnsKick: make(chan webRTCServerAPIConnsKickReq), } - s.log(logger.Info, "listener opened on "+address) - + str := "listener opened on " + address + " (HTTP)" + if udpMuxLn != nil { + str += ", " + iceUDPMuxAddress + " (ICE/UDP)" + } if tcpMuxLn != nil { - s.log(logger.Info, "ice mux tcp listener opened on "+iceTCPMuxAddress) + str += ", " + iceTCPMuxAddress + " (ICE/TCP)" } + s.log(logger.Info, str) if s.metrics != nil { s.metrics.webRTCServerSet(s) @@ -232,8 +248,9 @@ outer: &s.wg, s.pathManager, s, - s.iceTCPMux, s.iceHostNAT1To1IPs, + s.iceUDPMux, + s.iceTCPMux, ) s.conns[c] = struct{}{} @@ -281,12 +298,15 @@ outer: s.ctxCancel() hs.Shutdown(context.Background()) + s.ln.Close() // in case Shutdown() is called before Serve() + + if s.udpMuxLn != nil { + s.udpMuxLn.Close() + } if s.tcpMuxLn != nil { s.tcpMuxLn.Close() } - - s.ln.Close() // in case Shutdown() is called before Serve() } func (s *webRTCServer) onRequest(ctx *gin.Context) { diff --git a/rtsp-simple-server.yml b/rtsp-simple-server.yml index c6c9b703..5ec6e795 100644 --- a/rtsp-simple-server.yml +++ b/rtsp-simple-server.yml @@ -199,9 +199,15 @@ webrtcICEServers: [stun:stun.l.google.com:19302] # List of public IP addresses that are to be used as a host. # This is used typically for servers that are behind 1:1 D-NAT. webrtcICEHostNAT1To1IPs: [] +# Address of a ICE UDP listener in format host:port. +# If filled, ICE traffic will come through a single UDP port, +# allowing the deployment of the server inside a container or behind a NAT. +webrtcICEUDPMuxAddress: # Address of a ICE TCP listener in format host:port. # If filled, ICE traffic will come through a single TCP port, # allowing the deployment of the server inside a container or behind a NAT. +# At the moment, setting this parameter forces usage of the TCP protocol, +# which is not optimal for WebRTC. webrtcICETCPMuxAddress: ###############################################