mediamtx/internal/core/source_static.go

166 lines
3.5 KiB
Go
Raw Normal View History

package core
import (
"context"
"strings"
"sync"
"time"
"github.com/aler9/rtsp-simple-server/internal/conf"
"github.com/aler9/rtsp-simple-server/internal/logger"
)
const (
sourceStaticRetryPause = 5 * time.Second
)
type sourceStaticImpl interface {
Log(logger.Level, string, ...interface{})
run(context.Context) error
onSourceAPIDescribe() interface{}
}
type sourceStaticParent interface {
log(logger.Level, string, ...interface{})
onSourceStaticSetReady(req pathSourceStaticSetReadyReq) pathSourceStaticSetReadyRes
onSourceStaticSetNotReady(req pathSourceStaticSetNotReadyReq)
}
// sourceStatic is a static source.
type sourceStatic struct {
ur string
protocol conf.SourceProtocol
anyPortEnable bool
fingerprint string
readTimeout conf.StringDuration
writeTimeout conf.StringDuration
readBufferCount int
wg *sync.WaitGroup
parent sourceStaticParent
impl sourceStaticImpl
ctx context.Context
ctxCancel func()
}
func newSourceStatic(
parentCtx context.Context,
ur string,
protocol conf.SourceProtocol,
anyPortEnable bool,
fingerprint string,
readTimeout conf.StringDuration,
writeTimeout conf.StringDuration,
readBufferCount int,
wg *sync.WaitGroup,
parent sourceStaticParent,
) *sourceStatic {
ctx, ctxCancel := context.WithCancel(parentCtx)
s := &sourceStatic{
ur: ur,
protocol: protocol,
anyPortEnable: anyPortEnable,
fingerprint: fingerprint,
readTimeout: readTimeout,
writeTimeout: writeTimeout,
readBufferCount: readBufferCount,
wg: wg,
parent: parent,
ctx: ctx,
ctxCancel: ctxCancel,
}
switch {
case strings.HasPrefix(s.ur, "rtsp://") ||
strings.HasPrefix(s.ur, "rtsps://"):
s.impl = newRTSPSource(
s.ur,
s.protocol,
s.anyPortEnable,
s.fingerprint,
s.readTimeout,
s.writeTimeout,
s.readBufferCount,
s)
case strings.HasPrefix(s.ur, "rtmp://"):
s.impl = newRTMPSource(
s.ur,
s.readTimeout,
s.writeTimeout,
s)
case strings.HasPrefix(s.ur, "http://") ||
strings.HasPrefix(s.ur, "https://"):
s.impl = newHLSSource(
s.ur,
s.fingerprint,
s)
}
s.impl.Log(logger.Info, "started")
s.wg.Add(1)
go s.run()
return s
}
func (s *sourceStatic) close() {
s.impl.Log(logger.Info, "stopped")
s.ctxCancel()
}
func (s *sourceStatic) log(level logger.Level, format string, args ...interface{}) {
s.parent.log(level, format, args...)
}
func (s *sourceStatic) run() {
defer s.wg.Done()
outer:
for {
innerCtx, innerCtxCancel := context.WithCancel(context.Background())
innerErr := make(chan error)
go func() {
innerErr <- s.impl.run(innerCtx)
}()
select {
case err := <-innerErr:
innerCtxCancel()
s.impl.Log(logger.Info, "ERR: %v", err)
case <-s.ctx.Done():
innerCtxCancel()
<-innerErr
}
select {
case <-time.After(sourceStaticRetryPause):
case <-s.ctx.Done():
break outer
}
}
s.ctxCancel()
}
// onSourceAPIDescribe implements source.
func (s *sourceStatic) onSourceAPIDescribe() interface{} {
return s.impl.onSourceAPIDescribe()
}
// onSourceStaticSetReady is called by a sourceStaticImpl.
func (s *sourceStatic) onSourceStaticSetReady(req pathSourceStaticSetReadyReq) pathSourceStaticSetReadyRes {
req.source = s
return s.parent.onSourceStaticSetReady(req)
}
// onSourceStaticSetNotReady is called by a sourceStaticImpl.
func (s *sourceStatic) onSourceStaticSetNotReady(req pathSourceStaticSetNotReadyReq) {
req.source = s
s.parent.onSourceStaticSetNotReady(req)
}