Ready-to-use SRT / WebRTC / RTSP / RTMP / LL-HLS media server and media proxy that allows to read, publish, proxy, record and playback video and audio streams.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

175 lines
3.9 KiB

package clientman
import (
"sync"
"time"
"github.com/aler9/gortsplib/pkg/base"
"github.com/aler9/rtsp-simple-server/internal/client"
"github.com/aler9/rtsp-simple-server/internal/clienthls"
"github.com/aler9/rtsp-simple-server/internal/logger"
"github.com/aler9/rtsp-simple-server/internal/serverhls"
"github.com/aler9/rtsp-simple-server/internal/stats"
)
// PathManager is implemented by pathman.PathManager.
type PathManager interface {
OnClientDescribe(client.DescribeReq)
OnClientAnnounce(client.AnnounceReq)
OnClientSetupPlay(client.SetupPlayReq)
}
// Parent is implemented by program.
type Parent interface {
Log(logger.Level, string, ...interface{})
}
// ClientManager is a client manager.
type ClientManager struct {
hlsSegmentCount int
hlsSegmentDuration time.Duration
rtspAddress string
readTimeout time.Duration
writeTimeout time.Duration
readBufferCount int
runOnConnect string
runOnConnectRestart bool
protocols map[base.StreamProtocol]struct{}
stats *stats.Stats
pathMan PathManager
serverHLS *serverhls.Server
parent Parent
clients map[client.Client]struct{}
clientsByHLSPath map[string]*clienthls.Client
wg sync.WaitGroup
// in
clientClose chan client.Client
terminate chan struct{}
// out
done chan struct{}
}
// New allocates a ClientManager.
func New(
hlsSegmentCount int,
hlsSegmentDuration time.Duration,
rtspAddress string,
readTimeout time.Duration,
writeTimeout time.Duration,
readBufferCount int,
runOnConnect string,
runOnConnectRestart bool,
protocols map[base.StreamProtocol]struct{},
stats *stats.Stats,
pathMan PathManager,
serverHLS *serverhls.Server,
parent Parent) *ClientManager {
cm := &ClientManager{
hlsSegmentCount: hlsSegmentCount,
hlsSegmentDuration: hlsSegmentDuration,
rtspAddress: rtspAddress,
readTimeout: readTimeout,
writeTimeout: writeTimeout,
readBufferCount: readBufferCount,
runOnConnect: runOnConnect,
runOnConnectRestart: runOnConnectRestart,
protocols: protocols,
stats: stats,
pathMan: pathMan,
serverHLS: serverHLS,
parent: parent,
clients: make(map[client.Client]struct{}),
clientsByHLSPath: make(map[string]*clienthls.Client),
clientClose: make(chan client.Client),
terminate: make(chan struct{}),
done: make(chan struct{}),
}
go cm.run()
return cm
}
// Close closes a ClientManager.
func (cm *ClientManager) Close() {
close(cm.terminate)
<-cm.done
}
// Log is the main logging function.
func (cm *ClientManager) Log(level logger.Level, format string, args ...interface{}) {
cm.parent.Log(level, format, args...)
}
func (cm *ClientManager) run() {
defer close(cm.done)
hlsRequest := func() chan serverhls.Request {
if cm.serverHLS != nil {
return cm.serverHLS.Request()
}
return make(chan serverhls.Request)
}()
outer:
for {
select {
case req := <-hlsRequest:
c, ok := cm.clientsByHLSPath[req.Path]
if !ok {
c = clienthls.New(
cm.hlsSegmentCount,
cm.hlsSegmentDuration,
cm.readBufferCount,
&cm.wg,
cm.stats,
req.Path,
cm.pathMan,
cm)
cm.clients[c] = struct{}{}
cm.clientsByHLSPath[req.Path] = c
}
c.OnRequest(req)
case c := <-cm.clientClose:
if _, ok := cm.clients[c]; !ok {
continue
}
cm.onClientClose(c)
case <-cm.terminate:
break outer
}
}
go func() {
for range cm.clientClose {
}
}()
for c := range cm.clients {
c.Close()
}
cm.wg.Wait()
close(cm.clientClose)
}
func (cm *ClientManager) onClientClose(c client.Client) {
delete(cm.clients, c)
if hc, ok := c.(*clienthls.Client); ok {
delete(cm.clientsByHLSPath, hc.PathName())
}
c.Close()
}
// OnClientClose is called by a client.
func (cm *ClientManager) OnClientClose(c client.Client) {
cm.clientClose <- c
}