Browse Source

implement rtcp receiver reports; fix #22

pull/31/head
aler9 5 years ago
parent
commit
03f020ec33
  1. 8
      Makefile
  2. 14
      conf.yml
  3. 3
      go.mod
  4. 4
      go.sum
  5. 62
      main.go
  6. 191
      server-client.go
  7. 3
      server-udpl.go
  8. 29
      streamer-udpl.go
  9. 205
      streamer.go
  10. 140
      utils.go

8
Makefile

@ -65,10 +65,18 @@ endef @@ -65,10 +65,18 @@ endef
export DOCKERFILE_RUN
define CONFIG_RUN
#rtspPort: 8555
#rtpPort: 8002
#rtcpPort: 8003
paths:
all:
readUser: test
readPass: tast
proxied:
source: rtsp://192.168.10.1/unicast
sourceProtocol: udp
endef
export CONFIG_RUN

14
conf.yml

@ -1,20 +1,20 @@ @@ -1,20 +1,20 @@
# supported stream protocols (the handshake is always performed with TCP)
protocols: [udp, tcp]
# port of the TCP rtsp listener
# port of the TCP RTSP listener
rtspPort: 8554
# port of the UDP rtp listener
# port of the UDP RTP listener
rtpPort: 8000
# port of the UDP rtcp listener
# port of the UDP RTCP listener
rtcpPort: 8001
# timeout of read operations
readTimeout: 5s
# timeout of write operations
writeTimeout: 5s
# script to run when a client connects
preScript:
# script to run when a client disconnects
postScript:
# timeout of read operations
readTimeout: 5s
# timeout of write operations
writeTimeout: 5s
# enable pprof on port 9999 to monitor performance
pprof: false

3
go.mod

@ -6,7 +6,8 @@ require ( @@ -6,7 +6,8 @@ require (
github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751 // indirect
github.com/alecthomas/units v0.0.0-20190924025748-f65c72e2690d // indirect
github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68
github.com/stretchr/testify v1.4.0
github.com/pion/rtcp v1.2.3
github.com/stretchr/testify v1.5.1
gopkg.in/alecthomas/kingpin.v2 v2.2.6
gopkg.in/yaml.v2 v2.2.2
gortc.io/sdp v0.18.2

4
go.sum

@ -6,6 +6,8 @@ github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68 h1:apyYugiG/luHl0X @@ -6,6 +6,8 @@ github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68 h1:apyYugiG/luHl0X
github.com/aler9/gortsplib v0.0.0-20200710091324-fb7d7b008e68/go.mod h1:sL64nUkmrTVhlT/GCaxRXyI2Xk7m8XSdw5Uv8xKGPdc=
github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/pion/rtcp v1.2.3 h1:2wrhKnqgSz91Q5nzYTO07mQXztYPtxL8a0XOss4rJqA=
github.com/pion/rtcp v1.2.3/go.mod h1:zGhIv0RPRF0Z1Wiij22pUt5W/c9fevqSzT4jje/oK7I=
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
@ -13,6 +15,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN @@ -13,6 +15,8 @@ github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZN
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
github.com/stretchr/testify v1.5.1 h1:nOGnQDM7FYENwehXlg/kFVnos3rEvtKTjRvOWSzb6H4=
github.com/stretchr/testify v1.5.1/go.mod h1:5W2xD1RspED5o8YsWQXVCued0rvSQ+mT+I5cxcmMvtA=
gopkg.in/alecthomas/kingpin.v2 v2.2.6 h1:jMFz6MfLP0/4fUyZle81rXUoxOBFi19VUFKVDOQfozc=
gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=

62
main.go

@ -11,6 +11,7 @@ import ( @@ -11,6 +11,7 @@ import (
"regexp"
"time"
"github.com/aler9/gortsplib"
"gopkg.in/alecthomas/kingpin.v2"
"gopkg.in/yaml.v2"
"gortc.io/sdp"
@ -223,9 +224,9 @@ type publisher interface { @@ -223,9 +224,9 @@ type publisher interface {
type program struct {
conf *conf
protocols map[streamProtocol]struct{}
tcpl *serverTcpListener
udplRtp *serverUdpListener
udplRtcp *serverUdpListener
rtspl *serverTcpListener
rtpl *serverUdpListener
rtcpl *serverUdpListener
clients map[*serverClient]struct{}
streamers []*streamer
publishers map[string]publisher
@ -387,24 +388,24 @@ func newProgram(sargs []string, stdin io.Reader) (*program, error) { @@ -387,24 +388,24 @@ func newProgram(sargs []string, stdin io.Reader) (*program, error) {
http.DefaultServeMux = http.NewServeMux()
}
p.udplRtp, err = newServerUdpListener(p, conf.RtpPort, _TRACK_FLOW_TYPE_RTP)
p.rtpl, err = newServerUdpListener(p, conf.RtpPort, _TRACK_FLOW_TYPE_RTP)
if err != nil {
return nil, err
}
p.udplRtcp, err = newServerUdpListener(p, conf.RtcpPort, _TRACK_FLOW_TYPE_RTCP)
p.rtcpl, err = newServerUdpListener(p, conf.RtcpPort, _TRACK_FLOW_TYPE_RTCP)
if err != nil {
return nil, err
}
p.tcpl, err = newServerTcpListener(p)
p.rtspl, err = newServerTcpListener(p)
if err != nil {
return nil, err
}
go p.udplRtp.run()
go p.udplRtcp.run()
go p.tcpl.run()
go p.rtpl.run()
go p.rtcpl.run()
go p.rtspl.run()
for _, s := range p.streamers {
go s.run()
}
@ -548,7 +549,7 @@ outer: @@ -548,7 +549,7 @@ outer:
continue
}
client.udpLastFrameTime = time.Now()
client.rtcpReceivers[trackId].onFrame(evt.trackFlowType, evt.buf)
p.forwardFrame(client.path, trackId, evt.trackFlowType, evt.buf)
case programEventClientFrameTcp:
@ -613,9 +614,9 @@ outer: @@ -613,9 +614,9 @@ outer:
s.close()
}
p.tcpl.close()
p.udplRtcp.close()
p.udplRtp.close()
p.rtspl.close()
p.rtcpl.close()
p.rtpl.close()
for c := range p.clients {
c.close()
@ -659,31 +660,42 @@ func (p *program) findPublisher(addr *net.UDPAddr, trackFlowType trackFlowType) @@ -659,31 +660,42 @@ func (p *program) findPublisher(addr *net.UDPAddr, trackFlowType trackFlowType)
}
func (p *program) forwardFrame(path string, trackId int, trackFlowType trackFlowType, frame []byte) {
for c := range p.clients {
if c.path == path && c.state == _CLIENT_STATE_PLAY {
if c.streamProtocol == _STREAM_PROTOCOL_UDP {
for client := range p.clients {
if client.path == path && client.state == _CLIENT_STATE_PLAY {
if client.streamProtocol == _STREAM_PROTOCOL_UDP {
if trackFlowType == _TRACK_FLOW_TYPE_RTP {
p.udplRtp.write(&udpAddrBufPair{
p.rtpl.write(&udpAddrBufPair{
addr: &net.UDPAddr{
IP: c.ip(),
Zone: c.zone(),
Port: c.streamTracks[trackId].rtpPort,
IP: client.ip(),
Zone: client.zone(),
Port: client.streamTracks[trackId].rtpPort,
},
buf: frame,
})
} else {
p.udplRtcp.write(&udpAddrBufPair{
p.rtcpl.write(&udpAddrBufPair{
addr: &net.UDPAddr{
IP: c.ip(),
Zone: c.zone(),
Port: c.streamTracks[trackId].rtcpPort,
IP: client.ip(),
Zone: client.zone(),
Port: client.streamTracks[trackId].rtcpPort,
},
buf: frame,
})
}
} else {
c.writeFrame(trackFlowTypeToInterleavedChannel(trackId, trackFlowType), frame)
channel := trackFlowTypeToInterleavedChannel(trackId, trackFlowType)
buf := client.writeBuf.swap()
buf = buf[:len(frame)]
copy(buf, frame)
client.events <- serverClientEventFrameTcp{
frame: &gortsplib.InterleavedFrame{
Channel: channel,
Content: buf,
},
}
}
}
}

191
server-client.go

@ -14,10 +14,21 @@ import ( @@ -14,10 +14,21 @@ import (
)
const (
_UDP_CHECK_STREAM_INTERVAL = 5 * time.Second
_UDP_STREAM_DEAD_AFTER = 10 * time.Second
_CLIENT_CHECK_STREAM_INTERVAL = 5 * time.Second
_CLIENT_STREAM_DEAD_AFTER = 15 * time.Second
_CLIENT_RECEIVER_REPORT_INTERVAL = 10 * time.Second
)
type serverClientEvent interface {
isServerClientEvent()
}
type serverClientEventFrameTcp struct {
frame *gortsplib.InterleavedFrame
}
func (serverClientEventFrameTcp) isServerClientEvent() {}
type serverClientState int
const (
@ -65,11 +76,11 @@ type serverClient struct { @@ -65,11 +76,11 @@ type serverClient struct {
streamSdpParsed *sdp.Message // only if publisher
streamProtocol streamProtocol
streamTracks []*track
udpLastFrameTime time.Time
rtcpReceivers []*rtcpReceiver
readBuf *doubleBuffer
writeBuf *doubleBuffer
writeChan chan *gortsplib.InterleavedFrame // only if state = _CLIENT_STATE_PLAY
events chan serverClientEvent
done chan struct{}
}
@ -201,31 +212,45 @@ outer: @@ -201,31 +212,45 @@ outer:
func (c *serverClient) runPlay() bool {
if c.streamProtocol == _STREAM_PROTOCOL_TCP {
writeDone := make(chan struct{})
readDone := make(chan error)
go func() {
defer close(writeDone)
for frame := range c.writeChan {
c.conn.WriteInterleavedFrame(frame)
}
}()
buf := make([]byte, 2048)
for {
_, err := c.conn.NetConn().Read(buf)
if err != nil {
readDone <- err
break
}
}
}()
outer:
for {
select {
case err := <-readDone:
if err != io.EOF {
c.log("ERR: %s", err)
}
break
break outer
case rawEvt := <-c.events:
switch evt := rawEvt.(type) {
case serverClientEventFrameTcp:
c.conn.WriteInterleavedFrame(evt.frame)
}
}
}
go func() {
for range c.events {
}
}()
done := make(chan struct{})
c.p.events <- programEventClientClose{done, c}
<-done
close(c.writeChan)
<-writeDone
close(c.events)
} else {
for {
@ -255,27 +280,27 @@ func (c *serverClient) runRecord() bool { @@ -255,27 +280,27 @@ func (c *serverClient) runRecord() bool {
if c.streamProtocol == _STREAM_PROTOCOL_TCP {
frame := &gortsplib.InterleavedFrame{}
outer:
readDone := make(chan error)
go func() {
for {
frame.Content = c.readBuf.swap()
frame.Content = frame.Content[:cap(frame.Content)]
recv, err := c.conn.ReadInterleavedFrameOrRequest(frame)
if err != nil {
if err != io.EOF {
c.log("ERR: %s", err)
}
break outer
readDone <- err
break
}
switch recvt := recv.(type) {
case *gortsplib.InterleavedFrame:
trackId, trackFlowType := interleavedChannelToTrackFlowType(frame.Channel)
if trackId >= len(c.streamTracks) {
c.log("ERR: invalid track id '%d'", trackId)
break outer
readDone <- nil
break
}
c.rtcpReceivers[trackId].onFrame(trackFlowType, frame.Content)
c.p.events <- programEventClientFrameTcp{
c.path,
trackId,
@ -286,52 +311,116 @@ func (c *serverClient) runRecord() bool { @@ -286,52 +311,116 @@ func (c *serverClient) runRecord() bool {
case *gortsplib.Request:
err := c.handleRequest(recvt)
if err != nil {
break outer
readDone <- nil
break
}
}
}
}()
done := make(chan struct{})
c.p.events <- programEventClientClose{done, c}
<-done
checkStreamTicker := time.NewTicker(_CLIENT_CHECK_STREAM_INTERVAL)
receiverReportTicker := time.NewTicker(_CLIENT_RECEIVER_REPORT_INTERVAL)
} else {
c.udpLastFrameTime = time.Now()
outer1:
for {
select {
case err := <-readDone:
if err != nil && err != io.EOF {
c.log("ERR: %s", err)
}
break outer1
udpCheckStreamTicker := time.NewTicker(_UDP_CHECK_STREAM_INTERVAL)
udpCheckStreamDone := make(chan struct{})
go func() {
defer close(udpCheckStreamDone)
for range udpCheckStreamTicker.C {
if time.Since(c.udpLastFrameTime) >= _UDP_STREAM_DEAD_AFTER {
case <-checkStreamTicker.C:
for trackId := range c.streamTracks {
if time.Since(c.rtcpReceivers[trackId].lastFrameTime()) >= _CLIENT_STREAM_DEAD_AFTER {
c.log("ERR: stream is dead")
c.conn.NetConn().Close()
break
<-readDone
break outer1
}
}
}()
case <-receiverReportTicker.C:
for trackId := range c.streamTracks {
channel := trackFlowTypeToInterleavedChannel(trackId, _TRACK_FLOW_TYPE_RTCP)
frame := c.rtcpReceivers[trackId].report()
c.conn.WriteInterleavedFrame(&gortsplib.InterleavedFrame{
Channel: channel,
Content: frame,
})
}
}
}
checkStreamTicker.Stop()
receiverReportTicker.Stop()
} else {
readDone := make(chan error)
go func() {
for {
req, err := c.conn.ReadRequest()
if err != nil {
if err != io.EOF {
c.log("ERR: %s", err)
}
readDone <- err
break
}
err = c.handleRequest(req)
if err != nil {
readDone <- nil // err is not needed
break
}
}
}()
checkStreamTicker := time.NewTicker(_CLIENT_CHECK_STREAM_INTERVAL)
receiverReportTicker := time.NewTicker(_CLIENT_RECEIVER_REPORT_INTERVAL)
outer2:
for {
select {
case err := <-readDone:
if err != nil && err != io.EOF {
c.log("ERR: %s", err)
}
break outer2
case <-checkStreamTicker.C:
for trackId := range c.streamTracks {
if time.Since(c.rtcpReceivers[trackId].lastFrameTime()) >= _CLIENT_STREAM_DEAD_AFTER {
c.log("ERR: stream is dead")
c.conn.NetConn().Close()
<-readDone
break outer2
}
}
case <-receiverReportTicker.C:
for trackId := range c.streamTracks {
frame := c.rtcpReceivers[trackId].report()
c.p.rtcpl.writeChan <- &udpAddrBufPair{
addr: &net.UDPAddr{
IP: c.ip(),
Zone: c.zone(),
Port: c.streamTracks[trackId].rtcpPort,
},
buf: frame,
}
}
}
}
checkStreamTicker.Stop()
receiverReportTicker.Stop()
}
done := make(chan struct{})
c.p.events <- programEventClientClose{done, c}
<-done
udpCheckStreamTicker.Stop()
<-udpCheckStreamDone
for trackId := range c.streamTracks {
c.rtcpReceivers[trackId].close()
}
return false
@ -342,17 +431,6 @@ func (c *serverClient) close() { @@ -342,17 +431,6 @@ func (c *serverClient) close() {
<-c.done
}
func (c *serverClient) writeFrame(channel uint8, inbuf []byte) {
buf := c.writeBuf.swap()
buf = buf[:len(inbuf)]
copy(buf, inbuf)
c.writeChan <- &gortsplib.InterleavedFrame{
Channel: channel,
Content: buf,
}
}
func (c *serverClient) writeResError(req *gortsplib.Request, code gortsplib.StatusCode, err error) {
c.log("ERR: %s", err)
@ -532,7 +610,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error { @@ -532,7 +610,7 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error {
c.p.events <- programEventClientDescribe{path, res}
sdp := <-res
if sdp == nil {
c.writeResError(req, gortsplib.StatusBadRequest, fmt.Errorf("no one is streaming on path '%s'", path))
c.writeResError(req, gortsplib.StatusNotFound, fmt.Errorf("no one is publishing on path '%s'", path))
return errClientTerminate
}
@ -906,8 +984,10 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error { @@ -906,8 +984,10 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error {
},
})
if c.streamProtocol == _STREAM_PROTOCOL_TCP {
c.writeBuf = newDoubleBuffer(2048)
c.writeChan = make(chan *gortsplib.InterleavedFrame)
c.events = make(chan serverClientEvent)
}
// set state
res = make(chan error)
@ -948,6 +1028,11 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error { @@ -948,6 +1028,11 @@ func (c *serverClient) handleRequest(req *gortsplib.Request) error {
},
})
c.rtcpReceivers = make([]*rtcpReceiver, len(c.streamTracks))
for trackId := range c.streamTracks {
c.rtcpReceivers[trackId] = newRtcpReceiver()
}
res := make(chan error)
c.p.events <- programEventClientRecord{res, c}
<-res

3
server-udpl.go

@ -54,7 +54,9 @@ func (l *serverUdpListener) log(format string, args ...interface{}) { @@ -54,7 +54,9 @@ func (l *serverUdpListener) log(format string, args ...interface{}) {
}
func (l *serverUdpListener) run() {
writeDone := make(chan struct{})
go func() {
defer close(writeDone)
for w := range l.writeChan {
l.nconn.SetWriteDeadline(time.Now().Add(l.p.conf.WriteTimeout))
l.nconn.WriteTo(w.buf, w.addr)
@ -76,6 +78,7 @@ func (l *serverUdpListener) run() { @@ -76,6 +78,7 @@ func (l *serverUdpListener) run() {
}
close(l.writeChan)
<-writeDone
close(l.done)
}

29
streamer-udpl.go

@ -16,6 +16,7 @@ type streamerUdpListener struct { @@ -16,6 +16,7 @@ type streamerUdpListener struct {
running bool
readBuf *doubleBuffer
writeChan chan *udpAddrBufPair
done chan struct{}
}
@ -36,6 +37,7 @@ func newStreamerUdpListener(p *program, port int, streamer *streamer, @@ -36,6 +37,7 @@ func newStreamerUdpListener(p *program, port int, streamer *streamer,
publisherIp: publisherIp,
nconn: nconn,
readBuf: newDoubleBuffer(2048),
writeChan: make(chan *udpAddrBufPair),
done: make(chan struct{}),
}
@ -43,19 +45,28 @@ func newStreamerUdpListener(p *program, port int, streamer *streamer, @@ -43,19 +45,28 @@ func newStreamerUdpListener(p *program, port int, streamer *streamer,
}
func (l *streamerUdpListener) close() {
l.nconn.Close()
if l.running {
<-l.done
}
l.nconn.Close() // close twice
}
func (l *streamerUdpListener) start() {
l.running = true
go l.run()
}
func (l *streamerUdpListener) stop() {
l.nconn.Close()
<-l.done
}
func (l *streamerUdpListener) run() {
writeDone := make(chan struct{})
go func() {
defer close(writeDone)
for w := range l.writeChan {
l.nconn.SetWriteDeadline(time.Now().Add(l.p.conf.WriteTimeout))
l.nconn.WriteTo(w.buf, w.addr)
}
}()
for {
buf := l.readBuf.swap()
n, addr, err := l.nconn.ReadFromUDP(buf)
@ -67,10 +78,12 @@ func (l *streamerUdpListener) run() { @@ -67,10 +78,12 @@ func (l *streamerUdpListener) run() {
continue
}
l.streamer.udpLastFrameTime = time.Now()
l.streamer.rtcpReceivers[l.trackId].onFrame(l.trackFlowType, buf[:n])
l.p.events <- programEventStreamerFrame{l.streamer, l.trackId, l.trackFlowType, buf[:n]}
}
close(l.writeChan)
<-writeDone
close(l.done)
}

205
streamer.go

@ -14,16 +14,16 @@ import ( @@ -14,16 +14,16 @@ import (
)
const (
_DIAL_TIMEOUT = 10 * time.Second
_RETRY_INTERVAL = 5 * time.Second
_CHECK_STREAM_INTERVAL = 6 * time.Second
_STREAM_DEAD_AFTER = 5 * time.Second
_KEEPALIVE_INTERVAL = 60 * time.Second
_STREAMER_RETRY_INTERVAL = 5 * time.Second
_STREAMER_CHECK_STREAM_INTERVAL = 5 * time.Second
_STREAMER_STREAM_DEAD_AFTER = 15 * time.Second
_STREAMER_KEEPALIVE_INTERVAL = 60 * time.Second
_STREAMER_RECEIVER_REPORT_INTERVAL = 10 * time.Second
)
type streamerUdpListenerPair struct {
udplRtp *streamerUdpListener
udplRtcp *streamerUdpListener
rtpl *streamerUdpListener
rtcpl *streamerUdpListener
}
type streamer struct {
@ -35,8 +35,7 @@ type streamer struct { @@ -35,8 +35,7 @@ type streamer struct {
clientSdpParsed *sdp.Message
serverSdpText []byte
serverSdpParsed *sdp.Message
firstTime bool
udpLastFrameTime time.Time
rtcpReceivers []*rtcpReceiver
readBuf *doubleBuffer
terminate chan struct{}
@ -82,7 +81,6 @@ func newStreamer(p *program, path string, source string, sourceProtocol string) @@ -82,7 +81,6 @@ func newStreamer(p *program, path string, source string, sourceProtocol string)
path: path,
ur: ur,
proto: proto,
firstTime: true,
readBuf: newDoubleBuffer(512 * 1024),
terminate: make(chan struct{}),
done: make(chan struct{}),
@ -113,30 +111,26 @@ func (s *streamer) run() { @@ -113,30 +111,26 @@ func (s *streamer) run() {
if !ok {
break
}
}
close(s.done)
}
func (s *streamer) do() bool {
if s.firstTime {
s.firstTime = false
} else {
t := time.NewTimer(_RETRY_INTERVAL)
t := time.NewTimer(_STREAMER_RETRY_INTERVAL)
select {
case <-s.terminate:
return false
break
case <-t.C:
}
}
close(s.done)
}
func (s *streamer) do() bool {
s.log("initializing with protocol %s", s.proto)
var nconn net.Conn
var err error
dialDone := make(chan struct{})
go func() {
nconn, err = net.DialTimeout("tcp", s.ur.Host, _DIAL_TIMEOUT)
nconn, err = net.DialTimeout("tcp", s.ur.Host, s.p.conf.ReadTimeout)
close(dialDone)
}()
@ -251,16 +245,16 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool { @@ -251,16 +245,16 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
defer func() {
for _, pair := range streamerUdpListenerPairs {
pair.udplRtp.close()
pair.udplRtcp.close()
pair.rtpl.close()
pair.rtcpl.close()
}
}()
for i, media := range s.clientSdpParsed.Medias {
var rtpPort int
var rtcpPort int
var udplRtp *streamerUdpListener
var udplRtcp *streamerUdpListener
var rtpl *streamerUdpListener
var rtcpl *streamerUdpListener
func() {
for {
// choose two consecutive ports in range 65536-10000
@ -269,16 +263,16 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool { @@ -269,16 +263,16 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
rtcpPort = rtpPort + 1
var err error
udplRtp, err = newStreamerUdpListener(s.p, rtpPort, s, i,
rtpl, err = newStreamerUdpListener(s.p, rtpPort, s, i,
_TRACK_FLOW_TYPE_RTP, publisherIp)
if err != nil {
continue
}
udplRtcp, err = newStreamerUdpListener(s.p, rtcpPort, s, i,
rtcpl, err = newStreamerUdpListener(s.p, rtcpPort, s, i,
_TRACK_FLOW_TYPE_RTCP, publisherIp)
if err != nil {
udplRtp.close()
rtpl.close()
continue
}
@ -338,23 +332,23 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool { @@ -338,23 +332,23 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
})
if err != nil {
s.log("ERR: %s", err)
udplRtp.close()
udplRtcp.close()
rtpl.close()
rtcpl.close()
return true
}
if res.StatusCode != gortsplib.StatusOK {
s.log("ERR: SETUP returned code %d (%s)", res.StatusCode, res.StatusMessage)
udplRtp.close()
udplRtcp.close()
rtpl.close()
rtcpl.close()
return true
}
tsRaw, ok := res.Header["Transport"]
if !ok || len(tsRaw) != 1 {
s.log("ERR: transport header not provided")
udplRtp.close()
udplRtcp.close()
rtpl.close()
rtcpl.close()
return true
}
@ -362,17 +356,17 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool { @@ -362,17 +356,17 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
rtpServerPort, rtcpServerPort := th.GetPorts("server_port")
if rtpServerPort == 0 {
s.log("ERR: server ports not provided")
udplRtp.close()
udplRtcp.close()
rtpl.close()
rtcpl.close()
return true
}
udplRtp.publisherPort = rtpServerPort
udplRtcp.publisherPort = rtcpServerPort
rtpl.publisherPort = rtpServerPort
rtcpl.publisherPort = rtcpServerPort
streamerUdpListenerPairs = append(streamerUdpListenerPairs, streamerUdpListenerPair{
udplRtp: udplRtp,
udplRtcp: udplRtcp,
rtpl: rtpl,
rtcpl: rtcpl,
})
}
@ -395,30 +389,32 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool { @@ -395,30 +389,32 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
return true
}
for _, pair := range streamerUdpListenerPairs {
pair.udplRtp.start()
pair.udplRtcp.start()
s.rtcpReceivers = make([]*rtcpReceiver, len(s.clientSdpParsed.Medias))
for trackId := range s.clientSdpParsed.Medias {
s.rtcpReceivers[trackId] = newRtcpReceiver()
}
tickerSendKeepalive := time.NewTicker(_KEEPALIVE_INTERVAL)
defer tickerSendKeepalive.Stop()
for _, pair := range streamerUdpListenerPairs {
pair.rtpl.start()
pair.rtcpl.start()
}
s.udpLastFrameTime = time.Now()
tickerCheckStream := time.NewTicker(_CHECK_STREAM_INTERVAL)
defer tickerCheckStream.Stop()
sendKeepaliveTicker := time.NewTicker(_STREAMER_KEEPALIVE_INTERVAL)
checkStreamTicker := time.NewTicker(_STREAMER_CHECK_STREAM_INTERVAL)
receiverReportTicker := time.NewTicker(_STREAMER_RECEIVER_REPORT_INTERVAL)
s.p.events <- programEventStreamerReady{s}
defer func() {
s.p.events <- programEventStreamerNotReady{s}
}()
var ret bool
outer:
for {
select {
case <-s.terminate:
return false
ret = false
break outer
case <-tickerSendKeepalive.C:
case <-sendKeepaliveTicker.C:
_, err = conn.WriteRequest(&gortsplib.Request{
Method: gortsplib.OPTIONS,
Url: &url.URL{
@ -429,18 +425,52 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool { @@ -429,18 +425,52 @@ func (s *streamer) runUdp(conn *gortsplib.ConnClient) bool {
})
if err != nil {
s.log("ERR: %s", err)
return true
ret = true
break outer
}
case <-tickerCheckStream.C:
if time.Since(s.udpLastFrameTime) >= _STREAM_DEAD_AFTER {
case <-checkStreamTicker.C:
for trackId := range s.clientSdpParsed.Medias {
if time.Since(s.rtcpReceivers[trackId].lastFrameTime()) >= _STREAMER_STREAM_DEAD_AFTER {
s.log("ERR: stream is dead")
return true
ret = true
break outer
}
}
case <-receiverReportTicker.C:
for trackId := range s.clientSdpParsed.Medias {
frame := s.rtcpReceivers[trackId].report()
streamerUdpListenerPairs[trackId].rtcpl.writeChan <- &udpAddrBufPair{
addr: &net.UDPAddr{
IP: conn.NetConn().RemoteAddr().(*net.TCPAddr).IP,
Zone: conn.NetConn().RemoteAddr().(*net.TCPAddr).Zone,
Port: streamerUdpListenerPairs[trackId].rtcpl.publisherPort,
},
buf: frame,
}
}
}
}
sendKeepaliveTicker.Stop()
checkStreamTicker.Stop()
receiverReportTicker.Stop()
s.p.events <- programEventStreamerNotReady{s}
for _, pair := range streamerUdpListenerPairs {
pair.rtpl.stop()
pair.rtcpl.stop()
}
for trackId := range s.clientSdpParsed.Medias {
s.rtcpReceivers[trackId].close()
}
return ret
}
func (s *streamer) runTcp(conn *gortsplib.ConnClient) bool {
for i, media := range s.clientSdpParsed.Medias {
interleaved := fmt.Sprintf("interleaved=%d-%d", (i * 2), (i*2)+1)
@ -536,7 +566,7 @@ func (s *streamer) runTcp(conn *gortsplib.ConnClient) bool { @@ -536,7 +566,7 @@ func (s *streamer) runTcp(conn *gortsplib.ConnClient) bool {
frame := &gortsplib.InterleavedFrame{}
outer:
outer1:
for {
frame.Content = s.readBuf.swap()
frame.Content = frame.Content[:cap(frame.Content)]
@ -553,18 +583,19 @@ outer: @@ -553,18 +583,19 @@ outer:
s.log("ERR: PLAY returned code %d (%s)", recvt.StatusCode, recvt.StatusMessage)
return true
}
break outer
break outer1
case *gortsplib.InterleavedFrame:
// ignore the frames sent before the response
}
}
s.p.events <- programEventStreamerReady{s}
s.rtcpReceivers = make([]*rtcpReceiver, len(s.clientSdpParsed.Medias))
for trackId := range s.clientSdpParsed.Medias {
s.rtcpReceivers[trackId] = newRtcpReceiver()
}
defer func() {
s.p.events <- programEventStreamerNotReady{s}
}()
s.p.events <- programEventStreamerReady{s}
chanConnError := make(chan struct{})
go func() {
@ -580,18 +611,62 @@ outer: @@ -580,18 +611,62 @@ outer:
trackId, trackFlowType := interleavedChannelToTrackFlowType(frame.Channel)
s.rtcpReceivers[trackId].onFrame(trackFlowType, frame.Content)
s.p.events <- programEventStreamerFrame{s, trackId, trackFlowType, frame.Content}
}
}()
checkStreamTicker := time.NewTicker(_STREAMER_CHECK_STREAM_INTERVAL)
receiverReportTicker := time.NewTicker(_STREAMER_RECEIVER_REPORT_INTERVAL)
var ret bool
outer2:
for {
select {
case <-s.terminate:
return false
ret = false
break outer2
case <-chanConnError:
return true
ret = true
break outer2
case <-checkStreamTicker.C:
for trackId := range s.clientSdpParsed.Medias {
if time.Since(s.rtcpReceivers[trackId].lastFrameTime()) >= _STREAMER_STREAM_DEAD_AFTER {
s.log("ERR: stream is dead")
ret = true
break outer2
}
}
case <-receiverReportTicker.C:
for trackId := range s.clientSdpParsed.Medias {
frame := s.rtcpReceivers[trackId].report()
channel := trackFlowTypeToInterleavedChannel(trackId, _TRACK_FLOW_TYPE_RTCP)
conn.WriteInterleavedFrame(&gortsplib.InterleavedFrame{
Channel: channel,
Content: frame,
})
}
}
}
checkStreamTicker.Stop()
receiverReportTicker.Stop()
s.p.events <- programEventStreamerNotReady{s}
for trackId := range s.clientSdpParsed.Medias {
s.rtcpReceivers[trackId].close()
}
return ret
}
func (s *streamer) close() {
close(s.terminate)
<-s.done

140
utils.go

@ -2,7 +2,11 @@ package main @@ -2,7 +2,11 @@ package main
import (
"fmt"
"math/rand"
"net"
"time"
"github.com/pion/rtcp"
)
func parseIpCidrList(in []string) ([]interface{}, error) {
@ -90,3 +94,139 @@ func (db *doubleBuffer) swap() []byte { @@ -90,3 +94,139 @@ func (db *doubleBuffer) swap() []byte {
db.curBuf = !db.curBuf
return ret
}
type rtcpReceiverEvent interface {
isRtpReceiverEvent()
}
type rtcpReceiverEventFrameRtp struct {
sequenceNumber uint16
}
func (rtcpReceiverEventFrameRtp) isRtpReceiverEvent() {}
type rtcpReceiverEventFrameRtcp struct {
ssrc uint32
ntpTimeMiddle uint32
}
func (rtcpReceiverEventFrameRtcp) isRtpReceiverEvent() {}
type rtcpReceiverEventLastFrameTime struct {
res chan time.Time
}
func (rtcpReceiverEventLastFrameTime) isRtpReceiverEvent() {}
type rtcpReceiverEventReport struct {
res chan []byte
}
func (rtcpReceiverEventReport) isRtpReceiverEvent() {}
type rtcpReceiverEventTerminate struct{}
func (rtcpReceiverEventTerminate) isRtpReceiverEvent() {}
type rtcpReceiver struct {
events chan rtcpReceiverEvent
done chan struct{}
}
func newRtcpReceiver() *rtcpReceiver {
rr := &rtcpReceiver{
events: make(chan rtcpReceiverEvent),
done: make(chan struct{}),
}
go rr.run()
return rr
}
func (rr *rtcpReceiver) run() {
lastFrameTime := time.Now()
publisherSSRC := uint32(0)
receiverSSRC := rand.Uint32()
sequenceNumberCycles := uint16(0)
lastSequenceNumber := uint16(0)
lastSenderReport := uint32(0)
outer:
for rawEvt := range rr.events {
switch evt := rawEvt.(type) {
case rtcpReceiverEventFrameRtp:
if evt.sequenceNumber < lastSequenceNumber {
sequenceNumberCycles += 1
}
lastSequenceNumber = evt.sequenceNumber
lastFrameTime = time.Now()
case rtcpReceiverEventFrameRtcp:
publisherSSRC = evt.ssrc
lastSenderReport = evt.ntpTimeMiddle
case rtcpReceiverEventLastFrameTime:
evt.res <- lastFrameTime
case rtcpReceiverEventReport:
rr := &rtcp.ReceiverReport{
SSRC: receiverSSRC,
Reports: []rtcp.ReceptionReport{
{
SSRC: publisherSSRC,
LastSequenceNumber: uint32(sequenceNumberCycles)<<8 | uint32(lastSequenceNumber),
LastSenderReport: lastSenderReport,
},
},
}
frame, _ := rr.Marshal()
evt.res <- frame
case rtcpReceiverEventTerminate:
break outer
}
}
close(rr.events)
close(rr.done)
}
func (rr *rtcpReceiver) close() {
rr.events <- rtcpReceiverEventTerminate{}
<-rr.done
}
func (rr *rtcpReceiver) onFrame(trackFlowType trackFlowType, buf []byte) {
if trackFlowType == _TRACK_FLOW_TYPE_RTP {
// extract sequence number of first frame
if len(buf) >= 3 {
sequenceNumber := uint16(uint16(buf[2])<<8 | uint16(buf[1]))
rr.events <- rtcpReceiverEventFrameRtp{sequenceNumber}
}
} else {
frames, err := rtcp.Unmarshal(buf)
if err == nil {
for _, frame := range frames {
if senderReport, ok := (frame).(*rtcp.SenderReport); ok {
rr.events <- rtcpReceiverEventFrameRtcp{
senderReport.SSRC,
uint32(senderReport.NTPTime >> 16),
}
}
}
}
}
}
func (rr *rtcpReceiver) lastFrameTime() time.Time {
res := make(chan time.Time)
rr.events <- rtcpReceiverEventLastFrameTime{res}
return <-res
}
func (rr *rtcpReceiver) report() []byte {
res := make(chan []byte)
rr.events <- rtcpReceiverEventReport{res}
return <-res
}

Loading…
Cancel
Save