diff --git a/AUTHORS.txt b/AUTHORS.txt index 5d5a53c..779e5bc 100644 --- a/AUTHORS.txt +++ b/AUTHORS.txt @@ -19,5 +19,6 @@ Mathis Engelbart Max Hawkins Sean DuBois Sean DuBois +seb-dev Simone Gotti Woodrow Douglass diff --git a/receiver_report_test.go b/receiver_report_test.go index b049c79..7b29a51 100644 --- a/receiver_report_test.go +++ b/receiver_report_test.go @@ -49,6 +49,40 @@ func TestReceiverReportUnmarshal(t *testing.T) { ProfileExtensions: []byte{}, }, }, + { + Name: "valid with negative totalLost", + Data: []byte{ + // v=2, p=0, count=1, RR, len=7 + 0x81, 0xc9, 0x0, 0x7, + // ssrc=0x902f9e2e + 0x90, 0x2f, 0x9e, 0x2e, + // ssrc=0xbc5e9a40 + 0xbc, 0x5e, 0x9a, 0x40, + // fracLost=0, totalLost=-1 + 0x0, 0xff, 0xff, 0xff, + // lastSeq=0x46e1 + 0x0, 0x0, 0x46, 0xe1, + // jitter=273 + 0x0, 0x0, 0x1, 0x11, + // lsr=0x9f36432 + 0x9, 0xf3, 0x64, 0x32, + // delay=150137 + 0x0, 0x2, 0x4a, 0x79, + }, + Want: ReceiverReport{ + SSRC: 0x902f9e2e, + Reports: []ReceptionReport{{ + SSRC: 0xbc5e9a40, + FractionLost: 0, + TotalLost: -1, + LastSequenceNumber: 0x46e1, + Jitter: 273, + LastSenderReport: 0x9f36432, + Delay: 150137, + }}, + ProfileExtensions: []byte{}, + }, + }, { Name: "valid with extension data", Data: []byte{ diff --git a/reception_report.go b/reception_report.go index 5bff8f2..c8ffbda 100644 --- a/reception_report.go +++ b/reception_report.go @@ -14,7 +14,10 @@ type ReceptionReport struct { FractionLost uint8 // The total number of RTP data packets from source SSRC that have // been lost since the beginning of reception. - TotalLost uint32 + // https://www.ietf.org/rfc/rfc3550.txt Section 6.4 and appendix A.3 + // The total number of packets lost is a signed number and can be + // negative + TotalLost int32 // The low 16 bits contain the highest sequence number received in an // RTP data packet from source SSRC, and the most significant 16 // bits extend that sequence number with the corresponding count of @@ -71,13 +74,17 @@ func (r ReceptionReport) Marshal() ([]byte, error) { rawPacket[fractionLostOffset] = r.FractionLost // pack TotalLost into 24 bits - if r.TotalLost >= (1 << 25) { + // we first convert signed integer to unsigned before using bit operators + uTotalLost := uint32(r.TotalLost) + if uTotalLost&0xff800000 != 0xff800000 && uTotalLost&0xff800000 != 0 { return nil, errInvalidTotalLost } + // Convert int32 to int24 + uTotalLost = uTotalLost&0x80000000>>8 | uTotalLost&0x007fffff tlBytes := rawPacket[totalLostOffset:] - tlBytes[0] = byte(r.TotalLost >> 16) - tlBytes[1] = byte(r.TotalLost >> 8) - tlBytes[2] = byte(r.TotalLost) + tlBytes[0] = byte(uTotalLost >> 16) + tlBytes[1] = byte(uTotalLost >> 8) + tlBytes[2] = byte(uTotalLost) binary.BigEndian.PutUint32(rawPacket[lastSeqOffset:], r.LastSequenceNumber) binary.BigEndian.PutUint32(rawPacket[jitterOffset:], r.Jitter) @@ -115,7 +122,13 @@ func (r *ReceptionReport) Unmarshal(rawPacket []byte) error { r.FractionLost = rawPacket[fractionLostOffset] tlBytes := rawPacket[totalLostOffset:] - r.TotalLost = uint32(tlBytes[2]) | uint32(tlBytes[1])<<8 | uint32(tlBytes[0])<<16 + uTotalLost := uint32(tlBytes[2]) | uint32(tlBytes[1])<<8 | uint32(tlBytes[0])<<16 + // test sign + ua := uTotalLost & 0x007fffff + if uTotalLost&0x00800000 == 0x00800000 { + ua |= 0xff800000 + } + r.TotalLost = int32(ua) r.LastSequenceNumber = binary.BigEndian.Uint32(rawPacket[lastSeqOffset:]) r.Jitter = binary.BigEndian.Uint32(rawPacket[jitterOffset:])