7 changed files with 212 additions and 253 deletions
@ -0,0 +1,11 @@
@@ -0,0 +1,11 @@
|
||||
package core |
||||
|
||||
// sourceRedirect is a source that redirects to another one.
|
||||
type sourceRedirect struct{} |
||||
|
||||
// onSourceAPIDescribe implements source.
|
||||
func (*sourceRedirect) onSourceAPIDescribe() interface{} { |
||||
return struct { |
||||
Type string `json:"type"` |
||||
}{"redirect"} |
||||
} |
||||
@ -0,0 +1,165 @@
@@ -0,0 +1,165 @@
|
||||
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) |
||||
} |
||||
Loading…
Reference in new issue