diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc index a127bc1d2..51c86aaa9 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet.cc @@ -13,9 +13,28 @@ #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" #include "webrtc/system_wrappers/interface/logging.h" +using webrtc::RTCPUtility::PT_BYE; +using webrtc::RTCPUtility::PT_PSFB; +using webrtc::RTCPUtility::PT_RR; +using webrtc::RTCPUtility::PT_RTPFB; +using webrtc::RTCPUtility::PT_SR; + +using webrtc::RTCPUtility::RTCPPacketBYE; +using webrtc::RTCPUtility::RTCPPacketPSFBFIR; +using webrtc::RTCPUtility::RTCPPacketPSFBFIRItem; +using webrtc::RTCPUtility::RTCPPacketPSFBRPSI; +using webrtc::RTCPUtility::RTCPPacketReportBlockItem; +using webrtc::RTCPUtility::RTCPPacketRR; +using webrtc::RTCPUtility::RTCPPacketRTPFBNACK; +using webrtc::RTCPUtility::RTCPPacketRTPFBNACKItem; +using webrtc::RTCPUtility::RTCPPacketSR; + namespace webrtc { namespace rtcp { namespace { +// Unused SSRC of media source, set to 0. +const uint32_t kUnusedMediaSourceSsrc0 = 0; + void AssignUWord8(uint8_t* buffer, uint16_t* offset, uint8_t value) { buffer[(*offset)++] = value; } @@ -32,9 +51,34 @@ void AssignUWord32(uint8_t* buffer, uint16_t* offset, uint32_t value) { *offset += 4; } +uint16_t BlockToHeaderLength(uint16_t length_in_bytes) { + // Length in 32-bit words minus 1. + assert(length_in_bytes > 0); + assert(length_in_bytes % 4 == 0); + return (length_in_bytes / 4) - 1; +} + // From RFC 3550, RTP: A Transport Protocol for Real-Time Applications. // -// Sender report +// RTP header format. +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| RC/FMT | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateHeader(uint8_t count_or_format, // Depends on packet type. + uint8_t packet_type, + uint16_t length, + uint8_t* buffer, + uint16_t* pos) { + const uint8_t kVersion = 2; + AssignUWord8(buffer, pos, (kVersion << 6) + count_or_format); + AssignUWord8(buffer, pos, packet_type); + AssignUWord16(buffer, pos, length); +} + +// Sender report (SR) (RFC 3550). // 0 1 2 3 // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -53,13 +97,11 @@ void AssignUWord32(uint8_t* buffer, uint16_t* offset, uint32_t value) { // | sender's octet count | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -void CreateSenderReport(const RTCPUtility::RTCPPacketSR& sr, +void CreateSenderReport(const RTCPPacketSR& sr, + uint16_t length, uint8_t* buffer, uint16_t* pos) { - const uint16_t kLength = 6 + (6 * sr.NumberOfReportBlocks); - AssignUWord8(buffer, pos, 0x80 + sr.NumberOfReportBlocks); - AssignUWord8(buffer, pos, RTCPUtility::PT_SR); - AssignUWord16(buffer, pos, kLength); + CreateHeader(sr.NumberOfReportBlocks, PT_SR, length, buffer, pos); AssignUWord32(buffer, pos, sr.SenderSSRC); AssignUWord32(buffer, pos, sr.NTPMostSignificant); AssignUWord32(buffer, pos, sr.NTPLeastSignificant); @@ -68,7 +110,7 @@ void CreateSenderReport(const RTCPUtility::RTCPPacketSR& sr, AssignUWord32(buffer, pos, sr.SenderOctetCount); } -// Receiver report, header (RFC 3550). +// Receiver report (RR), header (RFC 3550). // // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -77,13 +119,11 @@ void CreateSenderReport(const RTCPUtility::RTCPPacketSR& sr, // | SSRC of packet sender | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -void CreateReceiverReport(const RTCPUtility::RTCPPacketRR& rr, +void CreateReceiverReport(const RTCPPacketRR& rr, + uint16_t length, uint8_t* buffer, uint16_t* pos) { - const uint16_t kLength = 1 + (6 * rr.NumberOfReportBlocks); - AssignUWord8(buffer, pos, 0x80 + rr.NumberOfReportBlocks); - AssignUWord8(buffer, pos, RTCPUtility::PT_RR); - AssignUWord16(buffer, pos, kLength); + CreateHeader(rr.NumberOfReportBlocks, PT_RR, length, buffer, pos); AssignUWord32(buffer, pos, rr.SenderSSRC); } @@ -104,10 +144,9 @@ void CreateReceiverReport(const RTCPUtility::RTCPPacketRR& rr, // | delay since last SR (DLSR) | // +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ -void CreateReportBlock( - const RTCPUtility::RTCPPacketReportBlockItem& report_block, - uint8_t* buffer, - uint16_t* pos) { +void CreateReportBlock(const RTCPPacketReportBlockItem& report_block, + uint8_t* buffer, + uint16_t* pos) { AssignUWord32(buffer, pos, report_block.SSRC); AssignUWord8(buffer, pos, report_block.FractionLost); AssignUWord24(buffer, pos, report_block.CumulativeNumOfPacketsLost); @@ -130,14 +169,12 @@ void CreateReportBlock( // (opt) | length | reason for leaving ... // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -void CreateBye(const RTCPUtility::RTCPPacketBYE& bye, +void CreateBye(const RTCPPacketBYE& bye, const std::vector& csrcs, + uint16_t length, uint8_t* buffer, uint16_t* pos) { - const uint8_t kNumSsrcAndCsrcs = 1 + csrcs.size(); - AssignUWord8(buffer, pos, 0x80 + kNumSsrcAndCsrcs); - AssignUWord8(buffer, pos, RTCPUtility::PT_BYE); - AssignUWord16(buffer, pos, kNumSsrcAndCsrcs); + CreateHeader(length, PT_BYE, length, buffer, pos); AssignUWord32(buffer, pos, bye.SenderSSRC); for (std::vector::const_iterator it = csrcs.begin(); it != csrcs.end(); ++it) { @@ -161,6 +198,64 @@ void CreateBye(const RTCPUtility::RTCPPacketBYE& bye, // : Feedback Control Information (FCI) : // : // + +// Generic NACK (RFC 4585). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PID | BLP | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateNack(const RTCPPacketRTPFBNACK& nack, + const std::vector& nack_fields, + uint16_t length, + uint8_t* buffer, + uint16_t* pos) { + const uint8_t kFmt = 1; + CreateHeader(kFmt, PT_RTPFB, length, buffer, pos); + AssignUWord32(buffer, pos, nack.SenderSSRC); + AssignUWord32(buffer, pos, nack.MediaSSRC); + for (std::vector::const_iterator + it = nack_fields.begin(); it != nack_fields.end(); ++it) { + AssignUWord16(buffer, pos, (*it).PacketID); + AssignUWord16(buffer, pos, (*it).BitMask); + } +} + +// Reference picture selection indication (RPSI) (RFC 4585). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PB |0| Payload Type| Native RPSI bit string | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | defined per codec ... | Padding (0) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateRpsi(const RTCPPacketPSFBRPSI& rpsi, + uint8_t padding_bytes, + uint16_t length, + uint8_t* buffer, + uint16_t* pos) { + // Native bit string should be a multiple of 8 bits. + assert(rpsi.NumberOfValidBits % 8 == 0); + const uint8_t kFmt = 3; + CreateHeader(kFmt, PT_PSFB, length, buffer, pos); + AssignUWord32(buffer, pos, rpsi.SenderSSRC); + AssignUWord32(buffer, pos, rpsi.MediaSSRC); + AssignUWord8(buffer, pos, padding_bytes * 8); + AssignUWord8(buffer, pos, rpsi.PayloadType); + memcpy(buffer + *pos, rpsi.NativeBitString, rpsi.NumberOfValidBits / 8); + *pos += rpsi.NumberOfValidBits / 8; + memset(buffer + *pos, 0, padding_bytes); + *pos += padding_bytes; +} + // Full intra request (FIR) (RFC 5104). // // FCI: @@ -173,27 +268,26 @@ void CreateBye(const RTCPUtility::RTCPPacketBYE& bye, // | Seq nr. | Reserved | // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -void CreateFirRequest(const RTCPUtility::RTCPPacketPSFBFIR& fir, - const RTCPUtility::RTCPPacketPSFBFIRItem& fir_item, - uint8_t* buffer, - uint16_t* pos) { - const uint16_t kLength = 4; +void CreateFir(const RTCPPacketPSFBFIR& fir, + const RTCPPacketPSFBFIRItem& fir_item, + uint16_t length, + uint8_t* buffer, + uint16_t* pos) { const uint8_t kFmt = 4; - AssignUWord8(buffer, pos, 0x80 + kFmt); - AssignUWord8(buffer, pos, RTCPUtility::PT_PSFB); - AssignUWord16(buffer, pos, kLength); + CreateHeader(kFmt, PT_PSFB, length, buffer, pos); AssignUWord32(buffer, pos, fir.SenderSSRC); - AssignUWord32(buffer, pos, 0); + AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); AssignUWord32(buffer, pos, fir_item.SSRC); AssignUWord8(buffer, pos, fir_item.CommandSequenceNumber); AssignUWord24(buffer, pos, 0); } -void AppendReportBlocks(const std::vector& report_blocks, - uint8_t* buffer, - uint16_t* pos) { - for (std::vector::const_iterator it = report_blocks.begin(); - it != report_blocks.end(); ++it) { +template +void AppendBlocks(const std::vector& blocks, + uint8_t* buffer, + uint16_t* pos) { + for (typename std::vector::const_iterator it = blocks.begin(); + it != blocks.end(); ++it) { (*it)->Create(buffer, pos); } } @@ -236,8 +330,8 @@ void SenderReport::Create(uint8_t* packet, LOG(LS_WARNING) << "Max packet size reached."; return; } - CreateSenderReport(sr_, packet, len); - AppendReportBlocks(report_blocks_, packet, len); + CreateSenderReport(sr_, BlockToHeaderLength(Length()), packet, len); + AppendBlocks(report_blocks_, packet, len); } void SenderReport::WithReportBlock(ReportBlock* block) { @@ -257,8 +351,8 @@ void ReceiverReport::Create(uint8_t* packet, LOG(LS_WARNING) << "Max packet size reached."; return; } - CreateReceiverReport(rr_, packet, len); - AppendReportBlocks(report_blocks_, packet, len); + CreateReceiverReport(rr_, BlockToHeaderLength(Length()), packet, len); + AppendBlocks(report_blocks_, packet, len); } void ReceiverReport::WithReportBlock(ReportBlock* block) { @@ -271,12 +365,16 @@ void ReceiverReport::WithReportBlock(ReportBlock* block) { rr_.NumberOfReportBlocks = report_blocks_.size(); } +void ReportBlock::Create(uint8_t* packet, uint16_t* len) const { + CreateReportBlock(report_block_, packet, len); +} + void Bye::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const { if (*len + Length() > max_len) { LOG(LS_WARNING) << "Max packet size reached."; return; } - CreateBye(bye_, csrcs_, packet, len); + CreateBye(bye_, csrcs_, BlockToHeaderLength(Length()), packet, len); } void Bye::WithCsrc(uint32_t csrc) { @@ -287,16 +385,81 @@ void Bye::WithCsrc(uint32_t csrc) { csrcs_.push_back(csrc); } +void Nack::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const { + assert(!nack_fields_.empty()); + if (*len + Length() > max_len) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateNack(nack_, nack_fields_, BlockToHeaderLength(Length()), packet, len); +} + +void Nack::WithList(const uint16_t* nack_list, int length) { + assert(nack_list); + assert(nack_fields_.empty()); + int i = 0; + while (i < length) { + uint16_t pid = nack_list[i++]; + // Bitmask specifies losses in any of the 16 packets following the pid. + uint16_t bitmask = 0; + while (i < length) { + int shift = static_cast(nack_list[i] - pid) - 1; + if (shift >= 0 && shift <= 15) { + bitmask |= (1 << shift); + ++i; + } else { + break; + } + } + RTCPUtility::RTCPPacketRTPFBNACKItem item; + item.PacketID = pid; + item.BitMask = bitmask; + nack_fields_.push_back(item); + } +} + +void Rpsi::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const { + assert(rpsi_.NumberOfValidBits > 0); + if (*len + Length() > max_len) { + LOG(LS_WARNING) << "Max packet size reached."; + return; + } + CreateRpsi(rpsi_, padding_bytes_, BlockToHeaderLength(Length()), packet, len); +} + +void Rpsi::WithPictureId(uint64_t picture_id) { + const uint32_t kPidBits = 7; + const uint64_t k7MsbZeroMask = 0x1ffffffffffffff; + uint8_t required_bytes = 0; + uint64_t shifted_pid = picture_id; + do { + ++required_bytes; + shifted_pid = (shifted_pid >> kPidBits) & k7MsbZeroMask; + } while (shifted_pid > 0); + + // Convert picture id to native bit string (natively defined by the video + // codec). + int pos = 0; + for (int i = required_bytes - 1; i > 0; i--) { + rpsi_.NativeBitString[pos++] = + 0x80 | static_cast(picture_id >> (i * kPidBits)); + } + rpsi_.NativeBitString[pos++] = static_cast(picture_id & 0x7f); + rpsi_.NumberOfValidBits = pos * 8; + + // Calculate padding bytes (to reach next 32-bit boundary, 1, 2 or 3 bytes). + padding_bytes_ = 4 - ((2 + required_bytes) % 4); + if (padding_bytes_ == 4) { + padding_bytes_ = 0; + } +} + void Fir::Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const { if (*len + Length() > max_len) { LOG(LS_WARNING) << "Max packet size reached."; return; } - CreateFirRequest(fir_, fir_item_, packet, len); -} - -void ReportBlock::Create(uint8_t* packet, uint16_t* len) const { - CreateReportBlock(report_block_, packet, len); + CreateFir(fir_, fir_item_, BlockToHeaderLength(Length()), packet, len); } } // namespace rtcp } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet.h index 13ad808b9..641bd7fda 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet.h @@ -244,7 +244,7 @@ class ReportBlock { report_block_.DelayLastSR = delay_last_sr; } - void Create(uint8_t* array, uint16_t* cur_pos) const; + void Create(uint8_t* packet, uint16_t* len) const; private: RTCPUtility::RTCPPacketReportBlockItem report_block_; @@ -285,7 +285,7 @@ class Bye : public RtcpPacket { private: uint16_t Length() const { - const uint16_t kByeBlockLen = 8 + 4*csrcs_.size(); + const uint16_t kByeBlockLen = 8 + 4 * csrcs_.size(); return kByeBlockLen; } @@ -309,6 +309,98 @@ class Bye : public RtcpPacket { // : Feedback Control Information (FCI) : // : +// Generic NACK (RFC 4585). +// +// FCI: +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PID | BLP | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Nack : public RtcpPacket { + public: + Nack() + : RtcpPacket() { + memset(&nack_, 0, sizeof(nack_)); + } + + virtual ~Nack() {} + + void From(uint32_t ssrc) { + nack_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + nack_.MediaSSRC = ssrc; + } + void WithList(const uint16_t* nack_list, int length); + + protected: + virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const + OVERRIDE; + + private: + uint16_t Length() const { + const uint16_t kNackBlockLen = 4 * (3 + nack_fields_.size()); + return kNackBlockLen; + } + + RTCPUtility::RTCPPacketRTPFBNACK nack_; + std::vector nack_fields_; + + DISALLOW_COPY_AND_ASSIGN(Nack); +}; + +// Reference picture selection indication (RPSI) (RFC 4585). +// +// FCI: +// +// 0 1 2 3 +// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | PB |0| Payload Type| Native RPSI bit string | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | defined per codec ... | Padding (0) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +class Rpsi : public RtcpPacket { + public: + Rpsi() + : RtcpPacket(), + padding_bytes_(0) { + memset(&rpsi_, 0, sizeof(rpsi_)); + } + + virtual ~Rpsi() {} + + void From(uint32_t ssrc) { + rpsi_.SenderSSRC = ssrc; + } + void To(uint32_t ssrc) { + rpsi_.MediaSSRC = ssrc; + } + void WithPayloadType(uint8_t payload) { + assert(payload <= 0x7f); + rpsi_.PayloadType = payload; + } + void WithPictureId(uint64_t picture_id); + + protected: + virtual void Create(uint8_t* packet, uint16_t* len, uint16_t max_len) const + OVERRIDE; + + private: + uint16_t Length() const { + const uint16_t kRpsiBlockLen = + 12 + 2 + (rpsi_.NumberOfValidBits / 8) + padding_bytes_; + return kRpsiBlockLen; + } + + uint8_t padding_bytes_; + RTCPUtility::RTCPPacketPSFBRPSI rpsi_; + + DISALLOW_COPY_AND_ASSIGN(Rpsi); +}; // Full intra request (FIR) (RFC 5104). // diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc index 0114acd89..d7ae91424 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet_unittest.cc @@ -18,11 +18,13 @@ using webrtc::rtcp::Bye; using webrtc::rtcp::Empty; using webrtc::rtcp::Fir; -using webrtc::rtcp::SenderReport; +using webrtc::rtcp::Nack; using webrtc::rtcp::RawPacket; using webrtc::rtcp::ReceiverReport; using webrtc::rtcp::ReportBlock; +using webrtc::rtcp::Rpsi; using webrtc::test::RtcpPacketParser; +using webrtc::rtcp::SenderReport; namespace webrtc { @@ -152,6 +154,121 @@ TEST(RtcpPacketTest, SrWithTwoReportBlocks) { EXPECT_EQ(1, parser.report_blocks_per_ssrc(kRemoteSsrc + 1)); } +TEST(RtcpPacketTest, Nack) { + Nack nack; + const uint16_t kList[] = {0, 1, 3, 8, 16}; + const uint16_t kListLength = sizeof(kList) / sizeof(kList[0]); + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kList, kListLength); + RawPacket packet = nack.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.nack()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.nack()->Ssrc()); + EXPECT_EQ(kRemoteSsrc, parser.nack()->MediaSsrc()); + EXPECT_EQ(1, parser.nack_item()->num_packets()); + std::vector seqs = parser.nack_item()->last_nack_list(); + EXPECT_EQ(kListLength, seqs.size()); + for (size_t i = 0; i < kListLength; ++i) { + EXPECT_EQ(kList[i], seqs[i]); + } +} + +TEST(RtcpPacketTest, NackWithWrap) { + Nack nack; + const uint16_t kList[] = {65500, 65516, 65534, 65535, 0, 1, 3, 20, 100}; + const uint16_t kListLength = sizeof(kList) / sizeof(kList[0]); + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kList, kListLength); + RawPacket packet = nack.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(1, parser.nack()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.nack()->Ssrc()); + EXPECT_EQ(kRemoteSsrc, parser.nack()->MediaSsrc()); + EXPECT_EQ(4, parser.nack_item()->num_packets()); + std::vector seqs = parser.nack_item()->last_nack_list(); + EXPECT_EQ(kListLength, seqs.size()); + for (size_t i = 0; i < kListLength; ++i) { + EXPECT_EQ(kList[i], seqs[i]); + } +} + +TEST(RtcpPacketTest, Rpsi) { + Rpsi rpsi; + // 1000001 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0x41; + const uint16_t kNumberOfValidBytes = 1; + rpsi.WithPayloadType(100); + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(100, parser.rpsi()->PayloadType()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, RpsiWithTwoByteNativeString) { + Rpsi rpsi; + // |1 0000001 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0x81; + const uint16_t kNumberOfValidBytes = 2; + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, RpsiWithThreeByteNativeString) { + Rpsi rpsi; + // 10000|00 100000|0 1000000 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0x102040; + const uint16_t kNumberOfValidBytes = 3; + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, RpsiWithFourByteNativeString) { + Rpsi rpsi; + // 1000|001 00001|01 100001|1 1000010 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0x84161C2; + const uint16_t kNumberOfValidBytes = 4; + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + +TEST(RtcpPacketTest, RpsiWithMaxPictureId) { + Rpsi rpsi; + // 1 1111111| 1111111 1|111111 11|11111 111|1111 1111|111 11111| + // 11 111111|1 1111111 (7 bits = 1 byte in native string). + const uint64_t kPictureId = 0xffffffffffffffff; + const uint16_t kNumberOfValidBytes = 10; + rpsi.WithPictureId(kPictureId); + + RawPacket packet = rpsi.Build(); + RtcpPacketParser parser; + parser.Parse(packet.buffer(), packet.buffer_length()); + EXPECT_EQ(kNumberOfValidBytes * 8, parser.rpsi()->NumberOfValidBits()); + EXPECT_EQ(kPictureId, parser.rpsi()->PictureId()); +} + TEST(RtcpPacketTest, Fir) { Fir fir; fir.From(kSenderSsrc); diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc b/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc index 705a38b01..9acab735e 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_utility.cc @@ -1266,31 +1266,27 @@ RTCPUtility::RTCPParserV2::ParseFBCommon(const RTCPCommonHeader& header) } } -bool -RTCPUtility::RTCPParserV2::ParseRPSIItem() -{ - // RFC 4585 6.3.3. Reference Picture Selection Indication (RPSI) - /* - 0 1 2 3 - 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | PB |0| Payload Type| Native RPSI bit string | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - | defined per codec ... | Padding (0) | - +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ - */ +bool RTCPUtility::RTCPParserV2::ParseRPSIItem() { + + // RFC 4585 6.3.3. Reference Picture Selection Indication (RPSI). + // + // 0 1 2 3 + // 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | PB |0| Payload Type| Native RPSI bit string | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + // | defined per codec ... | Padding (0) | + // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ const ptrdiff_t length = _ptrRTCPBlockEnd - _ptrRTCPData; - if (length < 4) - { + if (length < 4) { _state = State_TopLevel; EndCurrentBlock(); return false; } - if(length > 2+RTCP_RPSI_DATA_SIZE) - { + if (length > 2 + RTCP_RPSI_DATA_SIZE) { _state = State_TopLevel; EndCurrentBlock(); @@ -1299,12 +1295,14 @@ RTCPUtility::RTCPParserV2::ParseRPSIItem() _packetType = kRtcpPsfbRpsiCode; - uint8_t paddingBits = *_ptrRTCPData++; + uint8_t padding_bits = *_ptrRTCPData++; _packet.RPSI.PayloadType = *_ptrRTCPData++; - memcpy(_packet.RPSI.NativeBitString, _ptrRTCPData, length-2); + memcpy(_packet.RPSI.NativeBitString, _ptrRTCPData, length - 2); + _ptrRTCPData += length - 2; - _packet.RPSI.NumberOfValidBits = uint16_t(length-2)*8 - paddingBits; + _packet.RPSI.NumberOfValidBits = + static_cast(length - 2) * 8 - padding_bits; return true; } diff --git a/webrtc/test/rtcp_packet_parser.cc b/webrtc/test/rtcp_packet_parser.cc index 2e5880e2b..9ae8543d7 100644 --- a/webrtc/test/rtcp_packet_parser.cc +++ b/webrtc/test/rtcp_packet_parser.cc @@ -23,25 +23,54 @@ void RtcpPacketParser::Parse(const void *data, int len) { for (RTCPUtility::RTCPPacketTypes type = parser.Begin(); type != RTCPUtility::kRtcpNotValidCode; type = parser.Iterate()) { - if (type == RTCPUtility::kRtcpSrCode) { - sender_report_.Set(parser.Packet().SR); - } else if (type == RTCPUtility::kRtcpRrCode) { - receiver_report_.Set(parser.Packet().RR); - } else if (type == RTCPUtility::kRtcpByeCode) { - bye_.Set(parser.Packet().BYE); - } else if (type == RTCPUtility::kRtcpReportBlockItemCode) { - report_block_.Set(parser.Packet().ReportBlockItem); - ++report_blocks_per_ssrc_[parser.Packet().ReportBlockItem.SSRC]; - } else if (type == RTCPUtility::kRtcpPsfbFirCode) { - fir_.Set(parser.Packet().FIR); - } else if (type == webrtc::RTCPUtility::kRtcpPsfbFirItemCode) { - fir_item_.Set(parser.Packet().FIRItem); - } else if (type == RTCPUtility::kRtcpRtpfbNackCode) { - nack_.Set(parser.Packet().NACK); - } else if (type == RTCPUtility::kRtcpRtpfbNackItemCode) { - nack_item_.Set(parser.Packet().NACKItem); + switch (type) { + case RTCPUtility::kRtcpSrCode: + sender_report_.Set(parser.Packet().SR); + break; + case RTCPUtility::kRtcpRrCode: + receiver_report_.Set(parser.Packet().RR); + break; + case RTCPUtility::kRtcpByeCode: + bye_.Set(parser.Packet().BYE); + break; + case RTCPUtility::kRtcpReportBlockItemCode: + report_block_.Set(parser.Packet().ReportBlockItem); + ++report_blocks_per_ssrc_[parser.Packet().ReportBlockItem.SSRC]; + break; + case RTCPUtility::kRtcpPsfbRpsiCode: + rpsi_.Set(parser.Packet().RPSI); + break; + case RTCPUtility::kRtcpPsfbFirCode: + fir_.Set(parser.Packet().FIR); + break; + case RTCPUtility::kRtcpPsfbFirItemCode: + fir_item_.Set(parser.Packet().FIRItem); + break; + case RTCPUtility::kRtcpRtpfbNackCode: + nack_.Set(parser.Packet().NACK); + nack_item_.Clear(); + break; + case RTCPUtility::kRtcpRtpfbNackItemCode: + nack_item_.Set(parser.Packet().NACKItem); + break; + default: + break; } } } + +uint64_t Rpsi::PictureId() const { + assert(num_packets_ > 0); + uint16_t num_bytes = rpsi_.NumberOfValidBits / 8; + assert(num_bytes > 0); + uint64_t picture_id = 0; + for (uint16_t i = 0; i < num_bytes - 1; ++i) { + picture_id += (rpsi_.NativeBitString[i] & 0x7f); + picture_id <<= 7; + } + picture_id += (rpsi_.NativeBitString[num_bytes - 1] & 0x7f); + return picture_id; +} + } // namespace test } // namespace webrtc diff --git a/webrtc/test/rtcp_packet_parser.h b/webrtc/test/rtcp_packet_parser.h index a9e650cf2..4db58c356 100644 --- a/webrtc/test/rtcp_packet_parser.h +++ b/webrtc/test/rtcp_packet_parser.h @@ -23,167 +23,192 @@ namespace test { class RtcpPacketParser; -class SenderReport { +class PacketType { public: - SenderReport() : num_packets_(0) {} - ~SenderReport() {} + virtual ~PacketType() {} - int num_packets() { return num_packets_; } - uint32_t Ssrc() { return sr_.SenderSSRC; } - uint32_t NtpSec() { return sr_.NTPMostSignificant; } - uint32_t NtpFrac() { return sr_.NTPLeastSignificant; } - uint32_t RtpTimestamp() { return sr_.RTPTimestamp; } - uint32_t PacketCount() { return sr_.SenderPacketCount; } - uint32_t OctetCount() { return sr_.SenderOctetCount; } + int num_packets() const { return num_packets_; } + + protected: + PacketType() : num_packets_(0) {} + + int num_packets_; +}; + +class SenderReport : public PacketType { + public: + SenderReport() {} + virtual ~SenderReport() {} + + uint32_t Ssrc() const { return sr_.SenderSSRC; } + uint32_t NtpSec() const { return sr_.NTPMostSignificant; } + uint32_t NtpFrac() const { return sr_.NTPLeastSignificant; } + uint32_t RtpTimestamp() const { return sr_.RTPTimestamp; } + uint32_t PacketCount() const { return sr_.SenderPacketCount; } + uint32_t OctetCount() const { return sr_.SenderOctetCount; } private: friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketSR& sr) { sr_ = sr; ++num_packets_; } - int num_packets_; RTCPUtility::RTCPPacketSR sr_; }; -class ReceiverReport { +class ReceiverReport : public PacketType { public: - ReceiverReport() : num_packets_(0) {} - ~ReceiverReport() {} + ReceiverReport() {} + virtual ~ReceiverReport() {} - int num_packets() { return num_packets_; } - uint32_t Ssrc() { return rr_.SenderSSRC; } + uint32_t Ssrc() const { return rr_.SenderSSRC; } private: friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketRR& rr) { rr_ = rr; ++num_packets_; } - int num_packets_; RTCPUtility::RTCPPacketRR rr_; }; -class ReportBlock { +class ReportBlock : public PacketType { public: - ReportBlock() : num_packets_(0) {} - ~ReportBlock() {} + ReportBlock() {} + virtual ~ReportBlock() {} - int num_packets() { return num_packets_; } - uint32_t Ssrc() { return rb_.SSRC; } - uint8_t FractionLost() { return rb_.FractionLost; } - uint32_t CumPacketLost() { return rb_.CumulativeNumOfPacketsLost; } - uint32_t ExtHighestSeqNum() { return rb_.ExtendedHighestSequenceNumber; } - uint32_t Jitter() { return rb_.Jitter; } - uint32_t LastSr() { return rb_.LastSR; } - uint32_t DelayLastSr() { return rb_.DelayLastSR; } + uint32_t Ssrc() const { return rb_.SSRC; } + uint8_t FractionLost() const { return rb_.FractionLost; } + uint32_t CumPacketLost() const { return rb_.CumulativeNumOfPacketsLost; } + uint32_t ExtHighestSeqNum() const { return rb_.ExtendedHighestSequenceNumber;} + uint32_t Jitter() const { return rb_.Jitter; } + uint32_t LastSr() const { return rb_.LastSR; } + uint32_t DelayLastSr()const { return rb_.DelayLastSR; } private: friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketReportBlockItem& rb) { rb_ = rb; ++num_packets_; } - int num_packets_; RTCPUtility::RTCPPacketReportBlockItem rb_; }; -class Bye { +class Bye : public PacketType { public: - Bye() : num_packets_(0) {} - ~Bye() {} + Bye() {} + virtual ~Bye() {} - int num_packets() { return num_packets_; } - uint32_t Ssrc() { return bye_.SenderSSRC; } + uint32_t Ssrc() const { return bye_.SenderSSRC; } private: friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketBYE& bye) { bye_ = bye; ++num_packets_; } - int num_packets_; RTCPUtility::RTCPPacketBYE bye_; }; -class Fir { +class Rpsi : public PacketType { public: - Fir() : num_packets_(0) {} - ~Fir() {} + Rpsi() {} + virtual ~Rpsi() {} - int num_packets() { return num_packets_; } - uint32_t Ssrc() { return fir_.SenderSSRC; } + uint32_t Ssrc() const { return rpsi_.SenderSSRC; } + uint32_t MediaSsrc() const { return rpsi_.MediaSSRC; } + uint8_t PayloadType() const { return rpsi_.PayloadType; } + uint16_t NumberOfValidBits() const { return rpsi_.NumberOfValidBits; } + uint64_t PictureId() const; private: friend class RtcpPacketParser; + + void Set(const RTCPUtility::RTCPPacketPSFBRPSI& rpsi) { + rpsi_ = rpsi; + ++num_packets_; + } + + RTCPUtility::RTCPPacketPSFBRPSI rpsi_; +}; + +class Fir : public PacketType { + public: + Fir() {} + virtual ~Fir() {} + + uint32_t Ssrc() const { return fir_.SenderSSRC; } + + private: + friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketPSFBFIR& fir) { fir_ = fir; ++num_packets_; } - int num_packets_; RTCPUtility::RTCPPacketPSFBFIR fir_; }; -class FirItem { +class FirItem : public PacketType { public: - FirItem() : num_packets_(0) {} - ~FirItem() {} + FirItem() {} + virtual ~FirItem() {} - int num_packets() { return num_packets_; } - uint32_t Ssrc() { return fir_item_.SSRC; } - uint8_t SeqNum() { return fir_item_.CommandSequenceNumber; } + uint32_t Ssrc() const { return fir_item_.SSRC; } + uint8_t SeqNum() const { return fir_item_.CommandSequenceNumber; } private: friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketPSFBFIRItem& fir_item) { fir_item_ = fir_item; ++num_packets_; } - int num_packets_; RTCPUtility::RTCPPacketPSFBFIRItem fir_item_; }; -class Nack { +class Nack : public PacketType { public: - Nack() : num_packets_(0) {} - ~Nack() {} + Nack() {} + virtual ~Nack() {} - int num_packets() { return num_packets_; } - uint32_t Ssrc() { return nack_.SenderSSRC; } - uint32_t MediaSsrc() { return nack_.MediaSSRC; } + uint32_t Ssrc() const { return nack_.SenderSSRC; } + uint32_t MediaSsrc() const { return nack_.MediaSSRC; } private: friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketRTPFBNACK& nack) { nack_ = nack; ++num_packets_; } - int num_packets_; RTCPUtility::RTCPPacketRTPFBNACK nack_; }; -class NackItem { +class NackItem : public PacketType { public: - NackItem() : num_packets_(0) {} - ~NackItem() {} + NackItem() {} + virtual ~NackItem() {} - int num_packets() { return num_packets_; } - std::vector last_nack_list() { - assert(num_packets_ > 0); + std::vector last_nack_list() const { return last_nack_list_; } private: friend class RtcpPacketParser; + void Set(const RTCPUtility::RTCPPacketRTPFBNACKItem& nack_item) { - last_nack_list_.clear(); last_nack_list_.push_back(nack_item.PacketID); for (int i = 0; i < 16; ++i) { if (nack_item.BitMask & (1 << i)) { @@ -192,12 +217,11 @@ class NackItem { } ++num_packets_; } + void Clear() { last_nack_list_.clear(); } - int num_packets_; std::vector last_nack_list_; }; - class RtcpPacketParser { public: RtcpPacketParser(); @@ -209,6 +233,7 @@ class RtcpPacketParser { ReceiverReport* receiver_report() { return &receiver_report_; } ReportBlock* report_block() { return &report_block_; } Bye* bye() { return &bye_; } + Rpsi* rpsi() { return &rpsi_; } Fir* fir() { return &fir_; } FirItem* fir_item() { return &fir_item_; } Nack* nack() { return &nack_; } @@ -223,6 +248,7 @@ class RtcpPacketParser { ReceiverReport receiver_report_; ReportBlock report_block_; Bye bye_; + Rpsi rpsi_; Fir fir_; FirItem fir_item_; Nack nack_;