client: add write buffer

This commit is contained in:
aler9
2021-12-08 13:39:11 +01:00
parent ab465820ce
commit 5f3f7ec93a

138
client.go
View File

@@ -27,6 +27,7 @@ import (
"github.com/aler9/gortsplib/pkg/headers" "github.com/aler9/gortsplib/pkg/headers"
"github.com/aler9/gortsplib/pkg/liberrors" "github.com/aler9/gortsplib/pkg/liberrors"
"github.com/aler9/gortsplib/pkg/multibuffer" "github.com/aler9/gortsplib/pkg/multibuffer"
"github.com/aler9/gortsplib/pkg/ringbuffer"
"github.com/aler9/gortsplib/pkg/rtcpreceiver" "github.com/aler9/gortsplib/pkg/rtcpreceiver"
"github.com/aler9/gortsplib/pkg/rtcpsender" "github.com/aler9/gortsplib/pkg/rtcpsender"
) )
@@ -221,6 +222,8 @@ type Client struct {
tcpLastFrameTime int64 tcpLastFrameTime int64
keepaliveTimer *time.Timer keepaliveTimer *time.Timer
closeError error closeError error
writerRunning bool
writeBuffer *ringbuffer.RingBuffer
// connCloser channels // connCloser channels
connCloserTerminate chan struct{} connCloserTerminate chan struct{}
@@ -229,6 +232,9 @@ type Client struct {
// reader channels // reader channels
readerErr chan error readerErr chan error
// writer channels
writerDone chan struct{}
// in // in
options chan optionsReq options chan optionsReq
describe chan describeReq describe chan describeReq
@@ -672,6 +678,19 @@ func (c *Client) playRecordStart() {
// stop connCloser // stop connCloser
c.connCloserStop() c.connCloserStop()
// start writer
if c.state == clientStatePlay {
// when reading, writeBuffer is only used to send RTCP receiver reports,
// that are much smaller than RTP packets and are sent at a fixed interval.
// decrease RAM consumption by allocating less buffers.
c.writeBuffer = ringbuffer.New(8)
} else {
c.writeBuffer = ringbuffer.New(uint64(c.ReadBufferCount))
}
c.writerRunning = true
c.writerDone = make(chan struct{})
go c.runWriter()
// allow writing // allow writing
c.writeMutex.Lock() c.writeMutex.Lock()
c.writeFrameAllowed = true c.writeFrameAllowed = true
@@ -712,12 +731,11 @@ func (c *Client) playRecordStart() {
// start reader // start reader
c.readerErr = make(chan error) c.readerErr = make(chan error)
go func() { go c.runReader()
c.readerErr <- c.runReader()
}()
} }
func (c *Client) runReader() error { func (c *Client) runReader() {
c.readerErr <- func() error {
if *c.protocol == TransportUDP || *c.protocol == TransportUDPMulticast { if *c.protocol == TransportUDP || *c.protocol == TransportUDPMulticast {
for { for {
var res base.Response var res base.Response
@@ -777,6 +795,7 @@ func (c *Client) runReader() error {
} }
} }
} }
}()
} }
func (c *Client) playRecordStop(isClosing bool) { func (c *Client) playRecordStop(isClosing bool) {
@@ -796,6 +815,11 @@ func (c *Client) playRecordStop(isClosing bool) {
c.writeFrameAllowed = false c.writeFrameAllowed = false
c.writeMutex.Unlock() c.writeMutex.Unlock()
// stop writer
c.writeBuffer.Close()
<-c.writerDone
c.writerRunning = false
// start connCloser // start connCloser
if !isClosing { if !isClosing {
c.connCloserStart() c.connCloserStart()
@@ -1768,6 +1792,62 @@ func (c *Client) Seek(ra *headers.Range) (*base.Response, error) {
return c.Play(ra) return c.Play(ra)
} }
func (c *Client) runWriter() {
defer close(c.writerDone)
var writeFunc func(int, bool, []byte)
switch *c.protocol {
case TransportUDP, TransportUDPMulticast:
writeFunc = func(trackID int, isRTP bool, payload []byte) {
if isRTP {
if c.tracks[trackID].rtcpSender != nil {
c.tracks[trackID].rtcpSender.ProcessPacketRTP(time.Now(), payload)
}
c.tracks[trackID].udpRTPListener.write(payload)
} else {
c.tracks[trackID].udpRTCPListener.write(payload)
}
}
default: //TCP
writeFunc = func(trackID int, isRTP bool, payload []byte) {
if isRTP {
if c.tracks[trackID].rtcpSender != nil {
c.tracks[trackID].rtcpSender.ProcessPacketRTP(time.Now(), payload)
}
f := c.tracks[trackID].tcpRTPFrame
f.Payload = payload
c.tcpWriteMutex.Lock()
c.nconn.SetWriteDeadline(time.Now().Add(c.WriteTimeout))
f.Write(c.bw)
c.tcpWriteMutex.Unlock()
} else {
f := c.tracks[trackID].tcpRTCPFrame
f.Payload = payload
c.tcpWriteMutex.Lock()
c.nconn.SetWriteDeadline(time.Now().Add(c.WriteTimeout))
f.Write(c.bw)
c.tcpWriteMutex.Unlock()
}
}
}
for {
tmp, ok := c.writeBuffer.Pull()
if !ok {
return
}
data := tmp.(trackTypePayload)
writeFunc(data.trackID, data.isRTP, data.payload)
}
}
// WritePacketRTP writes a RTP packet. // WritePacketRTP writes a RTP packet.
func (c *Client) WritePacketRTP(trackID int, payload []byte) error { func (c *Client) WritePacketRTP(trackID int, payload []byte) error {
c.writeMutex.RLock() c.writeMutex.RLock()
@@ -1782,27 +1862,12 @@ func (c *Client) WritePacketRTP(trackID int, payload []byte) error {
} }
} }
now := time.Now() c.writeBuffer.Push(trackTypePayload{
trackID: trackID,
if c.tracks[trackID].rtcpSender != nil { isRTP: true,
c.tracks[trackID].rtcpSender.ProcessPacketRTP(now, payload) payload: payload,
} })
return nil
switch *c.protocol {
case TransportUDP, TransportUDPMulticast:
return c.tracks[trackID].udpRTPListener.write(payload)
default: // TCP
f := c.tracks[trackID].tcpRTPFrame
// a mutex is needed here since bufio.Writer is not thread safe.
c.tcpWriteMutex.Lock()
defer c.tcpWriteMutex.Unlock()
c.nconn.SetWriteDeadline(now.Add(c.WriteTimeout))
f.Payload = payload
return f.Write(c.bw)
}
} }
// WritePacketRTCP writes a RTCP packet. // WritePacketRTCP writes a RTCP packet.
@@ -1819,21 +1884,10 @@ func (c *Client) WritePacketRTCP(trackID int, payload []byte) error {
} }
} }
now := time.Now() c.writeBuffer.Push(trackTypePayload{
trackID: trackID,
switch *c.protocol { isRTP: false,
case TransportUDP, TransportUDPMulticast: payload: payload,
return c.tracks[trackID].udpRTCPListener.write(payload) })
return nil
default: // TCP
f := c.tracks[trackID].tcpRTCPFrame
// a mutex is needed here since bufio.Writer is not thread safe.
c.tcpWriteMutex.Lock()
defer c.tcpWriteMutex.Unlock()
c.nconn.SetWriteDeadline(now.Add(c.WriteTimeout))
f.Payload = payload
return f.Write(c.bw)
}
} }