2023-05-16 13:59:37 +00:00
|
|
|
package core
|
|
|
|
|
|
|
|
import (
|
|
|
|
"context"
|
|
|
|
"crypto/hmac"
|
2023-05-28 15:10:40 +00:00
|
|
|
"crypto/rand"
|
2023-05-16 13:59:37 +00:00
|
|
|
"crypto/sha1"
|
|
|
|
"encoding/base64"
|
|
|
|
"fmt"
|
|
|
|
"net"
|
2023-05-16 16:01:05 +00:00
|
|
|
"net/http"
|
2023-05-18 18:17:04 +00:00
|
|
|
"sort"
|
2023-05-16 13:59:37 +00:00
|
|
|
"strconv"
|
|
|
|
"sync"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
"github.com/google/uuid"
|
2023-11-12 12:46:25 +00:00
|
|
|
"github.com/pion/logging"
|
2023-10-25 09:48:57 +00:00
|
|
|
pwebrtc "github.com/pion/webrtc/v3"
|
2023-05-16 13:59:37 +00:00
|
|
|
|
2023-05-16 14:14:20 +00:00
|
|
|
"github.com/bluenviron/mediamtx/internal/conf"
|
2023-10-31 13:19:04 +00:00
|
|
|
"github.com/bluenviron/mediamtx/internal/defs"
|
2023-09-16 17:21:48 +00:00
|
|
|
"github.com/bluenviron/mediamtx/internal/externalcmd"
|
2023-05-16 14:14:20 +00:00
|
|
|
"github.com/bluenviron/mediamtx/internal/logger"
|
2023-10-26 19:46:18 +00:00
|
|
|
"github.com/bluenviron/mediamtx/internal/protocols/webrtc"
|
2023-10-31 13:19:04 +00:00
|
|
|
"github.com/bluenviron/mediamtx/internal/restrictnetwork"
|
2023-05-16 13:59:37 +00:00
|
|
|
)
|
|
|
|
|
2023-06-30 14:47:10 +00:00
|
|
|
const (
|
2023-07-23 18:18:58 +00:00
|
|
|
webrtcPauseAfterAuthError = 2 * time.Second
|
2023-06-30 14:47:10 +00:00
|
|
|
webrtcTurnSecretExpiration = 24 * 3600 * time.Second
|
2023-10-25 09:48:57 +00:00
|
|
|
webrtcPayloadMaxSize = 1188 // 1200 - 12 (RTP header)
|
2023-06-30 14:47:10 +00:00
|
|
|
)
|
2023-05-16 13:59:37 +00:00
|
|
|
|
2023-11-12 12:46:25 +00:00
|
|
|
type nilWriter struct{}
|
|
|
|
|
|
|
|
func (nilWriter) Write(p []byte) (int, error) {
|
|
|
|
return len(p), nil
|
|
|
|
}
|
|
|
|
|
|
|
|
var webrtcNilLogger = logging.NewDefaultLeveledLoggerForScope("", 0, &nilWriter{})
|
|
|
|
|
2023-07-30 20:30:41 +00:00
|
|
|
func randInt63() (int64, error) {
|
2023-05-28 15:10:40 +00:00
|
|
|
var b [8]byte
|
2023-07-30 20:30:41 +00:00
|
|
|
_, err := rand.Read(b[:])
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
2023-05-28 15:10:40 +00:00
|
|
|
return int64(uint64(b[0]&0b01111111)<<56 | uint64(b[1])<<48 | uint64(b[2])<<40 | uint64(b[3])<<32 |
|
2023-07-30 20:30:41 +00:00
|
|
|
uint64(b[4])<<24 | uint64(b[5])<<16 | uint64(b[6])<<8 | uint64(b[7])), nil
|
2023-05-28 15:10:40 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// https://cs.opensource.google/go/go/+/refs/tags/go1.20.4:src/math/rand/rand.go;l=119
|
2023-07-30 20:30:41 +00:00
|
|
|
func randInt63n(n int64) (int64, error) {
|
2023-05-28 15:10:40 +00:00
|
|
|
if n&(n-1) == 0 { // n is power of two, can mask
|
2023-07-30 20:30:41 +00:00
|
|
|
r, err := randInt63()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
return r & (n - 1), nil
|
2023-05-28 15:10:40 +00:00
|
|
|
}
|
2023-07-30 20:30:41 +00:00
|
|
|
|
2023-05-28 15:10:40 +00:00
|
|
|
max := int64((1 << 63) - 1 - (1<<63)%uint64(n))
|
2023-07-30 20:30:41 +00:00
|
|
|
|
|
|
|
v, err := randInt63()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
|
|
|
|
2023-05-28 15:10:40 +00:00
|
|
|
for v > max {
|
2023-07-30 20:30:41 +00:00
|
|
|
v, err = randInt63()
|
|
|
|
if err != nil {
|
|
|
|
return 0, err
|
|
|
|
}
|
2023-05-28 15:10:40 +00:00
|
|
|
}
|
2023-07-30 20:30:41 +00:00
|
|
|
|
|
|
|
return v % n, nil
|
2023-05-28 15:10:40 +00:00
|
|
|
}
|
|
|
|
|
2023-07-30 20:30:41 +00:00
|
|
|
func randomTurnUser() (string, error) {
|
2023-05-28 15:10:40 +00:00
|
|
|
const charset = "abcdefghijklmnopqrstuvwxyz1234567890"
|
|
|
|
b := make([]byte, 20)
|
|
|
|
for i := range b {
|
2023-07-30 20:30:41 +00:00
|
|
|
j, err := randInt63n(int64(len(charset)))
|
|
|
|
if err != nil {
|
|
|
|
return "", err
|
|
|
|
}
|
|
|
|
|
|
|
|
b[i] = charset[int(j)]
|
2023-05-28 15:10:40 +00:00
|
|
|
}
|
2023-07-30 20:30:41 +00:00
|
|
|
|
|
|
|
return string(b), nil
|
2023-05-28 15:10:40 +00:00
|
|
|
}
|
|
|
|
|
2023-05-16 13:59:37 +00:00
|
|
|
type webRTCManagerAPISessionsListRes struct {
|
2023-10-31 13:19:04 +00:00
|
|
|
data *defs.APIWebRTCSessionList
|
2023-05-16 13:59:37 +00:00
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
type webRTCManagerAPISessionsListReq struct {
|
|
|
|
res chan webRTCManagerAPISessionsListRes
|
|
|
|
}
|
|
|
|
|
2023-05-18 13:07:47 +00:00
|
|
|
type webRTCManagerAPISessionsGetRes struct {
|
2023-10-31 13:19:04 +00:00
|
|
|
data *defs.APIWebRTCSession
|
2023-05-18 13:07:47 +00:00
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
type webRTCManagerAPISessionsGetReq struct {
|
|
|
|
uuid uuid.UUID
|
|
|
|
res chan webRTCManagerAPISessionsGetRes
|
|
|
|
}
|
|
|
|
|
2023-07-31 19:20:09 +00:00
|
|
|
type webRTCManagerAPISessionsKickRes struct {
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
type webRTCManagerAPISessionsKickReq struct {
|
|
|
|
uuid uuid.UUID
|
|
|
|
res chan webRTCManagerAPISessionsKickRes
|
|
|
|
}
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
type webRTCNewSessionRes struct {
|
2023-05-16 16:01:05 +00:00
|
|
|
sx *webRTCSession
|
|
|
|
answer []byte
|
|
|
|
errStatusCode int
|
2023-10-28 12:08:34 +00:00
|
|
|
err error
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
type webRTCNewSessionReq struct {
|
2023-06-27 20:37:06 +00:00
|
|
|
pathName string
|
|
|
|
remoteAddr string
|
2023-07-23 17:31:34 +00:00
|
|
|
query string
|
|
|
|
user string
|
|
|
|
pass string
|
2023-06-27 20:37:06 +00:00
|
|
|
offer []byte
|
|
|
|
publish bool
|
2023-07-30 21:53:39 +00:00
|
|
|
res chan webRTCNewSessionRes
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
type webRTCAddSessionCandidatesRes struct {
|
2023-05-16 13:59:37 +00:00
|
|
|
sx *webRTCSession
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
type webRTCAddSessionCandidatesReq struct {
|
2023-05-16 13:59:37 +00:00
|
|
|
secret uuid.UUID
|
2023-10-25 09:48:57 +00:00
|
|
|
candidates []*pwebrtc.ICECandidateInit
|
2023-07-30 21:53:39 +00:00
|
|
|
res chan webRTCAddSessionCandidatesRes
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
2023-10-24 14:30:44 +00:00
|
|
|
type webRTCDeleteSessionRes struct {
|
|
|
|
err error
|
|
|
|
}
|
|
|
|
|
|
|
|
type webRTCDeleteSessionReq struct {
|
|
|
|
secret uuid.UUID
|
|
|
|
res chan webRTCDeleteSessionRes
|
|
|
|
}
|
|
|
|
|
2023-05-16 13:59:37 +00:00
|
|
|
type webRTCManagerParent interface {
|
|
|
|
logger.Writer
|
|
|
|
}
|
|
|
|
|
|
|
|
type webRTCManager struct {
|
2023-11-12 22:55:28 +00:00
|
|
|
Address string
|
|
|
|
Encryption bool
|
|
|
|
ServerKey string
|
|
|
|
ServerCert string
|
|
|
|
AllowOrigin string
|
|
|
|
TrustedProxies conf.IPsOrCIDRs
|
|
|
|
ReadTimeout conf.StringDuration
|
|
|
|
WriteQueueSize int
|
|
|
|
LocalUDPAddress string
|
|
|
|
LocalTCPAddress string
|
|
|
|
IPsFromInterfaces bool
|
|
|
|
IPsFromInterfacesList []string
|
|
|
|
AdditionalHosts []string
|
|
|
|
ICEServers []conf.WebRTCICEServer
|
|
|
|
ExternalCmdPool *externalcmd.Pool
|
|
|
|
PathManager *pathManager
|
|
|
|
Parent webRTCManagerParent
|
2023-05-16 13:59:37 +00:00
|
|
|
|
2023-08-03 21:12:05 +00:00
|
|
|
ctx context.Context
|
|
|
|
ctxCancel func()
|
|
|
|
httpServer *webRTCHTTPServer
|
|
|
|
udpMuxLn net.PacketConn
|
|
|
|
tcpMuxLn net.Listener
|
2023-10-25 09:48:57 +00:00
|
|
|
api *pwebrtc.API
|
2023-08-03 21:12:05 +00:00
|
|
|
sessions map[*webRTCSession]struct{}
|
|
|
|
sessionsBySecret map[uuid.UUID]*webRTCSession
|
2023-05-16 13:59:37 +00:00
|
|
|
|
|
|
|
// in
|
2023-07-30 21:53:39 +00:00
|
|
|
chNewSession chan webRTCNewSessionReq
|
|
|
|
chCloseSession chan *webRTCSession
|
|
|
|
chAddSessionCandidates chan webRTCAddSessionCandidatesReq
|
2023-10-24 14:30:44 +00:00
|
|
|
chDeleteSession chan webRTCDeleteSessionReq
|
2023-05-16 13:59:37 +00:00
|
|
|
chAPISessionsList chan webRTCManagerAPISessionsListReq
|
2023-05-18 18:17:04 +00:00
|
|
|
chAPISessionsGet chan webRTCManagerAPISessionsGetReq
|
2023-05-16 13:59:37 +00:00
|
|
|
chAPIConnsKick chan webRTCManagerAPISessionsKickReq
|
|
|
|
|
|
|
|
// out
|
|
|
|
done chan struct{}
|
|
|
|
}
|
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
func (m *webRTCManager) initialize() error {
|
2023-07-16 22:33:34 +00:00
|
|
|
ctx, ctxCancel := context.WithCancel(context.Background())
|
2023-05-16 13:59:37 +00:00
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
m.ctx = ctx
|
|
|
|
m.ctxCancel = ctxCancel
|
|
|
|
m.sessions = make(map[*webRTCSession]struct{})
|
|
|
|
m.sessionsBySecret = make(map[uuid.UUID]*webRTCSession)
|
|
|
|
m.chNewSession = make(chan webRTCNewSessionReq)
|
|
|
|
m.chCloseSession = make(chan *webRTCSession)
|
|
|
|
m.chAddSessionCandidates = make(chan webRTCAddSessionCandidatesReq)
|
|
|
|
m.chDeleteSession = make(chan webRTCDeleteSessionReq)
|
|
|
|
m.chAPISessionsList = make(chan webRTCManagerAPISessionsListReq)
|
|
|
|
m.chAPISessionsGet = make(chan webRTCManagerAPISessionsGetReq)
|
|
|
|
m.chAPIConnsKick = make(chan webRTCManagerAPISessionsKickReq)
|
|
|
|
m.done = make(chan struct{})
|
2023-05-16 13:59:37 +00:00
|
|
|
|
|
|
|
var err error
|
|
|
|
m.httpServer, err = newWebRTCHTTPServer(
|
2023-11-12 22:55:28 +00:00
|
|
|
m.Address,
|
|
|
|
m.Encryption,
|
|
|
|
m.ServerKey,
|
|
|
|
m.ServerCert,
|
|
|
|
m.AllowOrigin,
|
|
|
|
m.TrustedProxies,
|
|
|
|
m.ReadTimeout,
|
|
|
|
m.PathManager,
|
2023-05-16 13:59:37 +00:00
|
|
|
m,
|
|
|
|
)
|
|
|
|
if err != nil {
|
|
|
|
ctxCancel()
|
2023-11-12 22:55:28 +00:00
|
|
|
return err
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
apiConf := webrtc.APIConf{
|
|
|
|
LocalRandomUDP: false,
|
|
|
|
IPsFromInterfaces: m.IPsFromInterfaces,
|
|
|
|
IPsFromInterfacesList: m.IPsFromInterfacesList,
|
|
|
|
AdditionalHosts: m.AdditionalHosts,
|
|
|
|
}
|
2023-08-03 21:12:05 +00:00
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
if m.LocalUDPAddress != "" {
|
|
|
|
m.udpMuxLn, err = net.ListenPacket(restrictnetwork.Restrict("udp", m.LocalUDPAddress))
|
2023-05-16 13:59:37 +00:00
|
|
|
if err != nil {
|
|
|
|
m.httpServer.close()
|
|
|
|
ctxCancel()
|
2023-11-12 22:55:28 +00:00
|
|
|
return err
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
2023-11-12 22:55:28 +00:00
|
|
|
apiConf.ICEUDPMux = pwebrtc.NewICEUDPMux(webrtcNilLogger, m.udpMuxLn)
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
if m.LocalTCPAddress != "" {
|
|
|
|
m.tcpMuxLn, err = net.Listen(restrictnetwork.Restrict("tcp", m.LocalTCPAddress))
|
2023-05-16 13:59:37 +00:00
|
|
|
if err != nil {
|
|
|
|
m.udpMuxLn.Close()
|
|
|
|
m.httpServer.close()
|
|
|
|
ctxCancel()
|
2023-11-12 22:55:28 +00:00
|
|
|
return err
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
2023-11-12 22:55:28 +00:00
|
|
|
apiConf.ICETCPMux = pwebrtc.NewICETCPMux(webrtcNilLogger, m.tcpMuxLn, 8)
|
2023-08-03 21:12:05 +00:00
|
|
|
}
|
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
m.api, err = webrtc.NewAPI(apiConf)
|
2023-08-03 21:12:05 +00:00
|
|
|
if err != nil {
|
|
|
|
m.udpMuxLn.Close()
|
|
|
|
m.tcpMuxLn.Close()
|
|
|
|
m.httpServer.close()
|
|
|
|
ctxCancel()
|
2023-11-12 22:55:28 +00:00
|
|
|
return err
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
str := "listener opened on " + m.Address + " (HTTP)"
|
2023-05-16 13:59:37 +00:00
|
|
|
if m.udpMuxLn != nil {
|
2023-11-12 22:55:28 +00:00
|
|
|
str += ", " + m.LocalUDPAddress + " (ICE/UDP)"
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
if m.tcpMuxLn != nil {
|
2023-11-12 22:55:28 +00:00
|
|
|
str += ", " + m.LocalTCPAddress + " (ICE/TCP)"
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
m.Log(logger.Info, str)
|
|
|
|
|
|
|
|
go m.run()
|
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
return nil
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
// Log is the main logging function.
|
|
|
|
func (m *webRTCManager) Log(level logger.Level, format string, args ...interface{}) {
|
2023-11-12 22:55:28 +00:00
|
|
|
m.Parent.Log(level, "[WebRTC] "+format, args...)
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
func (m *webRTCManager) close() {
|
|
|
|
m.Log(logger.Info, "listener is closing")
|
|
|
|
m.ctxCancel()
|
|
|
|
<-m.done
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *webRTCManager) run() {
|
|
|
|
defer close(m.done)
|
|
|
|
|
|
|
|
var wg sync.WaitGroup
|
|
|
|
|
|
|
|
outer:
|
|
|
|
for {
|
|
|
|
select {
|
2023-07-30 21:53:39 +00:00
|
|
|
case req := <-m.chNewSession:
|
2023-05-16 13:59:37 +00:00
|
|
|
sx := newWebRTCSession(
|
|
|
|
m.ctx,
|
2023-11-12 22:55:28 +00:00
|
|
|
m.WriteQueueSize,
|
2023-08-03 21:12:05 +00:00
|
|
|
m.api,
|
2023-07-23 16:40:06 +00:00
|
|
|
req,
|
2023-05-16 13:59:37 +00:00
|
|
|
&wg,
|
2023-11-12 22:55:28 +00:00
|
|
|
m.ExternalCmdPool,
|
|
|
|
m.PathManager,
|
2023-05-16 13:59:37 +00:00
|
|
|
m,
|
|
|
|
)
|
|
|
|
m.sessions[sx] = struct{}{}
|
|
|
|
m.sessionsBySecret[sx.secret] = sx
|
2023-07-30 21:53:39 +00:00
|
|
|
req.res <- webRTCNewSessionRes{sx: sx}
|
2023-05-16 13:59:37 +00:00
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
case sx := <-m.chCloseSession:
|
2023-05-16 13:59:37 +00:00
|
|
|
delete(m.sessions, sx)
|
|
|
|
delete(m.sessionsBySecret, sx.secret)
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
case req := <-m.chAddSessionCandidates:
|
2023-05-16 13:59:37 +00:00
|
|
|
sx, ok := m.sessionsBySecret[req.secret]
|
|
|
|
if !ok {
|
2023-07-30 21:53:39 +00:00
|
|
|
req.res <- webRTCAddSessionCandidatesRes{err: fmt.Errorf("session not found")}
|
2023-05-16 13:59:37 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
req.res <- webRTCAddSessionCandidatesRes{sx: sx}
|
2023-05-16 13:59:37 +00:00
|
|
|
|
2023-10-24 14:30:44 +00:00
|
|
|
case req := <-m.chDeleteSession:
|
|
|
|
sx, ok := m.sessionsBySecret[req.secret]
|
|
|
|
if !ok {
|
|
|
|
req.res <- webRTCDeleteSessionRes{err: fmt.Errorf("session not found")}
|
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(m.sessions, sx)
|
|
|
|
delete(m.sessionsBySecret, sx.secret)
|
|
|
|
sx.close()
|
|
|
|
|
|
|
|
req.res <- webRTCDeleteSessionRes{}
|
|
|
|
|
2023-05-16 13:59:37 +00:00
|
|
|
case req := <-m.chAPISessionsList:
|
2023-10-31 13:19:04 +00:00
|
|
|
data := &defs.APIWebRTCSessionList{
|
|
|
|
Items: []*defs.APIWebRTCSession{},
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
for sx := range m.sessions {
|
2023-05-18 13:07:47 +00:00
|
|
|
data.Items = append(data.Items, sx.apiItem())
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
2023-05-18 18:17:04 +00:00
|
|
|
sort.Slice(data.Items, func(i, j int) bool {
|
|
|
|
return data.Items[i].Created.Before(data.Items[j].Created)
|
|
|
|
})
|
|
|
|
|
2023-05-16 13:59:37 +00:00
|
|
|
req.res <- webRTCManagerAPISessionsListRes{data: data}
|
|
|
|
|
2023-05-18 18:17:04 +00:00
|
|
|
case req := <-m.chAPISessionsGet:
|
2023-05-18 13:07:47 +00:00
|
|
|
sx := m.findSessionByUUID(req.uuid)
|
|
|
|
if sx == nil {
|
2023-10-27 18:23:46 +00:00
|
|
|
req.res <- webRTCManagerAPISessionsGetRes{err: fmt.Errorf("session not found")}
|
2023-05-18 13:07:47 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
req.res <- webRTCManagerAPISessionsGetRes{data: sx.apiItem()}
|
|
|
|
|
2023-05-16 13:59:37 +00:00
|
|
|
case req := <-m.chAPIConnsKick:
|
|
|
|
sx := m.findSessionByUUID(req.uuid)
|
|
|
|
if sx == nil {
|
2023-10-27 18:23:46 +00:00
|
|
|
req.res <- webRTCManagerAPISessionsKickRes{err: fmt.Errorf("session not found")}
|
2023-05-16 13:59:37 +00:00
|
|
|
continue
|
|
|
|
}
|
|
|
|
|
|
|
|
delete(m.sessions, sx)
|
|
|
|
delete(m.sessionsBySecret, sx.secret)
|
|
|
|
sx.close()
|
2023-10-24 14:30:44 +00:00
|
|
|
|
2023-05-16 13:59:37 +00:00
|
|
|
req.res <- webRTCManagerAPISessionsKickRes{}
|
|
|
|
|
|
|
|
case <-m.ctx.Done():
|
|
|
|
break outer
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
m.ctxCancel()
|
|
|
|
|
|
|
|
wg.Wait()
|
|
|
|
|
|
|
|
m.httpServer.close()
|
|
|
|
|
|
|
|
if m.udpMuxLn != nil {
|
|
|
|
m.udpMuxLn.Close()
|
|
|
|
}
|
|
|
|
|
|
|
|
if m.tcpMuxLn != nil {
|
|
|
|
m.tcpMuxLn.Close()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
func (m *webRTCManager) findSessionByUUID(uuid uuid.UUID) *webRTCSession {
|
|
|
|
for sx := range m.sessions {
|
|
|
|
if sx.uuid == uuid {
|
|
|
|
return sx
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return nil
|
|
|
|
}
|
|
|
|
|
2023-10-25 09:48:57 +00:00
|
|
|
func (m *webRTCManager) generateICEServers() ([]pwebrtc.ICEServer, error) {
|
2023-11-12 22:55:28 +00:00
|
|
|
ret := make([]pwebrtc.ICEServer, len(m.ICEServers))
|
2023-07-30 20:30:41 +00:00
|
|
|
|
2023-11-12 22:55:28 +00:00
|
|
|
for i, server := range m.ICEServers {
|
2023-06-30 14:47:10 +00:00
|
|
|
if server.Username == "AUTH_SECRET" {
|
|
|
|
expireDate := time.Now().Add(webrtcTurnSecretExpiration).Unix()
|
2023-07-30 20:30:41 +00:00
|
|
|
|
|
|
|
user, err := randomTurnUser()
|
|
|
|
if err != nil {
|
|
|
|
return nil, err
|
|
|
|
}
|
|
|
|
|
|
|
|
server.Username = strconv.FormatInt(expireDate, 10) + ":" + user
|
|
|
|
|
2023-06-30 14:47:10 +00:00
|
|
|
h := hmac.New(sha1.New, []byte(server.Password))
|
|
|
|
h.Write([]byte(server.Username))
|
2023-07-30 20:30:41 +00:00
|
|
|
|
2023-06-30 14:47:10 +00:00
|
|
|
server.Password = base64.StdEncoding.EncodeToString(h.Sum(nil))
|
|
|
|
}
|
|
|
|
|
2023-10-25 09:48:57 +00:00
|
|
|
ret[i] = pwebrtc.ICEServer{
|
2023-06-30 14:47:10 +00:00
|
|
|
URLs: []string{server.URL},
|
|
|
|
Username: server.Username,
|
|
|
|
Credential: server.Password,
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
}
|
2023-07-30 20:30:41 +00:00
|
|
|
|
|
|
|
return ret, nil
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
// newSession is called by webRTCHTTPServer.
|
|
|
|
func (m *webRTCManager) newSession(req webRTCNewSessionReq) webRTCNewSessionRes {
|
|
|
|
req.res = make(chan webRTCNewSessionRes)
|
2023-05-16 13:59:37 +00:00
|
|
|
|
|
|
|
select {
|
2023-07-30 21:53:39 +00:00
|
|
|
case m.chNewSession <- req:
|
2023-07-18 21:41:52 +00:00
|
|
|
res := <-req.res
|
2023-05-16 13:59:37 +00:00
|
|
|
|
2023-07-18 21:41:52 +00:00
|
|
|
return res.sx.new(req)
|
2023-05-16 13:59:37 +00:00
|
|
|
|
|
|
|
case <-m.ctx.Done():
|
2023-10-28 12:08:34 +00:00
|
|
|
return webRTCNewSessionRes{
|
|
|
|
errStatusCode: http.StatusInternalServerError,
|
|
|
|
err: fmt.Errorf("terminated"),
|
|
|
|
}
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
// closeSession is called by webRTCSession.
|
|
|
|
func (m *webRTCManager) closeSession(sx *webRTCSession) {
|
2023-05-16 13:59:37 +00:00
|
|
|
select {
|
2023-07-30 21:53:39 +00:00
|
|
|
case m.chCloseSession <- sx:
|
2023-05-16 13:59:37 +00:00
|
|
|
case <-m.ctx.Done():
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-07-30 21:53:39 +00:00
|
|
|
// addSessionCandidates is called by webRTCHTTPServer.
|
|
|
|
func (m *webRTCManager) addSessionCandidates(
|
|
|
|
req webRTCAddSessionCandidatesReq,
|
|
|
|
) webRTCAddSessionCandidatesRes {
|
|
|
|
req.res = make(chan webRTCAddSessionCandidatesRes)
|
2023-05-16 13:59:37 +00:00
|
|
|
select {
|
2023-07-30 21:53:39 +00:00
|
|
|
case m.chAddSessionCandidates <- req:
|
2023-05-16 13:59:37 +00:00
|
|
|
res1 := <-req.res
|
|
|
|
if res1.err != nil {
|
|
|
|
return res1
|
|
|
|
}
|
|
|
|
|
2023-07-18 21:41:52 +00:00
|
|
|
return res1.sx.addCandidates(req)
|
2023-05-16 13:59:37 +00:00
|
|
|
|
|
|
|
case <-m.ctx.Done():
|
2023-07-30 21:53:39 +00:00
|
|
|
return webRTCAddSessionCandidatesRes{err: fmt.Errorf("terminated")}
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-10-24 14:30:44 +00:00
|
|
|
// deleteSession is called by webRTCHTTPServer.
|
|
|
|
func (m *webRTCManager) deleteSession(req webRTCDeleteSessionReq) error {
|
|
|
|
req.res = make(chan webRTCDeleteSessionRes)
|
|
|
|
select {
|
|
|
|
case m.chDeleteSession <- req:
|
|
|
|
res := <-req.res
|
|
|
|
return res.err
|
|
|
|
|
|
|
|
case <-m.ctx.Done():
|
|
|
|
return fmt.Errorf("terminated")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2023-05-16 13:59:37 +00:00
|
|
|
// apiSessionsList is called by api.
|
2023-10-31 13:19:04 +00:00
|
|
|
func (m *webRTCManager) apiSessionsList() (*defs.APIWebRTCSessionList, error) {
|
2023-05-16 13:59:37 +00:00
|
|
|
req := webRTCManagerAPISessionsListReq{
|
|
|
|
res: make(chan webRTCManagerAPISessionsListRes),
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case m.chAPISessionsList <- req:
|
2023-05-18 13:07:47 +00:00
|
|
|
res := <-req.res
|
|
|
|
return res.data, res.err
|
|
|
|
|
|
|
|
case <-m.ctx.Done():
|
|
|
|
return nil, fmt.Errorf("terminated")
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// apiSessionsGet is called by api.
|
2023-10-31 13:19:04 +00:00
|
|
|
func (m *webRTCManager) apiSessionsGet(uuid uuid.UUID) (*defs.APIWebRTCSession, error) {
|
2023-05-18 13:07:47 +00:00
|
|
|
req := webRTCManagerAPISessionsGetReq{
|
|
|
|
uuid: uuid,
|
|
|
|
res: make(chan webRTCManagerAPISessionsGetRes),
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
2023-05-18 18:17:04 +00:00
|
|
|
case m.chAPISessionsGet <- req:
|
2023-05-18 13:07:47 +00:00
|
|
|
res := <-req.res
|
|
|
|
return res.data, res.err
|
2023-05-16 13:59:37 +00:00
|
|
|
|
|
|
|
case <-m.ctx.Done():
|
2023-05-18 13:07:47 +00:00
|
|
|
return nil, fmt.Errorf("terminated")
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
// apiSessionsKick is called by api.
|
2023-05-18 13:07:47 +00:00
|
|
|
func (m *webRTCManager) apiSessionsKick(uuid uuid.UUID) error {
|
2023-05-16 13:59:37 +00:00
|
|
|
req := webRTCManagerAPISessionsKickReq{
|
|
|
|
uuid: uuid,
|
|
|
|
res: make(chan webRTCManagerAPISessionsKickRes),
|
|
|
|
}
|
|
|
|
|
|
|
|
select {
|
|
|
|
case m.chAPIConnsKick <- req:
|
2023-05-18 13:07:47 +00:00
|
|
|
res := <-req.res
|
|
|
|
return res.err
|
2023-05-16 13:59:37 +00:00
|
|
|
|
|
|
|
case <-m.ctx.Done():
|
2023-05-18 13:07:47 +00:00
|
|
|
return fmt.Errorf("terminated")
|
2023-05-16 13:59:37 +00:00
|
|
|
}
|
|
|
|
}
|