add Client.PacketNTP(), ServerSession.PacketNTP()

This commit is contained in:
aler9
2023-08-15 00:56:01 +02:00
parent 4ad57d6a75
commit bfef17b717
19 changed files with 704 additions and 263 deletions

View File

@@ -285,8 +285,9 @@ type Client struct {
// private // private
// //
timeNow func() time.Time
senderReportPeriod time.Duration senderReportPeriod time.Duration
udpReceiverReportPeriod time.Duration receiverReportPeriod time.Duration
checkTimeoutPeriod time.Duration checkTimeoutPeriod time.Duration
keepalivePeriod time.Duration keepalivePeriod time.Duration
@@ -396,12 +397,15 @@ func (c *Client) Start(scheme string, host string) error {
} }
// private // private
if c.timeNow == nil {
c.timeNow = time.Now
}
if c.senderReportPeriod == 0 { if c.senderReportPeriod == 0 {
c.senderReportPeriod = 10 * time.Second c.senderReportPeriod = 10 * time.Second
} }
if c.udpReceiverReportPeriod == 0 { if c.receiverReportPeriod == 0 {
// some cameras require a maximum of 5secs between keepalives // some cameras require a maximum of 5secs between keepalives
c.udpReceiverReportPeriod = 5 * time.Second c.receiverReportPeriod = 5 * time.Second
} }
if c.checkTimeoutPeriod == 0 { if c.checkTimeoutPeriod == 0 {
c.checkTimeoutPeriod = 1 * time.Second c.checkTimeoutPeriod = 1 * time.Second
@@ -680,7 +684,7 @@ func (c *Client) playRecordStart() {
default: // TCP default: // TCP
c.checkTimeoutTimer = time.NewTimer(c.checkTimeoutPeriod) c.checkTimeoutTimer = time.NewTimer(c.checkTimeoutPeriod)
v := time.Now().Unix() v := c.timeNow().Unix()
c.tcpLastFrameTime = &v c.tcpLastFrameTime = &v
} }
} }
@@ -870,7 +874,7 @@ func (c *Client) atLeastOneUDPPacketHasBeenReceived() bool {
} }
func (c *Client) isInUDPTimeout() bool { func (c *Client) isInUDPTimeout() bool {
now := time.Now() now := c.timeNow()
for _, ct := range c.medias { for _, ct := range c.medias {
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 {
@@ -886,7 +890,7 @@ func (c *Client) isInUDPTimeout() bool {
} }
func (c *Client) isInTCPTimeout() bool { func (c *Client) isInTCPTimeout() bool {
now := time.Now() now := c.timeNow()
lft := time.Unix(atomic.LoadInt64(c.tcpLastFrameTime), 0) lft := time.Unix(atomic.LoadInt64(c.tcpLastFrameTime), 0)
return now.Sub(lft) >= c.ReadTimeout return now.Sub(lft) >= c.ReadTimeout
} }
@@ -1653,7 +1657,7 @@ func (c *Client) OnPacketRTCP(medi *media.Media, cb OnPacketRTCPFunc) {
// WritePacketRTP writes a RTP packet to the server. // WritePacketRTP writes a RTP packet to the server.
func (c *Client) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) error { func (c *Client) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) error {
return c.WritePacketRTPWithNTP(medi, pkt, time.Now()) return c.WritePacketRTPWithNTP(medi, pkt, c.timeNow())
} }
// WritePacketRTPWithNTP writes a RTP packet to the server. // WritePacketRTPWithNTP writes a RTP packet to the server.
@@ -1695,3 +1699,11 @@ func (c *Client) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) error {
cm.writePacketRTCP(byts) cm.writePacketRTCP(byts)
return nil return nil
} }
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
// The NTP timestamp is computed from sender reports.
func (c *Client) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {
cm := c.medias[medi]
ct := cm.formats[pkt.PayloadType]
return ct.rtcpReceiver.PacketNTP(pkt.Timestamp)
}

View File

@@ -18,8 +18,8 @@ type clientFormat struct {
cm *clientMedia cm *clientMedia
format format.Format format format.Format
udpReorderer *rtpreorderer.Reorderer // play udpReorderer *rtpreorderer.Reorderer // play
udpRTCPReceiver *rtcpreceiver.RTCPReceiver // play
tcpLossDetector *rtplossdetector.LossDetector // play tcpLossDetector *rtplossdetector.LossDetector // play
rtcpReceiver *rtcpreceiver.RTCPReceiver // play
rtcpSender *rtcpsender.RTCPSender // record rtcpSender *rtcpsender.RTCPSender // record
onPacketRTP OnPacketRTPFunc onPacketRTP OnPacketRTPFunc
} }
@@ -36,24 +36,29 @@ func (ct *clientFormat) start() {
if ct.cm.c.state == clientStatePlay { if ct.cm.c.state == clientStatePlay {
if ct.cm.udpRTPListener != nil { if ct.cm.udpRTPListener != nil {
ct.udpReorderer = rtpreorderer.New() ct.udpReorderer = rtpreorderer.New()
} else {
ct.tcpLossDetector = rtplossdetector.New()
}
var err error var err error
ct.udpRTCPReceiver, err = rtcpreceiver.New( ct.rtcpReceiver, err = rtcpreceiver.New(
ct.cm.c.udpReceiverReportPeriod, ct.format.ClockRate(),
nil, nil,
ct.format.ClockRate(), func(pkt rtcp.Packet) { ct.cm.c.receiverReportPeriod,
ct.cm.c.timeNow,
func(pkt rtcp.Packet) {
if ct.cm.udpRTPListener != nil {
ct.cm.c.WritePacketRTCP(ct.cm.media, pkt) //nolint:errcheck ct.cm.c.WritePacketRTCP(ct.cm.media, pkt) //nolint:errcheck
}
}) })
if err != nil { if err != nil {
panic(err) panic(err)
} }
} else {
ct.tcpLossDetector = rtplossdetector.New()
}
} else { } else {
ct.rtcpSender = rtcpsender.New( ct.rtcpSender = rtcpsender.New(
ct.format.ClockRate(), ct.format.ClockRate(),
ct.cm.c.senderReportPeriod, ct.cm.c.senderReportPeriod,
ct.cm.c.timeNow,
func(pkt rtcp.Packet) { func(pkt rtcp.Packet) {
if !ct.cm.c.DisableRTCPSenderReports { if !ct.cm.c.DisableRTCPSenderReports {
ct.cm.c.WritePacketRTCP(ct.cm.media, pkt) //nolint:errcheck ct.cm.c.WritePacketRTCP(ct.cm.media, pkt) //nolint:errcheck
@@ -63,9 +68,9 @@ func (ct *clientFormat) start() {
} }
func (ct *clientFormat) stop() { func (ct *clientFormat) stop() {
if ct.udpRTCPReceiver != nil { if ct.rtcpReceiver != nil {
ct.udpRTCPReceiver.Close() ct.rtcpReceiver.Close()
ct.udpRTCPReceiver = nil ct.rtcpReceiver = nil
} }
if ct.rtcpSender != nil { if ct.rtcpSender != nil {
@@ -95,10 +100,10 @@ func (ct *clientFormat) readRTPUDP(pkt *rtp.Packet) {
// do not return // do not return
} }
now := time.Now() now := ct.cm.c.timeNow()
for _, pkt := range packets { for _, pkt := range packets {
ct.udpRTCPReceiver.ProcessPacket(pkt, now, ct.format.PTSEqualsDTS(pkt)) ct.rtcpReceiver.ProcessPacket(pkt, now, ct.format.PTSEqualsDTS(pkt))
ct.onPacketRTP(pkt) ct.onPacketRTP(pkt)
} }
} }
@@ -117,5 +122,7 @@ func (ct *clientFormat) readRTPTCP(pkt *rtp.Packet) {
// do not return // do not return
} }
now := ct.cm.c.timeNow()
ct.rtcpReceiver.ProcessPacket(pkt, now, ct.format.PTSEqualsDTS(pkt))
ct.onPacketRTP(pkt) ct.onPacketRTP(pkt)
} }

View File

@@ -44,9 +44,7 @@ func (cm *clientMedia) close() {
func (cm *clientMedia) allocateUDPListeners(multicast bool, rtpAddress string, rtcpAddress string) error { func (cm *clientMedia) allocateUDPListeners(multicast bool, rtpAddress string, rtcpAddress string) error {
if rtpAddress != ":0" { if rtpAddress != ":0" {
l1, err := newClientUDPListener( l1, err := newClientUDPListener(
cm.c.ListenPacket, cm.c,
cm.c.AnyPortEnable,
cm.c.WriteTimeout,
multicast, multicast,
rtpAddress, rtpAddress,
) )
@@ -55,9 +53,7 @@ func (cm *clientMedia) allocateUDPListeners(multicast bool, rtpAddress string, r
} }
l2, err := newClientUDPListener( l2, err := newClientUDPListener(
cm.c.ListenPacket, cm.c,
cm.c.AnyPortEnable,
cm.c.WriteTimeout,
multicast, multicast,
rtcpAddress, rtcpAddress,
) )
@@ -71,11 +67,7 @@ func (cm *clientMedia) allocateUDPListeners(multicast bool, rtpAddress string, r
} }
var err error var err error
cm.udpRTPListener, cm.udpRTCPListener, err = newClientUDPListenerPair( cm.udpRTPListener, cm.udpRTCPListener, err = newClientUDPListenerPair(cm.c)
cm.c.ListenPacket,
cm.c.AnyPortEnable,
cm.c.WriteTimeout,
)
return err return err
} }
@@ -144,7 +136,7 @@ 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.udpRTCPReceiver.LastSSRC() tssrc, ok := format.rtcpReceiver.LastSSRC()
if ok && tssrc == ssrc { if ok && tssrc == ssrc {
return format return format
} }
@@ -183,7 +175,7 @@ func (cm *clientMedia) writePacketRTCP(byts []byte) {
} }
func (cm *clientMedia) readRTPTCPPlay(payload []byte) { func (cm *clientMedia) readRTPTCPPlay(payload []byte) {
now := time.Now() now := cm.c.timeNow()
atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix()) atomic.StoreInt64(cm.c.tcpLastFrameTime, now.Unix())
pkt := &rtp.Packet{} pkt := &rtp.Packet{}
@@ -203,7 +195,7 @@ func (cm *clientMedia) readRTPTCPPlay(payload []byte) {
} }
func (cm *clientMedia) readRTCPTCPPlay(payload []byte) { func (cm *clientMedia) readRTCPTCPPlay(payload []byte) {
now := time.Now() 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 {
@@ -219,6 +211,13 @@ func (cm *clientMedia) readRTCPTCPPlay(payload []byte) {
} }
for _, pkt := range packets { for _, pkt := range packets {
if sr, ok := pkt.(*rtcp.SenderReport); ok {
format := cm.findFormatWithSSRC(sr.SSRC)
if format != nil {
format.rtcpReceiver.ProcessSenderReport(sr, now)
}
}
cm.onPacketRTCP(pkt) cm.onPacketRTCP(pkt)
} }
} }
@@ -271,7 +270,7 @@ func (cm *clientMedia) readRTPUDPPlay(payload []byte) {
} }
func (cm *clientMedia) readRTCPUDPPlay(payload []byte) { func (cm *clientMedia) readRTCPUDPPlay(payload []byte) {
now := time.Now() now := cm.c.timeNow()
plen := len(payload) plen := len(payload)
atomic.AddUint64(cm.c.BytesReceived, uint64(plen)) atomic.AddUint64(cm.c.BytesReceived, uint64(plen))
@@ -291,7 +290,7 @@ func (cm *clientMedia) readRTCPUDPPlay(payload []byte) {
if sr, ok := pkt.(*rtcp.SenderReport); ok { if sr, ok := pkt.(*rtcp.SenderReport); ok {
format := cm.findFormatWithSSRC(sr.SSRC) format := cm.findFormatWithSSRC(sr.SSRC)
if format != nil { if format != nil {
format.udpRTCPReceiver.ProcessSenderReport(sr, now) format.rtcpReceiver.ProcessSenderReport(sr, now)
} }
} }

View File

@@ -2138,12 +2138,12 @@ func TestClientPlayRTCPReport(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// wait for the packet's SSRC to be saved // wait for the packet's SSRC to be saved
time.Sleep(500 * time.Millisecond) time.Sleep(200 * time.Millisecond)
sr := &rtcp.SenderReport{ sr := &rtcp.SenderReport{
SSRC: 753621, SSRC: 753621,
NTPTime: 0, NTPTime: ntpTimeGoToRTCP(time.Date(2017, 8, 12, 15, 30, 0, 0, time.UTC)),
RTPTime: 0, RTPTime: 54352,
PacketCount: 1, PacketCount: 1,
OctetCount: 4, OctetCount: 4,
} }
@@ -2167,7 +2167,7 @@ func TestClientPlayRTCPReport(t *testing.T) {
{ {
SSRC: rr.Reports[0].SSRC, SSRC: rr.Reports[0].SSRC,
LastSequenceNumber: 946, LastSequenceNumber: 946,
LastSenderReport: rr.Reports[0].LastSenderReport, LastSenderReport: 2641887232,
Delay: rr.Reports[0].Delay, Delay: rr.Reports[0].Delay,
}, },
}, },
@@ -2187,7 +2187,7 @@ func TestClientPlayRTCPReport(t *testing.T) {
}() }()
c := Client{ c := Client{
udpReceiverReportPeriod: 1 * time.Second, receiverReportPeriod: 500 * time.Millisecond,
} }
err = readAll(&c, "rtsp://localhost:8554/teststream", nil) err = readAll(&c, "rtsp://localhost:8554/teststream", nil)
@@ -3166,3 +3166,184 @@ func TestClientPlayDecodeErrors(t *testing.T) {
}) })
} }
} }
func TestClientPlayPacketNTP(t *testing.T) {
l, err := net.Listen("tcp", "localhost:8554")
require.NoError(t, err)
defer l.Close()
serverDone := make(chan struct{})
defer func() { <-serverDone }()
go func() {
defer close(serverDone)
nconn, err := l.Accept()
require.NoError(t, err)
defer nconn.Close()
conn := conn.NewConn(nconn)
req, err := conn.ReadRequest()
require.NoError(t, err)
require.Equal(t, base.Options, req.Method)
err = conn.WriteResponse(&base.Response{
StatusCode: base.StatusOK,
Header: base.Header{
"Public": base.HeaderValue{strings.Join([]string{
string(base.Describe),
string(base.Setup),
string(base.Play),
}, ", ")},
},
})
require.NoError(t, err)
req, err = conn.ReadRequest()
require.NoError(t, err)
require.Equal(t, base.Describe, req.Method)
medias := media.Medias{testH264Media}
resetMediaControls(medias)
err = conn.WriteResponse(&base.Response{
StatusCode: base.StatusOK,
Header: base.Header{
"Content-Type": base.HeaderValue{"application/sdp"},
"Content-Base": base.HeaderValue{"rtsp://localhost:8554/teststream/"},
},
Body: mustMarshalMedias(medias),
})
require.NoError(t, err)
req, err = conn.ReadRequest()
require.NoError(t, err)
require.Equal(t, base.Setup, req.Method)
var inTH headers.Transport
err = inTH.Unmarshal(req.Header["Transport"])
require.NoError(t, err)
l1, err := net.ListenPacket("udp", "localhost:27556")
require.NoError(t, err)
defer l1.Close()
l2, err := net.ListenPacket("udp", "localhost:27557")
require.NoError(t, err)
defer l2.Close()
err = conn.WriteResponse(&base.Response{
StatusCode: base.StatusOK,
Header: base.Header{
"Transport": headers.Transport{
Protocol: headers.TransportProtocolUDP,
Delivery: func() *headers.TransportDelivery {
v := headers.TransportDeliveryUnicast
return &v
}(),
ServerPorts: &[2]int{27556, 27557},
ClientPorts: inTH.ClientPorts,
}.Marshal(),
},
})
require.NoError(t, err)
req, err = conn.ReadRequest()
require.NoError(t, err)
require.Equal(t, base.Play, req.Method)
err = conn.WriteResponse(&base.Response{
StatusCode: base.StatusOK,
})
require.NoError(t, err)
// skip firewall opening
buf := make([]byte, 2048)
_, _, err = l2.ReadFrom(buf)
require.NoError(t, err)
pkt := rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 946,
Timestamp: 54352,
SSRC: 753621,
},
Payload: []byte{1, 2, 3, 4},
}
byts, _ := pkt.Marshal()
_, err = l1.WriteTo(byts, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: inTH.ClientPorts[0],
})
require.NoError(t, err)
// wait for the packet's SSRC to be saved
time.Sleep(100 * time.Millisecond)
sr := &rtcp.SenderReport{
SSRC: 753621,
NTPTime: ntpTimeGoToRTCP(time.Date(2017, 8, 12, 15, 30, 0, 0, time.UTC)),
RTPTime: 54352,
PacketCount: 1,
OctetCount: 4,
}
byts, _ = sr.Marshal()
_, err = l2.WriteTo(byts, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: inTH.ClientPorts[1],
})
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
pkt = rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 947,
Timestamp: 54352 + 90000,
SSRC: 753621,
},
Payload: []byte{5, 6, 7, 8},
}
byts, _ = pkt.Marshal()
_, err = l1.WriteTo(byts, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: inTH.ClientPorts[0],
})
require.NoError(t, err)
req, err = conn.ReadRequest()
require.NoError(t, err)
require.Equal(t, base.Teardown, req.Method)
err = conn.WriteResponse(&base.Response{
StatusCode: base.StatusOK,
})
require.NoError(t, err)
}()
c := Client{}
recv := make(chan struct{})
first := false
err = readAll(&c, "rtsp://localhost:8554/teststream",
func(medi *media.Media, forma format.Format, pkt *rtp.Packet) {
if !first {
first = true
} else {
ntp, ok := c.PacketNTP(medi, pkt)
require.Equal(t, true, ok)
require.Equal(t, time.Date(2017, 8, 12, 15, 30, 1, 0, time.UTC), ntp.UTC())
close(recv)
}
})
require.NoError(t, err)
defer c.Close()
<-recv
}

View File

@@ -5,6 +5,7 @@ import (
"crypto/tls" "crypto/tls"
"net" "net"
"strings" "strings"
"sync"
"testing" "testing"
"time" "time"
@@ -65,6 +66,11 @@ var testRTCPPacketMarshaled = func() []byte {
return byts return byts
}() }()
func ntpTimeGoToRTCP(v time.Time) uint64 {
s := uint64(v.UnixNano()) + 2208988800*1000000000
return (s/1000000000)<<32 | (s % 1000000000)
}
func record(c *Client, ur string, medias media.Medias, cb func(*media.Media, rtcp.Packet)) error { func record(c *Client, ur string, medias media.Medias, cb func(*media.Media, rtcp.Packet)) error {
u, err := url.Parse(ur) u, err := url.Parse(ur)
if err != nil { if err != nil {
@@ -1168,10 +1174,10 @@ func TestClientRecordRTCPReport(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, &rtcp.SenderReport{ require.Equal(t, &rtcp.SenderReport{
SSRC: 0x38F27A2F, SSRC: 0x38F27A2F,
NTPTime: packets[0].(*rtcp.SenderReport).NTPTime, NTPTime: ntpTimeGoToRTCP(time.Date(1996, 2, 13, 14, 33, 5, 0, time.UTC)),
RTPTime: packets[0].(*rtcp.SenderReport).RTPTime, RTPTime: 1300000 + 60*90000,
PacketCount: 2, PacketCount: 1,
OctetCount: 2, OctetCount: 1,
}, packets[0]) }, packets[0])
close(reportReceived) close(reportReceived)
@@ -1186,6 +1192,9 @@ func TestClientRecordRTCPReport(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
}() }()
var curTime time.Time
var curTimeMutex sync.Mutex
c := Client{ c := Client{
Transport: func() *Transport { Transport: func() *Transport {
if ca == "udp" { if ca == "udp" {
@@ -1195,7 +1204,12 @@ func TestClientRecordRTCPReport(t *testing.T) {
v := TransportTCP v := TransportTCP
return &v return &v
}(), }(),
senderReportPeriod: 500 * time.Millisecond, timeNow: func() time.Time {
curTimeMutex.Lock()
defer curTimeMutex.Unlock()
return curTime
},
senderReportPeriod: 100 * time.Millisecond,
} }
medi := testH264Media medi := testH264Media
@@ -1205,17 +1219,27 @@ func TestClientRecordRTCPReport(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
defer c.Close() defer c.Close()
for i := 0; i < 2; i++ { curTimeMutex.Lock()
err = c.WritePacketRTP(medi, &rtp.Packet{ curTime = time.Date(2013, 6, 10, 1, 0, 0, 0, time.UTC)
curTimeMutex.Unlock()
err = c.WritePacketRTPWithNTP(
medi,
&rtp.Packet{
Header: rtp.Header{ Header: rtp.Header{
Version: 2, Version: 2,
PayloadType: 96, PayloadType: 96,
SSRC: 0x38F27A2F, SSRC: 0x38F27A2F,
Timestamp: 1300000,
}, },
Payload: []byte{0x05}, // IDR Payload: []byte{0x05}, // IDR
}) },
time.Date(1996, 2, 13, 14, 32, 5, 0, time.UTC))
require.NoError(t, err) require.NoError(t, err)
}
curTimeMutex.Lock()
curTime = time.Date(2013, 6, 10, 1, 1, 0, 0, time.UTC)
curTimeMutex.Unlock()
<-reportReceived <-reportReceived
}) })

View File

@@ -25,8 +25,7 @@ func randInRange(max int) (int, error) {
} }
type clientUDPListener struct { type clientUDPListener struct {
anyPortEnable bool c *Client
writeTimeout time.Duration
pc net.PacketConn pc net.PacketConn
readFunc readFunc readFunc readFunc
@@ -40,11 +39,7 @@ type clientUDPListener struct {
done chan struct{} done chan struct{}
} }
func newClientUDPListenerPair( func newClientUDPListenerPair(c *Client) (*clientUDPListener, *clientUDPListener, error) {
listenPacket func(network, address string) (net.PacketConn, error),
anyPortEnable bool,
writeTimeout time.Duration,
) (*clientUDPListener, *clientUDPListener, error) {
// choose two consecutive ports in range 65535-10000 // choose two consecutive ports in range 65535-10000
// RTP port must be even and RTCP port odd // RTP port must be even and RTCP port odd
for { for {
@@ -55,9 +50,7 @@ func newClientUDPListenerPair(
rtpPort := v*2 + 10000 rtpPort := v*2 + 10000
rtpListener, err := newClientUDPListener( rtpListener, err := newClientUDPListener(
listenPacket, c,
anyPortEnable,
writeTimeout,
false, false,
net.JoinHostPort("", strconv.FormatInt(int64(rtpPort), 10)), net.JoinHostPort("", strconv.FormatInt(int64(rtpPort), 10)),
) )
@@ -67,9 +60,7 @@ func newClientUDPListenerPair(
rtcpPort := rtpPort + 1 rtcpPort := rtpPort + 1
rtcpListener, err := newClientUDPListener( rtcpListener, err := newClientUDPListener(
listenPacket, c,
anyPortEnable,
writeTimeout,
false, false,
net.JoinHostPort("", strconv.FormatInt(int64(rtcpPort), 10)), net.JoinHostPort("", strconv.FormatInt(int64(rtcpPort), 10)),
) )
@@ -83,9 +74,7 @@ func newClientUDPListenerPair(
} }
func newClientUDPListener( func newClientUDPListener(
listenPacket func(network, address string) (net.PacketConn, error), c *Client,
anyPortEnable bool,
writeTimeout time.Duration,
multicast bool, multicast bool,
address string, address string,
) (*clientUDPListener, error) { ) (*clientUDPListener, error) {
@@ -96,7 +85,7 @@ func newClientUDPListener(
return nil, err return nil, err
} }
tmp, err := listenPacket(restrictNetwork("udp", "224.0.0.0:"+port)) tmp, err := c.ListenPacket(restrictNetwork("udp", "224.0.0.0:"+port))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -115,7 +104,7 @@ func newClientUDPListener(
pc = tmp.(*net.UDPConn) pc = tmp.(*net.UDPConn)
} else { } else {
tmp, err := listenPacket(restrictNetwork("udp", address)) tmp, err := c.ListenPacket(restrictNetwork("udp", address))
if err != nil { if err != nil {
return nil, err return nil, err
} }
@@ -128,8 +117,7 @@ func newClientUDPListener(
} }
return &clientUDPListener{ return &clientUDPListener{
anyPortEnable: anyPortEnable, c: c,
writeTimeout: writeTimeout,
pc: pc, pc: pc,
lastPacketTime: int64Ptr(0), lastPacketTime: int64Ptr(0),
}, nil }, nil
@@ -176,13 +164,13 @@ func (u *clientUDPListener) run() {
// in case of anyPortEnable, store the port of the first packet we receive. // in case of anyPortEnable, store the port of the first packet we receive.
// this reduces security issues // this reduces security issues
if u.anyPortEnable && u.readPort == 0 { if u.c.AnyPortEnable && u.readPort == 0 {
u.readPort = uaddr.Port u.readPort = uaddr.Port
} else if u.readPort != uaddr.Port { } else if u.readPort != uaddr.Port {
continue continue
} }
now := time.Now() now := u.c.timeNow()
atomic.StoreInt64(u.lastPacketTime, now.Unix()) atomic.StoreInt64(u.lastPacketTime, now.Unix())
u.readFunc(buf[:n]) u.readFunc(buf[:n])
@@ -192,7 +180,7 @@ func (u *clientUDPListener) run() {
func (u *clientUDPListener) write(payload []byte) error { func (u *clientUDPListener) write(payload []byte) error {
// 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.writeTimeout)) u.pc.SetWriteDeadline(time.Now().Add(u.c.WriteTimeout))
_, err := u.pc.WriteTo(payload, u.writeAddr) _, err := u.pc.WriteTo(payload, u.writeAddr)
return err return err
} }

View File

@@ -10,6 +10,13 @@ import (
"github.com/pion/rtp" "github.com/pion/rtp"
) )
// seconds since 1st January 1900
// higher 32 bits are the integer part, lower 32 bits are the fractional part
func ntpTimeRTCPToGo(v uint64) time.Time {
nano := int64((v>>32)*1000000000+(v&0xFFFFFFFF)) - 2208988800*1000000000
return time.Unix(0, nano)
}
func randUint32() (uint32, error) { func randUint32() (uint32, error) {
var b [4]byte var b [4]byte
_, err := rand.Read(b[:]) _, err := rand.Read(b[:])
@@ -19,13 +26,12 @@ func randUint32() (uint32, error) {
return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil return uint32(b[0])<<24 | uint32(b[1])<<16 | uint32(b[2])<<8 | uint32(b[3]), nil
} }
var timeNow = time.Now
// RTCPReceiver is a utility to generate RTCP receiver reports. // RTCPReceiver is a utility to generate RTCP receiver reports.
type RTCPReceiver struct { type RTCPReceiver struct {
period time.Duration
receiverSSRC uint32
clockRate float64 clockRate float64
receiverSSRC uint32
period time.Duration
timeNow func() time.Time
writePacketRTCP func(rtcp.Packet) writePacketRTCP func(rtcp.Packet)
mutex sync.Mutex mutex sync.Mutex
@@ -36,7 +42,7 @@ type RTCPReceiver struct {
lastSSRC uint32 lastSSRC uint32
lastSequenceNumber uint16 lastSequenceNumber uint16
lastTimeRTP uint32 lastTimeRTP uint32
lastTimeNTP time.Time lastTimeSystem time.Time
totalLost uint32 totalLost uint32
totalLostSinceReport uint32 totalLostSinceReport uint32
totalSinceReport uint32 totalSinceReport uint32
@@ -44,8 +50,9 @@ type RTCPReceiver struct {
// data from RTCP packets // data from RTCP packets
firstSenderReportReceived bool firstSenderReportReceived bool
lastSenderReportNTP uint32 lastSenderReportTimeNTP uint64
lastSenderReportTime time.Time lastSenderReportTimeRTP uint32
lastSenderReportTimeSystem time.Time
terminate chan struct{} terminate chan struct{}
done chan struct{} done chan struct{}
@@ -53,9 +60,10 @@ type RTCPReceiver struct {
// New allocates a RTCPReceiver. // New allocates a RTCPReceiver.
func New( func New(
period time.Duration,
receiverSSRC *uint32,
clockRate int, clockRate int,
receiverSSRC *uint32,
period time.Duration,
timeNow func() time.Time,
writePacketRTCP func(rtcp.Packet), writePacketRTCP func(rtcp.Packet),
) (*RTCPReceiver, error) { ) (*RTCPReceiver, error) {
if receiverSSRC == nil { if receiverSSRC == nil {
@@ -66,10 +74,15 @@ func New(
receiverSSRC = &v receiverSSRC = &v
} }
if timeNow == nil {
timeNow = time.Now
}
rr := &RTCPReceiver{ rr := &RTCPReceiver{
period: period,
receiverSSRC: *receiverSSRC,
clockRate: float64(clockRate), clockRate: float64(clockRate),
receiverSSRC: *receiverSSRC,
period: period,
timeNow: timeNow,
writePacketRTCP: writePacketRTCP, writePacketRTCP: writePacketRTCP,
terminate: make(chan struct{}), terminate: make(chan struct{}),
done: make(chan struct{}), done: make(chan struct{}),
@@ -95,7 +108,7 @@ func (rr *RTCPReceiver) run() {
for { for {
select { select {
case <-t.C: case <-t.C:
report := rr.report(timeNow()) report := rr.report()
if report != nil { if report != nil {
rr.writePacketRTCP(report) rr.writePacketRTCP(report)
} }
@@ -106,14 +119,16 @@ func (rr *RTCPReceiver) run() {
} }
} }
func (rr *RTCPReceiver) report(ts time.Time) rtcp.Packet { func (rr *RTCPReceiver) report() rtcp.Packet {
rr.mutex.Lock() rr.mutex.Lock()
defer rr.mutex.Unlock() defer rr.mutex.Unlock()
if !rr.firstRTPPacketReceived || rr.clockRate == 0 { if !rr.firstRTPPacketReceived {
return nil return nil
} }
system := rr.timeNow()
report := &rtcp.ReceiverReport{ report := &rtcp.ReceiverReport{
SSRC: rr.receiverSSRC, SSRC: rr.receiverSSRC,
Reports: []rtcp.ReceptionReport{ Reports: []rtcp.ReceptionReport{
@@ -131,12 +146,12 @@ func (rr *RTCPReceiver) report(ts time.Time) rtcp.Packet {
if rr.firstSenderReportReceived { if rr.firstSenderReportReceived {
// middle 32 bits out of 64 in the NTP timestamp of last sender report // middle 32 bits out of 64 in the NTP timestamp of last sender report
report.Reports[0].LastSenderReport = rr.lastSenderReportNTP report.Reports[0].LastSenderReport = uint32(rr.lastSenderReportTimeNTP >> 16)
// delay, expressed in units of 1/65536 seconds, between // delay, expressed in units of 1/65536 seconds, between
// receiving the last SR packet from source SSRC_n and sending this // receiving the last SR packet from source SSRC_n and sending this
// reception report block // reception report block
report.Reports[0].Delay = uint32(ts.Sub(rr.lastSenderReportTime).Seconds() * 65536) report.Reports[0].Delay = uint32(system.Sub(rr.lastSenderReportTimeSystem).Seconds() * 65536)
} }
rr.totalLostSinceReport = 0 rr.totalLostSinceReport = 0
@@ -146,7 +161,7 @@ func (rr *RTCPReceiver) report(ts time.Time) rtcp.Packet {
} }
// ProcessPacket extracts the needed data from RTP packets. // ProcessPacket extracts the needed data from RTP packets.
func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsDTS bool) { func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, system time.Time, ptsEqualsDTS bool) {
rr.mutex.Lock() rr.mutex.Lock()
defer rr.mutex.Unlock() defer rr.mutex.Unlock()
@@ -160,7 +175,7 @@ func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsD
if ptsEqualsDTS { if ptsEqualsDTS {
rr.timeInitialized = true rr.timeInitialized = true
rr.lastTimeRTP = pkt.Timestamp rr.lastTimeRTP = pkt.Timestamp
rr.lastTimeNTP = ntp rr.lastTimeSystem = system
} }
// subsequent packets // subsequent packets
@@ -194,7 +209,7 @@ func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsD
if rr.timeInitialized { if rr.timeInitialized {
// update jitter // update jitter
// https://tools.ietf.org/html/rfc3550#page-39 // https://tools.ietf.org/html/rfc3550#page-39
D := ntp.Sub(rr.lastTimeNTP).Seconds()*rr.clockRate - D := system.Sub(rr.lastTimeSystem).Seconds()*rr.clockRate -
(float64(pkt.Timestamp) - float64(rr.lastTimeRTP)) (float64(pkt.Timestamp) - float64(rr.lastTimeRTP))
if D < 0 { if D < 0 {
D = -D D = -D
@@ -204,20 +219,21 @@ func (rr *RTCPReceiver) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsD
rr.timeInitialized = true rr.timeInitialized = true
rr.lastTimeRTP = pkt.Timestamp rr.lastTimeRTP = pkt.Timestamp
rr.lastTimeNTP = ntp rr.lastTimeSystem = system
rr.lastSSRC = pkt.SSRC rr.lastSSRC = pkt.SSRC
} }
} }
} }
// ProcessSenderReport extracts the needed data from RTCP sender reports. // ProcessSenderReport extracts the needed data from RTCP sender reports.
func (rr *RTCPReceiver) ProcessSenderReport(sr *rtcp.SenderReport, ts time.Time) { func (rr *RTCPReceiver) ProcessSenderReport(sr *rtcp.SenderReport, system time.Time) {
rr.mutex.Lock() rr.mutex.Lock()
defer rr.mutex.Unlock() defer rr.mutex.Unlock()
rr.firstSenderReportReceived = true rr.firstSenderReportReceived = true
rr.lastSenderReportNTP = uint32(sr.NTPTime >> 16) rr.lastSenderReportTimeNTP = sr.NTPTime
rr.lastSenderReportTime = ts rr.lastSenderReportTimeRTP = sr.RTPTime
rr.lastSenderReportTimeSystem = system
} }
// LastSSRC returns the SSRC of the last RTP packet. // LastSSRC returns the SSRC of the last RTP packet.
@@ -226,3 +242,18 @@ func (rr *RTCPReceiver) LastSSRC() (uint32, bool) {
defer rr.mutex.Unlock() defer rr.mutex.Unlock()
return rr.lastSSRC, rr.firstRTPPacketReceived return rr.lastSSRC, rr.firstRTPPacketReceived
} }
// PacketNTP returns the NTP timestamp of the packet.
func (rr *RTCPReceiver) PacketNTP(ts uint32) (time.Time, bool) {
rr.mutex.Lock()
defer rr.mutex.Unlock()
if !rr.firstSenderReportReceived {
return time.Time{}, false
}
timeDiff := int32(ts - rr.lastSenderReportTimeRTP)
timeDiffGo := (time.Duration(timeDiff) * time.Second) / time.Duration(rr.clockRate)
return ntpTimeRTCPToGo(rr.lastSenderReportTimeNTP).Add(timeDiffGo), true
}

View File

@@ -9,14 +9,20 @@ import (
"github.com/stretchr/testify/require" "github.com/stretchr/testify/require"
) )
func TestRTCPReceiverBase(t *testing.T) { func uint32Ptr(v uint32) *uint32 {
timeNow = func() time.Time { return &v
return time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
} }
done := make(chan struct{})
v := uint32(0x65f83afb)
rr, err := New(500*time.Millisecond, &v, 90000, func TestRTCPReceiverBase(t *testing.T) {
done := make(chan struct{})
rr, err := New(
90000,
uint32Ptr(0x65f83afb),
500*time.Millisecond,
func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
},
func(pkt rtcp.Packet) { func(pkt rtcp.Packet) {
require.Equal(t, &rtcp.ReceiverReport{ require.Equal(t, &rtcp.ReceiverReport{
SSRC: 0x65f83afb, SSRC: 0x65f83afb,
@@ -77,12 +83,15 @@ func TestRTCPReceiverBase(t *testing.T) {
func TestRTCPReceiverOverflow(t *testing.T) { func TestRTCPReceiverOverflow(t *testing.T) {
done := make(chan struct{}) done := make(chan struct{})
timeNow = func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
}
v := uint32(0x65f83afb)
rr, err := New(250*time.Millisecond, &v, 90000, func(pkt rtcp.Packet) { rr, err := New(
90000,
uint32Ptr(0x65f83afb),
250*time.Millisecond,
func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
},
func(pkt rtcp.Packet) {
require.Equal(t, &rtcp.ReceiverReport{ require.Equal(t, &rtcp.ReceiverReport{
SSRC: 0x65f83afb, SSRC: 0x65f83afb,
Reports: []rtcp.ReceptionReport{ Reports: []rtcp.ReceptionReport{
@@ -144,12 +153,15 @@ func TestRTCPReceiverOverflow(t *testing.T) {
func TestRTCPReceiverPacketLost(t *testing.T) { func TestRTCPReceiverPacketLost(t *testing.T) {
done := make(chan struct{}) done := make(chan struct{})
timeNow = func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
}
v := uint32(0x65f83afb)
rr, err := New(500*time.Millisecond, &v, 90000, func(pkt rtcp.Packet) { rr, err := New(
90000,
uint32Ptr(0x65f83afb),
500*time.Millisecond,
func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
},
func(pkt rtcp.Packet) {
require.Equal(t, &rtcp.ReceiverReport{ require.Equal(t, &rtcp.ReceiverReport{
SSRC: 0x65f83afb, SSRC: 0x65f83afb,
Reports: []rtcp.ReceptionReport{ Reports: []rtcp.ReceptionReport{
@@ -214,12 +226,15 @@ func TestRTCPReceiverPacketLost(t *testing.T) {
func TestRTCPReceiverOverflowPacketLost(t *testing.T) { func TestRTCPReceiverOverflowPacketLost(t *testing.T) {
done := make(chan struct{}) done := make(chan struct{})
timeNow = func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
}
v := uint32(0x65f83afb)
rr, err := New(500*time.Millisecond, &v, 90000, func(pkt rtcp.Packet) { rr, err := New(
90000,
uint32Ptr(0x65f83afb),
500*time.Millisecond,
func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 21, 0, time.UTC)
},
func(pkt rtcp.Packet) {
require.Equal(t, &rtcp.ReceiverReport{ require.Equal(t, &rtcp.ReceiverReport{
SSRC: 0x65f83afb, SSRC: 0x65f83afb,
Reports: []rtcp.ReceptionReport{ Reports: []rtcp.ReceptionReport{
@@ -284,12 +299,15 @@ func TestRTCPReceiverOverflowPacketLost(t *testing.T) {
func TestRTCPReceiverJitter(t *testing.T) { func TestRTCPReceiverJitter(t *testing.T) {
done := make(chan struct{}) done := make(chan struct{})
timeNow = func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
}
v := uint32(0x65f83afb)
rr, err := New(500*time.Millisecond, &v, 90000, func(pkt rtcp.Packet) { rr, err := New(
90000,
uint32Ptr(0x65f83afb),
500*time.Millisecond,
func() time.Time {
return time.Date(2008, 0o5, 20, 22, 15, 22, 0, time.UTC)
},
func(pkt rtcp.Packet) {
require.Equal(t, &rtcp.ReceiverReport{ require.Equal(t, &rtcp.ReceiverReport{
SSRC: 0x65f83afb, SSRC: 0x65f83afb,
Reports: []rtcp.ReceptionReport{ Reports: []rtcp.ReceptionReport{

View File

@@ -9,8 +9,6 @@ import (
"github.com/pion/rtp" "github.com/pion/rtp"
) )
var timeNow = time.Now
// seconds since 1st January 1900 // seconds since 1st January 1900
// higher 32 bits are the integer part, lower 32 bits are the fractional part // higher 32 bits are the integer part, lower 32 bits are the fractional part
func ntpTimeGoToRTCP(v time.Time) uint64 { func ntpTimeGoToRTCP(v time.Time) uint64 {
@@ -22,6 +20,7 @@ func ntpTimeGoToRTCP(v time.Time) uint64 {
type RTCPSender struct { type RTCPSender struct {
clockRate float64 clockRate float64
period time.Duration period time.Duration
timeNow func() time.Time
writePacketRTCP func(rtcp.Packet) writePacketRTCP func(rtcp.Packet)
mutex sync.Mutex mutex sync.Mutex
@@ -43,11 +42,17 @@ type RTCPSender struct {
func New( func New(
clockRate int, clockRate int,
period time.Duration, period time.Duration,
timeNow func() time.Time,
writePacketRTCP func(rtcp.Packet), writePacketRTCP func(rtcp.Packet),
) *RTCPSender { ) *RTCPSender {
if timeNow == nil {
timeNow = time.Now
}
rs := &RTCPSender{ rs := &RTCPSender{
clockRate: float64(clockRate), clockRate: float64(clockRate),
period: period, period: period,
timeNow: timeNow,
writePacketRTCP: writePacketRTCP, writePacketRTCP: writePacketRTCP,
terminate: make(chan struct{}), terminate: make(chan struct{}),
done: make(chan struct{}), done: make(chan struct{}),
@@ -92,7 +97,7 @@ func (rs *RTCPSender) report() rtcp.Packet {
return nil return nil
} }
systemTimeDiff := timeNow().Sub(rs.lastTimeSystem) systemTimeDiff := rs.timeNow().Sub(rs.lastTimeSystem)
ntpTime := rs.lastTimeNTP.Add(systemTimeDiff) ntpTime := rs.lastTimeNTP.Add(systemTimeDiff)
rtpTime := rs.lastTimeRTP + uint32(systemTimeDiff.Seconds()*rs.clockRate) rtpTime := rs.lastTimeRTP + uint32(systemTimeDiff.Seconds()*rs.clockRate)
@@ -114,7 +119,7 @@ func (rs *RTCPSender) ProcessPacket(pkt *rtp.Packet, ntp time.Time, ptsEqualsDTS
rs.initialized = true rs.initialized = true
rs.lastTimeRTP = pkt.Timestamp rs.lastTimeRTP = pkt.Timestamp
rs.lastTimeNTP = ntp rs.lastTimeNTP = ntp
rs.lastTimeSystem = timeNow() rs.lastTimeSystem = rs.timeNow()
} }
rs.lastSSRC = pkt.SSRC rs.lastSSRC = pkt.SSRC

View File

@@ -20,17 +20,16 @@ func TestRTCPSender(t *testing.T) {
curTime = v curTime = v
} }
timeNow = func() time.Time {
mutex.Lock()
defer mutex.Unlock()
return curTime
}
sent := make(chan struct{}) sent := make(chan struct{})
rs := New( rs := New(
90000, 90000,
100*time.Millisecond, 100*time.Millisecond,
func() time.Time {
mutex.Lock()
defer mutex.Unlock()
return curTime
},
func(pkt rtcp.Packet) { func(pkt rtcp.Packet) {
require.Equal(t, &rtcp.SenderReport{ require.Equal(t, &rtcp.SenderReport{
SSRC: 0xba9da416, SSRC: 0xba9da416,

View File

@@ -118,8 +118,9 @@ type Server struct {
// private // private
// //
udpReceiverReportPeriod time.Duration timeNow func() time.Time
senderReportPeriod time.Duration senderReportPeriod time.Duration
receiverReportPeriod time.Duration
sessionTimeout time.Duration sessionTimeout time.Duration
checkStreamPeriod time.Duration checkStreamPeriod time.Duration
@@ -176,12 +177,15 @@ func (s *Server) Start() error {
} }
// private // private
if s.udpReceiverReportPeriod == 0 { if s.timeNow == nil {
s.udpReceiverReportPeriod = 10 * time.Second s.timeNow = time.Now
} }
if s.senderReportPeriod == 0 { if s.senderReportPeriod == 0 {
s.senderReportPeriod = 10 * time.Second s.senderReportPeriod = 10 * time.Second
} }
if s.receiverReportPeriod == 0 {
s.receiverReportPeriod = 10 * time.Second
}
if s.sessionTimeout == 0 { if s.sessionTimeout == 0 {
s.sessionTimeout = 1 * 60 * time.Second s.sessionTimeout = 1 * 60 * time.Second
} }

View File

@@ -6,6 +6,7 @@ import (
"net" "net"
"strconv" "strconv"
"strings" "strings"
"sync"
"sync/atomic" "sync/atomic"
"testing" "testing"
"time" "time"
@@ -977,6 +978,9 @@ func TestServerPlayRTCPReport(t *testing.T) {
t.Run(ca, func(t *testing.T) { t.Run(ca, func(t *testing.T) {
var stream *ServerStream var stream *ServerStream
var curTime time.Time
var curTimeMutex sync.Mutex
s := &Server{ s := &Server{
Handler: &testServerHandler{ Handler: &testServerHandler{
onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) { onDescribe: func(ctx *ServerHandlerOnDescribeCtx) (*base.Response, *ServerStream, error) {
@@ -995,10 +999,15 @@ func TestServerPlayRTCPReport(t *testing.T) {
}, nil }, nil
}, },
}, },
senderReportPeriod: 1 * time.Second,
RTSPAddress: "localhost:8554", RTSPAddress: "localhost:8554",
UDPRTPAddress: "127.0.0.1:8000", UDPRTPAddress: "127.0.0.1:8000",
UDPRTCPAddress: "127.0.0.1:8001", UDPRTCPAddress: "127.0.0.1:8001",
timeNow: func() time.Time {
curTimeMutex.Lock()
defer curTimeMutex.Unlock()
return curTime
},
senderReportPeriod: 100 * time.Millisecond,
} }
err := s.Start() err := s.Start()
@@ -1052,17 +1061,27 @@ func TestServerPlayRTCPReport(t *testing.T) {
doPlay(t, conn, "rtsp://localhost:8554/teststream", session) doPlay(t, conn, "rtsp://localhost:8554/teststream", session)
for i := 0; i < 2; i++ { curTimeMutex.Lock()
err := stream.WritePacketRTP(stream.Medias()[0], &rtp.Packet{ curTime = time.Date(2014, 6, 7, 15, 0, 0, 0, time.UTC)
curTimeMutex.Unlock()
err = stream.WritePacketRTPWithNTP(
stream.Medias()[0],
&rtp.Packet{
Header: rtp.Header{ Header: rtp.Header{
Version: 2, Version: 2,
PayloadType: 96, PayloadType: 96,
SSRC: 0x38F27A2F, SSRC: 0x38F27A2F,
Timestamp: 240000,
}, },
Payload: []byte{0x05}, // IDR Payload: []byte{0x05}, // IDR
}) },
time.Date(2017, 8, 10, 12, 22, 0, 0, time.UTC))
require.NoError(t, err) require.NoError(t, err)
}
curTimeMutex.Lock()
curTime = time.Date(2014, 6, 7, 15, 0, 30, 0, time.UTC)
curTimeMutex.Unlock()
var buf []byte var buf []byte
@@ -1073,10 +1092,8 @@ func TestServerPlayRTCPReport(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
buf = buf[:n] buf = buf[:n]
} else { } else {
for i := 0; i < 2; i++ {
_, err := conn.ReadInterleavedFrame() _, err := conn.ReadInterleavedFrame()
require.NoError(t, err) require.NoError(t, err)
}
f, err := conn.ReadInterleavedFrame() f, err := conn.ReadInterleavedFrame()
require.NoError(t, err) require.NoError(t, err)
@@ -1088,10 +1105,10 @@ func TestServerPlayRTCPReport(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
require.Equal(t, &rtcp.SenderReport{ require.Equal(t, &rtcp.SenderReport{
SSRC: 0x38F27A2F, SSRC: 0x38F27A2F,
NTPTime: packets[0].(*rtcp.SenderReport).NTPTime, NTPTime: ntpTimeGoToRTCP(time.Date(2017, 8, 10, 12, 22, 30, 0, time.UTC)),
RTPTime: packets[0].(*rtcp.SenderReport).RTPTime, RTPTime: 240000 + 90000*30,
PacketCount: 2, PacketCount: 1,
OctetCount: 2, OctetCount: 1,
}, packets[0]) }, packets[0])
doTeardown(t, conn, "rtsp://localhost:8554/teststream", session) doTeardown(t, conn, "rtsp://localhost:8554/teststream", session)

View File

@@ -818,10 +818,10 @@ func TestServerRecordRTCPReport(t *testing.T) {
}, nil }, nil
}, },
}, },
udpReceiverReportPeriod: 1 * time.Second,
UDPRTPAddress: "127.0.0.1:8000", UDPRTPAddress: "127.0.0.1:8000",
UDPRTCPAddress: "127.0.0.1:8001", UDPRTCPAddress: "127.0.0.1:8001",
RTSPAddress: "localhost:8554", RTSPAddress: "localhost:8554",
receiverReportPeriod: 500 * time.Millisecond,
} }
err := s.Start() err := s.Start()
@@ -874,7 +874,7 @@ func TestServerRecordRTCPReport(t *testing.T) {
Timestamp: 54352, Timestamp: 54352,
SSRC: 753621, SSRC: 753621,
}, },
Payload: []byte{0x01, 0x02, 0x03, 0x04}, Payload: []byte{1, 2, 3, 4},
}).Marshal() }).Marshal()
_, err = l1.WriteTo(byts, &net.UDPAddr{ _, err = l1.WriteTo(byts, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"), IP: net.ParseIP("127.0.0.1"),
@@ -883,11 +883,11 @@ func TestServerRecordRTCPReport(t *testing.T) {
require.NoError(t, err) require.NoError(t, err)
// wait for the packet's SSRC to be saved // wait for the packet's SSRC to be saved
time.Sleep(500 * time.Millisecond) time.Sleep(200 * time.Millisecond)
byts, _ = (&rtcp.SenderReport{ byts, _ = (&rtcp.SenderReport{
SSRC: 753621, SSRC: 753621,
NTPTime: 0xcbddcc34999997ff, NTPTime: ntpTimeGoToRTCP(time.Date(2018, 2, 20, 19, 0, 0, 0, time.UTC)),
RTPTime: 54352, RTPTime: 54352,
PacketCount: 1, PacketCount: 1,
OctetCount: 4, OctetCount: 4,
@@ -916,7 +916,7 @@ func TestServerRecordRTCPReport(t *testing.T) {
{ {
SSRC: rr.Reports[0].SSRC, SSRC: rr.Reports[0].SSRC,
LastSequenceNumber: 534, LastSequenceNumber: 534,
LastSenderReport: rr.Reports[0].LastSenderReport, LastSenderReport: 4004511744,
Delay: rr.Reports[0].Delay, Delay: rr.Reports[0].Delay,
}, },
}, },
@@ -1418,3 +1418,137 @@ func TestServerRecordDecodeErrors(t *testing.T) {
}) })
} }
} }
func TestServerRecordPacketNTP(t *testing.T) {
recv := make(chan struct{})
first := false
s := &Server{
Handler: &testServerHandler{
onAnnounce: func(ctx *ServerHandlerOnAnnounceCtx) (*base.Response, error) {
return &base.Response{
StatusCode: base.StatusOK,
}, nil
},
onSetup: func(ctx *ServerHandlerOnSetupCtx) (*base.Response, *ServerStream, error) {
return &base.Response{
StatusCode: base.StatusOK,
}, nil, nil
},
onRecord: func(ctx *ServerHandlerOnRecordCtx) (*base.Response, error) {
ctx.Session.OnPacketRTPAny(func(medi *media.Media, forma format.Format, pkt *rtp.Packet) {
if !first {
first = true
} else {
ntp, ok := ctx.Session.PacketNTP(medi, pkt)
require.Equal(t, true, ok)
require.Equal(t, time.Date(2018, 2, 20, 19, 0, 1, 0, time.UTC), ntp.UTC())
close(recv)
}
})
return &base.Response{
StatusCode: base.StatusOK,
}, nil
},
},
UDPRTPAddress: "127.0.0.1:8000",
UDPRTCPAddress: "127.0.0.1:8001",
RTSPAddress: "localhost:8554",
}
err := s.Start()
require.NoError(t, err)
defer s.Close()
nconn, err := net.Dial("tcp", "localhost:8554")
require.NoError(t, err)
defer nconn.Close()
conn := conn.NewConn(nconn)
medias := media.Medias{testH264Media}
resetMediaControls(medias)
doAnnounce(t, conn, "rtsp://localhost:8554/teststream", medias)
l1, err := net.ListenPacket("udp", "localhost:34556")
require.NoError(t, err)
defer l1.Close()
l2, err := net.ListenPacket("udp", "localhost:34557")
require.NoError(t, err)
defer l2.Close()
inTH := &headers.Transport{
Delivery: func() *headers.TransportDelivery {
v := headers.TransportDeliveryUnicast
return &v
}(),
Mode: func() *headers.TransportMode {
v := headers.TransportModeRecord
return &v
}(),
Protocol: headers.TransportProtocolUDP,
ClientPorts: &[2]int{34556, 34557},
}
res, th := doSetup(t, conn, "rtsp://localhost:8554/teststream/"+medias[0].Control, inTH, "")
session := readSession(t, res)
doRecord(t, conn, "rtsp://localhost:8554/teststream", session)
byts, _ := (&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 534,
Timestamp: 54352,
SSRC: 753621,
},
Payload: []byte{1, 2, 3, 4},
}).Marshal()
_, err = l1.WriteTo(byts, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: th.ServerPorts[0],
})
require.NoError(t, err)
// wait for the packet's SSRC to be saved
time.Sleep(100 * time.Millisecond)
byts, _ = (&rtcp.SenderReport{
SSRC: 753621,
NTPTime: ntpTimeGoToRTCP(time.Date(2018, 2, 20, 19, 0, 0, 0, time.UTC)),
RTPTime: 54352,
PacketCount: 1,
OctetCount: 4,
}).Marshal()
_, err = l2.WriteTo(byts, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: th.ServerPorts[1],
})
require.NoError(t, err)
time.Sleep(100 * time.Millisecond)
byts, _ = (&rtp.Packet{
Header: rtp.Header{
Version: 2,
Marker: true,
PayloadType: 96,
SequenceNumber: 535,
Timestamp: 54352 + 90000,
SSRC: 753621,
},
Payload: []byte{1, 2, 3, 4},
}).Marshal()
_, err = l1.WriteTo(byts, &net.UDPAddr{
IP: net.ParseIP("127.0.0.1"),
Port: th.ServerPorts[0],
})
require.NoError(t, err)
<-recv
}

View File

@@ -181,7 +181,7 @@ func newServerSession(
bytesReceived: new(uint64), bytesReceived: new(uint64),
bytesSent: new(uint64), bytesSent: new(uint64),
conns: make(map[*ServerConn]struct{}), conns: make(map[*ServerConn]struct{}),
lastRequestTime: time.Now(), lastRequestTime: s.timeNow(),
udpCheckStreamTimer: emptyTimer(), udpCheckStreamTimer: emptyTimer(),
chHandleRequest: make(chan sessionRequestReq), chHandleRequest: make(chan sessionRequestReq),
chRemoveConn: make(chan *ServerConn), chRemoveConn: make(chan *ServerConn),
@@ -326,7 +326,7 @@ func (ss *ServerSession) runInner() error {
for { for {
select { select {
case req := <-ss.chHandleRequest: case req := <-ss.chHandleRequest:
ss.lastRequestTime = time.Now() ss.lastRequestTime = ss.s.timeNow()
if _, ok := ss.conns[req.sc]; !ok { if _, ok := ss.conns[req.sc]; !ok {
ss.conns[req.sc] = struct{}{} ss.conns[req.sc] = struct{}{}
@@ -402,7 +402,7 @@ func (ss *ServerSession) runInner() error {
} }
case <-ss.udpCheckStreamTimer.C: case <-ss.udpCheckStreamTimer.C:
now := time.Now() now := ss.s.timeNow()
lft := atomic.LoadInt64(ss.udpLastPacketTime) lft := atomic.LoadInt64(ss.udpLastPacketTime)
@@ -873,7 +873,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
ss.state = ServerSessionStatePlay ss.state = ServerSessionStatePlay
v := time.Now().Unix() v := ss.s.timeNow().Unix()
ss.udpLastPacketTime = &v ss.udpLastPacketTime = &v
for _, sm := range ss.setuppedMedias { for _, sm := range ss.setuppedMedias {
@@ -897,7 +897,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
} }
var ri headers.RTPInfo var ri headers.RTPInfo
now := time.Now() now := ss.s.timeNow()
for _, sm := range ss.setuppedMediasOrdered { for _, sm := range ss.setuppedMediasOrdered {
entry := ss.setuppedStream.rtpInfoEntry(sm.media, now) entry := ss.setuppedStream.rtpInfoEntry(sm.media, now)
@@ -965,7 +965,7 @@ func (ss *ServerSession) handleRequestInner(sc *ServerConn, req *base.Request) (
ss.state = ServerSessionStateRecord ss.state = ServerSessionStateRecord
v := time.Now().Unix() v := ss.s.timeNow().Unix()
ss.udpLastPacketTime = &v ss.udpLastPacketTime = &v
for _, sm := range ss.setuppedMedias { for _, sm := range ss.setuppedMedias {
@@ -1186,6 +1186,14 @@ func (ss *ServerSession) WritePacketRTCP(medi *media.Media, pkt rtcp.Packet) err
return nil return nil
} }
// PacketNTP returns the NTP timestamp of an incoming RTP packet.
// The NTP timestamp is computed from sender reports.
func (ss *ServerSession) PacketNTP(medi *media.Media, pkt *rtp.Packet) (time.Time, bool) {
sm := ss.setuppedMedias[medi]
sf := sm.formats[pkt.PayloadType]
return sf.rtcpReceiver.PacketNTP(pkt.Timestamp)
}
func (ss *ServerSession) handleRequest(req sessionRequestReq) (*base.Response, *ServerSession, error) { func (ss *ServerSession) handleRequest(req sessionRequestReq) (*base.Response, *ServerSession, error) {
select { select {
case ss.chHandleRequest <- req: case ss.chHandleRequest <- req:

View File

@@ -18,7 +18,7 @@ type serverSessionFormat struct {
format format.Format format format.Format
udpReorderer *rtpreorderer.Reorderer udpReorderer *rtpreorderer.Reorderer
tcpLossDetector *rtplossdetector.LossDetector tcpLossDetector *rtplossdetector.LossDetector
udpRTCPReceiver *rtcpreceiver.RTCPReceiver rtcpReceiver *rtcpreceiver.RTCPReceiver
onPacketRTP OnPacketRTPFunc onPacketRTP OnPacketRTPFunc
} }
@@ -34,27 +34,31 @@ func (sf *serverSessionFormat) start() {
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.New() sf.udpReorderer = rtpreorderer.New()
} else {
sf.tcpLossDetector = rtplossdetector.New()
}
var err error var err error
sf.udpRTCPReceiver, err = rtcpreceiver.New( sf.rtcpReceiver, err = rtcpreceiver.New(
sf.sm.ss.s.udpReceiverReportPeriod,
nil,
sf.format.ClockRate(), sf.format.ClockRate(),
nil,
sf.sm.ss.s.receiverReportPeriod,
sf.sm.ss.s.timeNow,
func(pkt rtcp.Packet) { func(pkt rtcp.Packet) {
if *sf.sm.ss.setuppedTransport == TransportUDP || *sf.sm.ss.setuppedTransport == TransportUDPMulticast {
sf.sm.ss.WritePacketRTCP(sf.sm.media, pkt) //nolint:errcheck sf.sm.ss.WritePacketRTCP(sf.sm.media, pkt) //nolint:errcheck
}
}) })
if err != nil { if err != nil {
panic(err) panic(err)
} }
} else {
sf.tcpLossDetector = rtplossdetector.New()
}
} }
} }
func (sf *serverSessionFormat) stop() { func (sf *serverSessionFormat) stop() {
if sf.udpRTCPReceiver != nil { if sf.rtcpReceiver != nil {
sf.udpRTCPReceiver.Close() sf.rtcpReceiver.Close()
sf.udpRTCPReceiver = nil sf.rtcpReceiver = nil
} }
} }
@@ -73,7 +77,7 @@ func (sf *serverSessionFormat) readRTPUDP(pkt *rtp.Packet, now time.Time) {
} }
for _, pkt := range packets { for _, pkt := range packets {
sf.udpRTCPReceiver.ProcessPacket(pkt, now, sf.format.PTSEqualsDTS(pkt)) sf.rtcpReceiver.ProcessPacket(pkt, now, sf.format.PTSEqualsDTS(pkt))
sf.onPacketRTP(pkt) sf.onPacketRTP(pkt)
} }
} }
@@ -92,5 +96,7 @@ func (sf *serverSessionFormat) readRTPTCP(pkt *rtp.Packet) {
// do not return // do not return
} }
now := sf.sm.ss.s.timeNow()
sf.rtcpReceiver.ProcessPacket(pkt, now, sf.format.PTSEqualsDTS(pkt))
sf.onPacketRTP(pkt) sf.onPacketRTP(pkt)
} }

View File

@@ -160,7 +160,7 @@ func (sm *serverSessionMedia) readRTCPUDPPlay(payload []byte) {
return return
} }
now := time.Now() now := sm.ss.s.timeNow()
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix()) atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
for _, pkt := range packets { for _, pkt := range packets {
@@ -191,7 +191,7 @@ func (sm *serverSessionMedia) readRTPUDPRecord(payload []byte) {
return return
} }
now := time.Now() now := sm.ss.s.timeNow()
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix()) atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
forma.readRTPUDP(pkt, now) forma.readRTPUDP(pkt, now)
@@ -213,19 +213,17 @@ func (sm *serverSessionMedia) readRTCPUDPRecord(payload []byte) {
return return
} }
now := time.Now() now := sm.ss.s.timeNow()
atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix()) atomic.StoreInt64(sm.ss.udpLastPacketTime, now.Unix())
for _, pkt := range packets { for _, pkt := range packets {
if sr, ok := pkt.(*rtcp.SenderReport); ok { if sr, ok := pkt.(*rtcp.SenderReport); ok {
format := serverFindFormatWithSSRC(sm.formats, sr.SSRC) format := serverFindFormatWithSSRC(sm.formats, sr.SSRC)
if format != nil { if format != nil {
format.udpRTCPReceiver.ProcessSenderReport(sr, now) format.rtcpReceiver.ProcessSenderReport(sr, now)
}
} }
} }
for _, pkt := range packets {
sm.onPacketRTCP(pkt) sm.onPacketRTCP(pkt)
} }
} }
@@ -281,7 +279,16 @@ func (sm *serverSessionMedia) readRTCPTCPRecord(payload []byte) {
return return
} }
now := sm.ss.s.timeNow()
for _, pkt := range packets { for _, pkt := range packets {
if sr, ok := pkt.(*rtcp.SenderReport); ok {
format := serverFindFormatWithSSRC(sm.formats, sr.SSRC)
if format != nil {
format.rtcpReceiver.ProcessSenderReport(sr, now)
}
}
sm.onPacketRTCP(pkt) sm.onPacketRTCP(pkt)
} }
} }

View File

@@ -236,7 +236,7 @@ func (st *ServerStream) readerSetInactive(ss *ServerSession) {
// WritePacketRTP writes a RTP packet to all the readers of the stream. // WritePacketRTP writes a RTP packet to all the readers of the stream.
func (st *ServerStream) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) error { func (st *ServerStream) WritePacketRTP(medi *media.Media, pkt *rtp.Packet) error {
return st.WritePacketRTPWithNTP(medi, pkt, time.Now()) return st.WritePacketRTPWithNTP(medi, pkt, st.s.timeNow())
} }
// WritePacketRTPWithNTP writes a RTP packet to all the readers of the stream. // WritePacketRTPWithNTP writes a RTP packet to all the readers of the stream.

View File

@@ -35,6 +35,7 @@ func newServerStreamMedia(st *ServerStream, medi *media.Media, trackID int) *ser
tr.rtcpSender = rtcpsender.New( tr.rtcpSender = rtcpsender.New(
forma.ClockRate(), forma.ClockRate(),
st.s.senderReportPeriod, st.s.senderReportPeriod,
st.s.timeNow,
func(pkt rtcp.Packet) { func(pkt rtcp.Packet) {
if !st.s.DisableRTCPSenderReports { if !st.s.DisableRTCPSenderReports {
st.WritePacketRTCP(cmedia, pkt) //nolint:errcheck st.WritePacketRTCP(cmedia, pkt) //nolint:errcheck

View File

@@ -15,7 +15,7 @@ func serverFindFormatWithSSRC(
ssrc uint32, ssrc uint32,
) *serverSessionFormat { ) *serverSessionFormat {
for _, format := range formats { for _, format := range formats {
tssrc, ok := format.udpRTCPReceiver.LastSSRC() tssrc, ok := format.rtcpReceiver.LastSSRC()
if ok && tssrc == ssrc { if ok && tssrc == ssrc {
return format return format
} }