diff --git a/pkg/rtcpreceiver/rtcpreceiver.go b/pkg/rtcpreceiver/rtcpreceiver.go index 569f591d..caf5b81f 100644 --- a/pkg/rtcpreceiver/rtcpreceiver.go +++ b/pkg/rtcpreceiver/rtcpreceiver.go @@ -18,6 +18,10 @@ type RtcpReceiver struct { sequenceNumberCycles uint16 lastSequenceNumber uint16 lastSenderReportTime uint32 + totalLost uint32 + totalLostSinceRR uint32 + totalSinceRR uint32 + firstRtpReceived bool } // New allocates a RtcpReceiver. @@ -42,10 +46,26 @@ func (rr *RtcpReceiver) OnFrame(streamType base.StreamType, buf []byte) { // extract the sequence number of the first frame sequenceNumber := uint16(buf[2])<<8 | uint16(buf[3]) - if sequenceNumber < rr.lastSequenceNumber { - rr.sequenceNumberCycles += 1 + if !rr.firstRtpReceived { + rr.firstRtpReceived = true + rr.totalSinceRR = 1 + rr.lastSequenceNumber = sequenceNumber + + } else { + diff := (sequenceNumber - rr.lastSequenceNumber) + + if sequenceNumber != (rr.lastSequenceNumber + 1) { + rr.totalLost += uint32(diff) - 1 + rr.totalLostSinceRR += uint32(diff) - 1 + } + + if sequenceNumber < rr.lastSequenceNumber { + rr.sequenceNumberCycles += 1 + } + + rr.totalSinceRR += uint32(diff) + rr.lastSequenceNumber = sequenceNumber } - rr.lastSequenceNumber = sequenceNumber } } else { @@ -75,10 +95,21 @@ func (rr *RtcpReceiver) Report() []byte { SSRC: rr.senderSSRC, LastSequenceNumber: uint32(rr.sequenceNumberCycles)<<16 | uint32(rr.lastSequenceNumber), LastSenderReport: rr.lastSenderReportTime, + FractionLost: func() uint8 { + f := float64(rr.totalLostSinceRR) / float64(rr.totalSinceRR) + + // equivalent to taking the integer part after multiplying the + // loss fraction by 256 + return uint8(f * 256) + }(), + TotalLost: rr.totalLost, }, }, } + rr.totalLostSinceRR = 0 + rr.totalSinceRR = 0 + byts, _ := report.Marshal() return byts } diff --git a/pkg/rtcpreceiver/rtcpreceiver_test.go b/pkg/rtcpreceiver/rtcpreceiver_test.go index d7add5ca..7874755a 100644 --- a/pkg/rtcpreceiver/rtcpreceiver_test.go +++ b/pkg/rtcpreceiver/rtcpreceiver_test.go @@ -10,10 +10,20 @@ import ( "github.com/aler9/gortsplib/pkg/base" ) -func TestRtcpReceiver(t *testing.T) { +func TestRtcpReceiverBase(t *testing.T) { v := uint32(0x65f83afb) rr := New(&v) + srPkt := rtcp.SenderReport{ + SSRC: 0xba9da416, + NTPTime: 0xe363887a17ced916, + RTPTime: 1287981738, + PacketCount: 714, + OctetCount: 859127, + } + byts, _ := srPkt.Marshal() + rr.OnFrame(base.StreamTypeRtcp, byts) + rtpPkt := rtp.Packet{ Header: rtp.Header{ Version: 2, @@ -25,19 +35,9 @@ func TestRtcpReceiver(t *testing.T) { }, Payload: []byte("\x00\x00"), } - byts, _ := rtpPkt.Marshal() + byts, _ = rtpPkt.Marshal() rr.OnFrame(base.StreamTypeRtp, byts) - srPkt := rtcp.SenderReport{ - SSRC: 0xba9da416, - NTPTime: 0xe363887a17ced916, - RTPTime: 1287981738, - PacketCount: 714, - OctetCount: 859127, - } - byts, _ = srPkt.Marshal() - rr.OnFrame(base.StreamTypeRtcp, byts) - res := rr.Report() expectedPkt := rtcp.ReceiverReport{ @@ -45,20 +45,35 @@ func TestRtcpReceiver(t *testing.T) { Reports: []rtcp.ReceptionReport{ { SSRC: 0xba9da416, - LastSequenceNumber: uint32(946), - LastSenderReport: uint32(0x887a17ce), + LastSequenceNumber: 946, + LastSenderReport: 0x887a17ce, }, }, } expected, _ := expectedPkt.Marshal() require.Equal(t, expected, res) +} - rtpPkt = rtp.Packet{ +func TestRtcpReceiverSequenceOverflow(t *testing.T) { + v := uint32(0x65f83afb) + rr := New(&v) + + srPkt := rtcp.SenderReport{ + SSRC: 0xba9da416, + NTPTime: 0xe363887a17ced916, + RTPTime: 1287981738, + PacketCount: 714, + OctetCount: 859127, + } + byts, _ := srPkt.Marshal() + rr.OnFrame(base.StreamTypeRtcp, byts) + + rtpPkt := rtp.Packet{ Header: rtp.Header{ Version: 2, Marker: true, PayloadType: 96, - SequenceNumber: 945, + SequenceNumber: 0xffff, Timestamp: 1287987768, SSRC: 0xba9da416, }, @@ -67,18 +82,158 @@ func TestRtcpReceiver(t *testing.T) { byts, _ = rtpPkt.Marshal() rr.OnFrame(base.StreamTypeRtp, byts) - res = rr.Report() + rtpPkt = rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Marker: true, + PayloadType: 96, + SequenceNumber: 0x0000, + Timestamp: 1287987768, + SSRC: 0xba9da416, + }, + Payload: []byte("\x00\x00"), + } + byts, _ = rtpPkt.Marshal() + rr.OnFrame(base.StreamTypeRtp, byts) - expectedPkt = rtcp.ReceiverReport{ + res := rr.Report() + + expectedPkt := rtcp.ReceiverReport{ SSRC: 0x65f83afb, Reports: []rtcp.ReceptionReport{ { SSRC: 0xba9da416, - LastSequenceNumber: uint32(1<<16 | 945), - LastSenderReport: uint32(0x887a17ce), + LastSequenceNumber: 1<<16 | 0x0000, + LastSenderReport: 0x887a17ce, }, }, } - expected, _ = expectedPkt.Marshal() + expected, _ := expectedPkt.Marshal() + require.Equal(t, expected, res) +} + +func TestRtcpReceiverPacketLost(t *testing.T) { + v := uint32(0x65f83afb) + rr := New(&v) + + srPkt := rtcp.SenderReport{ + SSRC: 0xba9da416, + NTPTime: 0xe363887a17ced916, + RTPTime: 1287981738, + PacketCount: 714, + OctetCount: 859127, + } + byts, _ := srPkt.Marshal() + rr.OnFrame(base.StreamTypeRtcp, byts) + + rtpPkt := rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Marker: true, + PayloadType: 96, + SequenceNumber: 0x0120, + Timestamp: 1287987768, + SSRC: 0xba9da416, + }, + Payload: []byte("\x00\x00"), + } + byts, _ = rtpPkt.Marshal() + rr.OnFrame(base.StreamTypeRtp, byts) + + rtpPkt = rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Marker: true, + PayloadType: 96, + SequenceNumber: 0x0122, + Timestamp: 1287987768, + SSRC: 0xba9da416, + }, + Payload: []byte("\x00\x00"), + } + byts, _ = rtpPkt.Marshal() + rr.OnFrame(base.StreamTypeRtp, byts) + + res := rr.Report() + + expectedPkt := rtcp.ReceiverReport{ + SSRC: 0x65f83afb, + Reports: []rtcp.ReceptionReport{ + { + SSRC: 0xba9da416, + LastSequenceNumber: 0x0122, + LastSenderReport: 0x887a17ce, + FractionLost: func() uint8 { + v := float64(1) / 3 + return uint8(v * 256) + }(), + TotalLost: 1, + }, + }, + } + expected, _ := expectedPkt.Marshal() + require.Equal(t, expected, res) +} + +func TestRtcpReceiverSequenceOverflowPacketLost(t *testing.T) { + v := uint32(0x65f83afb) + rr := New(&v) + + srPkt := rtcp.SenderReport{ + SSRC: 0xba9da416, + NTPTime: 0xe363887a17ced916, + RTPTime: 1287981738, + PacketCount: 714, + OctetCount: 859127, + } + byts, _ := srPkt.Marshal() + rr.OnFrame(base.StreamTypeRtcp, byts) + + rtpPkt := rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Marker: true, + PayloadType: 96, + SequenceNumber: 0xffff, + Timestamp: 1287987768, + SSRC: 0xba9da416, + }, + Payload: []byte("\x00\x00"), + } + byts, _ = rtpPkt.Marshal() + rr.OnFrame(base.StreamTypeRtp, byts) + + rtpPkt = rtp.Packet{ + Header: rtp.Header{ + Version: 2, + Marker: true, + PayloadType: 96, + SequenceNumber: 0x0002, + Timestamp: 1287987768, + SSRC: 0xba9da416, + }, + Payload: []byte("\x00\x00"), + } + byts, _ = rtpPkt.Marshal() + rr.OnFrame(base.StreamTypeRtp, byts) + + res := rr.Report() + + expectedPkt := rtcp.ReceiverReport{ + SSRC: 0x65f83afb, + Reports: []rtcp.ReceptionReport{ + { + SSRC: 0xba9da416, + LastSequenceNumber: 1<<16 | 0x0002, + LastSenderReport: 0x887a17ce, + FractionLost: func() uint8 { + v := float64(2) / 4 + return uint8(v * 256) + }(), + TotalLost: 2, + }, + }, + } + expected, _ := expectedPkt.Marshal() require.Equal(t, expected, res) }