normalize sources

This commit is contained in:
aler9
2022-07-24 18:11:53 +02:00
parent 8267f4c3bb
commit 34eb1d1e7a
3 changed files with 206 additions and 203 deletions

View File

@@ -74,9 +74,20 @@ func (s *hlsSource) run() {
outer: outer:
for { for {
ok := s.runInner() innerCtx, innerCtxCancel := context.WithCancel(context.Background())
if !ok { innerErr := make(chan error)
break outer go func() {
innerErr <- s.runInner(innerCtx)
}()
select {
case err := <-innerErr:
innerCtxCancel()
s.Log(logger.Info, "ERR: %v", err)
case <-s.ctx.Done():
innerCtxCancel()
<-innerErr
} }
select { select {
@@ -89,7 +100,7 @@ outer:
s.ctxCancel() s.ctxCancel()
} }
func (s *hlsSource) runInner() bool { func (s *hlsSource) runInner(innerCtx context.Context) error {
var stream *stream var stream *stream
var videoTrackID int var videoTrackID int
var audioTrackID int var audioTrackID int
@@ -198,19 +209,17 @@ func (s *hlsSource) runInner() bool {
s, s,
) )
if err != nil { if err != nil {
s.Log(logger.Info, "ERR: %v", err) return err
return true
} }
select { select {
case err := <-c.Wait(): case err := <-c.Wait():
s.Log(logger.Info, "ERR: %v", err) return err
return true
case <-s.ctx.Done(): case <-innerCtx.Done():
c.Close() c.Close()
<-c.Wait() <-c.Wait()
return false return nil
} }
} }

View File

@@ -61,7 +61,7 @@ func newRTMPSource(
ctxCancel: ctxCancel, ctxCancel: ctxCancel,
} }
s.log(logger.Info, "started") s.Log(logger.Info, "started")
s.wg.Add(1) s.wg.Add(1)
go s.run() go s.run()
@@ -71,11 +71,11 @@ func newRTMPSource(
// Close closes a Source. // Close closes a Source.
func (s *rtmpSource) close() { func (s *rtmpSource) close() {
s.log(logger.Info, "stopped") s.Log(logger.Info, "stopped")
s.ctxCancel() s.ctxCancel()
} }
func (s *rtmpSource) log(level logger.Level, format string, args ...interface{}) { func (s *rtmpSource) Log(level logger.Level, format string, args ...interface{}) {
s.parent.log(level, "[rtmp source] "+format, args...) s.parent.log(level, "[rtmp source] "+format, args...)
} }
@@ -84,9 +84,20 @@ func (s *rtmpSource) run() {
outer: outer:
for { for {
ok := s.runInner() innerCtx, innerCtxCancel := context.WithCancel(context.Background())
if !ok { innerErr := make(chan error)
break outer go func() {
innerErr <- s.runInner(innerCtx)
}()
select {
case err := <-innerErr:
innerCtxCancel()
s.Log(logger.Info, "ERR: %v", err)
case <-s.ctx.Done():
innerCtxCancel()
<-innerErr
} }
select { select {
@@ -99,186 +110,167 @@ outer:
s.ctxCancel() s.ctxCancel()
} }
func (s *rtmpSource) runInner() bool { func (s *rtmpSource) runInner(innerCtx context.Context) error {
innerCtx, innerCtxCancel := context.WithCancel(s.ctx) s.Log(logger.Debug, "connecting")
runErr := make(chan error) u, err := url.Parse(s.ur)
if err != nil {
return err
}
// add default port
_, _, err = net.SplitHostPort(u.Host)
if err != nil {
u.Host = net.JoinHostPort(u.Host, "1935")
}
ctx2, cancel2 := context.WithTimeout(innerCtx, time.Duration(s.readTimeout))
defer cancel2()
var d net.Dialer
nconn, err := d.DialContext(ctx2, "tcp", u.Host)
if err != nil {
return err
}
conn := rtmp.NewConn(nconn)
readDone := make(chan error)
go func() { go func() {
runErr <- func() error { readDone <- func() error {
s.log(logger.Debug, "connecting") nconn.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout)))
nconn.SetWriteDeadline(time.Now().Add(time.Duration(s.writeTimeout)))
u, err := url.Parse(s.ur) err = conn.InitializeClient(u, true)
if err != nil { if err != nil {
return err return err
} }
// add default port nconn.SetWriteDeadline(time.Time{})
_, _, err = net.SplitHostPort(u.Host) nconn.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout)))
if err != nil { videoTrack, audioTrack, err := conn.ReadTracks()
u.Host = net.JoinHostPort(u.Host, "1935")
}
ctx2, cancel2 := context.WithTimeout(innerCtx, time.Duration(s.readTimeout))
defer cancel2()
var d net.Dialer
nconn, err := d.DialContext(ctx2, "tcp", u.Host)
if err != nil { if err != nil {
return err return err
} }
conn := rtmp.NewConn(nconn) var tracks gortsplib.Tracks
videoTrackID := -1
audioTrackID := -1
readDone := make(chan error) var h264Encoder *rtph264.Encoder
go func() { if videoTrack != nil {
readDone <- func() error { h264Encoder = &rtph264.Encoder{PayloadType: 96}
nconn.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout))) h264Encoder.Init()
nconn.SetWriteDeadline(time.Now().Add(time.Duration(s.writeTimeout))) videoTrackID = len(tracks)
err = conn.InitializeClient(u, true) tracks = append(tracks, videoTrack)
if err != nil { }
return err
}
nconn.SetWriteDeadline(time.Time{}) var aacEncoder *rtpaac.Encoder
nconn.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout))) if audioTrack != nil {
videoTrack, audioTrack, err := conn.ReadTracks() aacEncoder = &rtpaac.Encoder{
if err != nil { PayloadType: 96,
return err SampleRate: audioTrack.ClockRate(),
} SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
}
aacEncoder.Init()
audioTrackID = len(tracks)
tracks = append(tracks, audioTrack)
}
var tracks gortsplib.Tracks res := s.parent.onSourceStaticSetReady(pathSourceStaticSetReadyReq{
videoTrackID := -1 source: s,
audioTrackID := -1 tracks: tracks,
})
if res.err != nil {
return res.err
}
var h264Encoder *rtph264.Encoder s.Log(logger.Info, "ready")
if videoTrack != nil {
h264Encoder = &rtph264.Encoder{PayloadType: 96}
h264Encoder.Init()
videoTrackID = len(tracks)
tracks = append(tracks, videoTrack)
}
var aacEncoder *rtpaac.Encoder defer func() {
if audioTrack != nil { s.parent.onSourceStaticSetNotReady(pathSourceStaticSetNotReadyReq{source: s})
aacEncoder = &rtpaac.Encoder{
PayloadType: 96,
SampleRate: audioTrack.ClockRate(),
SizeLength: 13,
IndexLength: 3,
IndexDeltaLength: 3,
}
aacEncoder.Init()
audioTrackID = len(tracks)
tracks = append(tracks, audioTrack)
}
res := s.parent.onSourceStaticSetReady(pathSourceStaticSetReadyReq{
source: s,
tracks: tracks,
})
if res.err != nil {
return res.err
}
s.log(logger.Info, "ready")
defer func() {
s.parent.onSourceStaticSetNotReady(pathSourceStaticSetNotReadyReq{source: s})
}()
for {
nconn.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout)))
msg, err := conn.ReadMessage()
if err != nil {
return err
}
switch tmsg := msg.(type) {
case *message.MsgVideo:
if tmsg.H264Type == flvio.AVC_NALU {
if videoTrack == nil {
return fmt.Errorf("received an H264 packet, but track is not set up")
}
nalus, err := h264.AVCCUnmarshal(tmsg.Payload)
if err != nil {
return fmt.Errorf("unable to decode AVCC: %v", err)
}
pts := tmsg.DTS + tmsg.PTSDelta
pkts, err := h264Encoder.Encode(nalus, pts)
if err != nil {
return fmt.Errorf("error while encoding H264: %v", err)
}
lastPkt := len(pkts) - 1
for i, pkt := range pkts {
if i != lastPkt {
res.stream.writeData(&data{
trackID: videoTrackID,
rtp: pkt,
ptsEqualsDTS: false,
})
} else {
res.stream.writeData(&data{
trackID: videoTrackID,
rtp: pkt,
ptsEqualsDTS: h264.IDRPresent(nalus),
h264NALUs: nalus,
h264PTS: pts,
})
}
}
}
case *message.MsgAudio:
if tmsg.AACType == flvio.AAC_RAW {
if audioTrack == nil {
return fmt.Errorf("received an AAC packet, but track is not set up")
}
pkts, err := aacEncoder.Encode([][]byte{tmsg.Payload}, tmsg.DTS)
if err != nil {
return fmt.Errorf("error while encoding AAC: %v", err)
}
for _, pkt := range pkts {
res.stream.writeData(&data{
trackID: audioTrackID,
rtp: pkt,
ptsEqualsDTS: true,
})
}
}
}
}
}()
}() }()
select { for {
case err := <-readDone: nconn.SetReadDeadline(time.Now().Add(time.Duration(s.readTimeout)))
nconn.Close() msg, err := conn.ReadMessage()
return err if err != nil {
return err
}
case <-innerCtx.Done(): switch tmsg := msg.(type) {
nconn.Close() case *message.MsgVideo:
<-readDone if tmsg.H264Type == flvio.AVC_NALU {
return nil if videoTrack == nil {
return fmt.Errorf("received an H264 packet, but track is not set up")
}
nalus, err := h264.AVCCUnmarshal(tmsg.Payload)
if err != nil {
return fmt.Errorf("unable to decode AVCC: %v", err)
}
pts := tmsg.DTS + tmsg.PTSDelta
pkts, err := h264Encoder.Encode(nalus, pts)
if err != nil {
return fmt.Errorf("error while encoding H264: %v", err)
}
lastPkt := len(pkts) - 1
for i, pkt := range pkts {
if i != lastPkt {
res.stream.writeData(&data{
trackID: videoTrackID,
rtp: pkt,
ptsEqualsDTS: false,
})
} else {
res.stream.writeData(&data{
trackID: videoTrackID,
rtp: pkt,
ptsEqualsDTS: h264.IDRPresent(nalus),
h264NALUs: nalus,
h264PTS: pts,
})
}
}
}
case *message.MsgAudio:
if tmsg.AACType == flvio.AAC_RAW {
if audioTrack == nil {
return fmt.Errorf("received an AAC packet, but track is not set up")
}
pkts, err := aacEncoder.Encode([][]byte{tmsg.Payload}, tmsg.DTS)
if err != nil {
return fmt.Errorf("error while encoding AAC: %v", err)
}
for _, pkt := range pkts {
res.stream.writeData(&data{
trackID: audioTrackID,
rtp: pkt,
ptsEqualsDTS: true,
})
}
}
}
} }
}() }()
}() }()
select { select {
case err := <-runErr: case err := <-readDone:
innerCtxCancel() nconn.Close()
s.log(logger.Info, "ERR: %s", err) return err
return true
case <-s.ctx.Done(): case <-innerCtx.Done():
innerCtxCancel() nconn.Close()
<-runErr <-readDone
return false return nil
} }
} }

View File

@@ -71,7 +71,7 @@ func newRTSPSource(
ctxCancel: ctxCancel, ctxCancel: ctxCancel,
} }
s.log(logger.Info, "started") s.Log(logger.Info, "started")
s.wg.Add(1) s.wg.Add(1)
go s.run() go s.run()
@@ -80,44 +80,49 @@ func newRTSPSource(
} }
func (s *rtspSource) close() { func (s *rtspSource) close() {
s.log(logger.Info, "stopped") s.Log(logger.Info, "stopped")
s.ctxCancel() s.ctxCancel()
} }
func (s *rtspSource) log(level logger.Level, format string, args ...interface{}) { func (s *rtspSource) Log(level logger.Level, format string, args ...interface{}) {
s.parent.log(level, "[rtsp source] "+format, args...) s.parent.log(level, "[rtsp source] "+format, args...)
} }
func (s *rtspSource) run() { func (s *rtspSource) run() {
defer s.wg.Done() defer s.wg.Done()
outer:
for { for {
ok := func() bool { innerCtx, innerCtxCancel := context.WithCancel(context.Background())
ok := s.runInner() innerErr := make(chan error)
if !ok { go func() {
return false innerErr <- s.runInner(innerCtx)
}
select {
case <-time.After(rtspSourceRetryPause):
return true
case <-s.ctx.Done():
return false
}
}() }()
if !ok {
break select {
case err := <-innerErr:
innerCtxCancel()
s.Log(logger.Info, "ERR: %v", err)
case <-s.ctx.Done():
innerCtxCancel()
<-innerErr
}
select {
case <-time.After(rtspSourceRetryPause):
case <-s.ctx.Done():
break outer
} }
} }
s.ctxCancel() s.ctxCancel()
} }
func (s *rtspSource) runInner() bool { func (s *rtspSource) runInner(innerCtx context.Context) error {
s.log(logger.Debug, "connecting") s.Log(logger.Debug, "connecting")
var tlsConfig *tls.Config var tlsConfig *tls.Config
if s.fingerprint != "" { if s.fingerprint != "" {
tlsConfig = &tls.Config{ tlsConfig = &tls.Config{
InsecureSkipVerify: true, InsecureSkipVerify: true,
@@ -145,23 +150,21 @@ func (s *rtspSource) runInner() bool {
ReadBufferCount: s.readBufferCount, ReadBufferCount: s.readBufferCount,
AnyPortEnable: s.anyPortEnable, AnyPortEnable: s.anyPortEnable,
OnRequest: func(req *base.Request) { OnRequest: func(req *base.Request) {
s.log(logger.Debug, "c->s %v", req) s.Log(logger.Debug, "c->s %v", req)
}, },
OnResponse: func(res *base.Response) { OnResponse: func(res *base.Response) {
s.log(logger.Debug, "s->c %v", res) s.Log(logger.Debug, "s->c %v", res)
}, },
} }
u, err := url.Parse(s.ur) u, err := url.Parse(s.ur)
if err != nil { if err != nil {
s.log(logger.Info, "ERR: %s", err) return err
return true
} }
err = c.Start(u.Scheme, u.Host) err = c.Start(u.Scheme, u.Host)
if err != nil { if err != nil {
s.log(logger.Info, "ERR: %s", err) return err
return true
} }
defer c.Close() defer c.Close()
@@ -188,7 +191,7 @@ func (s *rtspSource) runInner() bool {
return res.err return res.err
} }
s.log(logger.Info, "ready") s.Log(logger.Info, "ready")
defer func() { defer func() {
s.parent.onSourceStaticSetNotReady(pathSourceStaticSetNotReadyReq{source: s}) s.parent.onSourceStaticSetNotReady(pathSourceStaticSetNotReadyReq{source: s})
@@ -223,13 +226,12 @@ func (s *rtspSource) runInner() bool {
select { select {
case err := <-readErr: case err := <-readErr:
s.log(logger.Info, "ERR: %s", err) return err
return true
case <-s.ctx.Done(): case <-innerCtx.Done():
c.Close() c.Close()
<-readErr <-readErr
return false return nil
} }
} }