mirror of
https://github.com/aler9/gortsplib
synced 2025-10-04 23:02:45 +08:00
This commit is contained in:
279
client.go
279
client.go
@@ -19,6 +19,8 @@ import (
|
|||||||
"github.com/pion/rtcp"
|
"github.com/pion/rtcp"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v4/internal/rtcpreceiver"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/internal/rtcpsender"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/auth"
|
"github.com/bluenviron/gortsplib/v4/pkg/auth"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/bytecounter"
|
"github.com/bluenviron/gortsplib/v4/pkg/bytecounter"
|
||||||
@@ -269,8 +271,10 @@ type Client struct {
|
|||||||
// explicitly request back channels to the server.
|
// explicitly request back channels to the server.
|
||||||
RequestBackChannels bool
|
RequestBackChannels bool
|
||||||
// pointer to a variable that stores received bytes.
|
// pointer to a variable that stores received bytes.
|
||||||
|
// Deprecated: use Client.Stats()
|
||||||
BytesReceived *uint64
|
BytesReceived *uint64
|
||||||
// pointer to a variable that stores sent bytes.
|
// pointer to a variable that stores sent bytes.
|
||||||
|
// Deprecated: use Client.Stats()
|
||||||
BytesSent *uint64
|
BytesSent *uint64
|
||||||
|
|
||||||
//
|
//
|
||||||
@@ -326,7 +330,7 @@ type Client struct {
|
|||||||
effectiveTransport *Transport
|
effectiveTransport *Transport
|
||||||
backChannelSetupped bool
|
backChannelSetupped bool
|
||||||
stdChannelSetupped bool
|
stdChannelSetupped bool
|
||||||
medias map[*description.Media]*clientMedia
|
setuppedMedias map[*description.Media]*clientMedia
|
||||||
tcpCallbackByChannel map[int]readFunc
|
tcpCallbackByChannel map[int]readFunc
|
||||||
lastRange *headers.Range
|
lastRange *headers.Range
|
||||||
checkTimeoutTimer *time.Timer
|
checkTimeoutTimer *time.Timer
|
||||||
@@ -341,6 +345,8 @@ type Client struct {
|
|||||||
mustClose bool
|
mustClose bool
|
||||||
tcpFrame *base.InterleavedFrame
|
tcpFrame *base.InterleavedFrame
|
||||||
tcpBuffer []byte
|
tcpBuffer []byte
|
||||||
|
bytesReceived *uint64
|
||||||
|
bytesSent *uint64
|
||||||
|
|
||||||
// in
|
// in
|
||||||
chOptions chan optionsReq
|
chOptions chan optionsReq
|
||||||
@@ -380,12 +386,6 @@ func (c *Client) Start(scheme string, host string) error {
|
|||||||
if c.UserAgent == "" {
|
if c.UserAgent == "" {
|
||||||
c.UserAgent = "gortsplib"
|
c.UserAgent = "gortsplib"
|
||||||
}
|
}
|
||||||
if c.BytesReceived == nil {
|
|
||||||
c.BytesReceived = new(uint64)
|
|
||||||
}
|
|
||||||
if c.BytesSent == nil {
|
|
||||||
c.BytesSent = new(uint64)
|
|
||||||
}
|
|
||||||
|
|
||||||
// system functions
|
// system functions
|
||||||
if c.DialContext == nil {
|
if c.DialContext == nil {
|
||||||
@@ -454,6 +454,18 @@ func (c *Client) Start(scheme string, host string) error {
|
|||||||
c.checkTimeoutTimer = emptyTimer()
|
c.checkTimeoutTimer = emptyTimer()
|
||||||
c.keepalivePeriod = 30 * time.Second
|
c.keepalivePeriod = 30 * time.Second
|
||||||
c.keepaliveTimer = emptyTimer()
|
c.keepaliveTimer = emptyTimer()
|
||||||
|
|
||||||
|
if c.BytesReceived != nil {
|
||||||
|
c.bytesReceived = c.BytesReceived
|
||||||
|
} else {
|
||||||
|
c.bytesReceived = new(uint64)
|
||||||
|
}
|
||||||
|
if c.BytesSent != nil {
|
||||||
|
c.bytesSent = c.BytesSent
|
||||||
|
} else {
|
||||||
|
c.bytesSent = new(uint64)
|
||||||
|
}
|
||||||
|
|
||||||
c.chOptions = make(chan optionsReq)
|
c.chOptions = make(chan optionsReq)
|
||||||
c.chDescribe = make(chan describeReq)
|
c.chDescribe = make(chan describeReq)
|
||||||
c.chAnnounce = make(chan announceReq)
|
c.chAnnounce = make(chan announceReq)
|
||||||
@@ -739,7 +751,7 @@ func (c *Client) doClose() {
|
|||||||
c.conn = nil
|
c.conn = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, cm := range c.medias {
|
for _, cm := range c.setuppedMedias {
|
||||||
cm.close()
|
cm.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -757,7 +769,7 @@ func (c *Client) reset() {
|
|||||||
c.effectiveTransport = nil
|
c.effectiveTransport = nil
|
||||||
c.backChannelSetupped = false
|
c.backChannelSetupped = false
|
||||||
c.stdChannelSetupped = false
|
c.stdChannelSetupped = false
|
||||||
c.medias = nil
|
c.setuppedMedias = nil
|
||||||
c.tcpCallbackByChannel = nil
|
c.tcpCallbackByChannel = nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -781,7 +793,7 @@ func (c *Client) trySwitchingProtocol() error {
|
|||||||
|
|
||||||
prevConnURL := c.connURL
|
prevConnURL := c.connURL
|
||||||
prevBaseURL := c.baseURL
|
prevBaseURL := c.baseURL
|
||||||
prevMedias := c.medias
|
prevMedias := c.setuppedMedias
|
||||||
|
|
||||||
c.reset()
|
c.reset()
|
||||||
|
|
||||||
@@ -801,9 +813,9 @@ func (c *Client) trySwitchingProtocol() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
c.medias[i].onPacketRTCP = cm.onPacketRTCP
|
c.setuppedMedias[i].onPacketRTCP = cm.onPacketRTCP
|
||||||
for j, tr := range cm.formats {
|
for j, tr := range cm.formats {
|
||||||
c.medias[i].formats[j].onPacketRTP = tr.onPacketRTP
|
c.setuppedMedias[i].formats[j].onPacketRTP = tr.onPacketRTP
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -854,7 +866,7 @@ func (c *Client) startTransportRoutines() {
|
|||||||
|
|
||||||
c.timeDecoder = rtptime.NewGlobalDecoder2()
|
c.timeDecoder = rtptime.NewGlobalDecoder2()
|
||||||
|
|
||||||
for _, cm := range c.medias {
|
for _, cm := range c.setuppedMedias {
|
||||||
cm.start()
|
cm.start()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -894,7 +906,7 @@ func (c *Client) stopTransportRoutines() {
|
|||||||
c.checkTimeoutTimer = emptyTimer()
|
c.checkTimeoutTimer = emptyTimer()
|
||||||
c.keepaliveTimer = emptyTimer()
|
c.keepaliveTimer = emptyTimer()
|
||||||
|
|
||||||
for _, cm := range c.medias {
|
for _, cm := range c.setuppedMedias {
|
||||||
cm.stop()
|
cm.stop()
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -935,7 +947,7 @@ func (c *Client) connOpen() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
c.nconn = nconn
|
c.nconn = nconn
|
||||||
bc := bytecounter.New(c.nconn, c.BytesReceived, c.BytesSent)
|
bc := bytecounter.New(c.nconn, c.bytesReceived, c.bytesSent)
|
||||||
c.conn = conn.NewConn(bc)
|
c.conn = conn.NewConn(bc)
|
||||||
c.reader = &clientReader{
|
c.reader = &clientReader{
|
||||||
c: c,
|
c: c,
|
||||||
@@ -1021,7 +1033,7 @@ func (c *Client) do(req *base.Request, skipResponse bool) (*base.Response, error
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) atLeastOneUDPPacketHasBeenReceived() bool {
|
func (c *Client) atLeastOneUDPPacketHasBeenReceived() bool {
|
||||||
for _, ct := range c.medias {
|
for _, ct := range c.setuppedMedias {
|
||||||
lft := atomic.LoadInt64(ct.udpRTPListener.lastPacketTime)
|
lft := atomic.LoadInt64(ct.udpRTPListener.lastPacketTime)
|
||||||
if lft != 0 {
|
if lft != 0 {
|
||||||
return true
|
return true
|
||||||
@@ -1037,7 +1049,7 @@ func (c *Client) atLeastOneUDPPacketHasBeenReceived() bool {
|
|||||||
|
|
||||||
func (c *Client) isInUDPTimeout() bool {
|
func (c *Client) isInUDPTimeout() bool {
|
||||||
now := c.timeNow()
|
now := c.timeNow()
|
||||||
for _, ct := range c.medias {
|
for _, ct := range c.setuppedMedias {
|
||||||
lft := time.Unix(atomic.LoadInt64(ct.udpRTPListener.lastPacketTime), 0)
|
lft := time.Unix(atomic.LoadInt64(ct.udpRTPListener.lastPacketTime), 0)
|
||||||
if now.Sub(lft) < c.ReadTimeout {
|
if now.Sub(lft) < c.ReadTimeout {
|
||||||
return false
|
return false
|
||||||
@@ -1347,9 +1359,10 @@ func (c *Client) doSetup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
cm := &clientMedia{
|
cm := &clientMedia{
|
||||||
c: c,
|
c: c,
|
||||||
onPacketRTCP: func(rtcp.Packet) {},
|
media: medi,
|
||||||
}
|
}
|
||||||
|
cm.initialize()
|
||||||
|
|
||||||
if c.effectiveTransport == nil {
|
if c.effectiveTransport == nil {
|
||||||
if c.connURL.Scheme == "rtsps" { // always use TCP if encrypted
|
if c.connURL.Scheme == "rtsps" { // always use TCP if encrypted
|
||||||
@@ -1583,12 +1596,11 @@ func (c *Client) doSetup(
|
|||||||
cm.tcpChannel = thRes.InterleavedIDs[0]
|
cm.tcpChannel = thRes.InterleavedIDs[0]
|
||||||
}
|
}
|
||||||
|
|
||||||
if c.medias == nil {
|
if c.setuppedMedias == nil {
|
||||||
c.medias = make(map[*description.Media]*clientMedia)
|
c.setuppedMedias = make(map[*description.Media]*clientMedia)
|
||||||
}
|
}
|
||||||
|
|
||||||
c.medias[medi] = cm
|
c.setuppedMedias[medi] = cm
|
||||||
cm.setMedia(medi)
|
|
||||||
|
|
||||||
c.baseURL = baseURL
|
c.baseURL = baseURL
|
||||||
c.effectiveTransport = &desiredTransport
|
c.effectiveTransport = &desiredTransport
|
||||||
@@ -1607,7 +1619,7 @@ func (c *Client) doSetup(
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (c *Client) isChannelPairInUse(channel int) bool {
|
func (c *Client) isChannelPairInUse(channel int) bool {
|
||||||
for _, cm := range c.medias {
|
for _, cm := range c.setuppedMedias {
|
||||||
if (cm.tcpChannel+1) == channel || cm.tcpChannel == channel || cm.tcpChannel == (channel+1) {
|
if (cm.tcpChannel+1) == channel || cm.tcpChannel == channel || cm.tcpChannel == (channel+1) {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@@ -1712,7 +1724,7 @@ func (c *Client) doPlay(ra *headers.Range) (*base.Response, error) {
|
|||||||
// don't do this with multicast, otherwise the RTP packet is going to be broadcasted
|
// don't do this with multicast, otherwise the RTP packet is going to be broadcasted
|
||||||
// to all listeners, including us, messing up the stream.
|
// to all listeners, including us, messing up the stream.
|
||||||
if *c.effectiveTransport == TransportUDP {
|
if *c.effectiveTransport == TransportUDP {
|
||||||
for _, cm := range c.medias {
|
for _, cm := range c.setuppedMedias {
|
||||||
byts, _ := (&rtp.Packet{Header: rtp.Header{Version: 2}}).Marshal()
|
byts, _ := (&rtp.Packet{Header: rtp.Header{Version: 2}}).Marshal()
|
||||||
cm.udpRTPListener.write(byts) //nolint:errcheck
|
cm.udpRTPListener.write(byts) //nolint:errcheck
|
||||||
|
|
||||||
@@ -1852,9 +1864,9 @@ func (c *Client) Seek(ra *headers.Range) (*base.Response, error) {
|
|||||||
return c.Play(ra)
|
return c.Play(ra)
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPacketRTPAny sets the callback that is called when a RTP packet is read from any setupped media.
|
// OnPacketRTPAny sets a callback that is called when a RTP packet is read from any setupped media.
|
||||||
func (c *Client) OnPacketRTPAny(cb OnPacketRTPAnyFunc) {
|
func (c *Client) OnPacketRTPAny(cb OnPacketRTPAnyFunc) {
|
||||||
for _, cm := range c.medias {
|
for _, cm := range c.setuppedMedias {
|
||||||
cmedia := cm.media
|
cmedia := cm.media
|
||||||
for _, forma := range cm.media.Formats {
|
for _, forma := range cm.media.Formats {
|
||||||
c.OnPacketRTP(cm.media, forma, func(pkt *rtp.Packet) {
|
c.OnPacketRTP(cm.media, forma, func(pkt *rtp.Packet) {
|
||||||
@@ -1864,9 +1876,9 @@ func (c *Client) OnPacketRTPAny(cb OnPacketRTPAnyFunc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPacketRTCPAny sets the callback that is called when a RTCP packet is read from any setupped media.
|
// OnPacketRTCPAny sets a callback that is called when a RTCP packet is read from any setupped media.
|
||||||
func (c *Client) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) {
|
func (c *Client) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) {
|
||||||
for _, cm := range c.medias {
|
for _, cm := range c.setuppedMedias {
|
||||||
cmedia := cm.media
|
cmedia := cm.media
|
||||||
c.OnPacketRTCP(cm.media, func(pkt rtcp.Packet) {
|
c.OnPacketRTCP(cm.media, func(pkt rtcp.Packet) {
|
||||||
cb(cmedia, pkt)
|
cb(cmedia, pkt)
|
||||||
@@ -1874,16 +1886,16 @@ func (c *Client) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPacketRTP sets the callback that is called when a RTP packet is read.
|
// OnPacketRTP sets a callback that is called when a RTP packet is read.
|
||||||
func (c *Client) OnPacketRTP(medi *description.Media, forma format.Format, cb OnPacketRTPFunc) {
|
func (c *Client) OnPacketRTP(medi *description.Media, forma format.Format, cb OnPacketRTPFunc) {
|
||||||
cm := c.medias[medi]
|
cm := c.setuppedMedias[medi]
|
||||||
ct := cm.formats[forma.PayloadType()]
|
ct := cm.formats[forma.PayloadType()]
|
||||||
ct.onPacketRTP = cb
|
ct.onPacketRTP = cb
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPacketRTCP sets the callback that is called when a RTCP packet is read.
|
// OnPacketRTCP sets a callback that is called when a RTCP packet is read.
|
||||||
func (c *Client) OnPacketRTCP(medi *description.Media, cb OnPacketRTCPFunc) {
|
func (c *Client) OnPacketRTCP(medi *description.Media, cb OnPacketRTCPFunc) {
|
||||||
cm := c.medias[medi]
|
cm := c.setuppedMedias[medi]
|
||||||
cm.onPacketRTCP = cb
|
cm.onPacketRTCP = cb
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1908,13 +1920,13 @@ func (c *Client) WritePacketRTPWithNTP(medi *description.Media, pkt *rtp.Packet,
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
cm := c.medias[medi]
|
cm := c.setuppedMedias[medi]
|
||||||
cf := cm.formats[pkt.PayloadType]
|
cf := cm.formats[pkt.PayloadType]
|
||||||
|
|
||||||
cf.rtcpSender.ProcessPacket(pkt, ntp, cf.format.PTSEqualsDTS(pkt))
|
cf.rtcpSender.ProcessPacketRTP(pkt, ntp, cf.format.PTSEqualsDTS(pkt))
|
||||||
|
|
||||||
ok := c.writer.push(func() error {
|
ok := c.writer.push(func() error {
|
||||||
return cm.writePacketRTPInQueue(byts)
|
return cf.writePacketRTPInQueue(byts)
|
||||||
})
|
})
|
||||||
if !ok {
|
if !ok {
|
||||||
return liberrors.ErrClientWriteQueueFull{}
|
return liberrors.ErrClientWriteQueueFull{}
|
||||||
@@ -1936,7 +1948,7 @@ func (c *Client) WritePacketRTCP(medi *description.Media, pkt rtcp.Packet) error
|
|||||||
default:
|
default:
|
||||||
}
|
}
|
||||||
|
|
||||||
cm := c.medias[medi]
|
cm := c.setuppedMedias[medi]
|
||||||
|
|
||||||
ok := c.writer.push(func() error {
|
ok := c.writer.push(func() error {
|
||||||
return cm.writePacketRTCPInQueue(byts)
|
return cm.writePacketRTCPInQueue(byts)
|
||||||
@@ -1953,7 +1965,7 @@ func (c *Client) WritePacketRTCP(medi *description.Media, pkt rtcp.Packet) error
|
|||||||
//
|
//
|
||||||
// Deprecated: replaced by PacketPTS2.
|
// Deprecated: replaced by PacketPTS2.
|
||||||
func (c *Client) PacketPTS(medi *description.Media, pkt *rtp.Packet) (time.Duration, bool) {
|
func (c *Client) PacketPTS(medi *description.Media, pkt *rtp.Packet) (time.Duration, bool) {
|
||||||
cm := c.medias[medi]
|
cm := c.setuppedMedias[medi]
|
||||||
ct := cm.formats[pkt.PayloadType]
|
ct := cm.formats[pkt.PayloadType]
|
||||||
|
|
||||||
v, ok := c.timeDecoder.Decode(ct.format, pkt)
|
v, ok := c.timeDecoder.Decode(ct.format, pkt)
|
||||||
@@ -1967,7 +1979,7 @@ func (c *Client) PacketPTS(medi *description.Media, pkt *rtp.Packet) (time.Durat
|
|||||||
// PacketPTS2 returns the PTS of an incoming RTP packet.
|
// PacketPTS2 returns the PTS of an incoming RTP packet.
|
||||||
// It is computed by decoding the packet timestamp and sychronizing it with other tracks.
|
// It is computed by decoding the packet timestamp and sychronizing it with other tracks.
|
||||||
func (c *Client) PacketPTS2(medi *description.Media, pkt *rtp.Packet) (int64, bool) {
|
func (c *Client) PacketPTS2(medi *description.Media, pkt *rtp.Packet) (int64, bool) {
|
||||||
cm := c.medias[medi]
|
cm := c.setuppedMedias[medi]
|
||||||
ct := cm.formats[pkt.PayloadType]
|
ct := cm.formats[pkt.PayloadType]
|
||||||
return c.timeDecoder.Decode(ct.format, pkt)
|
return c.timeDecoder.Decode(ct.format, pkt)
|
||||||
}
|
}
|
||||||
@@ -1975,7 +1987,194 @@ func (c *Client) PacketPTS2(medi *description.Media, pkt *rtp.Packet) (int64, bo
|
|||||||
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
|
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
|
||||||
// The NTP timestamp is computed from RTCP sender reports.
|
// The NTP timestamp is computed from RTCP sender reports.
|
||||||
func (c *Client) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time, bool) {
|
func (c *Client) PacketNTP(medi *description.Media, pkt *rtp.Packet) (time.Time, bool) {
|
||||||
cm := c.medias[medi]
|
cm := c.setuppedMedias[medi]
|
||||||
ct := cm.formats[pkt.PayloadType]
|
ct := cm.formats[pkt.PayloadType]
|
||||||
return ct.rtcpReceiver.PacketNTP(pkt.Timestamp)
|
return ct.rtcpReceiver.PacketNTP(pkt.Timestamp)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats returns client statistics.
|
||||||
|
func (c *Client) Stats() *ClientStats {
|
||||||
|
return &ClientStats{
|
||||||
|
Conn: StatsConn{
|
||||||
|
BytesReceived: atomic.LoadUint64(c.bytesReceived),
|
||||||
|
BytesSent: atomic.LoadUint64(c.bytesSent),
|
||||||
|
},
|
||||||
|
Session: StatsSession{
|
||||||
|
BytesReceived: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.bytesReceived)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
BytesSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.bytesSent)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsReceived: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
for _, f := range sm.formats {
|
||||||
|
v += atomic.LoadUint64(f.rtpPacketsReceived)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
for _, f := range sm.formats {
|
||||||
|
v += atomic.LoadUint64(f.rtpPacketsSent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsLost: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
for _, f := range sm.formats {
|
||||||
|
v += atomic.LoadUint64(f.rtpPacketsLost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsInError: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.rtpPacketsInError)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPJitter: func() float64 {
|
||||||
|
v := float64(0)
|
||||||
|
n := float64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
for _, fo := range sm.formats {
|
||||||
|
if fo.rtcpReceiver != nil {
|
||||||
|
stats := fo.rtcpReceiver.Stats()
|
||||||
|
if stats != nil {
|
||||||
|
v += stats.Jitter
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v / n
|
||||||
|
}(),
|
||||||
|
RTCPPacketsReceived: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.rtcpPacketsReceived)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTCPPacketsSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.rtcpPacketsSent)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTCPPacketsInError: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range c.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.rtcpPacketsInError)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
Medias: func() map[*description.Media]StatsSessionMedia { //nolint:dupl
|
||||||
|
ret := make(map[*description.Media]StatsSessionMedia, len(c.setuppedMedias))
|
||||||
|
|
||||||
|
for med, sm := range c.setuppedMedias {
|
||||||
|
ret[med] = StatsSessionMedia{
|
||||||
|
BytesReceived: atomic.LoadUint64(sm.bytesReceived),
|
||||||
|
BytesSent: atomic.LoadUint64(sm.bytesSent),
|
||||||
|
RTPPacketsInError: atomic.LoadUint64(sm.rtpPacketsInError),
|
||||||
|
RTCPPacketsReceived: atomic.LoadUint64(sm.rtcpPacketsReceived),
|
||||||
|
RTCPPacketsSent: atomic.LoadUint64(sm.rtcpPacketsSent),
|
||||||
|
RTCPPacketsInError: atomic.LoadUint64(sm.rtcpPacketsInError),
|
||||||
|
Formats: func() map[format.Format]StatsSessionFormat {
|
||||||
|
ret := make(map[format.Format]StatsSessionFormat, len(sm.formats))
|
||||||
|
|
||||||
|
for _, fo := range sm.formats {
|
||||||
|
recvStats := func() *rtcpreceiver.Stats {
|
||||||
|
if fo.rtcpReceiver != nil {
|
||||||
|
return fo.rtcpReceiver.Stats()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
sentStats := func() *rtcpsender.Stats {
|
||||||
|
if fo.rtcpSender != nil {
|
||||||
|
return fo.rtcpSender.Stats()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
ret[fo.format] = StatsSessionFormat{ //nolint:dupl
|
||||||
|
RTPPacketsReceived: atomic.LoadUint64(fo.rtpPacketsReceived),
|
||||||
|
RTPPacketsSent: atomic.LoadUint64(fo.rtpPacketsSent),
|
||||||
|
RTPPacketsLost: atomic.LoadUint64(fo.rtpPacketsLost),
|
||||||
|
LocalSSRC: func() uint32 {
|
||||||
|
if fo.rtcpReceiver != nil {
|
||||||
|
return *fo.rtcpReceiver.LocalSSRC
|
||||||
|
}
|
||||||
|
if sentStats != nil {
|
||||||
|
return sentStats.LocalSSRC
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
RemoteSSRC: func() uint32 {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.RemoteSSRC
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
RTPPacketsLastSequenceNumber: func() uint16 {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.LastSequenceNumber
|
||||||
|
}
|
||||||
|
if sentStats != nil {
|
||||||
|
return sentStats.LastSequenceNumber
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
RTPPacketsLastRTP: func() uint32 {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.LastRTP
|
||||||
|
}
|
||||||
|
if sentStats != nil {
|
||||||
|
return sentStats.LastRTP
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
RTPPacketsLastNTP: func() time.Time {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.LastNTP
|
||||||
|
}
|
||||||
|
if sentStats != nil {
|
||||||
|
return sentStats.LastNTP
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}(),
|
||||||
|
RTPJitter: func() float64 {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.Jitter
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}(),
|
||||||
|
},
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,9 @@
|
|||||||
package gortsplib
|
package gortsplib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/pion/rtcp"
|
"github.com/pion/rtcp"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
@@ -17,13 +20,29 @@ type clientFormat struct {
|
|||||||
format format.Format
|
format format.Format
|
||||||
onPacketRTP OnPacketRTPFunc
|
onPacketRTP OnPacketRTPFunc
|
||||||
|
|
||||||
udpReorderer *rtpreorderer.Reorderer // play
|
udpReorderer *rtpreorderer.Reorderer // play
|
||||||
tcpLossDetector *rtplossdetector.LossDetector // play
|
tcpLossDetector *rtplossdetector.LossDetector // play
|
||||||
rtcpReceiver *rtcpreceiver.RTCPReceiver // play
|
rtcpReceiver *rtcpreceiver.RTCPReceiver // play
|
||||||
rtcpSender *rtcpsender.RTCPSender // record or back channel
|
rtcpSender *rtcpsender.RTCPSender // record or back channel
|
||||||
|
writePacketRTPInQueue func([]byte) error
|
||||||
|
rtpPacketsReceived *uint64
|
||||||
|
rtpPacketsSent *uint64
|
||||||
|
rtpPacketsLost *uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cf *clientFormat) initialize() {
|
||||||
|
cf.rtpPacketsReceived = new(uint64)
|
||||||
|
cf.rtpPacketsSent = new(uint64)
|
||||||
|
cf.rtpPacketsLost = new(uint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cf *clientFormat) start() {
|
func (cf *clientFormat) start() {
|
||||||
|
if cf.cm.udpRTPListener != nil {
|
||||||
|
cf.writePacketRTPInQueue = cf.writePacketRTPInQueueUDP
|
||||||
|
} else {
|
||||||
|
cf.writePacketRTPInQueue = cf.writePacketRTPInQueueTCP
|
||||||
|
}
|
||||||
|
|
||||||
if cf.cm.c.state == clientStateRecord || cf.cm.media.IsBackChannel {
|
if cf.cm.c.state == clientStateRecord || cf.cm.media.IsBackChannel {
|
||||||
cf.rtcpSender = &rtcpsender.RTCPSender{
|
cf.rtcpSender = &rtcpsender.RTCPSender{
|
||||||
ClockRate: cf.format.ClockRate(),
|
ClockRate: cf.format.ClockRate(),
|
||||||
@@ -72,40 +91,70 @@ func (cf *clientFormat) stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cf *clientFormat) readRTPUDP(pkt *rtp.Packet) {
|
func (cf *clientFormat) readPacketRTPUDP(pkt *rtp.Packet) {
|
||||||
packets, lost := cf.udpReorderer.Process(pkt)
|
packets, lost := cf.udpReorderer.Process(pkt)
|
||||||
if lost != 0 {
|
if lost != 0 {
|
||||||
cf.cm.c.OnPacketLost(liberrors.ErrClientRTPPacketsLost{Lost: lost})
|
cf.onPacketRTPLost(lost)
|
||||||
// do not return
|
// do not return
|
||||||
}
|
}
|
||||||
|
|
||||||
now := cf.cm.c.timeNow()
|
now := cf.cm.c.timeNow()
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
err := cf.rtcpReceiver.ProcessPacket(pkt, now, cf.format.PTSEqualsDTS(pkt))
|
cf.handlePacketRTP(pkt, now)
|
||||||
if err != nil {
|
|
||||||
cf.cm.c.OnDecodeError(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
cf.onPacketRTP(pkt)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cf *clientFormat) readRTPTCP(pkt *rtp.Packet) {
|
func (cf *clientFormat) readPacketRTPTCP(pkt *rtp.Packet) {
|
||||||
lost := cf.tcpLossDetector.Process(pkt)
|
lost := cf.tcpLossDetector.Process(pkt)
|
||||||
if lost != 0 {
|
if lost != 0 {
|
||||||
cf.cm.c.OnPacketLost(liberrors.ErrClientRTPPacketsLost{Lost: lost})
|
cf.onPacketRTPLost(lost)
|
||||||
// do not return
|
// do not return
|
||||||
}
|
}
|
||||||
|
|
||||||
now := cf.cm.c.timeNow()
|
now := cf.cm.c.timeNow()
|
||||||
|
|
||||||
err := cf.rtcpReceiver.ProcessPacket(pkt, now, cf.format.PTSEqualsDTS(pkt))
|
cf.handlePacketRTP(pkt, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cf *clientFormat) handlePacketRTP(pkt *rtp.Packet, now time.Time) {
|
||||||
|
err := cf.rtcpReceiver.ProcessPacketRTP(pkt, now, cf.format.PTSEqualsDTS(pkt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cf.cm.c.OnDecodeError(err)
|
cf.cm.onPacketRTPDecodeError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(cf.rtpPacketsReceived, 1)
|
||||||
|
|
||||||
cf.onPacketRTP(pkt)
|
cf.onPacketRTP(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cf *clientFormat) onPacketRTPLost(lost uint) {
|
||||||
|
atomic.AddUint64(cf.rtpPacketsLost, uint64(lost))
|
||||||
|
cf.cm.c.OnPacketLost(liberrors.ErrClientRTPPacketsLost{Lost: lost})
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cf *clientFormat) writePacketRTPInQueueUDP(payload []byte) error {
|
||||||
|
err := cf.cm.udpRTPListener.write(payload)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(cf.cm.bytesSent, uint64(len(payload)))
|
||||||
|
atomic.AddUint64(cf.rtpPacketsSent, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cf *clientFormat) writePacketRTPInQueueTCP(payload []byte) error {
|
||||||
|
cf.cm.c.tcpFrame.Channel = cf.cm.tcpChannel
|
||||||
|
cf.cm.c.tcpFrame.Payload = payload
|
||||||
|
cf.cm.c.nconn.SetWriteDeadline(time.Now().Add(cf.cm.c.WriteTimeout))
|
||||||
|
err := cf.cm.c.conn.WriteInterleavedFrame(cf.cm.c.tcpFrame, cf.cm.c.tcpBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(cf.cm.bytesSent, uint64(len(payload)))
|
||||||
|
atomic.AddUint64(cf.rtpPacketsSent, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
182
client_media.go
182
client_media.go
@@ -13,16 +13,43 @@ import (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type clientMedia struct {
|
type clientMedia struct {
|
||||||
c *Client
|
c *Client
|
||||||
onPacketRTCP OnPacketRTCPFunc
|
media *description.Media
|
||||||
|
|
||||||
media *description.Media
|
onPacketRTCP OnPacketRTCPFunc
|
||||||
formats map[uint8]*clientFormat
|
formats map[uint8]*clientFormat
|
||||||
tcpChannel int
|
tcpChannel int
|
||||||
udpRTPListener *clientUDPListener
|
udpRTPListener *clientUDPListener
|
||||||
udpRTCPListener *clientUDPListener
|
udpRTCPListener *clientUDPListener
|
||||||
writePacketRTPInQueue func([]byte) error
|
|
||||||
writePacketRTCPInQueue func([]byte) error
|
writePacketRTCPInQueue func([]byte) error
|
||||||
|
bytesReceived *uint64
|
||||||
|
bytesSent *uint64
|
||||||
|
rtpPacketsInError *uint64
|
||||||
|
rtcpPacketsReceived *uint64
|
||||||
|
rtcpPacketsSent *uint64
|
||||||
|
rtcpPacketsInError *uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *clientMedia) initialize() {
|
||||||
|
cm.onPacketRTCP = func(rtcp.Packet) {}
|
||||||
|
cm.bytesReceived = new(uint64)
|
||||||
|
cm.bytesSent = new(uint64)
|
||||||
|
cm.rtpPacketsInError = new(uint64)
|
||||||
|
cm.rtcpPacketsReceived = new(uint64)
|
||||||
|
cm.rtcpPacketsSent = new(uint64)
|
||||||
|
cm.rtcpPacketsInError = new(uint64)
|
||||||
|
|
||||||
|
cm.formats = make(map[uint8]*clientFormat)
|
||||||
|
|
||||||
|
for _, forma := range cm.media.Formats {
|
||||||
|
f := &clientFormat{
|
||||||
|
cm: cm,
|
||||||
|
format: forma,
|
||||||
|
onPacketRTP: func(*rtp.Packet) {},
|
||||||
|
}
|
||||||
|
f.initialize()
|
||||||
|
cm.formats[forma.PayloadType()] = f
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) close() {
|
func (cm *clientMedia) close() {
|
||||||
@@ -71,33 +98,18 @@ func (cm *clientMedia) allocateUDPListeners(
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) setMedia(medi *description.Media) {
|
|
||||||
cm.media = medi
|
|
||||||
|
|
||||||
cm.formats = make(map[uint8]*clientFormat)
|
|
||||||
for _, forma := range medi.Formats {
|
|
||||||
cm.formats[forma.PayloadType()] = &clientFormat{
|
|
||||||
cm: cm,
|
|
||||||
format: forma,
|
|
||||||
onPacketRTP: func(*rtp.Packet) {},
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cm *clientMedia) start() {
|
func (cm *clientMedia) start() {
|
||||||
if cm.udpRTPListener != nil {
|
if cm.udpRTPListener != nil {
|
||||||
cm.writePacketRTPInQueue = cm.writePacketRTPInQueueUDP
|
|
||||||
cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueUDP
|
cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueUDP
|
||||||
|
|
||||||
if cm.c.state == clientStateRecord || cm.media.IsBackChannel {
|
if cm.c.state == clientStateRecord || cm.media.IsBackChannel {
|
||||||
cm.udpRTPListener.readFunc = cm.readRTPUDPRecord
|
cm.udpRTPListener.readFunc = cm.readPacketRTPUDPRecord
|
||||||
cm.udpRTCPListener.readFunc = cm.readRTCPUDPRecord
|
cm.udpRTCPListener.readFunc = cm.readPacketRTCPUDPRecord
|
||||||
} else {
|
} else {
|
||||||
cm.udpRTPListener.readFunc = cm.readRTPUDPPlay
|
cm.udpRTPListener.readFunc = cm.readPacketRTPUDPPlay
|
||||||
cm.udpRTCPListener.readFunc = cm.readRTCPUDPPlay
|
cm.udpRTCPListener.readFunc = cm.readPacketRTCPUDPPlay
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
cm.writePacketRTPInQueue = cm.writePacketRTPInQueueTCP
|
|
||||||
cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueTCP
|
cm.writePacketRTCPInQueue = cm.writePacketRTCPInQueueTCP
|
||||||
|
|
||||||
if cm.c.tcpCallbackByChannel == nil {
|
if cm.c.tcpCallbackByChannel == nil {
|
||||||
@@ -105,11 +117,11 @@ func (cm *clientMedia) start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if cm.c.state == clientStateRecord || cm.media.IsBackChannel {
|
if cm.c.state == clientStateRecord || cm.media.IsBackChannel {
|
||||||
cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readRTPTCPRecord
|
cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readPacketRTPTCPRecord
|
||||||
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readRTCPTCPRecord
|
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readPacketRTCPTCPRecord
|
||||||
} else {
|
} else {
|
||||||
cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readRTPTCPPlay
|
cm.c.tcpCallbackByChannel[cm.tcpChannel] = cm.readPacketRTPTCPPlay
|
||||||
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readRTCPTCPPlay
|
cm.c.tcpCallbackByChannel[cm.tcpChannel+1] = cm.readPacketRTCPTCPPlay
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -136,73 +148,82 @@ func (cm *clientMedia) stop() {
|
|||||||
|
|
||||||
func (cm *clientMedia) findFormatWithSSRC(ssrc uint32) *clientFormat {
|
func (cm *clientMedia) findFormatWithSSRC(ssrc uint32) *clientFormat {
|
||||||
for _, format := range cm.formats {
|
for _, format := range cm.formats {
|
||||||
tssrc, ok := format.rtcpReceiver.SenderSSRC()
|
stats := format.rtcpReceiver.Stats()
|
||||||
if ok && tssrc == ssrc {
|
if stats != nil && stats.RemoteSSRC == ssrc {
|
||||||
return format
|
return format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) writePacketRTPInQueueUDP(payload []byte) error {
|
|
||||||
return cm.udpRTPListener.write(payload)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (cm *clientMedia) writePacketRTCPInQueueUDP(payload []byte) error {
|
func (cm *clientMedia) writePacketRTCPInQueueUDP(payload []byte) error {
|
||||||
return cm.udpRTCPListener.write(payload)
|
err := cm.udpRTCPListener.write(payload)
|
||||||
}
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) writePacketRTPInQueueTCP(payload []byte) error {
|
atomic.AddUint64(cm.bytesSent, uint64(len(payload)))
|
||||||
cm.c.tcpFrame.Channel = cm.tcpChannel
|
atomic.AddUint64(cm.rtcpPacketsSent, 1)
|
||||||
cm.c.tcpFrame.Payload = payload
|
return nil
|
||||||
cm.c.nconn.SetWriteDeadline(time.Now().Add(cm.c.WriteTimeout))
|
|
||||||
return cm.c.conn.WriteInterleavedFrame(cm.c.tcpFrame, cm.c.tcpBuffer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) writePacketRTCPInQueueTCP(payload []byte) error {
|
func (cm *clientMedia) writePacketRTCPInQueueTCP(payload []byte) error {
|
||||||
cm.c.tcpFrame.Channel = cm.tcpChannel + 1
|
cm.c.tcpFrame.Channel = cm.tcpChannel + 1
|
||||||
cm.c.tcpFrame.Payload = payload
|
cm.c.tcpFrame.Payload = payload
|
||||||
cm.c.nconn.SetWriteDeadline(time.Now().Add(cm.c.WriteTimeout))
|
cm.c.nconn.SetWriteDeadline(time.Now().Add(cm.c.WriteTimeout))
|
||||||
return cm.c.conn.WriteInterleavedFrame(cm.c.tcpFrame, cm.c.tcpBuffer)
|
err := cm.c.conn.WriteInterleavedFrame(cm.c.tcpFrame, cm.c.tcpBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(cm.bytesSent, uint64(len(payload)))
|
||||||
|
atomic.AddUint64(cm.rtcpPacketsSent, 1)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) readRTPTCPPlay(payload []byte) bool {
|
func (cm *clientMedia) readPacketRTPTCPPlay(payload []byte) bool {
|
||||||
|
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
now := cm.c.timeNow()
|
now := cm.c.timeNow()
|
||||||
atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix())
|
atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix())
|
||||||
|
|
||||||
pkt := &rtp.Packet{}
|
pkt := &rtp.Packet{}
|
||||||
err := pkt.Unmarshal(payload)
|
err := pkt.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cm.c.OnDecodeError(err)
|
cm.onPacketRTPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forma, ok := cm.formats[pkt.PayloadType]
|
forma, ok := cm.formats[pkt.PayloadType]
|
||||||
if !ok {
|
if !ok {
|
||||||
cm.c.OnDecodeError(liberrors.ErrClientRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
cm.onPacketRTPDecodeError(liberrors.ErrClientRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forma.readRTPTCP(pkt)
|
forma.readPacketRTPTCP(pkt)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) readRTCPTCPPlay(payload []byte) bool {
|
func (cm *clientMedia) readPacketRTCPTCPPlay(payload []byte) bool {
|
||||||
|
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
now := cm.c.timeNow()
|
now := cm.c.timeNow()
|
||||||
atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix())
|
atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix())
|
||||||
|
|
||||||
if len(payload) > udpMaxPayloadSize {
|
if len(payload) > udpMaxPayloadSize {
|
||||||
cm.c.OnDecodeError(liberrors.ErrClientRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
cm.onPacketRTCPDecodeError(liberrors.ErrClientRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
packets, err := rtcp.Unmarshal(payload)
|
packets, err := rtcp.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cm.c.OnDecodeError(err)
|
cm.onPacketRTCPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(cm.rtcpPacketsReceived, uint64(len(packets)))
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
||||||
format := cm.findFormatWithSSRC(sr.SSRC)
|
format := cm.findFormatWithSSRC(sr.SSRC)
|
||||||
@@ -217,22 +238,26 @@ func (cm *clientMedia) readRTCPTCPPlay(payload []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) readRTPTCPRecord(_ []byte) bool {
|
func (cm *clientMedia) readPacketRTPTCPRecord(_ []byte) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) readRTCPTCPRecord(payload []byte) bool {
|
func (cm *clientMedia) readPacketRTCPTCPRecord(payload []byte) bool {
|
||||||
|
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
if len(payload) > udpMaxPayloadSize {
|
if len(payload) > udpMaxPayloadSize {
|
||||||
cm.c.OnDecodeError(liberrors.ErrClientRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
cm.onPacketRTCPDecodeError(liberrors.ErrClientRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
packets, err := rtcp.Unmarshal(payload)
|
packets, err := rtcp.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cm.c.OnDecodeError(err)
|
cm.onPacketRTCPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(cm.rtcpPacketsReceived, uint64(len(packets)))
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
cm.onPacketRTCP(pkt)
|
cm.onPacketRTCP(pkt)
|
||||||
}
|
}
|
||||||
@@ -240,47 +265,50 @@ func (cm *clientMedia) readRTCPTCPRecord(payload []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) readRTPUDPPlay(payload []byte) bool {
|
func (cm *clientMedia) readPacketRTPUDPPlay(payload []byte) bool {
|
||||||
plen := len(payload)
|
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
if plen == (udpMaxPayloadSize + 1) {
|
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||||
cm.c.OnDecodeError(liberrors.ErrClientRTPPacketTooBigUDP{})
|
cm.onPacketRTPDecodeError(liberrors.ErrClientRTPPacketTooBigUDP{})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt := &rtp.Packet{}
|
pkt := &rtp.Packet{}
|
||||||
err := pkt.Unmarshal(payload)
|
err := pkt.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cm.c.OnDecodeError(err)
|
cm.onPacketRTPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forma, ok := cm.formats[pkt.PayloadType]
|
forma, ok := cm.formats[pkt.PayloadType]
|
||||||
if !ok {
|
if !ok {
|
||||||
cm.c.OnDecodeError(liberrors.ErrClientRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
cm.onPacketRTPDecodeError(liberrors.ErrClientRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forma.readRTPUDP(pkt)
|
forma.readPacketRTPUDP(pkt)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) readRTCPUDPPlay(payload []byte) bool {
|
func (cm *clientMedia) readPacketRTCPUDPPlay(payload []byte) bool {
|
||||||
now := cm.c.timeNow()
|
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||||
plen := len(payload)
|
|
||||||
|
|
||||||
if plen == (udpMaxPayloadSize + 1) {
|
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||||
cm.c.OnDecodeError(liberrors.ErrClientRTCPPacketTooBigUDP{})
|
cm.onPacketRTCPDecodeError(liberrors.ErrClientRTCPPacketTooBigUDP{})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
packets, err := rtcp.Unmarshal(payload)
|
packets, err := rtcp.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cm.c.OnDecodeError(err)
|
cm.onPacketRTCPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
now := cm.c.timeNow()
|
||||||
|
|
||||||
|
atomic.AddUint64(cm.rtcpPacketsReceived, uint64(len(packets)))
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
||||||
format := cm.findFormatWithSSRC(sr.SSRC)
|
format := cm.findFormatWithSSRC(sr.SSRC)
|
||||||
@@ -295,27 +323,39 @@ func (cm *clientMedia) readRTCPUDPPlay(payload []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) readRTPUDPRecord(_ []byte) bool {
|
func (cm *clientMedia) readPacketRTPUDPRecord(_ []byte) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (cm *clientMedia) readRTCPUDPRecord(payload []byte) bool {
|
func (cm *clientMedia) readPacketRTCPUDPRecord(payload []byte) bool {
|
||||||
plen := len(payload)
|
atomic.AddUint64(cm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
if plen == (udpMaxPayloadSize + 1) {
|
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||||
cm.c.OnDecodeError(liberrors.ErrClientRTCPPacketTooBigUDP{})
|
cm.onPacketRTCPDecodeError(liberrors.ErrClientRTCPPacketTooBigUDP{})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
packets, err := rtcp.Unmarshal(payload)
|
packets, err := rtcp.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
cm.c.OnDecodeError(err)
|
cm.onPacketRTCPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(cm.rtcpPacketsReceived, uint64(len(packets)))
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
cm.onPacketRTCP(pkt)
|
cm.onPacketRTCP(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (cm *clientMedia) onPacketRTPDecodeError(err error) {
|
||||||
|
atomic.AddUint64(cm.rtpPacketsInError, 1)
|
||||||
|
cm.c.OnDecodeError(err)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (cm *clientMedia) onPacketRTCPDecodeError(err error) {
|
||||||
|
atomic.AddUint64(cm.rtcpPacketsInError, 1)
|
||||||
|
cm.c.OnDecodeError(err)
|
||||||
|
}
|
||||||
|
@@ -545,10 +545,11 @@ func TestClientPlay(t *testing.T) {
|
|||||||
|
|
||||||
<-packetRecv
|
<-packetRecv
|
||||||
|
|
||||||
require.Greater(t, atomic.LoadUint64(c.BytesSent), uint64(620))
|
s := c.Stats()
|
||||||
require.Less(t, atomic.LoadUint64(c.BytesSent), uint64(850))
|
require.Greater(t, s.Session.BytesSent, uint64(19))
|
||||||
require.Greater(t, atomic.LoadUint64(c.BytesReceived), uint64(580))
|
require.Less(t, s.Session.BytesSent, uint64(41))
|
||||||
require.Less(t, atomic.LoadUint64(c.BytesReceived), uint64(650))
|
require.Greater(t, s.Session.BytesReceived, uint64(31))
|
||||||
|
require.Less(t, s.Session.BytesReceived, uint64(37))
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -7,7 +7,6 @@ import (
|
|||||||
"net"
|
"net"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"sync/atomic"
|
|
||||||
"testing"
|
"testing"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
@@ -336,10 +335,11 @@ func TestClientRecord(t *testing.T) {
|
|||||||
|
|
||||||
<-recvDone
|
<-recvDone
|
||||||
|
|
||||||
require.Greater(t, atomic.LoadUint64(c.BytesSent), uint64(730))
|
s := c.Stats()
|
||||||
require.Less(t, atomic.LoadUint64(c.BytesSent), uint64(760))
|
require.Greater(t, s.Session.BytesSent, uint64(15))
|
||||||
require.Greater(t, atomic.LoadUint64(c.BytesReceived), uint64(180))
|
require.Less(t, s.Session.BytesSent, uint64(17))
|
||||||
require.Less(t, atomic.LoadUint64(c.BytesReceived), uint64(210))
|
require.Greater(t, s.Session.BytesReceived, uint64(19))
|
||||||
|
require.Less(t, s.Session.BytesReceived, uint64(21))
|
||||||
|
|
||||||
c.Close()
|
c.Close()
|
||||||
<-done
|
<-done
|
||||||
|
7
client_stats.go
Normal file
7
client_stats.go
Normal file
@@ -0,0 +1,7 @@
|
|||||||
|
package gortsplib
|
||||||
|
|
||||||
|
// ClientStats are client statistics
|
||||||
|
type ClientStats struct {
|
||||||
|
Conn StatsConn
|
||||||
|
Session StatsSession
|
||||||
|
}
|
@@ -173,8 +173,6 @@ func (u *clientUDPListener) run() {
|
|||||||
now := u.c.timeNow()
|
now := u.c.timeNow()
|
||||||
atomic.StoreInt64(u.lastPacketTime, now.Unix())
|
atomic.StoreInt64(u.lastPacketTime, now.Unix())
|
||||||
|
|
||||||
atomic.AddUint64(u.c.BytesReceived, uint64(n))
|
|
||||||
|
|
||||||
if u.readFunc(buf[:n]) {
|
if u.readFunc(buf[:n]) {
|
||||||
createNewBuffer()
|
createNewBuffer()
|
||||||
}
|
}
|
||||||
@@ -182,8 +180,6 @@ func (u *clientUDPListener) run() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (u *clientUDPListener) write(payload []byte) error {
|
func (u *clientUDPListener) write(payload []byte) error {
|
||||||
atomic.AddUint64(u.c.BytesSent, uint64(len(payload)))
|
|
||||||
|
|
||||||
// no mutex is needed here since Write() has an internal lock.
|
// no mutex is needed here since Write() has an internal lock.
|
||||||
// https://github.com/golang/go/issues/27203#issuecomment-534386117
|
// https://github.com/golang/go/issues/27203#issuecomment-534386117
|
||||||
u.pc.SetWriteDeadline(time.Now().Add(u.c.WriteTimeout))
|
u.pc.SetWriteDeadline(time.Now().Add(u.c.WriteTimeout))
|
||||||
|
@@ -30,7 +30,7 @@ func randUint32() (uint32, error) {
|
|||||||
// RTCPReceiver is a utility to generate RTCP receiver reports.
|
// RTCPReceiver is a utility to generate RTCP receiver reports.
|
||||||
type RTCPReceiver struct {
|
type RTCPReceiver struct {
|
||||||
ClockRate int
|
ClockRate int
|
||||||
ReceiverSSRC *uint32
|
LocalSSRC *uint32
|
||||||
Period time.Duration
|
Period time.Duration
|
||||||
TimeNow func() time.Time
|
TimeNow func() time.Time
|
||||||
WritePacketRTCP func(rtcp.Packet)
|
WritePacketRTCP func(rtcp.Packet)
|
||||||
@@ -42,7 +42,7 @@ type RTCPReceiver struct {
|
|||||||
timeInitialized bool
|
timeInitialized bool
|
||||||
sequenceNumberCycles uint16
|
sequenceNumberCycles uint16
|
||||||
lastSequenceNumber uint16
|
lastSequenceNumber uint16
|
||||||
senderSSRC uint32
|
remoteSSRC uint32
|
||||||
lastTimeRTP uint32
|
lastTimeRTP uint32
|
||||||
lastTimeSystem time.Time
|
lastTimeSystem time.Time
|
||||||
totalLost uint32
|
totalLost uint32
|
||||||
@@ -62,12 +62,12 @@ type RTCPReceiver struct {
|
|||||||
|
|
||||||
// Initialize initializes RTCPReceiver.
|
// Initialize initializes RTCPReceiver.
|
||||||
func (rr *RTCPReceiver) Initialize() error {
|
func (rr *RTCPReceiver) Initialize() error {
|
||||||
if rr.ReceiverSSRC == nil {
|
if rr.LocalSSRC == nil {
|
||||||
v, err := randUint32()
|
v, err := randUint32()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
rr.ReceiverSSRC = &v
|
rr.LocalSSRC = &v
|
||||||
}
|
}
|
||||||
|
|
||||||
if rr.TimeNow == nil {
|
if rr.TimeNow == nil {
|
||||||
@@ -119,10 +119,10 @@ func (rr *RTCPReceiver) report() rtcp.Packet {
|
|||||||
system := rr.TimeNow()
|
system := rr.TimeNow()
|
||||||
|
|
||||||
report := &rtcp.ReceiverReport{
|
report := &rtcp.ReceiverReport{
|
||||||
SSRC: *rr.ReceiverSSRC,
|
SSRC: *rr.LocalSSRC,
|
||||||
Reports: []rtcp.ReceptionReport{
|
Reports: []rtcp.ReceptionReport{
|
||||||
{
|
{
|
||||||
SSRC: rr.senderSSRC,
|
SSRC: rr.remoteSSRC,
|
||||||
LastSequenceNumber: uint32(rr.sequenceNumberCycles)<<16 | uint32(rr.lastSequenceNumber),
|
LastSequenceNumber: uint32(rr.sequenceNumberCycles)<<16 | uint32(rr.lastSequenceNumber),
|
||||||
// equivalent to taking the integer part after multiplying the
|
// equivalent to taking the integer part after multiplying the
|
||||||
// loss fraction by 256
|
// loss fraction by 256
|
||||||
@@ -149,8 +149,8 @@ func (rr *RTCPReceiver) report() rtcp.Packet {
|
|||||||
return report
|
return report
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessPacket extracts the needed data from RTP packets.
|
// ProcessPacketRTP extracts the needed data from RTP packets.
|
||||||
func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, system time.Time, ptsEqualsDTS bool) error {
|
func (rr *RTCPReceiver) ProcessPacketRTP(pkt *rtp.Packet, system time.Time, ptsEqualsDTS bool) error {
|
||||||
rr.mutex.Lock()
|
rr.mutex.Lock()
|
||||||
defer rr.mutex.Unlock()
|
defer rr.mutex.Unlock()
|
||||||
|
|
||||||
@@ -159,7 +159,7 @@ func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, system time.Time, ptsEqua
|
|||||||
rr.firstRTPPacketReceived = true
|
rr.firstRTPPacketReceived = true
|
||||||
rr.totalSinceReport = 1
|
rr.totalSinceReport = 1
|
||||||
rr.lastSequenceNumber = pkt.SequenceNumber
|
rr.lastSequenceNumber = pkt.SequenceNumber
|
||||||
rr.senderSSRC = pkt.SSRC
|
rr.remoteSSRC = pkt.SSRC
|
||||||
|
|
||||||
if ptsEqualsDTS {
|
if ptsEqualsDTS {
|
||||||
rr.timeInitialized = true
|
rr.timeInitialized = true
|
||||||
@@ -169,8 +169,8 @@ func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, system time.Time, ptsEqua
|
|||||||
|
|
||||||
// subsequent packets
|
// subsequent packets
|
||||||
} else {
|
} else {
|
||||||
if pkt.SSRC != rr.senderSSRC {
|
if pkt.SSRC != rr.remoteSSRC {
|
||||||
return fmt.Errorf("received packet with wrong SSRC %d, expected %d", pkt.SSRC, rr.senderSSRC)
|
return fmt.Errorf("received packet with wrong SSRC %d, expected %d", pkt.SSRC, rr.remoteSSRC)
|
||||||
}
|
}
|
||||||
|
|
||||||
diff := int32(pkt.SequenceNumber) - int32(rr.lastSequenceNumber)
|
diff := int32(pkt.SequenceNumber) - int32(rr.lastSequenceNumber)
|
||||||
@@ -229,11 +229,7 @@ func (rr *RTCPReceiver) ProcessSenderReport(sr *rtcp.SenderReport, system time.T
|
|||||||
rr.lastSenderReportTimeSystem = system
|
rr.lastSenderReportTimeSystem = system
|
||||||
}
|
}
|
||||||
|
|
||||||
// PacketNTP returns the NTP timestamp of the packet.
|
func (rr *RTCPReceiver) packetNTPUnsafe(ts uint32) (time.Time, bool) {
|
||||||
func (rr *RTCPReceiver) PacketNTP(ts uint32) (time.Time, bool) {
|
|
||||||
rr.mutex.Lock()
|
|
||||||
defer rr.mutex.Unlock()
|
|
||||||
|
|
||||||
if !rr.firstSenderReportReceived {
|
if !rr.firstSenderReportReceived {
|
||||||
return time.Time{}, false
|
return time.Time{}, false
|
||||||
}
|
}
|
||||||
@@ -244,9 +240,39 @@ func (rr *RTCPReceiver) PacketNTP(ts uint32) (time.Time, bool) {
|
|||||||
return ntpTimeRTCPToGo(rr.lastSenderReportTimeNTP).Add(timeDiffGo), true
|
return ntpTimeRTCPToGo(rr.lastSenderReportTimeNTP).Add(timeDiffGo), true
|
||||||
}
|
}
|
||||||
|
|
||||||
// SenderSSRC returns the SSRC of outgoing RTP packets.
|
// PacketNTP returns the NTP timestamp of the packet.
|
||||||
func (rr *RTCPReceiver) SenderSSRC() (uint32, bool) {
|
func (rr *RTCPReceiver) PacketNTP(ts uint32) (time.Time, bool) {
|
||||||
|
rr.mutex.Lock()
|
||||||
|
defer rr.mutex.Unlock()
|
||||||
|
|
||||||
|
return rr.packetNTPUnsafe(ts)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats are statistics.
|
||||||
|
type Stats struct {
|
||||||
|
RemoteSSRC uint32
|
||||||
|
LastSequenceNumber uint16
|
||||||
|
LastRTP uint32
|
||||||
|
LastNTP time.Time
|
||||||
|
Jitter float64
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stats returns statistics.
|
||||||
|
func (rr *RTCPReceiver) Stats() *Stats {
|
||||||
rr.mutex.RLock()
|
rr.mutex.RLock()
|
||||||
defer rr.mutex.RUnlock()
|
defer rr.mutex.RUnlock()
|
||||||
return rr.senderSSRC, rr.firstRTPPacketReceived
|
|
||||||
|
if !rr.firstRTPPacketReceived {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
ntp, _ := rr.packetNTPUnsafe(rr.lastTimeRTP)
|
||||||
|
|
||||||
|
return &Stats{
|
||||||
|
RemoteSSRC: rr.remoteSSRC,
|
||||||
|
LastSequenceNumber: rr.lastSequenceNumber,
|
||||||
|
LastRTP: rr.lastTimeRTP,
|
||||||
|
LastNTP: ntp,
|
||||||
|
Jitter: rr.jitter,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -17,9 +17,9 @@ func TestRTCPReceiverBase(t *testing.T) {
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
rr := &RTCPReceiver{
|
rr := &RTCPReceiver{
|
||||||
ClockRate: 90000,
|
ClockRate: 90000,
|
||||||
ReceiverSSRC: uint32Ptr(0x65f83afb),
|
LocalSSRC: uint32Ptr(0x65f83afb),
|
||||||
Period: 500 * time.Millisecond,
|
Period: 500 * time.Millisecond,
|
||||||
TimeNow: func() time.Time {
|
TimeNow: func() time.Time {
|
||||||
return time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
|
return time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
|
||||||
},
|
},
|
||||||
@@ -64,7 +64,7 @@ func TestRTCPReceiverBase(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rtpPkt = rtp.Packet{
|
rtpPkt = rtp.Packet{
|
||||||
@@ -79,7 +79,7 @@ func TestRTCPReceiverBase(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
@@ -89,9 +89,9 @@ func TestRTCPReceiverOverflow(t *testing.T) {
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
rr := &RTCPReceiver{
|
rr := &RTCPReceiver{
|
||||||
ClockRate: 90000,
|
ClockRate: 90000,
|
||||||
ReceiverSSRC: uint32Ptr(0x65f83afb),
|
LocalSSRC: uint32Ptr(0x65f83afb),
|
||||||
Period: 250 * time.Millisecond,
|
Period: 250 * time.Millisecond,
|
||||||
TimeNow: func() time.Time {
|
TimeNow: func() time.Time {
|
||||||
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
||||||
},
|
},
|
||||||
@@ -138,7 +138,7 @@ func TestRTCPReceiverOverflow(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rtpPkt = rtp.Packet{
|
rtpPkt = rtp.Packet{
|
||||||
@@ -153,7 +153,7 @@ func TestRTCPReceiverOverflow(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
@@ -163,9 +163,9 @@ func TestRTCPReceiverPacketLost(t *testing.T) {
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
rr := &RTCPReceiver{
|
rr := &RTCPReceiver{
|
||||||
ClockRate: 90000,
|
ClockRate: 90000,
|
||||||
ReceiverSSRC: uint32Ptr(0x65f83afb),
|
LocalSSRC: uint32Ptr(0x65f83afb),
|
||||||
Period: 500 * time.Millisecond,
|
Period: 500 * time.Millisecond,
|
||||||
TimeNow: func() time.Time {
|
TimeNow: func() time.Time {
|
||||||
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
||||||
},
|
},
|
||||||
@@ -215,7 +215,7 @@ func TestRTCPReceiverPacketLost(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rtpPkt = rtp.Packet{
|
rtpPkt = rtp.Packet{
|
||||||
@@ -230,7 +230,7 @@ func TestRTCPReceiverPacketLost(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
@@ -240,9 +240,9 @@ func TestRTCPReceiverOverflowPacketLost(t *testing.T) {
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
rr := &RTCPReceiver{
|
rr := &RTCPReceiver{
|
||||||
ClockRate: 90000,
|
ClockRate: 90000,
|
||||||
ReceiverSSRC: uint32Ptr(0x65f83afb),
|
LocalSSRC: uint32Ptr(0x65f83afb),
|
||||||
Period: 500 * time.Millisecond,
|
Period: 500 * time.Millisecond,
|
||||||
TimeNow: func() time.Time {
|
TimeNow: func() time.Time {
|
||||||
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
||||||
},
|
},
|
||||||
@@ -292,7 +292,7 @@ func TestRTCPReceiverOverflowPacketLost(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rtpPkt = rtp.Packet{
|
rtpPkt = rtp.Packet{
|
||||||
@@ -307,7 +307,7 @@ func TestRTCPReceiverOverflowPacketLost(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
@@ -317,9 +317,9 @@ func TestRTCPReceiverJitter(t *testing.T) {
|
|||||||
done := make(chan struct{})
|
done := make(chan struct{})
|
||||||
|
|
||||||
rr := &RTCPReceiver{
|
rr := &RTCPReceiver{
|
||||||
ClockRate: 90000,
|
ClockRate: 90000,
|
||||||
ReceiverSSRC: uint32Ptr(0x65f83afb),
|
LocalSSRC: uint32Ptr(0x65f83afb),
|
||||||
Period: 500 * time.Millisecond,
|
Period: 500 * time.Millisecond,
|
||||||
TimeNow: func() time.Time {
|
TimeNow: func() time.Time {
|
||||||
return time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
|
return time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
|
||||||
},
|
},
|
||||||
@@ -365,7 +365,7 @@ func TestRTCPReceiverJitter(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rtpPkt = rtp.Packet{
|
rtpPkt = rtp.Packet{
|
||||||
@@ -380,7 +380,7 @@ func TestRTCPReceiverJitter(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, true)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
rtpPkt = rtp.Packet{
|
rtpPkt = rtp.Packet{
|
||||||
@@ -395,7 +395,7 @@ func TestRTCPReceiverJitter(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
|
||||||
err = rr.ProcessPacket(&rtpPkt, ts, false)
|
err = rr.ProcessPacketRTP(&rtpPkt, ts, false)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
<-done
|
<-done
|
||||||
|
@@ -26,11 +26,11 @@ type RTCPSender struct {
|
|||||||
mutex sync.RWMutex
|
mutex sync.RWMutex
|
||||||
|
|
||||||
// data from RTP packets
|
// data from RTP packets
|
||||||
initialized bool
|
firstRTPPacketSent bool
|
||||||
lastTimeRTP uint32
|
lastTimeRTP uint32
|
||||||
lastTimeNTP time.Time
|
lastTimeNTP time.Time
|
||||||
lastTimeSystem time.Time
|
lastTimeSystem time.Time
|
||||||
senderSSRC uint32
|
localSSRC uint32
|
||||||
lastSequenceNumber uint16
|
lastSequenceNumber uint16
|
||||||
packetCount uint32
|
packetCount uint32
|
||||||
octetCount uint32
|
octetCount uint32
|
||||||
@@ -81,7 +81,7 @@ func (rs *RTCPSender) report() rtcp.Packet {
|
|||||||
rs.mutex.Lock()
|
rs.mutex.Lock()
|
||||||
defer rs.mutex.Unlock()
|
defer rs.mutex.Unlock()
|
||||||
|
|
||||||
if !rs.initialized {
|
if !rs.firstRTPPacketSent {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -90,7 +90,7 @@ func (rs *RTCPSender) report() rtcp.Packet {
|
|||||||
rtpTime := rs.lastTimeRTP + uint32(systemTimeDiff.Seconds()*float64(rs.ClockRate))
|
rtpTime := rs.lastTimeRTP + uint32(systemTimeDiff.Seconds()*float64(rs.ClockRate))
|
||||||
|
|
||||||
return &rtcp.SenderReport{
|
return &rtcp.SenderReport{
|
||||||
SSRC: rs.senderSSRC,
|
SSRC: rs.localSSRC,
|
||||||
NTPTime: ntpTimeGoToRTCP(ntpTime),
|
NTPTime: ntpTimeGoToRTCP(ntpTime),
|
||||||
RTPTime: rtpTime,
|
RTPTime: rtpTime,
|
||||||
PacketCount: rs.packetCount,
|
PacketCount: rs.packetCount,
|
||||||
@@ -98,17 +98,17 @@ func (rs *RTCPSender) report() rtcp.Packet {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessPacket extracts data from RTP packets.
|
// ProcessPacketRTP extracts data from RTP packets.
|
||||||
func (rs *RTCPSender) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsDTS bool) {
|
func (rs *RTCPSender) ProcessPacketRTP(pkt *rtp.Packet, ntp time.Time, ptsEqualsDTS bool) {
|
||||||
rs.mutex.Lock()
|
rs.mutex.Lock()
|
||||||
defer rs.mutex.Unlock()
|
defer rs.mutex.Unlock()
|
||||||
|
|
||||||
if ptsEqualsDTS {
|
if ptsEqualsDTS {
|
||||||
rs.initialized = true
|
rs.firstRTPPacketSent = true
|
||||||
rs.lastTimeRTP = pkt.Timestamp
|
rs.lastTimeRTP = pkt.Timestamp
|
||||||
rs.lastTimeNTP = ntp
|
rs.lastTimeNTP = ntp
|
||||||
rs.lastTimeSystem = rs.TimeNow()
|
rs.lastTimeSystem = rs.TimeNow()
|
||||||
rs.senderSSRC = pkt.SSRC
|
rs.localSSRC = pkt.SSRC
|
||||||
}
|
}
|
||||||
|
|
||||||
rs.lastSequenceNumber = pkt.SequenceNumber
|
rs.lastSequenceNumber = pkt.SequenceNumber
|
||||||
@@ -117,16 +117,27 @@ func (rs *RTCPSender) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsDTS
|
|||||||
rs.octetCount += uint32(len(pkt.Payload))
|
rs.octetCount += uint32(len(pkt.Payload))
|
||||||
}
|
}
|
||||||
|
|
||||||
// SenderSSRC returns the SSRC of outgoing RTP packets.
|
// Stats are statistics.
|
||||||
func (rs *RTCPSender) SenderSSRC() (uint32, bool) {
|
type Stats struct {
|
||||||
rs.mutex.RLock()
|
LocalSSRC uint32
|
||||||
defer rs.mutex.RUnlock()
|
LastSequenceNumber uint16
|
||||||
return rs.senderSSRC, rs.initialized
|
LastRTP uint32
|
||||||
|
LastNTP time.Time
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastPacketData returns metadata of the last RTP packet.
|
// Stats returns statistics.
|
||||||
func (rs *RTCPSender) LastPacketData() (uint16, uint32, time.Time, bool) {
|
func (rs *RTCPSender) Stats() *Stats {
|
||||||
rs.mutex.RLock()
|
rs.mutex.RLock()
|
||||||
defer rs.mutex.RUnlock()
|
defer rs.mutex.RUnlock()
|
||||||
return rs.lastSequenceNumber, rs.lastTimeRTP, rs.lastTimeNTP, rs.initialized
|
|
||||||
|
if !rs.firstRTPPacketSent {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
return &Stats{
|
||||||
|
LocalSSRC: rs.localSSRC,
|
||||||
|
LastSequenceNumber: rs.lastSequenceNumber,
|
||||||
|
LastRTP: rs.lastTimeRTP,
|
||||||
|
LastNTP: rs.lastTimeNTP,
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@@ -63,7 +63,7 @@ func TestRTCPSender(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts := time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
ts := time.Date(2008, 0o5, 20, 22, 15, 20, 0, time.UTC)
|
||||||
rs.ProcessPacket(&rtpPkt, ts, true)
|
rs.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
|
|
||||||
setCurTime(time.Date(2008, 5, 20, 22, 16, 22, 0, time.UTC))
|
setCurTime(time.Date(2008, 5, 20, 22, 16, 22, 0, time.UTC))
|
||||||
rtpPkt = rtp.Packet{
|
rtpPkt = rtp.Packet{
|
||||||
@@ -78,7 +78,7 @@ func TestRTCPSender(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
|
||||||
rs.ProcessPacket(&rtpPkt, ts, true)
|
rs.ProcessPacketRTP(&rtpPkt, ts, true)
|
||||||
|
|
||||||
rtpPkt = rtp.Packet{
|
rtpPkt = rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
@@ -92,7 +92,7 @@ func TestRTCPSender(t *testing.T) {
|
|||||||
Payload: []byte("\x00\x00"),
|
Payload: []byte("\x00\x00"),
|
||||||
}
|
}
|
||||||
ts = time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
|
ts = time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
|
||||||
rs.ProcessPacket(&rtpPkt, ts, false)
|
rs.ProcessPacketRTP(&rtpPkt, ts, false)
|
||||||
|
|
||||||
setCurTime(time.Date(2008, 5, 20, 22, 16, 24, 0, time.UTC))
|
setCurTime(time.Date(2008, 5, 20, 22, 16, 24, 0, time.UTC))
|
||||||
|
|
||||||
|
@@ -13,7 +13,7 @@ type LossDetector struct {
|
|||||||
|
|
||||||
// Process processes a RTP packet.
|
// Process processes a RTP packet.
|
||||||
// It returns the number of lost packets.
|
// It returns the number of lost packets.
|
||||||
func (r *LossDetector) Process(pkt *rtp.Packet) int {
|
func (r *LossDetector) Process(pkt *rtp.Packet) uint {
|
||||||
if !r.initialized {
|
if !r.initialized {
|
||||||
r.initialized = true
|
r.initialized = true
|
||||||
r.expectedSeqNum = pkt.SequenceNumber + 1
|
r.expectedSeqNum = pkt.SequenceNumber + 1
|
||||||
@@ -23,7 +23,7 @@ func (r *LossDetector) Process(pkt *rtp.Packet) int {
|
|||||||
if pkt.SequenceNumber != r.expectedSeqNum {
|
if pkt.SequenceNumber != r.expectedSeqNum {
|
||||||
diff := pkt.SequenceNumber - r.expectedSeqNum
|
diff := pkt.SequenceNumber - r.expectedSeqNum
|
||||||
r.expectedSeqNum = pkt.SequenceNumber + 1
|
r.expectedSeqNum = pkt.SequenceNumber + 1
|
||||||
return int(diff)
|
return uint(diff)
|
||||||
}
|
}
|
||||||
|
|
||||||
r.expectedSeqNum = pkt.SequenceNumber + 1
|
r.expectedSeqNum = pkt.SequenceNumber + 1
|
||||||
|
@@ -15,19 +15,19 @@ func TestLossDetector(t *testing.T) {
|
|||||||
SequenceNumber: 65530,
|
SequenceNumber: 65530,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Equal(t, 0, c)
|
require.Equal(t, uint(0), c)
|
||||||
|
|
||||||
c = d.Process(&rtp.Packet{
|
c = d.Process(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
SequenceNumber: 65531,
|
SequenceNumber: 65531,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Equal(t, 0, c)
|
require.Equal(t, uint(0), c)
|
||||||
|
|
||||||
c = d.Process(&rtp.Packet{
|
c = d.Process(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
SequenceNumber: 65535,
|
SequenceNumber: 65535,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Equal(t, 3, c)
|
require.Equal(t, uint(3), c)
|
||||||
}
|
}
|
||||||
|
@@ -27,7 +27,7 @@ func (r *Reorderer) Initialize() {
|
|||||||
|
|
||||||
// Process processes a RTP packet.
|
// Process processes a RTP packet.
|
||||||
// It returns a sequence of ordered packets and the number of lost packets.
|
// It returns a sequence of ordered packets and the number of lost packets.
|
||||||
func (r *Reorderer) Process(pkt *rtp.Packet) ([]*rtp.Packet, int) {
|
func (r *Reorderer) Process(pkt *rtp.Packet) ([]*rtp.Packet, uint) {
|
||||||
if !r.initialized {
|
if !r.initialized {
|
||||||
r.initialized = true
|
r.initialized = true
|
||||||
r.expectedSeqNum = pkt.SequenceNumber + 1
|
r.expectedSeqNum = pkt.SequenceNumber + 1
|
||||||
@@ -86,7 +86,7 @@ func (r *Reorderer) Process(pkt *rtp.Packet) ([]*rtp.Packet, int) {
|
|||||||
ret[pos] = pkt
|
ret[pos] = pkt
|
||||||
|
|
||||||
r.expectedSeqNum = pkt.SequenceNumber + 1
|
r.expectedSeqNum = pkt.SequenceNumber + 1
|
||||||
return ret, int(relPos) - n + 1
|
return ret, uint(int(relPos) - n + 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// there's a missing packet
|
// there's a missing packet
|
||||||
|
@@ -164,7 +164,7 @@ func TestReorder(t *testing.T) {
|
|||||||
for _, entry := range sequence {
|
for _, entry := range sequence {
|
||||||
out, missing := r.Process(entry.in)
|
out, missing := r.Process(entry.in)
|
||||||
require.Equal(t, entry.out, out)
|
require.Equal(t, entry.out, out)
|
||||||
require.Equal(t, 0, missing)
|
require.Equal(t, uint(0), missing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -173,7 +173,7 @@ func TestBufferIsFull(t *testing.T) {
|
|||||||
r.Initialize()
|
r.Initialize()
|
||||||
r.absPos = 25
|
r.absPos = 25
|
||||||
sn := uint16(1564)
|
sn := uint16(1564)
|
||||||
toMiss := 34
|
toMiss := uint(34)
|
||||||
|
|
||||||
out, missing := r.Process(&rtp.Packet{
|
out, missing := r.Process(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
@@ -185,19 +185,19 @@ func TestBufferIsFull(t *testing.T) {
|
|||||||
SequenceNumber: sn,
|
SequenceNumber: sn,
|
||||||
},
|
},
|
||||||
}}, out)
|
}}, out)
|
||||||
require.Equal(t, 0, missing)
|
require.Equal(t, uint(0), missing)
|
||||||
sn++
|
sn++
|
||||||
|
|
||||||
var expected []*rtp.Packet
|
var expected []*rtp.Packet
|
||||||
|
|
||||||
for i := 0; i < 64-toMiss; i++ {
|
for i := uint(0); i < 64-toMiss; i++ {
|
||||||
out, missing = r.Process(&rtp.Packet{
|
out, missing = r.Process(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
SequenceNumber: sn + uint16(toMiss),
|
SequenceNumber: sn + uint16(toMiss),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Equal(t, []*rtp.Packet(nil), out)
|
require.Equal(t, []*rtp.Packet(nil), out)
|
||||||
require.Equal(t, 0, missing)
|
require.Equal(t, uint(0), missing)
|
||||||
|
|
||||||
expected = append(expected, &rtp.Packet{
|
expected = append(expected, &rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
@@ -242,7 +242,7 @@ func TestReset(t *testing.T) {
|
|||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Equal(t, []*rtp.Packet(nil), out)
|
require.Equal(t, []*rtp.Packet(nil), out)
|
||||||
require.Equal(t, 0, missing)
|
require.Equal(t, uint(0), missing)
|
||||||
sn++
|
sn++
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -256,5 +256,5 @@ func TestReset(t *testing.T) {
|
|||||||
SequenceNumber: sn,
|
SequenceNumber: sn,
|
||||||
},
|
},
|
||||||
}}, out)
|
}}, out)
|
||||||
require.Equal(t, 0, missing)
|
require.Equal(t, uint(0), missing)
|
||||||
}
|
}
|
||||||
|
@@ -251,7 +251,7 @@ func (e ErrClientWriteQueueFull) Error() string {
|
|||||||
|
|
||||||
// ErrClientRTPPacketsLost is an error that can be returned by a client.
|
// ErrClientRTPPacketsLost is an error that can be returned by a client.
|
||||||
type ErrClientRTPPacketsLost struct {
|
type ErrClientRTPPacketsLost struct {
|
||||||
Lost int
|
Lost uint
|
||||||
}
|
}
|
||||||
|
|
||||||
// Error implements the error interface.
|
// Error implements the error interface.
|
||||||
|
@@ -24,7 +24,7 @@ func New(
|
|||||||
) (*RTCPReceiver, error) {
|
) (*RTCPReceiver, error) {
|
||||||
rr := &rtcpreceiver.RTCPReceiver{
|
rr := &rtcpreceiver.RTCPReceiver{
|
||||||
ClockRate: clockRate,
|
ClockRate: clockRate,
|
||||||
ReceiverSSRC: receiverSSRC,
|
LocalSSRC: receiverSSRC,
|
||||||
Period: period,
|
Period: period,
|
||||||
TimeNow: timeNow,
|
TimeNow: timeNow,
|
||||||
WritePacketRTCP: writePacketRTCP,
|
WritePacketRTCP: writePacketRTCP,
|
||||||
@@ -44,7 +44,7 @@ func (rr *RTCPReceiver) Close() {
|
|||||||
|
|
||||||
// ProcessPacket extracts the needed data from RTP packets.
|
// ProcessPacket extracts the needed data from RTP packets.
|
||||||
func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, system time.Time, ptsEqualsDTS bool) error {
|
func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, system time.Time, ptsEqualsDTS bool) error {
|
||||||
return (*rtcpreceiver.RTCPReceiver)(rr).ProcessPacket(pkt, system, ptsEqualsDTS)
|
return (*rtcpreceiver.RTCPReceiver)(rr).ProcessPacketRTP(pkt, system, ptsEqualsDTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// ProcessSenderReport extracts the needed data from RTCP sender reports.
|
// ProcessSenderReport extracts the needed data from RTCP sender reports.
|
||||||
@@ -59,5 +59,9 @@ func (rr *RTCPReceiver) PacketNTP(ts uint32) (time.Time, bool) {
|
|||||||
|
|
||||||
// SenderSSRC returns the SSRC of outgoing RTP packets.
|
// SenderSSRC returns the SSRC of outgoing RTP packets.
|
||||||
func (rr *RTCPReceiver) SenderSSRC() (uint32, bool) {
|
func (rr *RTCPReceiver) SenderSSRC() (uint32, bool) {
|
||||||
return (*rtcpreceiver.RTCPReceiver)(rr).SenderSSRC()
|
stats := (*rtcpreceiver.RTCPReceiver)(rr).Stats()
|
||||||
|
if stats == nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
return stats.RemoteSSRC, true
|
||||||
}
|
}
|
||||||
|
@@ -39,15 +39,25 @@ func (rs *RTCPSender) Close() {
|
|||||||
|
|
||||||
// ProcessPacket extracts data from RTP packets.
|
// ProcessPacket extracts data from RTP packets.
|
||||||
func (rs *RTCPSender) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsDTS bool) {
|
func (rs *RTCPSender) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsDTS bool) {
|
||||||
(*rtcpsender.RTCPSender)(rs).ProcessPacket(pkt, ntp, ptsEqualsDTS)
|
(*rtcpsender.RTCPSender)(rs).ProcessPacketRTP(pkt, ntp, ptsEqualsDTS)
|
||||||
}
|
}
|
||||||
|
|
||||||
// SenderSSRC returns the SSRC of outgoing RTP packets.
|
// SenderSSRC returns the SSRC of outgoing RTP packets.
|
||||||
func (rs *RTCPSender) SenderSSRC() (uint32, bool) {
|
func (rs *RTCPSender) SenderSSRC() (uint32, bool) {
|
||||||
return (*rtcpsender.RTCPSender)(rs).SenderSSRC()
|
stats := (*rtcpsender.RTCPSender)(rs).Stats()
|
||||||
|
if stats == nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats.LocalSSRC, true
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastPacketData returns metadata of the last RTP packet.
|
// LastPacketData returns metadata of the last RTP packet.
|
||||||
func (rs *RTCPSender) LastPacketData() (uint16, uint32, time.Time, bool) {
|
func (rs *RTCPSender) LastPacketData() (uint16, uint32, time.Time, bool) {
|
||||||
return (*rtcpsender.RTCPSender)(rs).LastPacketData()
|
stats := (*rtcpsender.RTCPSender)(rs).Stats()
|
||||||
|
if stats == nil {
|
||||||
|
return 0, 0, time.Time{}, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats.LastSequenceNumber, stats.LastRTP, stats.LastNTP, true
|
||||||
}
|
}
|
||||||
|
@@ -18,6 +18,6 @@ func New() *LossDetector {
|
|||||||
|
|
||||||
// Process processes a RTP packet.
|
// Process processes a RTP packet.
|
||||||
// It returns the number of lost packets.
|
// It returns the number of lost packets.
|
||||||
func (r *LossDetector) Process(pkt *rtp.Packet) int {
|
func (r *LossDetector) Process(pkt *rtp.Packet) uint {
|
||||||
return (*rtplossdetector.LossDetector)(r).Process(pkt)
|
return (*rtplossdetector.LossDetector)(r).Process(pkt)
|
||||||
}
|
}
|
||||||
|
@@ -15,19 +15,19 @@ func TestLossDetector(t *testing.T) {
|
|||||||
SequenceNumber: 65530,
|
SequenceNumber: 65530,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Equal(t, 0, c)
|
require.Equal(t, uint(0), c)
|
||||||
|
|
||||||
c = d.Process(&rtp.Packet{
|
c = d.Process(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
SequenceNumber: 65531,
|
SequenceNumber: 65531,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Equal(t, 0, c)
|
require.Equal(t, uint(0), c)
|
||||||
|
|
||||||
c = d.Process(&rtp.Packet{
|
c = d.Process(&rtp.Packet{
|
||||||
Header: rtp.Header{
|
Header: rtp.Header{
|
||||||
SequenceNumber: 65535,
|
SequenceNumber: 65535,
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
require.Equal(t, 3, c)
|
require.Equal(t, uint(3), c)
|
||||||
}
|
}
|
||||||
|
@@ -22,6 +22,6 @@ func New() *Reorderer {
|
|||||||
|
|
||||||
// Process processes a RTP packet.
|
// Process processes a RTP packet.
|
||||||
// It returns a sequence of ordered packets and the number of lost packets.
|
// It returns a sequence of ordered packets and the number of lost packets.
|
||||||
func (r *Reorderer) Process(pkt *rtp.Packet) ([]*rtp.Packet, int) {
|
func (r *Reorderer) Process(pkt *rtp.Packet) ([]*rtp.Packet, uint) {
|
||||||
return (*rtpreorderer.Reorderer)(r).Process(pkt)
|
return (*rtpreorderer.Reorderer)(r).Process(pkt)
|
||||||
}
|
}
|
||||||
|
@@ -101,11 +101,15 @@ func (sc *ServerConn) NetConn() net.Conn {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BytesReceived returns the number of read bytes.
|
// BytesReceived returns the number of read bytes.
|
||||||
|
//
|
||||||
|
// Deprecated: replaced by Stats()
|
||||||
func (sc *ServerConn) BytesReceived() uint64 {
|
func (sc *ServerConn) BytesReceived() uint64 {
|
||||||
return sc.bc.BytesReceived()
|
return sc.bc.BytesReceived()
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesSent returns the number of written bytes.
|
// BytesSent returns the number of written bytes.
|
||||||
|
//
|
||||||
|
// Deprecated: replaced by Stats()
|
||||||
func (sc *ServerConn) BytesSent() uint64 {
|
func (sc *ServerConn) BytesSent() uint64 {
|
||||||
return sc.bc.BytesSent()
|
return sc.bc.BytesSent()
|
||||||
}
|
}
|
||||||
@@ -120,6 +124,14 @@ func (sc *ServerConn) UserData() interface{} {
|
|||||||
return sc.userData
|
return sc.userData
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Stats returns connection statistics.
|
||||||
|
func (sc *ServerConn) Stats() *StatsConn {
|
||||||
|
return &StatsConn{
|
||||||
|
BytesReceived: sc.bc.BytesReceived(),
|
||||||
|
BytesSent: sc.bc.BytesSent(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
func (sc *ServerConn) ip() net.IP {
|
func (sc *ServerConn) ip() net.IP {
|
||||||
return sc.remoteAddr.IP
|
return sc.remoteAddr.IP
|
||||||
}
|
}
|
||||||
|
@@ -3,7 +3,6 @@ package gortsplib
|
|||||||
import (
|
import (
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
"sync/atomic"
|
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||||
@@ -131,8 +130,6 @@ func (cr *serverConnReader) readFuncTCP() error {
|
|||||||
return liberrors.ErrServerUnexpectedResponse{}
|
return liberrors.ErrServerUnexpectedResponse{}
|
||||||
|
|
||||||
case *base.InterleavedFrame:
|
case *base.InterleavedFrame:
|
||||||
atomic.AddUint64(cr.sc.session.bytesReceived, uint64(len(what.Payload)))
|
|
||||||
|
|
||||||
if cb, ok := cr.sc.session.tcpCallbackByChannel[what.Channel]; ok {
|
if cb, ok := cr.sc.session.tcpCallbackByChannel[what.Channel]; ok {
|
||||||
cb(what.Payload)
|
cb(what.Payload)
|
||||||
}
|
}
|
||||||
|
@@ -609,10 +609,11 @@ func TestServerPlay(t *testing.T) {
|
|||||||
close(nconnOpened)
|
close(nconnOpened)
|
||||||
},
|
},
|
||||||
onConnClose: func(ctx *ServerHandlerOnConnCloseCtx) {
|
onConnClose: func(ctx *ServerHandlerOnConnCloseCtx) {
|
||||||
require.Greater(t, ctx.Conn.BytesSent(), uint64(810))
|
s := ctx.Conn.Stats()
|
||||||
require.Less(t, ctx.Conn.BytesSent(), uint64(1150))
|
require.Greater(t, s.BytesSent, uint64(810))
|
||||||
require.Greater(t, ctx.Conn.BytesReceived(), uint64(440))
|
require.Less(t, s.BytesSent, uint64(1150))
|
||||||
require.Less(t, ctx.Conn.BytesReceived(), uint64(660))
|
require.Greater(t, s.BytesReceived, uint64(440))
|
||||||
|
require.Less(t, s.BytesReceived, uint64(660))
|
||||||
|
|
||||||
close(nconnClosed)
|
close(nconnClosed)
|
||||||
},
|
},
|
||||||
@@ -621,10 +622,11 @@ func TestServerPlay(t *testing.T) {
|
|||||||
},
|
},
|
||||||
onSessionClose: func(ctx *ServerHandlerOnSessionCloseCtx) {
|
onSessionClose: func(ctx *ServerHandlerOnSessionCloseCtx) {
|
||||||
if transport != "multicast" {
|
if transport != "multicast" {
|
||||||
require.Greater(t, ctx.Session.BytesSent(), uint64(50))
|
s := ctx.Session.Stats()
|
||||||
require.Less(t, ctx.Session.BytesSent(), uint64(60))
|
require.Greater(t, s.BytesSent, uint64(50))
|
||||||
require.Greater(t, ctx.Session.BytesReceived(), uint64(15))
|
require.Less(t, s.BytesSent, uint64(60))
|
||||||
require.Less(t, ctx.Session.BytesReceived(), uint64(25))
|
require.Greater(t, s.BytesReceived, uint64(15))
|
||||||
|
require.Less(t, s.BytesReceived, uint64(25))
|
||||||
}
|
}
|
||||||
|
|
||||||
close(sessionClosed)
|
close(sessionClosed)
|
||||||
@@ -2325,7 +2327,7 @@ func TestServerPlayNoInterleavedIDs(t *testing.T) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestServerPlayBytesSent(t *testing.T) {
|
func TestServerPlayStreamStats(t *testing.T) {
|
||||||
var stream *ServerStream
|
var stream *ServerStream
|
||||||
|
|
||||||
s := &Server{
|
s := &Server{
|
||||||
@@ -2355,7 +2357,6 @@ func TestServerPlayBytesSent(t *testing.T) {
|
|||||||
err := s.Start()
|
err := s.Start()
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
defer s.Close()
|
defer s.Close()
|
||||||
|
|
||||||
stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}})
|
stream = NewServerStream(s, &description.Session{Medias: []*description.Media{testH264Media}})
|
||||||
defer stream.Close()
|
defer stream.Close()
|
||||||
|
|
||||||
@@ -2393,5 +2394,6 @@ func TestServerPlayBytesSent(t *testing.T) {
|
|||||||
err = stream.WritePacketRTP(stream.Description().Medias[0], &testRTPPacket)
|
err = stream.WritePacketRTP(stream.Description().Medias[0], &testRTPPacket)
|
||||||
require.NoError(t, err)
|
require.NoError(t, err)
|
||||||
|
|
||||||
require.Equal(t, uint64(16*2), stream.BytesSent())
|
st := stream.Stats()
|
||||||
|
require.Equal(t, uint64(16*2), st.BytesSent)
|
||||||
}
|
}
|
||||||
|
@@ -545,10 +545,11 @@ func TestServerRecord(t *testing.T) {
|
|||||||
close(nconnOpened)
|
close(nconnOpened)
|
||||||
},
|
},
|
||||||
onConnClose: func(ctx *ServerHandlerOnConnCloseCtx) {
|
onConnClose: func(ctx *ServerHandlerOnConnCloseCtx) {
|
||||||
require.Greater(t, ctx.Conn.BytesSent(), uint64(510))
|
s := ctx.Conn.Stats()
|
||||||
require.Less(t, ctx.Conn.BytesSent(), uint64(560))
|
require.Greater(t, s.BytesSent, uint64(510))
|
||||||
require.Greater(t, ctx.Conn.BytesReceived(), uint64(1000))
|
require.Less(t, s.BytesSent, uint64(560))
|
||||||
require.Less(t, ctx.Conn.BytesReceived(), uint64(1200))
|
require.Greater(t, s.BytesReceived, uint64(1000))
|
||||||
|
require.Less(t, s.BytesReceived, uint64(1200))
|
||||||
|
|
||||||
close(nconnClosed)
|
close(nconnClosed)
|
||||||
},
|
},
|
||||||
@@ -556,10 +557,11 @@ func TestServerRecord(t *testing.T) {
|
|||||||
close(sessionOpened)
|
close(sessionOpened)
|
||||||
},
|
},
|
||||||
onSessionClose: func(ctx *ServerHandlerOnSessionCloseCtx) {
|
onSessionClose: func(ctx *ServerHandlerOnSessionCloseCtx) {
|
||||||
require.Greater(t, ctx.Session.BytesSent(), uint64(75))
|
s := ctx.Session.Stats()
|
||||||
require.Less(t, ctx.Session.BytesSent(), uint64(130))
|
require.Greater(t, s.BytesSent, uint64(75))
|
||||||
require.Greater(t, ctx.Session.BytesReceived(), uint64(70))
|
require.Less(t, s.BytesSent, uint64(130))
|
||||||
require.Less(t, ctx.Session.BytesReceived(), uint64(80))
|
require.Greater(t, s.BytesReceived, uint64(70))
|
||||||
|
require.Less(t, s.BytesReceived, uint64(80))
|
||||||
|
|
||||||
close(sessionClosed)
|
close(sessionClosed)
|
||||||
},
|
},
|
||||||
|
@@ -14,6 +14,8 @@ import (
|
|||||||
"github.com/pion/rtcp"
|
"github.com/pion/rtcp"
|
||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v4/internal/rtcpreceiver"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/internal/rtcpsender"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
"github.com/bluenviron/gortsplib/v4/pkg/base"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
@@ -186,7 +188,7 @@ func generateRTPInfo(
|
|||||||
Scheme: u.Scheme,
|
Scheme: u.Scheme,
|
||||||
Host: u.Host,
|
Host: u.Host,
|
||||||
Path: setuppedPath + "/trackID=" +
|
Path: setuppedPath + "/trackID=" +
|
||||||
strconv.FormatInt(int64(setuppedStream.streamMedias[sm.media].trackID), 10),
|
strconv.FormatInt(int64(setuppedStream.medias[sm.media].trackID), 10),
|
||||||
}).String()
|
}).String()
|
||||||
ri = append(ri, entry)
|
ri = append(ri, entry)
|
||||||
}
|
}
|
||||||
@@ -235,8 +237,6 @@ type ServerSession struct {
|
|||||||
secretID string // must not be shared, allows to take ownership of the session
|
secretID string // must not be shared, allows to take ownership of the session
|
||||||
ctx context.Context
|
ctx context.Context
|
||||||
ctxCancel func()
|
ctxCancel func()
|
||||||
bytesReceived *uint64
|
|
||||||
bytesSent *uint64
|
|
||||||
userData interface{}
|
userData interface{}
|
||||||
conns map[*ServerConn]struct{}
|
conns map[*ServerConn]struct{}
|
||||||
state ServerSessionState
|
state ServerSessionState
|
||||||
@@ -272,11 +272,10 @@ func (ss *ServerSession) initialize() {
|
|||||||
ss.secretID = secretID
|
ss.secretID = secretID
|
||||||
ss.ctx = ctx
|
ss.ctx = ctx
|
||||||
ss.ctxCancel = ctxCancel
|
ss.ctxCancel = ctxCancel
|
||||||
ss.bytesReceived = new(uint64)
|
|
||||||
ss.bytesSent = new(uint64)
|
|
||||||
ss.conns = make(map[*ServerConn]struct{})
|
ss.conns = make(map[*ServerConn]struct{})
|
||||||
ss.lastRequestTime = ss.s.timeNow()
|
ss.lastRequestTime = ss.s.timeNow()
|
||||||
ss.udpCheckStreamTimer = emptyTimer()
|
ss.udpCheckStreamTimer = emptyTimer()
|
||||||
|
|
||||||
ss.chHandleRequest = make(chan sessionRequestReq)
|
ss.chHandleRequest = make(chan sessionRequestReq)
|
||||||
ss.chRemoveConn = make(chan *ServerConn)
|
ss.chRemoveConn = make(chan *ServerConn)
|
||||||
ss.chStartWriter = make(chan struct{})
|
ss.chStartWriter = make(chan struct{})
|
||||||
@@ -291,13 +290,25 @@ func (ss *ServerSession) Close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BytesReceived returns the number of read bytes.
|
// BytesReceived returns the number of read bytes.
|
||||||
|
//
|
||||||
|
// Deprecated: replaced by Stats()
|
||||||
func (ss *ServerSession) BytesReceived() uint64 {
|
func (ss *ServerSession) BytesReceived() uint64 {
|
||||||
return atomic.LoadUint64(ss.bytesReceived)
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.bytesReceived)
|
||||||
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesSent returns the number of written bytes.
|
// BytesSent returns the number of written bytes.
|
||||||
|
//
|
||||||
|
// Deprecated: replaced by Stats()
|
||||||
func (ss *ServerSession) BytesSent() uint64 {
|
func (ss *ServerSession) BytesSent() uint64 {
|
||||||
return atomic.LoadUint64(ss.bytesSent)
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.bytesSent)
|
||||||
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// State returns the state of the session.
|
// State returns the state of the session.
|
||||||
@@ -349,25 +360,190 @@ func (ss *ServerSession) UserData() interface{} {
|
|||||||
return ss.userData
|
return ss.userData
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *ServerSession) onPacketLost(err error) {
|
// Stats returns server session statistics.
|
||||||
if h, ok := ss.s.Handler.(ServerHandlerOnPacketLost); ok {
|
func (ss *ServerSession) Stats() *StatsSession {
|
||||||
h.OnPacketLost(&ServerHandlerOnPacketLostCtx{
|
return &StatsSession{
|
||||||
Session: ss,
|
BytesReceived: func() uint64 {
|
||||||
Error: err,
|
v := uint64(0)
|
||||||
})
|
for _, sm := range ss.setuppedMedias {
|
||||||
} else {
|
v += atomic.LoadUint64(sm.bytesReceived)
|
||||||
log.Println(err.Error())
|
}
|
||||||
}
|
return v
|
||||||
}
|
}(),
|
||||||
|
BytesSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.bytesSent)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsReceived: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
for _, f := range sm.formats {
|
||||||
|
v += atomic.LoadUint64(f.rtpPacketsReceived)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
for _, f := range sm.formats {
|
||||||
|
v += atomic.LoadUint64(f.rtpPacketsSent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsLost: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
for _, f := range sm.formats {
|
||||||
|
v += atomic.LoadUint64(f.rtpPacketsLost)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsInError: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.rtpPacketsInError)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPJitter: func() float64 {
|
||||||
|
v := float64(0)
|
||||||
|
n := float64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
for _, fo := range sm.formats {
|
||||||
|
if fo.rtcpReceiver != nil {
|
||||||
|
stats := fo.rtcpReceiver.Stats()
|
||||||
|
if stats != nil {
|
||||||
|
v += stats.Jitter
|
||||||
|
n++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v / n
|
||||||
|
}(),
|
||||||
|
RTCPPacketsReceived: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.rtcpPacketsReceived)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTCPPacketsSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.rtcpPacketsSent)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTCPPacketsInError: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, sm := range ss.setuppedMedias {
|
||||||
|
v += atomic.LoadUint64(sm.rtcpPacketsInError)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
Medias: func() map[*description.Media]StatsSessionMedia { //nolint:dupl
|
||||||
|
ret := make(map[*description.Media]StatsSessionMedia, len(ss.setuppedMedias))
|
||||||
|
|
||||||
func (ss *ServerSession) onDecodeError(err error) {
|
for med, sm := range ss.setuppedMedias {
|
||||||
if h, ok := ss.s.Handler.(ServerHandlerOnDecodeError); ok {
|
ret[med] = StatsSessionMedia{
|
||||||
h.OnDecodeError(&ServerHandlerOnDecodeErrorCtx{
|
BytesReceived: atomic.LoadUint64(sm.bytesReceived),
|
||||||
Session: ss,
|
BytesSent: atomic.LoadUint64(sm.bytesSent),
|
||||||
Error: err,
|
RTPPacketsInError: atomic.LoadUint64(sm.rtpPacketsInError),
|
||||||
})
|
RTCPPacketsReceived: atomic.LoadUint64(sm.rtcpPacketsReceived),
|
||||||
} else {
|
RTCPPacketsSent: atomic.LoadUint64(sm.rtcpPacketsSent),
|
||||||
log.Println(err.Error())
|
RTCPPacketsInError: atomic.LoadUint64(sm.rtcpPacketsInError),
|
||||||
|
Formats: func() map[format.Format]StatsSessionFormat {
|
||||||
|
ret := make(map[format.Format]StatsSessionFormat, len(sm.formats))
|
||||||
|
|
||||||
|
for _, fo := range sm.formats {
|
||||||
|
recvStats := func() *rtcpreceiver.Stats {
|
||||||
|
if fo.rtcpReceiver != nil {
|
||||||
|
return fo.rtcpReceiver.Stats()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
rtcpSender := func() *rtcpsender.RTCPSender {
|
||||||
|
if ss.setuppedStream != nil {
|
||||||
|
return ss.setuppedStream.medias[med].formats[fo.format.PayloadType()].rtcpSender
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
sentStats := func() *rtcpsender.Stats {
|
||||||
|
if rtcpSender != nil {
|
||||||
|
return rtcpSender.Stats()
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}()
|
||||||
|
|
||||||
|
ret[fo.format] = StatsSessionFormat{ //nolint:dupl
|
||||||
|
RTPPacketsReceived: atomic.LoadUint64(fo.rtpPacketsReceived),
|
||||||
|
RTPPacketsSent: atomic.LoadUint64(fo.rtpPacketsSent),
|
||||||
|
RTPPacketsLost: atomic.LoadUint64(fo.rtpPacketsLost),
|
||||||
|
LocalSSRC: func() uint32 {
|
||||||
|
if fo.rtcpReceiver != nil {
|
||||||
|
return *fo.rtcpReceiver.LocalSSRC
|
||||||
|
}
|
||||||
|
if sentStats != nil {
|
||||||
|
return sentStats.LocalSSRC
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
RemoteSSRC: func() uint32 {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.RemoteSSRC
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
RTPPacketsLastSequenceNumber: func() uint16 {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.LastSequenceNumber
|
||||||
|
}
|
||||||
|
if sentStats != nil {
|
||||||
|
return sentStats.LastSequenceNumber
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
RTPPacketsLastRTP: func() uint32 {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.LastRTP
|
||||||
|
}
|
||||||
|
if sentStats != nil {
|
||||||
|
return sentStats.LastRTP
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
RTPPacketsLastNTP: func() time.Time {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.LastNTP
|
||||||
|
}
|
||||||
|
if sentStats != nil {
|
||||||
|
return sentStats.LastNTP
|
||||||
|
}
|
||||||
|
return time.Time{}
|
||||||
|
}(),
|
||||||
|
RTPJitter: func() float64 {
|
||||||
|
if recvStats != nil {
|
||||||
|
return recvStats.Jitter
|
||||||
|
}
|
||||||
|
return 0
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}(),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -848,7 +1024,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
th := headers.Transport{}
|
th := headers.Transport{}
|
||||||
|
|
||||||
if ss.state == ServerSessionStatePrePlay {
|
if ss.state == ServerSessionStatePrePlay {
|
||||||
ssrc, ok := stream.senderSSRC(medi)
|
ssrc, ok := stream.localSSRC(medi)
|
||||||
if ok {
|
if ok {
|
||||||
th.SSRC = &ssrc
|
th.SSRC = &ssrc
|
||||||
}
|
}
|
||||||
@@ -894,7 +1070,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
|
|||||||
th.Delivery = &de
|
th.Delivery = &de
|
||||||
v := uint(127)
|
v := uint(127)
|
||||||
th.TTL = &v
|
th.TTL = &v
|
||||||
d := stream.streamMedias[medi].multicastWriter.ip()
|
d := stream.medias[medi].multicastWriter.ip()
|
||||||
th.Destination = &d
|
th.Destination = &d
|
||||||
th.Ports = &[2]int{ss.s.MulticastRTPPort, ss.s.MulticastRTCPPort}
|
th.Ports = &[2]int{ss.s.MulticastRTPPort, ss.s.MulticastRTCPPort}
|
||||||
|
|
||||||
@@ -1229,7 +1405,7 @@ func (ss *ServerSession) findFreeChannelPair() int {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPacketRTPAny sets the callback that is called when a RTP packet is read from any setupped media.
|
// OnPacketRTPAny sets a callback that is called when a RTP packet is read from any setupped media.
|
||||||
func (ss *ServerSession) OnPacketRTPAny(cb OnPacketRTPAnyFunc) {
|
func (ss *ServerSession) OnPacketRTPAny(cb OnPacketRTPAnyFunc) {
|
||||||
for _, sm := range ss.setuppedMedias {
|
for _, sm := range ss.setuppedMedias {
|
||||||
cmedia := sm.media
|
cmedia := sm.media
|
||||||
@@ -1241,7 +1417,7 @@ func (ss *ServerSession) OnPacketRTPAny(cb OnPacketRTPAnyFunc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPacketRTCPAny sets the callback that is called when a RTCP packet is read from any setupped media.
|
// OnPacketRTCPAny sets a callback that is called when a RTCP packet is read from any setupped media.
|
||||||
func (ss *ServerSession) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) {
|
func (ss *ServerSession) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) {
|
||||||
for _, sm := range ss.setuppedMedias {
|
for _, sm := range ss.setuppedMedias {
|
||||||
cmedia := sm.media
|
cmedia := sm.media
|
||||||
@@ -1251,24 +1427,25 @@ func (ss *ServerSession) OnPacketRTCPAny(cb OnPacketRTCPAnyFunc) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPacketRTP sets the callback that is called when a RTP packet is read.
|
// OnPacketRTP sets a callback that is called when a RTP packet is read.
|
||||||
func (ss *ServerSession) OnPacketRTP(medi *description.Media, forma format.Format, cb OnPacketRTPFunc) {
|
func (ss *ServerSession) OnPacketRTP(medi *description.Media, forma format.Format, cb OnPacketRTPFunc) {
|
||||||
sm := ss.setuppedMedias[medi]
|
sm := ss.setuppedMedias[medi]
|
||||||
st := sm.formats[forma.PayloadType()]
|
st := sm.formats[forma.PayloadType()]
|
||||||
st.onPacketRTP = cb
|
st.onPacketRTP = cb
|
||||||
}
|
}
|
||||||
|
|
||||||
// OnPacketRTCP sets the callback that is called when a RTCP packet is read.
|
// OnPacketRTCP sets a callback that is called when a RTCP packet is read.
|
||||||
func (ss *ServerSession) OnPacketRTCP(medi *description.Media, cb OnPacketRTCPFunc) {
|
func (ss *ServerSession) OnPacketRTCP(medi *description.Media, cb OnPacketRTCPFunc) {
|
||||||
sm := ss.setuppedMedias[medi]
|
sm := ss.setuppedMedias[medi]
|
||||||
sm.onPacketRTCP = cb
|
sm.onPacketRTCP = cb
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *ServerSession) writePacketRTP(medi *description.Media, byts []byte) error {
|
func (ss *ServerSession) writePacketRTP(medi *description.Media, payloadType uint8, byts []byte) error {
|
||||||
sm := ss.setuppedMedias[medi]
|
sm := ss.setuppedMedias[medi]
|
||||||
|
sf := sm.formats[payloadType]
|
||||||
|
|
||||||
ok := sm.ss.writer.push(func() error {
|
ok := ss.writer.push(func() error {
|
||||||
return sm.writePacketRTPInQueue(byts)
|
return sf.writePacketRTPInQueue(byts)
|
||||||
})
|
})
|
||||||
if !ok {
|
if !ok {
|
||||||
return liberrors.ErrServerWriteQueueFull{}
|
return liberrors.ErrServerWriteQueueFull{}
|
||||||
@@ -1286,7 +1463,7 @@ func (ss *ServerSession) WritePacketRTP(medi *description.Media, pkt *rtp.Packet
|
|||||||
}
|
}
|
||||||
byts = byts[:n]
|
byts = byts[:n]
|
||||||
|
|
||||||
return ss.writePacketRTP(medi, byts)
|
return ss.writePacketRTP(medi, pkt.PayloadType, byts)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (ss *ServerSession) writePacketRTCP(medi *description.Media, byts []byte) error {
|
func (ss *ServerSession) writePacketRTCP(medi *description.Media, byts []byte) error {
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package gortsplib
|
package gortsplib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/pion/rtcp"
|
"github.com/pion/rtcp"
|
||||||
@@ -18,12 +20,30 @@ type serverSessionFormat struct {
|
|||||||
format format.Format
|
format format.Format
|
||||||
onPacketRTP OnPacketRTPFunc
|
onPacketRTP OnPacketRTPFunc
|
||||||
|
|
||||||
udpReorderer *rtpreorderer.Reorderer
|
udpReorderer *rtpreorderer.Reorderer
|
||||||
tcpLossDetector *rtplossdetector.LossDetector
|
tcpLossDetector *rtplossdetector.LossDetector
|
||||||
rtcpReceiver *rtcpreceiver.RTCPReceiver
|
rtcpReceiver *rtcpreceiver.RTCPReceiver
|
||||||
|
writePacketRTPInQueue func([]byte) error
|
||||||
|
rtpPacketsReceived *uint64
|
||||||
|
rtpPacketsSent *uint64
|
||||||
|
rtpPacketsLost *uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *serverSessionFormat) initialize() {
|
||||||
|
sf.rtpPacketsReceived = new(uint64)
|
||||||
|
sf.rtpPacketsSent = new(uint64)
|
||||||
|
sf.rtpPacketsLost = new(uint64)
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sf *serverSessionFormat) start() {
|
func (sf *serverSessionFormat) start() {
|
||||||
|
switch *sf.sm.ss.setuppedTransport {
|
||||||
|
case TransportUDP, TransportUDPMulticast:
|
||||||
|
sf.writePacketRTPInQueue = sf.writePacketRTPInQueueUDP
|
||||||
|
|
||||||
|
default:
|
||||||
|
sf.writePacketRTPInQueue = sf.writePacketRTPInQueueTCP
|
||||||
|
}
|
||||||
|
|
||||||
if sf.sm.ss.state != ServerSessionStatePlay {
|
if sf.sm.ss.state != ServerSessionStatePlay {
|
||||||
if *sf.sm.ss.setuppedTransport == TransportUDP || *sf.sm.ss.setuppedTransport == TransportUDPMulticast {
|
if *sf.sm.ss.setuppedTransport == TransportUDP || *sf.sm.ss.setuppedTransport == TransportUDPMulticast {
|
||||||
sf.udpReorderer = &rtpreorderer.Reorderer{}
|
sf.udpReorderer = &rtpreorderer.Reorderer{}
|
||||||
@@ -56,38 +76,76 @@ func (sf *serverSessionFormat) stop() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sf *serverSessionFormat) readRTPUDP(pkt *rtp.Packet, now time.Time) {
|
func (sf *serverSessionFormat) readPacketRTPUDP(pkt *rtp.Packet, now time.Time) {
|
||||||
packets, lost := sf.udpReorderer.Process(pkt)
|
packets, lost := sf.udpReorderer.Process(pkt)
|
||||||
if lost != 0 {
|
if lost != 0 {
|
||||||
sf.sm.ss.onPacketLost(liberrors.ErrServerRTPPacketsLost{Lost: lost})
|
sf.onPacketRTPLost(lost)
|
||||||
// do not return
|
// do not return
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
err := sf.rtcpReceiver.ProcessPacket(pkt, now, sf.format.PTSEqualsDTS(pkt))
|
sf.handlePacketRTP(pkt, now)
|
||||||
if err != nil {
|
|
||||||
sf.sm.ss.onDecodeError(err)
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
|
|
||||||
sf.onPacketRTP(pkt)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sf *serverSessionFormat) readRTPTCP(pkt *rtp.Packet) {
|
func (sf *serverSessionFormat) readPacketRTPTCP(pkt *rtp.Packet) {
|
||||||
lost := sf.tcpLossDetector.Process(pkt)
|
lost := sf.tcpLossDetector.Process(pkt)
|
||||||
if lost != 0 {
|
if lost != 0 {
|
||||||
sf.sm.ss.onPacketLost(liberrors.ErrServerRTPPacketsLost{Lost: lost})
|
sf.onPacketRTPLost(lost)
|
||||||
// do not return
|
// do not return
|
||||||
}
|
}
|
||||||
|
|
||||||
now := sf.sm.ss.s.timeNow()
|
now := sf.sm.ss.s.timeNow()
|
||||||
|
|
||||||
err := sf.rtcpReceiver.ProcessPacket(pkt, now, sf.format.PTSEqualsDTS(pkt))
|
sf.handlePacketRTP(pkt, now)
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *serverSessionFormat) handlePacketRTP(pkt *rtp.Packet, now time.Time) {
|
||||||
|
err := sf.rtcpReceiver.ProcessPacketRTP(pkt, now, sf.format.PTSEqualsDTS(pkt))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sf.sm.ss.onDecodeError(err)
|
sf.sm.onPacketRTPDecodeError(err)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(sf.rtpPacketsReceived, 1)
|
||||||
|
|
||||||
sf.onPacketRTP(pkt)
|
sf.onPacketRTP(pkt)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sf *serverSessionFormat) onPacketRTPLost(lost uint) {
|
||||||
|
atomic.AddUint64(sf.rtpPacketsLost, uint64(lost))
|
||||||
|
|
||||||
|
if h, ok := sf.sm.ss.s.Handler.(ServerHandlerOnPacketLost); ok {
|
||||||
|
h.OnPacketLost(&ServerHandlerOnPacketLostCtx{
|
||||||
|
Session: sf.sm.ss,
|
||||||
|
Error: liberrors.ErrServerRTPPacketsLost{Lost: lost},
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log.Println(liberrors.ErrServerRTPPacketsLost{Lost: lost}.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *serverSessionFormat) writePacketRTPInQueueUDP(payload []byte) error {
|
||||||
|
err := sf.sm.ss.s.udpRTPListener.write(payload, sf.sm.udpRTPWriteAddr)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(sf.sm.bytesSent, uint64(len(payload)))
|
||||||
|
atomic.AddUint64(sf.rtpPacketsSent, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sf *serverSessionFormat) writePacketRTPInQueueTCP(payload []byte) error {
|
||||||
|
sf.sm.ss.tcpFrame.Channel = sf.sm.tcpChannel
|
||||||
|
sf.sm.ss.tcpFrame.Payload = payload
|
||||||
|
sf.sm.ss.tcpConn.nconn.SetWriteDeadline(time.Now().Add(sf.sm.ss.s.WriteTimeout))
|
||||||
|
err := sf.sm.ss.tcpConn.conn.WriteInterleavedFrame(sf.sm.ss.tcpFrame, sf.sm.ss.tcpBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(sf.sm.bytesSent, uint64(len(payload)))
|
||||||
|
atomic.AddUint64(sf.rtpPacketsSent, 1)
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
@@ -1,6 +1,7 @@
|
|||||||
package gortsplib
|
package gortsplib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"sync/atomic"
|
"sync/atomic"
|
||||||
"time"
|
"time"
|
||||||
@@ -23,20 +24,33 @@ type serverSessionMedia struct {
|
|||||||
udpRTCPReadPort int
|
udpRTCPReadPort int
|
||||||
udpRTCPWriteAddr *net.UDPAddr
|
udpRTCPWriteAddr *net.UDPAddr
|
||||||
formats map[uint8]*serverSessionFormat // record only
|
formats map[uint8]*serverSessionFormat // record only
|
||||||
writePacketRTPInQueue func([]byte) error
|
|
||||||
writePacketRTCPInQueue func([]byte) error
|
writePacketRTCPInQueue func([]byte) error
|
||||||
|
bytesReceived *uint64
|
||||||
|
bytesSent *uint64
|
||||||
|
rtpPacketsInError *uint64
|
||||||
|
rtcpPacketsReceived *uint64
|
||||||
|
rtcpPacketsSent *uint64
|
||||||
|
rtcpPacketsInError *uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) initialize() {
|
func (sm *serverSessionMedia) initialize() {
|
||||||
if sm.ss.state == ServerSessionStatePreRecord {
|
sm.bytesReceived = new(uint64)
|
||||||
sm.formats = make(map[uint8]*serverSessionFormat)
|
sm.bytesSent = new(uint64)
|
||||||
for _, forma := range sm.media.Formats {
|
sm.rtpPacketsInError = new(uint64)
|
||||||
sm.formats[forma.PayloadType()] = &serverSessionFormat{
|
sm.rtcpPacketsReceived = new(uint64)
|
||||||
sm: sm,
|
sm.rtcpPacketsSent = new(uint64)
|
||||||
format: forma,
|
sm.rtcpPacketsInError = new(uint64)
|
||||||
onPacketRTP: func(*rtp.Packet) {},
|
|
||||||
}
|
sm.formats = make(map[uint8]*serverSessionFormat)
|
||||||
|
|
||||||
|
for _, forma := range sm.media.Formats {
|
||||||
|
f := &serverSessionFormat{
|
||||||
|
sm: sm,
|
||||||
|
format: forma,
|
||||||
|
onPacketRTP: func(*rtp.Packet) {},
|
||||||
}
|
}
|
||||||
|
f.initialize()
|
||||||
|
sm.formats[forma.PayloadType()] = f
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -49,7 +63,6 @@ func (sm *serverSessionMedia) start() {
|
|||||||
|
|
||||||
switch *sm.ss.setuppedTransport {
|
switch *sm.ss.setuppedTransport {
|
||||||
case TransportUDP, TransportUDPMulticast:
|
case TransportUDP, TransportUDPMulticast:
|
||||||
sm.writePacketRTPInQueue = sm.writePacketRTPInQueueUDP
|
|
||||||
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueUDP
|
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueUDP
|
||||||
|
|
||||||
if *sm.ss.setuppedTransport == TransportUDP {
|
if *sm.ss.setuppedTransport == TransportUDP {
|
||||||
@@ -57,7 +70,7 @@ func (sm *serverSessionMedia) start() {
|
|||||||
// firewall opening is performed with RTCP sender reports generated by ServerStream
|
// firewall opening is performed with RTCP sender reports generated by ServerStream
|
||||||
|
|
||||||
// readers can send RTCP packets only
|
// readers can send RTCP packets only
|
||||||
sm.ss.s.udpRTCPListener.addClient(sm.ss.author.ip(), sm.udpRTCPReadPort, sm.readRTCPUDPPlay)
|
sm.ss.s.udpRTCPListener.addClient(sm.ss.author.ip(), sm.udpRTCPReadPort, sm.readPacketRTCPUDPPlay)
|
||||||
} else {
|
} else {
|
||||||
// open the firewall by sending empty packets to the counterpart.
|
// open the firewall by sending empty packets to the counterpart.
|
||||||
byts, _ := (&rtp.Packet{Header: rtp.Header{Version: 2}}).Marshal()
|
byts, _ := (&rtp.Packet{Header: rtp.Header{Version: 2}}).Marshal()
|
||||||
@@ -66,13 +79,12 @@ func (sm *serverSessionMedia) start() {
|
|||||||
byts, _ = (&rtcp.ReceiverReport{}).Marshal()
|
byts, _ = (&rtcp.ReceiverReport{}).Marshal()
|
||||||
sm.ss.s.udpRTCPListener.write(byts, sm.udpRTCPWriteAddr) //nolint:errcheck
|
sm.ss.s.udpRTCPListener.write(byts, sm.udpRTCPWriteAddr) //nolint:errcheck
|
||||||
|
|
||||||
sm.ss.s.udpRTPListener.addClient(sm.ss.author.ip(), sm.udpRTPReadPort, sm.readRTPUDPRecord)
|
sm.ss.s.udpRTPListener.addClient(sm.ss.author.ip(), sm.udpRTPReadPort, sm.readPacketRTPUDPRecord)
|
||||||
sm.ss.s.udpRTCPListener.addClient(sm.ss.author.ip(), sm.udpRTCPReadPort, sm.readRTCPUDPRecord)
|
sm.ss.s.udpRTCPListener.addClient(sm.ss.author.ip(), sm.udpRTCPReadPort, sm.readPacketRTCPUDPRecord)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
case TransportTCP:
|
case TransportTCP:
|
||||||
sm.writePacketRTPInQueue = sm.writePacketRTPInQueueTCP
|
|
||||||
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueTCP
|
sm.writePacketRTCPInQueue = sm.writePacketRTCPInQueueTCP
|
||||||
|
|
||||||
if sm.ss.tcpCallbackByChannel == nil {
|
if sm.ss.tcpCallbackByChannel == nil {
|
||||||
@@ -80,11 +92,11 @@ func (sm *serverSessionMedia) start() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if sm.ss.state == ServerSessionStatePlay {
|
if sm.ss.state == ServerSessionStatePlay {
|
||||||
sm.ss.tcpCallbackByChannel[sm.tcpChannel] = sm.readRTPTCPPlay
|
sm.ss.tcpCallbackByChannel[sm.tcpChannel] = sm.readPacketRTPTCPPlay
|
||||||
sm.ss.tcpCallbackByChannel[sm.tcpChannel+1] = sm.readRTCPTCPPlay
|
sm.ss.tcpCallbackByChannel[sm.tcpChannel+1] = sm.readPacketRTCPTCPPlay
|
||||||
} else {
|
} else {
|
||||||
sm.ss.tcpCallbackByChannel[sm.tcpChannel] = sm.readRTPTCPRecord
|
sm.ss.tcpCallbackByChannel[sm.tcpChannel] = sm.readPacketRTPTCPRecord
|
||||||
sm.ss.tcpCallbackByChannel[sm.tcpChannel+1] = sm.readRTCPTCPRecord
|
sm.ss.tcpCallbackByChannel[sm.tcpChannel+1] = sm.readPacketRTCPTCPRecord
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -102,59 +114,58 @@ func (sm *serverSessionMedia) stop() {
|
|||||||
|
|
||||||
func (sm *serverSessionMedia) findFormatWithSSRC(ssrc uint32) *serverSessionFormat {
|
func (sm *serverSessionMedia) findFormatWithSSRC(ssrc uint32) *serverSessionFormat {
|
||||||
for _, format := range sm.formats {
|
for _, format := range sm.formats {
|
||||||
tssrc, ok := format.rtcpReceiver.SenderSSRC()
|
stats := format.rtcpReceiver.Stats()
|
||||||
if ok && tssrc == ssrc {
|
if stats != nil && stats.RemoteSSRC == ssrc {
|
||||||
return format
|
return format
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) writePacketRTPInQueueUDP(payload []byte) error {
|
|
||||||
atomic.AddUint64(sm.ss.bytesSent, uint64(len(payload)))
|
|
||||||
return sm.ss.s.udpRTPListener.write(payload, sm.udpRTPWriteAddr)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (sm *serverSessionMedia) writePacketRTCPInQueueUDP(payload []byte) error {
|
func (sm *serverSessionMedia) writePacketRTCPInQueueUDP(payload []byte) error {
|
||||||
atomic.AddUint64(sm.ss.bytesSent, uint64(len(payload)))
|
err := sm.ss.s.udpRTCPListener.write(payload, sm.udpRTCPWriteAddr)
|
||||||
return sm.ss.s.udpRTCPListener.write(payload, sm.udpRTCPWriteAddr)
|
if err != nil {
|
||||||
}
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) writePacketRTPInQueueTCP(payload []byte) error {
|
atomic.AddUint64(sm.bytesSent, uint64(len(payload)))
|
||||||
atomic.AddUint64(sm.ss.bytesSent, uint64(len(payload)))
|
atomic.AddUint64(sm.rtcpPacketsSent, 1)
|
||||||
sm.ss.tcpFrame.Channel = sm.tcpChannel
|
return nil
|
||||||
sm.ss.tcpFrame.Payload = payload
|
|
||||||
sm.ss.tcpConn.nconn.SetWriteDeadline(time.Now().Add(sm.ss.s.WriteTimeout))
|
|
||||||
return sm.ss.tcpConn.conn.WriteInterleavedFrame(sm.ss.tcpFrame, sm.ss.tcpBuffer)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) writePacketRTCPInQueueTCP(payload []byte) error {
|
func (sm *serverSessionMedia) writePacketRTCPInQueueTCP(payload []byte) error {
|
||||||
atomic.AddUint64(sm.ss.bytesSent, uint64(len(payload)))
|
|
||||||
sm.ss.tcpFrame.Channel = sm.tcpChannel + 1
|
sm.ss.tcpFrame.Channel = sm.tcpChannel + 1
|
||||||
sm.ss.tcpFrame.Payload = payload
|
sm.ss.tcpFrame.Payload = payload
|
||||||
sm.ss.tcpConn.nconn.SetWriteDeadline(time.Now().Add(sm.ss.s.WriteTimeout))
|
sm.ss.tcpConn.nconn.SetWriteDeadline(time.Now().Add(sm.ss.s.WriteTimeout))
|
||||||
return sm.ss.tcpConn.conn.WriteInterleavedFrame(sm.ss.tcpFrame, sm.ss.tcpBuffer)
|
err := sm.ss.tcpConn.conn.WriteInterleavedFrame(sm.ss.tcpFrame, sm.ss.tcpBuffer)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(sm.bytesSent, uint64(len(payload)))
|
||||||
|
atomic.AddUint64(sm.rtcpPacketsSent, 1)
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) readRTCPUDPPlay(payload []byte) bool {
|
func (sm *serverSessionMedia) readPacketRTCPUDPPlay(payload []byte) bool {
|
||||||
plen := len(payload)
|
atomic.AddUint64(sm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
atomic.AddUint64(sm.ss.bytesReceived, uint64(plen))
|
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||||
|
sm.onPacketRTCPDecodeError(liberrors.ErrServerRTCPPacketTooBigUDP{})
|
||||||
if plen == (udpMaxPayloadSize + 1) {
|
|
||||||
sm.ss.onDecodeError(liberrors.ErrServerRTCPPacketTooBigUDP{})
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
packets, err := rtcp.Unmarshal(payload)
|
packets, err := rtcp.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sm.ss.onDecodeError(err)
|
sm.onPacketRTCPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
now := sm.ss.s.timeNow()
|
now := sm.ss.s.timeNow()
|
||||||
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
|
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
|
||||||
|
|
||||||
|
atomic.AddUint64(sm.rtcpPacketsReceived, uint64(len(packets)))
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
sm.onPacketRTCP(pkt)
|
sm.onPacketRTCP(pkt)
|
||||||
}
|
}
|
||||||
@@ -162,56 +173,54 @@ func (sm *serverSessionMedia) readRTCPUDPPlay(payload []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) readRTPUDPRecord(payload []byte) bool {
|
func (sm *serverSessionMedia) readPacketRTPUDPRecord(payload []byte) bool {
|
||||||
plen := len(payload)
|
atomic.AddUint64(sm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
atomic.AddUint64(sm.ss.bytesReceived, uint64(plen))
|
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||||
|
sm.onPacketRTPDecodeError(liberrors.ErrServerRTPPacketTooBigUDP{})
|
||||||
if plen == (udpMaxPayloadSize + 1) {
|
|
||||||
sm.ss.onDecodeError(liberrors.ErrServerRTPPacketTooBigUDP{})
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
pkt := &rtp.Packet{}
|
pkt := &rtp.Packet{}
|
||||||
err := pkt.Unmarshal(payload)
|
err := pkt.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sm.ss.onDecodeError(err)
|
sm.onPacketRTPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forma, ok := sm.formats[pkt.PayloadType]
|
forma, ok := sm.formats[pkt.PayloadType]
|
||||||
if !ok {
|
if !ok {
|
||||||
sm.ss.onDecodeError(liberrors.ErrServerRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
sm.onPacketRTPDecodeError(liberrors.ErrServerRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
now := sm.ss.s.timeNow()
|
now := sm.ss.s.timeNow()
|
||||||
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
|
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
|
||||||
|
|
||||||
forma.readRTPUDP(pkt, now)
|
forma.readPacketRTPUDP(pkt, now)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) readRTCPUDPRecord(payload []byte) bool {
|
func (sm *serverSessionMedia) readPacketRTCPUDPRecord(payload []byte) bool {
|
||||||
plen := len(payload)
|
atomic.AddUint64(sm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
atomic.AddUint64(sm.ss.bytesReceived, uint64(plen))
|
if len(payload) == (udpMaxPayloadSize + 1) {
|
||||||
|
sm.onPacketRTCPDecodeError(liberrors.ErrServerRTCPPacketTooBigUDP{})
|
||||||
if plen == (udpMaxPayloadSize + 1) {
|
|
||||||
sm.ss.onDecodeError(liberrors.ErrServerRTCPPacketTooBigUDP{})
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
packets, err := rtcp.Unmarshal(payload)
|
packets, err := rtcp.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sm.ss.onDecodeError(err)
|
sm.onPacketRTCPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
now := sm.ss.s.timeNow()
|
now := sm.ss.s.timeNow()
|
||||||
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
|
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
|
||||||
|
|
||||||
|
atomic.AddUint64(sm.rtcpPacketsReceived, uint64(len(packets)))
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
||||||
format := sm.findFormatWithSSRC(sr.SSRC)
|
format := sm.findFormatWithSSRC(sr.SSRC)
|
||||||
@@ -226,22 +235,26 @@ func (sm *serverSessionMedia) readRTCPUDPRecord(payload []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) readRTPTCPPlay(_ []byte) bool {
|
func (sm *serverSessionMedia) readPacketRTPTCPPlay(_ []byte) bool {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) readRTCPTCPPlay(payload []byte) bool {
|
func (sm *serverSessionMedia) readPacketRTCPTCPPlay(payload []byte) bool {
|
||||||
|
atomic.AddUint64(sm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
if len(payload) > udpMaxPayloadSize {
|
if len(payload) > udpMaxPayloadSize {
|
||||||
sm.ss.onDecodeError(liberrors.ErrServerRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
sm.onPacketRTCPDecodeError(liberrors.ErrServerRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
packets, err := rtcp.Unmarshal(payload)
|
packets, err := rtcp.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sm.ss.onDecodeError(err)
|
sm.onPacketRTCPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(sm.rtcpPacketsReceived, uint64(len(packets)))
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
sm.onPacketRTCP(pkt)
|
sm.onPacketRTCP(pkt)
|
||||||
}
|
}
|
||||||
@@ -249,39 +262,45 @@ func (sm *serverSessionMedia) readRTCPTCPPlay(payload []byte) bool {
|
|||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) readRTPTCPRecord(payload []byte) bool {
|
func (sm *serverSessionMedia) readPacketRTPTCPRecord(payload []byte) bool {
|
||||||
|
atomic.AddUint64(sm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
pkt := &rtp.Packet{}
|
pkt := &rtp.Packet{}
|
||||||
err := pkt.Unmarshal(payload)
|
err := pkt.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sm.ss.onDecodeError(err)
|
sm.onPacketRTPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forma, ok := sm.formats[pkt.PayloadType]
|
forma, ok := sm.formats[pkt.PayloadType]
|
||||||
if !ok {
|
if !ok {
|
||||||
sm.ss.onDecodeError(liberrors.ErrServerRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
sm.onPacketRTPDecodeError(liberrors.ErrServerRTPPacketUnknownPayloadType{PayloadType: pkt.PayloadType})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
forma.readRTPTCP(pkt)
|
forma.readPacketRTPTCP(pkt)
|
||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverSessionMedia) readRTCPTCPRecord(payload []byte) bool {
|
func (sm *serverSessionMedia) readPacketRTCPTCPRecord(payload []byte) bool {
|
||||||
|
atomic.AddUint64(sm.bytesReceived, uint64(len(payload)))
|
||||||
|
|
||||||
if len(payload) > udpMaxPayloadSize {
|
if len(payload) > udpMaxPayloadSize {
|
||||||
sm.ss.onDecodeError(liberrors.ErrServerRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
sm.onPacketRTCPDecodeError(liberrors.ErrServerRTCPPacketTooBig{L: len(payload), Max: udpMaxPayloadSize})
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
packets, err := rtcp.Unmarshal(payload)
|
packets, err := rtcp.Unmarshal(payload)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
sm.ss.onDecodeError(err)
|
sm.onPacketRTCPDecodeError(err)
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
now := sm.ss.s.timeNow()
|
now := sm.ss.s.timeNow()
|
||||||
|
|
||||||
|
atomic.AddUint64(sm.rtcpPacketsReceived, uint64(len(packets)))
|
||||||
|
|
||||||
for _, pkt := range packets {
|
for _, pkt := range packets {
|
||||||
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
if sr, ok := pkt.(*rtcp.SenderReport); ok {
|
||||||
format := sm.findFormatWithSSRC(sr.SSRC)
|
format := sm.findFormatWithSSRC(sr.SSRC)
|
||||||
@@ -295,3 +314,29 @@ func (sm *serverSessionMedia) readRTCPTCPRecord(payload []byte) bool {
|
|||||||
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func (sm *serverSessionMedia) onPacketRTPDecodeError(err error) {
|
||||||
|
atomic.AddUint64(sm.rtpPacketsInError, 1)
|
||||||
|
|
||||||
|
if h, ok := sm.ss.s.Handler.(ServerHandlerOnDecodeError); ok {
|
||||||
|
h.OnDecodeError(&ServerHandlerOnDecodeErrorCtx{
|
||||||
|
Session: sm.ss,
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (sm *serverSessionMedia) onPacketRTCPDecodeError(err error) {
|
||||||
|
atomic.AddUint64(sm.rtcpPacketsInError, 1)
|
||||||
|
|
||||||
|
if h, ok := sm.ss.s.Handler.(ServerHandlerOnDecodeError); ok {
|
||||||
|
h.OnDecodeError(&ServerHandlerOnDecodeErrorCtx{
|
||||||
|
Session: sm.ss,
|
||||||
|
Error: err,
|
||||||
|
})
|
||||||
|
} else {
|
||||||
|
log.Println(err.Error())
|
||||||
|
}
|
||||||
|
}
|
||||||
|
110
server_stream.go
110
server_stream.go
@@ -9,6 +9,7 @@ import (
|
|||||||
"github.com/pion/rtp"
|
"github.com/pion/rtp"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
"github.com/bluenviron/gortsplib/v4/pkg/headers"
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
"github.com/bluenviron/gortsplib/v4/pkg/liberrors"
|
||||||
)
|
)
|
||||||
@@ -36,9 +37,8 @@ type ServerStream struct {
|
|||||||
readers map[*ServerSession]struct{}
|
readers map[*ServerSession]struct{}
|
||||||
multicastReaderCount int
|
multicastReaderCount int
|
||||||
activeUnicastReaders map[*ServerSession]struct{}
|
activeUnicastReaders map[*ServerSession]struct{}
|
||||||
streamMedias map[*description.Media]*serverStreamMedia
|
medias map[*description.Media]*serverStreamMedia
|
||||||
closed bool
|
closed bool
|
||||||
bytesSent *uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// NewServerStream allocates a ServerStream.
|
// NewServerStream allocates a ServerStream.
|
||||||
@@ -48,10 +48,9 @@ func NewServerStream(s *Server, desc *description.Session) *ServerStream {
|
|||||||
desc: desc,
|
desc: desc,
|
||||||
readers: make(map[*ServerSession]struct{}),
|
readers: make(map[*ServerSession]struct{}),
|
||||||
activeUnicastReaders: make(map[*ServerSession]struct{}),
|
activeUnicastReaders: make(map[*ServerSession]struct{}),
|
||||||
bytesSent: new(uint64),
|
|
||||||
}
|
}
|
||||||
|
|
||||||
st.streamMedias = make(map[*description.Media]*serverStreamMedia, len(desc.Medias))
|
st.medias = make(map[*description.Media]*serverStreamMedia, len(desc.Medias))
|
||||||
for i, medi := range desc.Medias {
|
for i, medi := range desc.Medias {
|
||||||
sm := &serverStreamMedia{
|
sm := &serverStreamMedia{
|
||||||
st: st,
|
st: st,
|
||||||
@@ -59,7 +58,7 @@ func NewServerStream(s *Server, desc *description.Session) *ServerStream {
|
|||||||
trackID: i,
|
trackID: i,
|
||||||
}
|
}
|
||||||
sm.initialize()
|
sm.initialize()
|
||||||
st.streamMedias[medi] = sm
|
st.medias[medi] = sm
|
||||||
}
|
}
|
||||||
|
|
||||||
return st
|
return st
|
||||||
@@ -75,14 +74,20 @@ func (st *ServerStream) Close() {
|
|||||||
ss.Close()
|
ss.Close()
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, sm := range st.streamMedias {
|
for _, sm := range st.medias {
|
||||||
sm.close()
|
sm.close()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BytesSent returns the number of written bytes.
|
// BytesSent returns the number of written bytes.
|
||||||
|
//
|
||||||
|
// Deprecated: replaced by Stats()
|
||||||
func (st *ServerStream) BytesSent() uint64 {
|
func (st *ServerStream) BytesSent() uint64 {
|
||||||
return atomic.LoadUint64(st.bytesSent)
|
v := uint64(0)
|
||||||
|
for _, me := range st.medias {
|
||||||
|
v += atomic.LoadUint64(me.bytesSent)
|
||||||
|
}
|
||||||
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
// Description returns the description of the stream.
|
// Description returns the description of the stream.
|
||||||
@@ -90,27 +95,84 @@ func (st *ServerStream) Description() *description.Session {
|
|||||||
return st.desc
|
return st.desc
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *ServerStream) senderSSRC(medi *description.Media) (uint32, bool) {
|
// Stats returns stream statistics.
|
||||||
|
func (st *ServerStream) Stats() *ServerStreamStats {
|
||||||
|
return &ServerStreamStats{
|
||||||
|
BytesSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, me := range st.medias {
|
||||||
|
v += atomic.LoadUint64(me.bytesSent)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTPPacketsSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, me := range st.medias {
|
||||||
|
for _, f := range me.formats {
|
||||||
|
v += atomic.LoadUint64(f.rtpPacketsSent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
RTCPPacketsSent: func() uint64 {
|
||||||
|
v := uint64(0)
|
||||||
|
for _, me := range st.medias {
|
||||||
|
v += atomic.LoadUint64(me.rtcpPacketsSent)
|
||||||
|
}
|
||||||
|
return v
|
||||||
|
}(),
|
||||||
|
Medias: func() map[*description.Media]ServerStreamStatsMedia {
|
||||||
|
ret := make(map[*description.Media]ServerStreamStatsMedia, len(st.medias))
|
||||||
|
|
||||||
|
for med, sm := range st.medias {
|
||||||
|
ret[med] = ServerStreamStatsMedia{
|
||||||
|
BytesSent: atomic.LoadUint64(sm.bytesSent),
|
||||||
|
RTCPPacketsSent: atomic.LoadUint64(sm.rtcpPacketsSent),
|
||||||
|
Formats: func() map[format.Format]ServerStreamStatsFormat {
|
||||||
|
ret := make(map[format.Format]ServerStreamStatsFormat)
|
||||||
|
|
||||||
|
for _, fo := range sm.formats {
|
||||||
|
ret[fo.format] = ServerStreamStatsFormat{
|
||||||
|
RTPPacketsSent: atomic.LoadUint64(fo.rtpPacketsSent),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ret
|
||||||
|
}(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func (st *ServerStream) localSSRC(medi *description.Media) (uint32, bool) {
|
||||||
st.mutex.Lock()
|
st.mutex.Lock()
|
||||||
defer st.mutex.Unlock()
|
defer st.mutex.Unlock()
|
||||||
|
|
||||||
sm := st.streamMedias[medi]
|
sm := st.medias[medi]
|
||||||
|
|
||||||
// senderSSRC() is used to fill SSRC inside the Transport header.
|
// localSSRC() is used to fill SSRC inside the Transport header.
|
||||||
// if there are multiple formats inside a single media stream,
|
// if there are multiple formats inside a single media stream,
|
||||||
// do not return anything, since Transport headers don't support multiple SSRCs.
|
// do not return anything, since Transport headers don't support multiple SSRCs.
|
||||||
if len(sm.formats) > 1 {
|
if len(sm.formats) > 1 {
|
||||||
return 0, false
|
return 0, false
|
||||||
}
|
}
|
||||||
|
|
||||||
return firstFormat(sm.formats).rtcpSender.SenderSSRC()
|
stats := firstFormat(sm.formats).rtcpSender.Stats()
|
||||||
|
if stats == nil {
|
||||||
|
return 0, false
|
||||||
|
}
|
||||||
|
|
||||||
|
return stats.LocalSSRC, true
|
||||||
}
|
}
|
||||||
|
|
||||||
func (st *ServerStream) rtpInfoEntry(medi *description.Media, now time.Time) *headers.RTPInfoEntry {
|
func (st *ServerStream) rtpInfoEntry(medi *description.Media, now time.Time) *headers.RTPInfoEntry {
|
||||||
st.mutex.Lock()
|
st.mutex.Lock()
|
||||||
defer st.mutex.Unlock()
|
defer st.mutex.Unlock()
|
||||||
|
|
||||||
sm := st.streamMedias[medi]
|
sm := st.medias[medi]
|
||||||
|
|
||||||
// if there are multiple formats inside a single media stream,
|
// if there are multiple formats inside a single media stream,
|
||||||
// do not generate a RTP-Info entry, since RTP-Info doesn't support
|
// do not generate a RTP-Info entry, since RTP-Info doesn't support
|
||||||
@@ -121,8 +183,8 @@ func (st *ServerStream) rtpInfoEntry(medi *description.Media, now time.Time) *he
|
|||||||
|
|
||||||
format := firstFormat(sm.formats)
|
format := firstFormat(sm.formats)
|
||||||
|
|
||||||
lastSeqNum, lastTimeRTP, lastTimeNTP, ok := format.rtcpSender.LastPacketData()
|
stats := format.rtcpSender.Stats()
|
||||||
if !ok {
|
if stats == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -132,13 +194,13 @@ func (st *ServerStream) rtpInfoEntry(medi *description.Media, now time.Time) *he
|
|||||||
}
|
}
|
||||||
|
|
||||||
// sequence number of the first packet of the stream
|
// sequence number of the first packet of the stream
|
||||||
seqNum := lastSeqNum + 1
|
seqNum := stats.LastSequenceNumber + 1
|
||||||
|
|
||||||
// RTP timestamp corresponding to the time value in
|
// RTP timestamp corresponding to the time value in
|
||||||
// the Range response header.
|
// the Range response header.
|
||||||
// remove a small quantity in order to avoid DTS > PTS
|
// remove a small quantity in order to avoid DTS > PTS
|
||||||
ts := uint32(uint64(lastTimeRTP) +
|
ts := uint32(uint64(stats.LastRTP) +
|
||||||
uint64(now.Sub(lastTimeNTP).Seconds()*float64(clockRate)) -
|
uint64(now.Sub(stats.LastNTP).Seconds()*float64(clockRate)) -
|
||||||
uint64(clockRate)/10)
|
uint64(clockRate)/10)
|
||||||
|
|
||||||
return &headers.RTPInfoEntry{
|
return &headers.RTPInfoEntry{
|
||||||
@@ -175,7 +237,7 @@ func (st *ServerStream) readerAdd(
|
|||||||
|
|
||||||
case TransportUDPMulticast:
|
case TransportUDPMulticast:
|
||||||
if st.multicastReaderCount == 0 {
|
if st.multicastReaderCount == 0 {
|
||||||
for _, media := range st.streamMedias {
|
for _, media := range st.medias {
|
||||||
mw := &serverMulticastWriter{
|
mw := &serverMulticastWriter{
|
||||||
s: st.s,
|
s: st.s,
|
||||||
}
|
}
|
||||||
@@ -207,7 +269,7 @@ func (st *ServerStream) readerRemove(ss *ServerSession) {
|
|||||||
if *ss.setuppedTransport == TransportUDPMulticast {
|
if *ss.setuppedTransport == TransportUDPMulticast {
|
||||||
st.multicastReaderCount--
|
st.multicastReaderCount--
|
||||||
if st.multicastReaderCount == 0 {
|
if st.multicastReaderCount == 0 {
|
||||||
for _, media := range st.streamMedias {
|
for _, media := range st.medias {
|
||||||
media.multicastWriter.close()
|
media.multicastWriter.close()
|
||||||
media.multicastWriter = nil
|
media.multicastWriter = nil
|
||||||
}
|
}
|
||||||
@@ -225,9 +287,9 @@ func (st *ServerStream) readerSetActive(ss *ServerSession) {
|
|||||||
|
|
||||||
if *ss.setuppedTransport == TransportUDPMulticast {
|
if *ss.setuppedTransport == TransportUDPMulticast {
|
||||||
for medi, sm := range ss.setuppedMedias {
|
for medi, sm := range ss.setuppedMedias {
|
||||||
streamMedia := st.streamMedias[medi]
|
streamMedia := st.medias[medi]
|
||||||
streamMedia.multicastWriter.rtcpl.addClient(
|
streamMedia.multicastWriter.rtcpl.addClient(
|
||||||
ss.author.ip(), streamMedia.multicastWriter.rtcpl.port(), sm.readRTCPUDPPlay)
|
ss.author.ip(), streamMedia.multicastWriter.rtcpl.port(), sm.readPacketRTCPUDPPlay)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
st.activeUnicastReaders[ss] = struct{}{}
|
st.activeUnicastReaders[ss] = struct{}{}
|
||||||
@@ -244,7 +306,7 @@ func (st *ServerStream) readerSetInactive(ss *ServerSession) {
|
|||||||
|
|
||||||
if *ss.setuppedTransport == TransportUDPMulticast {
|
if *ss.setuppedTransport == TransportUDPMulticast {
|
||||||
for medi := range ss.setuppedMedias {
|
for medi := range ss.setuppedMedias {
|
||||||
streamMedia := st.streamMedias[medi]
|
streamMedia := st.medias[medi]
|
||||||
streamMedia.multicastWriter.rtcpl.removeClient(ss.author.ip(), streamMedia.multicastWriter.rtcpl.port())
|
streamMedia.multicastWriter.rtcpl.removeClient(ss.author.ip(), streamMedia.multicastWriter.rtcpl.port())
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -274,7 +336,7 @@ func (st *ServerStream) WritePacketRTPWithNTP(medi *description.Media, pkt *rtp.
|
|||||||
return liberrors.ErrServerStreamClosed{}
|
return liberrors.ErrServerStreamClosed{}
|
||||||
}
|
}
|
||||||
|
|
||||||
sm := st.streamMedias[medi]
|
sm := st.medias[medi]
|
||||||
sf := sm.formats[pkt.PayloadType]
|
sf := sm.formats[pkt.PayloadType]
|
||||||
return sf.writePacketRTP(byts, pkt, ntp)
|
return sf.writePacketRTP(byts, pkt, ntp)
|
||||||
}
|
}
|
||||||
@@ -293,6 +355,6 @@ func (st *ServerStream) WritePacketRTCP(medi *description.Media, pkt rtcp.Packet
|
|||||||
return liberrors.ErrServerStreamClosed{}
|
return liberrors.ErrServerStreamClosed{}
|
||||||
}
|
}
|
||||||
|
|
||||||
sm := st.streamMedias[medi]
|
sm := st.medias[medi]
|
||||||
return sm.writePacketRTCP(byts)
|
return sm.writePacketRTCP(byts)
|
||||||
}
|
}
|
||||||
|
@@ -15,10 +15,13 @@ type serverStreamFormat struct {
|
|||||||
sm *serverStreamMedia
|
sm *serverStreamMedia
|
||||||
format format.Format
|
format format.Format
|
||||||
|
|
||||||
rtcpSender *rtcpsender.RTCPSender
|
rtcpSender *rtcpsender.RTCPSender
|
||||||
|
rtpPacketsSent *uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sf *serverStreamFormat) initialize() {
|
func (sf *serverStreamFormat) initialize() {
|
||||||
|
sf.rtpPacketsSent = new(uint64)
|
||||||
|
|
||||||
sf.rtcpSender = &rtcpsender.RTCPSender{
|
sf.rtcpSender = &rtcpsender.RTCPSender{
|
||||||
ClockRate: sf.format.ClockRate(),
|
ClockRate: sf.format.ClockRate(),
|
||||||
Period: sf.sm.st.s.senderReportPeriod,
|
Period: sf.sm.st.s.senderReportPeriod,
|
||||||
@@ -33,19 +36,21 @@ func (sf *serverStreamFormat) initialize() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sf *serverStreamFormat) writePacketRTP(byts []byte, pkt *rtp.Packet, ntp time.Time) error {
|
func (sf *serverStreamFormat) writePacketRTP(byts []byte, pkt *rtp.Packet, ntp time.Time) error {
|
||||||
sf.rtcpSender.ProcessPacket(pkt, ntp, sf.format.PTSEqualsDTS(pkt))
|
sf.rtcpSender.ProcessPacketRTP(pkt, ntp, sf.format.PTSEqualsDTS(pkt))
|
||||||
|
|
||||||
le := uint64(len(byts))
|
le := uint64(len(byts))
|
||||||
|
|
||||||
// send unicast
|
// send unicast
|
||||||
for r := range sf.sm.st.activeUnicastReaders {
|
for r := range sf.sm.st.activeUnicastReaders {
|
||||||
if _, ok := r.setuppedMedias[sf.sm.media]; ok {
|
if _, ok := r.setuppedMedias[sf.sm.media]; ok {
|
||||||
err := r.writePacketRTP(sf.sm.media, byts)
|
err := r.writePacketRTP(sf.sm.media, pkt.PayloadType, byts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.onStreamWriteError(err)
|
r.onStreamWriteError(err)
|
||||||
} else {
|
continue
|
||||||
atomic.AddUint64(sf.sm.st.bytesSent, le)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(sf.sm.bytesSent, le)
|
||||||
|
atomic.AddUint64(sf.rtpPacketsSent, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -55,7 +60,9 @@ func (sf *serverStreamFormat) writePacketRTP(byts []byte, pkt *rtp.Packet, ntp t
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
atomic.AddUint64(sf.sm.st.bytesSent, le)
|
|
||||||
|
atomic.AddUint64(sf.sm.bytesSent, le)
|
||||||
|
atomic.AddUint64(sf.rtpPacketsSent, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
@@ -1,6 +1,8 @@
|
|||||||
package gortsplib
|
package gortsplib
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"sync/atomic"
|
||||||
|
|
||||||
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -11,9 +13,14 @@ type serverStreamMedia struct {
|
|||||||
|
|
||||||
formats map[uint8]*serverStreamFormat
|
formats map[uint8]*serverStreamFormat
|
||||||
multicastWriter *serverMulticastWriter
|
multicastWriter *serverMulticastWriter
|
||||||
|
bytesSent *uint64
|
||||||
|
rtcpPacketsSent *uint64
|
||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverStreamMedia) initialize() {
|
func (sm *serverStreamMedia) initialize() {
|
||||||
|
sm.bytesSent = new(uint64)
|
||||||
|
sm.rtcpPacketsSent = new(uint64)
|
||||||
|
|
||||||
sm.formats = make(map[uint8]*serverStreamFormat)
|
sm.formats = make(map[uint8]*serverStreamFormat)
|
||||||
for _, forma := range sm.media.Formats {
|
for _, forma := range sm.media.Formats {
|
||||||
sf := &serverStreamFormat{
|
sf := &serverStreamFormat{
|
||||||
@@ -38,13 +45,19 @@ func (sm *serverStreamMedia) close() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (sm *serverStreamMedia) writePacketRTCP(byts []byte) error {
|
func (sm *serverStreamMedia) writePacketRTCP(byts []byte) error {
|
||||||
|
le := len(byts)
|
||||||
|
|
||||||
// send unicast
|
// send unicast
|
||||||
for r := range sm.st.activeUnicastReaders {
|
for r := range sm.st.activeUnicastReaders {
|
||||||
if _, ok := r.setuppedMedias[sm.media]; ok {
|
if _, ok := r.setuppedMedias[sm.media]; ok {
|
||||||
err := r.writePacketRTCP(sm.media, byts)
|
err := r.writePacketRTCP(sm.media, byts)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
r.onStreamWriteError(err)
|
r.onStreamWriteError(err)
|
||||||
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(sm.bytesSent, uint64(le))
|
||||||
|
atomic.AddUint64(sm.rtcpPacketsSent, 1)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -54,6 +67,9 @@ func (sm *serverStreamMedia) writePacketRTCP(byts []byte) error {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
atomic.AddUint64(sm.bytesSent, uint64(le))
|
||||||
|
atomic.AddUint64(sm.rtcpPacketsSent, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
|
36
server_stream_stats.go
Normal file
36
server_stream_stats.go
Normal file
@@ -0,0 +1,36 @@
|
|||||||
|
package gortsplib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
|
)
|
||||||
|
|
||||||
|
// ServerStreamStatsFormat are stream format statistics.
|
||||||
|
type ServerStreamStatsFormat struct {
|
||||||
|
// number of sent RTP packets
|
||||||
|
RTPPacketsSent uint64
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerStreamStatsMedia are stream media statistics.
|
||||||
|
type ServerStreamStatsMedia struct {
|
||||||
|
// sent bytes
|
||||||
|
BytesSent uint64
|
||||||
|
// number of sent RTCP packets
|
||||||
|
RTCPPacketsSent uint64
|
||||||
|
|
||||||
|
// format statistics
|
||||||
|
Formats map[format.Format]ServerStreamStatsFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
// ServerStreamStats are stream statistics.
|
||||||
|
type ServerStreamStats struct {
|
||||||
|
// sent bytes
|
||||||
|
BytesSent uint64
|
||||||
|
// number of sent RTP packets
|
||||||
|
RTPPacketsSent uint64
|
||||||
|
// number of sent RTCP packets
|
||||||
|
RTCPPacketsSent uint64
|
||||||
|
|
||||||
|
// media statistics
|
||||||
|
Medias map[*description.Media]ServerStreamStatsMedia
|
||||||
|
}
|
9
stats_conn.go
Normal file
9
stats_conn.go
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
package gortsplib
|
||||||
|
|
||||||
|
// StatsConn are connection statistics.
|
||||||
|
type StatsConn struct {
|
||||||
|
// received bytes
|
||||||
|
BytesReceived uint64
|
||||||
|
// sent bytes
|
||||||
|
BytesSent uint64
|
||||||
|
}
|
76
stats_session.go
Normal file
76
stats_session.go
Normal file
@@ -0,0 +1,76 @@
|
|||||||
|
package gortsplib
|
||||||
|
|
||||||
|
import (
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/description"
|
||||||
|
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||||||
|
)
|
||||||
|
|
||||||
|
// StatsSessionFormat are session format statistics.
|
||||||
|
type StatsSessionFormat struct {
|
||||||
|
// number of RTP packets correctly received and processed
|
||||||
|
RTPPacketsReceived uint64
|
||||||
|
// number of sent RTP packets
|
||||||
|
RTPPacketsSent uint64
|
||||||
|
// number of lost RTP packets
|
||||||
|
RTPPacketsLost uint64
|
||||||
|
// mean jitter of received RTP packets
|
||||||
|
RTPJitter float64
|
||||||
|
// local SSRC
|
||||||
|
LocalSSRC uint32
|
||||||
|
// remote SSRC
|
||||||
|
RemoteSSRC uint32
|
||||||
|
// last sequence number of incoming/outgoing RTP packets
|
||||||
|
RTPPacketsLastSequenceNumber uint16
|
||||||
|
// last RTP time of incoming/outgoing RTP packets
|
||||||
|
RTPPacketsLastRTP uint32
|
||||||
|
// last NTP time of incoming/outgoing NTP packets
|
||||||
|
RTPPacketsLastNTP time.Time
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsSessionMedia are session media statistics.
|
||||||
|
type StatsSessionMedia struct {
|
||||||
|
// received bytes
|
||||||
|
BytesReceived uint64
|
||||||
|
// sent bytes
|
||||||
|
BytesSent uint64
|
||||||
|
// number of RTP packets that could not be processed
|
||||||
|
RTPPacketsInError uint64
|
||||||
|
// number of RTCP packets correctly received and processed
|
||||||
|
RTCPPacketsReceived uint64
|
||||||
|
// number of sent RTCP packets
|
||||||
|
RTCPPacketsSent uint64
|
||||||
|
// number of RTCP packets that could not be processed
|
||||||
|
RTCPPacketsInError uint64
|
||||||
|
|
||||||
|
// format statistics
|
||||||
|
Formats map[format.Format]StatsSessionFormat
|
||||||
|
}
|
||||||
|
|
||||||
|
// StatsSession are session statistics.
|
||||||
|
type StatsSession struct {
|
||||||
|
// received bytes
|
||||||
|
BytesReceived uint64
|
||||||
|
// sent bytes
|
||||||
|
BytesSent uint64
|
||||||
|
// number of RTP packets correctly received and processed
|
||||||
|
RTPPacketsReceived uint64
|
||||||
|
// number of sent RTP packets
|
||||||
|
RTPPacketsSent uint64
|
||||||
|
// number of lost RTP packets
|
||||||
|
RTPPacketsLost uint64
|
||||||
|
// number of RTP packets that could not be processed
|
||||||
|
RTPPacketsInError uint64
|
||||||
|
// mean jitter of received RTP packets
|
||||||
|
RTPJitter float64
|
||||||
|
// number of RTCP packets correctly received and processed
|
||||||
|
RTCPPacketsReceived uint64
|
||||||
|
// number of sent RTCP packets
|
||||||
|
RTCPPacketsSent uint64
|
||||||
|
// number of RTCP packets that could not be processed
|
||||||
|
RTCPPacketsInError uint64
|
||||||
|
|
||||||
|
// media statistics
|
||||||
|
Medias map[*description.Media]StatsSessionMedia
|
||||||
|
}
|
Reference in New Issue
Block a user