mediamtx/sourcertsp.go

322 lines
5.8 KiB
Go
Raw Normal View History

package main
import (
2020-07-19 20:39:38 +00:00
"sync"
2020-09-19 21:52:06 +00:00
"sync/atomic"
"time"
"github.com/aler9/gortsplib"
)
const (
2020-10-03 19:10:41 +00:00
sourceRtspRetryInterval = 5 * time.Second
)
2020-10-03 19:10:41 +00:00
type sourceRtspState int
const (
2020-10-03 19:10:41 +00:00
sourceRtspStateStopped sourceRtspState = iota
sourceRtspStateRunning
)
2020-10-03 19:10:41 +00:00
type sourceRtsp struct {
2020-08-31 13:46:03 +00:00
p *program
path *path
2020-10-03 19:10:41 +00:00
state sourceRtspState
2020-08-31 13:46:03 +00:00
tracks []*gortsplib.Track
innerRunning bool
innerTerminate chan struct{}
innerDone chan struct{}
2020-10-03 19:10:41 +00:00
setState chan sourceRtspState
2020-08-31 13:46:03 +00:00
terminate chan struct{}
done chan struct{}
}
2020-10-03 19:10:41 +00:00
func newSourceRtsp(p *program, path *path) *sourceRtsp {
s := &sourceRtsp{
p: p,
path: path,
2020-10-03 19:10:41 +00:00
setState: make(chan sourceRtspState),
terminate: make(chan struct{}),
done: make(chan struct{}),
}
2020-10-03 19:10:41 +00:00
atomic.AddInt64(p.countSourcesRtsp, +1)
2020-09-19 21:52:06 +00:00
2020-10-03 19:10:41 +00:00
if path.conf.SourceOnDemand {
s.state = sourceRtspStateStopped
} else {
2020-10-03 19:10:41 +00:00
s.state = sourceRtspStateRunning
atomic.AddInt64(p.countSourcesRtspRunning, +1)
}
return s
}
2020-10-03 19:10:41 +00:00
func (s *sourceRtsp) isPublisher() {}
2020-10-03 19:10:41 +00:00
func (s *sourceRtsp) run(initialState sourceRtspState) {
2020-09-03 14:31:52 +00:00
s.applyState(initialState)
outer:
for {
select {
case state := <-s.setState:
2020-08-31 13:46:03 +00:00
s.applyState(state)
case <-s.terminate:
break outer
}
}
2020-08-31 13:46:03 +00:00
if s.innerRunning {
close(s.innerTerminate)
<-s.innerDone
}
close(s.setState)
close(s.done)
}
2020-10-03 19:10:41 +00:00
func (s *sourceRtsp) applyState(state sourceRtspState) {
if state == sourceRtspStateRunning {
2020-08-31 13:46:03 +00:00
if !s.innerRunning {
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source started")
2020-08-31 13:46:03 +00:00
s.innerRunning = true
s.innerTerminate = make(chan struct{})
s.innerDone = make(chan struct{})
go s.runInner()
}
} else {
if s.innerRunning {
close(s.innerTerminate)
<-s.innerDone
s.innerRunning = false
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source stopped")
2020-08-31 13:46:03 +00:00
}
}
}
2020-10-03 19:10:41 +00:00
func (s *sourceRtsp) runInner() {
2020-08-31 13:46:03 +00:00
defer close(s.innerDone)
2020-10-03 19:10:41 +00:00
outer:
for {
2020-10-03 19:10:41 +00:00
ok := s.runInnerInner()
if !ok {
break outer
}
2020-07-30 11:31:18 +00:00
2020-10-03 19:10:41 +00:00
t := time.NewTimer(sourceRtspRetryInterval)
defer t.Stop()
2020-07-30 11:31:18 +00:00
2020-10-03 19:10:41 +00:00
select {
case <-s.innerTerminate:
break outer
case <-t.C:
}
}
}
2020-10-03 19:10:41 +00:00
func (s *sourceRtsp) runInnerInner() bool {
s.path.log("connecting to rtsp source")
2020-07-19 20:39:38 +00:00
var conn *gortsplib.ConnClient
var err error
2020-10-03 19:10:41 +00:00
dialDone := make(chan struct{}, 1)
go func() {
2020-10-03 19:10:41 +00:00
defer close(dialDone)
2020-07-19 20:39:38 +00:00
conn, err = gortsplib.NewConnClient(gortsplib.ConnClientConf{
2020-10-03 19:10:41 +00:00
Host: s.path.conf.sourceUrl.Host,
2020-09-20 12:17:36 +00:00
ReadTimeout: s.p.conf.ReadTimeout,
WriteTimeout: s.p.conf.WriteTimeout,
ReadBufferCount: 2,
2020-07-19 20:39:38 +00:00
})
}()
select {
2020-08-31 13:46:03 +00:00
case <-s.innerTerminate:
return false
case <-dialDone:
}
if err != nil {
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
return true
}
2020-10-03 19:10:41 +00:00
_, err = conn.Options(s.path.conf.sourceUrl)
if err != nil {
conn.Close()
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
return true
}
2020-10-03 19:10:41 +00:00
tracks, _, err := conn.Describe(s.path.conf.sourceUrl)
if err != nil {
conn.Close()
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
return true
}
// create a filtered SDP that is used by the server (not by the client)
2020-10-03 19:10:41 +00:00
s.path.publisherSdp = tracks.Write()
2020-09-05 12:51:36 +00:00
s.path.publisherTrackCount = len(tracks)
2020-10-03 19:10:41 +00:00
s.tracks = tracks
2020-10-03 19:10:41 +00:00
if s.path.conf.sourceProtocolParsed == gortsplib.StreamProtocolUDP {
2020-09-05 11:19:55 +00:00
return s.runUDP(conn)
} else {
2020-09-05 11:19:55 +00:00
return s.runTCP(conn)
}
}
2020-10-03 19:10:41 +00:00
func (s *sourceRtsp) runUDP(conn *gortsplib.ConnClient) bool {
2020-07-19 20:39:38 +00:00
for _, track := range s.tracks {
2020-10-03 19:10:41 +00:00
_, err := conn.SetupUDP(s.path.conf.sourceUrl, gortsplib.SetupModePlay, track, 0, 0)
if err != nil {
conn.Close()
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
return true
}
}
2020-10-03 19:10:41 +00:00
_, err := conn.Play(s.path.conf.sourceUrl)
if err != nil {
conn.Close()
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
return true
}
2020-10-03 19:10:41 +00:00
s.p.sourceRtspReady <- s
s.path.log("rtsp source ready")
2020-07-19 20:39:38 +00:00
var wg sync.WaitGroup
// receive RTP packets
2020-09-27 13:52:48 +00:00
for _, track := range s.tracks {
wg.Add(1)
2020-09-27 13:52:48 +00:00
go func(track *gortsplib.Track) {
2020-07-19 20:39:38 +00:00
defer wg.Done()
for {
2020-09-27 13:52:48 +00:00
buf, err := conn.ReadFrameUDP(track, gortsplib.StreamTypeRtp)
2020-07-19 20:39:38 +00:00
if err != nil {
break
}
2020-09-27 13:52:48 +00:00
s.p.readersMap.forwardFrame(s.path, track.Id,
2020-09-20 12:17:36 +00:00
gortsplib.StreamTypeRtp, buf)
2020-07-19 20:39:38 +00:00
}
2020-09-27 13:52:48 +00:00
}(track)
}
2020-07-19 20:39:38 +00:00
// receive RTCP packets
2020-09-27 13:52:48 +00:00
for _, track := range s.tracks {
wg.Add(1)
2020-09-27 13:52:48 +00:00
go func(track *gortsplib.Track) {
2020-07-19 20:39:38 +00:00
defer wg.Done()
for {
2020-09-27 13:52:48 +00:00
buf, err := conn.ReadFrameUDP(track, gortsplib.StreamTypeRtcp)
2020-07-19 20:39:38 +00:00
if err != nil {
break
}
2020-09-27 13:52:48 +00:00
s.p.readersMap.forwardFrame(s.path, track.Id,
2020-09-20 12:17:36 +00:00
gortsplib.StreamTypeRtcp, buf)
2020-07-19 20:39:38 +00:00
}
2020-09-27 13:52:48 +00:00
}(track)
2020-07-19 20:39:38 +00:00
}
2020-07-19 15:54:31 +00:00
2020-07-19 16:50:43 +00:00
tcpConnDone := make(chan error)
2020-07-19 15:54:31 +00:00
go func() {
2020-10-03 19:10:41 +00:00
tcpConnDone <- conn.LoopUDP(s.path.conf.sourceUrl)
2020-07-19 15:54:31 +00:00
}()
var ret bool
outer:
for {
select {
2020-08-31 13:46:03 +00:00
case <-s.innerTerminate:
conn.Close()
2020-07-19 16:50:43 +00:00
<-tcpConnDone
ret = false
break outer
2020-07-19 15:54:31 +00:00
case err := <-tcpConnDone:
conn.Close()
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
2020-07-19 15:54:31 +00:00
ret = true
break outer
}
}
2020-07-19 20:39:38 +00:00
wg.Wait()
2020-10-03 19:10:41 +00:00
s.p.sourceRtspNotReady <- s
s.path.log("rtsp source not ready")
return ret
}
2020-10-03 19:10:41 +00:00
func (s *sourceRtsp) runTCP(conn *gortsplib.ConnClient) bool {
2020-07-19 15:54:31 +00:00
for _, track := range s.tracks {
2020-10-03 19:10:41 +00:00
_, err := conn.SetupTCP(s.path.conf.sourceUrl, gortsplib.SetupModePlay, track)
if err != nil {
conn.Close()
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
return true
}
}
2020-10-03 19:10:41 +00:00
_, err := conn.Play(s.path.conf.sourceUrl)
if err != nil {
conn.Close()
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
return true
}
2020-10-03 19:10:41 +00:00
s.p.sourceRtspReady <- s
s.path.log("rtsp source ready")
2020-07-19 16:50:43 +00:00
tcpConnDone := make(chan error)
go func() {
for {
2020-09-27 13:52:48 +00:00
frame, err := conn.ReadFrameTCP()
if err != nil {
tcpConnDone <- err
return
}
s.p.readersMap.forwardFrame(s.path, frame.TrackId, frame.StreamType, frame.Content)
}
}()
var ret bool
2020-07-12 20:53:22 +00:00
outer:
for {
select {
2020-08-31 13:46:03 +00:00
case <-s.innerTerminate:
conn.Close()
2020-07-19 16:50:43 +00:00
<-tcpConnDone
ret = false
2020-07-12 20:53:22 +00:00
break outer
case err := <-tcpConnDone:
conn.Close()
2020-10-03 19:10:41 +00:00
s.path.log("rtsp source ERR: %s", err)
ret = true
2020-07-12 20:53:22 +00:00
break outer
}
}
2020-10-03 19:10:41 +00:00
s.p.sourceRtspNotReady <- s
s.path.log("rtsp source not ready")
return ret
}