hls muxer: when hlsAlwaysRemux is on, automatically recreate muxers in

case of errors
This commit is contained in:
aler9 2022-07-24 13:05:15 +02:00
parent c769088e6b
commit 8a4743fe9a
4 changed files with 83 additions and 41 deletions

View File

@ -112,8 +112,8 @@ type hlsMuxerParent interface {
type hlsMuxer struct {
name string
remoteAddr string
externalAuthenticationURL string
hlsAlwaysRemux bool
hlsVariant conf.HLSVariant
hlsSegmentCount int
hlsSegmentDuration conf.StringDuration
@ -143,7 +143,6 @@ func newHLSMuxer(
name string,
remoteAddr string,
externalAuthenticationURL string,
hlsAlwaysRemux bool,
hlsVariant conf.HLSVariant,
hlsSegmentCount int,
hlsSegmentDuration conf.StringDuration,
@ -160,8 +159,8 @@ func newHLSMuxer(
m := &hlsMuxer{
name: name,
remoteAddr: remoteAddr,
externalAuthenticationURL: externalAuthenticationURL,
hlsAlwaysRemux: hlsAlwaysRemux,
hlsVariant: hlsVariant,
hlsSegmentCount: hlsSegmentCount,
hlsSegmentDuration: hlsSegmentDuration,
@ -398,7 +397,7 @@ func (m *hlsMuxer) runInner(innerCtx context.Context, innerReady chan struct{})
select {
case <-closeCheckTicker.C:
t := time.Unix(atomic.LoadInt64(m.lastRequestTime), 0)
if !m.hlsAlwaysRemux && time.Since(t) >= closeAfterInactivity {
if m.remoteAddr != "" && time.Since(t) >= closeAfterInactivity {
m.ringBuffer.Close()
<-writerDone
return fmt.Errorf("not used anymore")

View File

@ -76,10 +76,11 @@ type hlsServer struct {
muxers map[string]*hlsMuxer
// in
pathSourceReady chan *path
request chan *hlsMuxerRequest
muxerClose chan *hlsMuxer
apiMuxersList chan hlsServerAPIMuxersListReq
pathSourceReady chan *path
pathSourceNotReady chan *path
request chan *hlsMuxerRequest
muxerClose chan *hlsMuxer
apiMuxersList chan hlsServerAPIMuxersListReq
}
func newHLSServer(
@ -142,6 +143,7 @@ func newHLSServer(
tlsConfig: tlsConfig,
muxers: make(map[string]*hlsMuxer),
pathSourceReady: make(chan *path),
pathSourceNotReady: make(chan *path),
request: make(chan *hlsMuxerRequest),
muxerClose: make(chan *hlsMuxer),
apiMuxersList: make(chan hlsServerAPIMuxersListReq),
@ -204,6 +206,15 @@ outer:
s.findOrCreateMuxer(pa.Name(), "", nil)
}
case pa := <-s.pathSourceNotReady:
if s.hlsAlwaysRemux {
c, ok := s.muxers[pa.Name()]
if ok {
c.close()
delete(s.muxers, pa.Name())
}
}
case req := <-s.request:
s.findOrCreateMuxer(req.dir, req.ctx.ClientIP(), req)
@ -213,6 +224,10 @@ outer:
}
delete(s.muxers, c.PathName())
if s.hlsAlwaysRemux && c.remoteAddr == "" {
s.findOrCreateMuxer(c.PathName(), "", nil)
}
case req := <-s.apiMuxersList:
muxers := make(map[string]*hlsMuxer)
@ -331,7 +346,6 @@ func (s *hlsServer) findOrCreateMuxer(pathName string, remoteAddr string, req *h
pathName,
remoteAddr,
s.externalAuthenticationURL,
s.hlsAlwaysRemux,
s.hlsVariant,
s.hlsSegmentCount,
s.hlsSegmentDuration,
@ -358,7 +372,7 @@ func (s *hlsServer) onMuxerClose(c *hlsMuxer) {
}
}
// onPathSourceReady is called by core.
// onPathSourceReady is called by pathManager.
func (s *hlsServer) onPathSourceReady(pa *path) {
select {
case s.pathSourceReady <- pa:
@ -366,6 +380,14 @@ func (s *hlsServer) onPathSourceReady(pa *path) {
}
}
// onPathSourceNotReady is called by pathManager.
func (s *hlsServer) onPathSourceNotReady(pa *path) {
select {
case s.pathSourceNotReady <- pa:
case <-s.ctx.Done():
}
}
// onAPIHLSMuxersList is called by api.
func (s *hlsServer) onAPIHLSMuxersList(req hlsServerAPIMuxersListReq) hlsServerAPIMuxersListRes {
req.res = make(chan hlsServerAPIMuxersListRes)

View File

@ -62,6 +62,7 @@ func (pathErrAuthCritical) Error() string {
type pathParent interface {
log(logger.Level, string, ...interface{})
onPathSourceReady(*path)
onPathSourceNotReady(*path)
onPathClose(*path)
}
@ -533,7 +534,9 @@ func (pa *path) run() {
req.res <- pathReaderSetupPlayRes{err: fmt.Errorf("terminated")}
}
pa.sourceSetNotReady()
if pa.sourceReady {
pa.sourceSetNotReady()
}
if pa.source != nil {
if source, ok := pa.source.(sourceStatic); ok {
@ -655,8 +658,6 @@ func (pa *path) sourceSetReady(tracks gortsplib.Tracks) {
pa.sourceReady = true
pa.stream = newStream(tracks)
pa.parent.onPathSourceReady(pa)
if pa.conf.RunOnReady != "" {
pa.log(logger.Info, "runOnReady command started")
pa.onReadyCmd = externalcmd.NewCmd(
@ -668,9 +669,13 @@ func (pa *path) sourceSetReady(tracks gortsplib.Tracks) {
pa.log(logger.Info, "runOnReady command exited with code %d", co)
})
}
pa.parent.onPathSourceReady(pa)
}
func (pa *path) sourceSetNotReady() {
pa.parent.onPathSourceNotReady(pa)
for r := range pa.readers {
pa.doReaderRemove(r)
r.close()

View File

@ -11,7 +11,8 @@ import (
)
type pathManagerHLSServer interface {
onPathSourceReady(pa *path)
onPathSourceReady(*path)
onPathSourceNotReady(*path)
}
type pathManagerParent interface {
@ -35,14 +36,15 @@ type pathManager struct {
paths map[string]*path
// in
confReload chan map[string]*conf.PathConf
pathClose chan *path
pathSourceReady chan *path
describe chan pathDescribeReq
readerSetupPlay chan pathReaderSetupPlayReq
publisherAnnounce chan pathPublisherAnnounceReq
hlsServerSet chan pathManagerHLSServer
apiPathsList chan pathAPIPathsListReq
confReload chan map[string]*conf.PathConf
pathClose chan *path
pathSourceReady chan *path
pathSourceNotReady chan *path
describe chan pathDescribeReq
readerSetupPlay chan pathReaderSetupPlayReq
publisherAnnounce chan pathPublisherAnnounceReq
hlsServerSet chan pathManagerHLSServer
apiPathsList chan pathAPIPathsListReq
}
func newPathManager(
@ -59,25 +61,26 @@ func newPathManager(
ctx, ctxCancel := context.WithCancel(parentCtx)
pm := &pathManager{
rtspAddress: rtspAddress,
readTimeout: readTimeout,
writeTimeout: writeTimeout,
readBufferCount: readBufferCount,
pathConfs: pathConfs,
externalCmdPool: externalCmdPool,
metrics: metrics,
parent: parent,
ctx: ctx,
ctxCancel: ctxCancel,
paths: make(map[string]*path),
confReload: make(chan map[string]*conf.PathConf),
pathClose: make(chan *path),
pathSourceReady: make(chan *path),
describe: make(chan pathDescribeReq),
readerSetupPlay: make(chan pathReaderSetupPlayReq),
publisherAnnounce: make(chan pathPublisherAnnounceReq),
hlsServerSet: make(chan pathManagerHLSServer),
apiPathsList: make(chan pathAPIPathsListReq),
rtspAddress: rtspAddress,
readTimeout: readTimeout,
writeTimeout: writeTimeout,
readBufferCount: readBufferCount,
pathConfs: pathConfs,
externalCmdPool: externalCmdPool,
metrics: metrics,
parent: parent,
ctx: ctx,
ctxCancel: ctxCancel,
paths: make(map[string]*path),
confReload: make(chan map[string]*conf.PathConf),
pathClose: make(chan *path),
pathSourceReady: make(chan *path),
pathSourceNotReady: make(chan *path),
describe: make(chan pathDescribeReq),
readerSetupPlay: make(chan pathReaderSetupPlayReq),
publisherAnnounce: make(chan pathPublisherAnnounceReq),
hlsServerSet: make(chan pathManagerHLSServer),
apiPathsList: make(chan pathAPIPathsListReq),
}
for pathConfName, pathConf := range pm.pathConfs {
@ -165,6 +168,11 @@ outer:
pm.hlsServer.onPathSourceReady(pa)
}
case pa := <-pm.pathSourceNotReady:
if pm.hlsServer != nil {
pm.hlsServer.onPathSourceNotReady(pa)
}
case req := <-pm.describe:
pathConfName, pathConf, pathMatches, err := pm.findPathConf(req.pathName)
if err != nil {
@ -323,6 +331,14 @@ func (pm *pathManager) onPathSourceReady(pa *path) {
}
}
// onPathSourceNotReady is called by path.
func (pm *pathManager) onPathSourceNotReady(pa *path) {
select {
case pm.pathSourceNotReady <- pa:
case <-pm.ctx.Done():
}
}
// onPathClose is called by path.
func (pm *pathManager) onPathClose(pa *path) {
select {