From d8dccd57ea39ef7ce5f2fcb9b133be33fe1c6687 Mon Sep 17 00:00:00 2001 From: danilchap Date: Wed, 20 Jan 2016 12:08:51 -0800 Subject: [PATCH] uses standard types instead of RTCPUtility type to store data. got member read accessors, got Parse function. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit BUG=webrtc:5260 R=åsapersson Review URL: https://codereview.webrtc.org/1552773002 Cr-Commit-Position: refs/heads/master@{#11324} --- .../rtp_rtcp/source/rtcp_packet/remb.cc | 178 ++++++++++-------- .../rtp_rtcp/source/rtcp_packet/remb.h | 48 ++--- .../source/rtcp_packet/remb_unittest.cc | 121 ++++++++++-- 3 files changed, 227 insertions(+), 120 deletions(-) diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.cc index 59c809e154..3b33982a83 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.cc @@ -10,86 +10,88 @@ #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h" +#include "webrtc/base/checks.h" #include "webrtc/base/logging.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" -using webrtc::RTCPUtility::PT_PSFB; -using webrtc::RTCPUtility::RTCPPacketPSFBAPP; -using webrtc::RTCPUtility::RTCPPacketPSFBREMBItem; +using webrtc::RTCPUtility::RtcpCommonHeader; namespace webrtc { namespace rtcp { -namespace { -const uint32_t kUnusedMediaSourceSsrc0 = 0; - -void AssignUWord8(uint8_t* buffer, size_t* offset, uint8_t value) { - buffer[(*offset)++] = value; -} - -void AssignUWord32(uint8_t* buffer, size_t* offset, uint32_t value) { - ByteWriter::WriteBigEndian(buffer + *offset, value); - *offset += 4; -} - -void ComputeMantissaAnd6bitBase2Exponent(uint32_t input_base10, - uint8_t bits_mantissa, - uint32_t* mantissa, - uint8_t* exp) { - // input_base10 = mantissa * 2^exp - assert(bits_mantissa <= 32); - uint32_t mantissa_max = (1 << bits_mantissa) - 1; - uint8_t exponent = 0; - for (uint32_t i = 0; i < 64; ++i) { - if (input_base10 <= (mantissa_max << i)) { - exponent = i; - break; - } - } - *exp = exponent; - *mantissa = (input_base10 >> exponent); -} - // Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb). // -// 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| FMT=15 | PT=206 | length | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | SSRC of packet sender | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | SSRC of media source | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Unique identifier 'R' 'E' 'M' 'B' | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | Num SSRC | BR Exp | BR Mantissa | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | SSRC feedback | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | ... | -void CreateRemb(const RTCPPacketPSFBAPP& remb, - const RTCPPacketPSFBREMBItem& remb_item, - uint8_t* buffer, - size_t* pos) { - uint32_t mantissa = 0; - uint8_t exp = 0; - ComputeMantissaAnd6bitBase2Exponent(remb_item.BitRate, 18, &mantissa, &exp); +// 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| FMT=15 | PT=206 | length | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// 0 | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | Unused = 0 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 8 | Unique identifier 'R' 'E' 'M' 'B' | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 12 | Num SSRC | BR Exp | BR Mantissa | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 16 | SSRC feedback | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : ... : +bool Remb::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + RTC_DCHECK(header.count_or_format == kFeedbackMessageType); - AssignUWord32(buffer, pos, remb.SenderSSRC); - AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); - AssignUWord8(buffer, pos, 'R'); - AssignUWord8(buffer, pos, 'E'); - AssignUWord8(buffer, pos, 'M'); - AssignUWord8(buffer, pos, 'B'); - AssignUWord8(buffer, pos, remb_item.NumberOfSSRCs); - AssignUWord8(buffer, pos, (exp << 2) + ((mantissa >> 16) & 0x03)); - AssignUWord8(buffer, pos, mantissa >> 8); - AssignUWord8(buffer, pos, mantissa); - for (uint8_t i = 0; i < remb_item.NumberOfSSRCs; ++i) { - AssignUWord32(buffer, pos, remb_item.SSRCs[i]); + if (header.payload_size_bytes < 16) { + LOG(LS_WARNING) << "Payload length " << header.payload_size_bytes + << " is too small for Remb packet."; + return false; } + if (kUniqueIdentifier != ByteReader::ReadBigEndian(&payload[8])) { + LOG(LS_WARNING) << "REMB identifier not found, not a REMB packet."; + return false; + } + uint8_t number_of_ssrcs = payload[12]; + if (header.payload_size_bytes != + kCommonFeedbackLength + (2 + number_of_ssrcs) * 4) { + LOG(LS_WARNING) << "Payload size " << header.payload_size_bytes + << " does not match " << number_of_ssrcs << " ssrcs."; + return false; + } + + ParseCommonFeedback(payload); + uint8_t exponenta = payload[13] >> 2; + uint32_t mantissa = (static_cast(payload[13] & 0x03) << 16) | + ByteReader::ReadBigEndian(&payload[14]); + bitrate_bps_ = (mantissa << exponenta); + + const uint8_t* next_ssrc = payload + 16; + ssrcs_.clear(); + ssrcs_.reserve(number_of_ssrcs); + for (uint8_t i = 0; i < number_of_ssrcs; ++i) { + ssrcs_.push_back(ByteReader::ReadBigEndian(next_ssrc)); + next_ssrc += sizeof(uint32_t); + } + + return true; +} + +bool Remb::AppliesTo(uint32_t ssrc) { + if (ssrcs_.size() >= kMaxNumberOfSsrcs) { + LOG(LS_WARNING) << "Max number of REMB feedback SSRCs reached."; + return false; + } + ssrcs_.push_back(ssrc); + return true; +} + +bool Remb::AppliesToMany(const std::vector& ssrcs) { + if (ssrcs_.size() + ssrcs.size() > kMaxNumberOfSsrcs) { + LOG(LS_WARNING) << "Not enough space for all given SSRCs."; + return false; + } + // Append. + ssrcs_.insert(ssrcs_.end(), ssrcs.begin(), ssrcs.end()); + return true; } -} // namespace bool Remb::Create(uint8_t* packet, size_t* index, @@ -99,19 +101,33 @@ bool Remb::Create(uint8_t* packet, if (!OnBufferFull(packet, index, callback)) return false; } - const uint8_t kFmt = 15; - CreateHeader(kFmt, PT_PSFB, HeaderLength(), packet, index); - CreateRemb(remb_, remb_item_, packet, index); + size_t index_end = *index + BlockLength(); + CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet, + index); + RTC_DCHECK_EQ(0u, Psfb::media_ssrc()); + CreateCommonFeedback(packet + *index); + *index += kCommonFeedbackLength; + + ByteWriter::WriteBigEndian(packet + *index, kUniqueIdentifier); + *index += sizeof(uint32_t); + const uint32_t kMaxMantissa = 0x3ffff; // 18 bits. + uint32_t mantissa = bitrate_bps_; + uint8_t exponenta = 0; + while (mantissa > kMaxMantissa) { + mantissa >>= 1; + ++exponenta; + } + packet[(*index)++] = ssrcs_.size(); + packet[(*index)++] = (exponenta << 2) | (mantissa >> 16); + ByteWriter::WriteBigEndian(packet + *index, mantissa & 0xffff); + *index += sizeof(uint16_t); + + for (uint32_t ssrc : ssrcs_) { + ByteWriter::WriteBigEndian(packet + *index, ssrc); + *index += sizeof(uint32_t); + } + RTC_DCHECK_EQ(index_end, *index); return true; } - -void Remb::AppliesTo(uint32_t ssrc) { - if (remb_item_.NumberOfSSRCs >= kMaxNumberOfSsrcs) { - LOG(LS_WARNING) << "Max number of REMB feedback SSRCs reached."; - return; - } - remb_item_.SSRCs[remb_item_.NumberOfSSRCs++] = ssrc; -} - } // namespace rtcp } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h index 56dbb8b766..d58f052b14 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h @@ -12,30 +12,31 @@ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMB_H_ #include + #include "webrtc/base/basictypes.h" -#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" namespace webrtc { namespace rtcp { // Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb). -class Remb : public RtcpPacket { +class Remb : public Psfb { public: - Remb() : RtcpPacket() { - memset(&remb_, 0, sizeof(remb_)); - memset(&remb_item_, 0, sizeof(remb_item_)); - } + static const uint8_t kFeedbackMessageType = 15; - virtual ~Remb() {} + Remb() : bitrate_bps_(0) {} + ~Remb() override {} - void From(uint32_t ssrc) { - remb_.SenderSSRC = ssrc; - } - void AppliesTo(uint32_t ssrc); + // Parse assumes header is already parsed and validated. + bool Parse(const RTCPUtility::RtcpCommonHeader& header, + const uint8_t* payload); // Size of the payload is in the header. - void WithBitrateBps(uint32_t bitrate_bps) { - remb_item_.BitRate = bitrate_bps; - } + bool AppliesTo(uint32_t ssrc); + bool AppliesToMany(const std::vector& ssrcs); + void WithBitrateBps(uint32_t bitrate_bps) { bitrate_bps_ = bitrate_bps; } + + uint32_t bitrate_bps() const { return bitrate_bps_; } + const std::vector& ssrcs() const { return ssrcs_; } protected: bool Create(uint8_t* packet, @@ -43,15 +44,20 @@ class Remb : public RtcpPacket { size_t max_length, RtcpPacket::PacketReadyCallback* callback) const override; - private: - static const int kMaxNumberOfSsrcs = 0xff; - - size_t BlockLength() const { - return (remb_item_.NumberOfSSRCs + 5) * 4; + size_t BlockLength() const override { + return kHeaderLength + kCommonFeedbackLength + (2 + ssrcs_.size()) * 4; } - RTCPUtility::RTCPPacketPSFBAPP remb_; - RTCPUtility::RTCPPacketPSFBREMBItem remb_item_; + private: + static const size_t kMaxNumberOfSsrcs = 0xff; + static const uint32_t kUniqueIdentifier = 0x52454D42; // 'R' 'E' 'M' 'B'. + + // Media ssrc is unused, shadow base class setter and getter. + void To(uint32_t); + uint32_t media_ssrc() const; + + uint32_t bitrate_bps_; + std::vector ssrcs_; RTC_DISALLOW_COPY_AND_ASSIGN(Remb); }; diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb_unittest.cc index 487a076b1a..e18c208921 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/remb_unittest.cc @@ -12,35 +12,120 @@ #include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/test/rtcp_packet_parser.h" +using testing::ElementsAreArray; +using testing::IsEmpty; +using testing::make_tuple; using webrtc::rtcp::RawPacket; using webrtc::rtcp::Remb; -using webrtc::test::RtcpPacketParser; +using webrtc::RTCPUtility::RtcpCommonHeader; +using webrtc::RTCPUtility::RtcpParseCommonHeader; namespace webrtc { +namespace { const uint32_t kSenderSsrc = 0x12345678; -const uint32_t kRemoteSsrc = 0x23456789; +const uint32_t kRemoteSsrcs[] = {0x23456789, 0x2345678a, 0x2345678b}; +const uint32_t kBitrateBps = 0x3fb93 * 2; // 522022; +const uint8_t kPacket[] = {0x8f, 206, 0x00, 0x07, 0x12, 0x34, 0x56, 0x78, + 0x00, 0x00, 0x00, 0x00, 'R', 'E', 'M', 'B', + 0x03, 0x07, 0xfb, 0x93, 0x23, 0x45, 0x67, 0x89, + 0x23, 0x45, 0x67, 0x8a, 0x23, 0x45, 0x67, 0x8b}; +const size_t kPacketLength = sizeof(kPacket); -TEST(RtcpPacketRembTest, Remb) { +bool ParseRemb(const uint8_t* buffer, size_t length, Remb* remb) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(buffer, length, &header)); + EXPECT_EQ(length, header.BlockSize()); + return remb->Parse(header, buffer + RtcpCommonHeader::kHeaderSizeBytes); +} + +TEST(RtcpPacketRembTest, Create) { Remb remb; remb.From(kSenderSsrc); - remb.AppliesTo(kRemoteSsrc); - remb.AppliesTo(kRemoteSsrc + 1); - remb.AppliesTo(kRemoteSsrc + 2); - remb.WithBitrateBps(261011); + remb.AppliesTo(kRemoteSsrcs[0]); + remb.AppliesTo(kRemoteSsrcs[1]); + remb.AppliesTo(kRemoteSsrcs[2]); + remb.WithBitrateBps(kBitrateBps); rtc::scoped_ptr packet(remb.Build()); - RtcpPacketParser parser; - parser.Parse(packet->Buffer(), packet->Length()); - EXPECT_EQ(1, parser.psfb_app()->num_packets()); - EXPECT_EQ(kSenderSsrc, parser.psfb_app()->Ssrc()); - EXPECT_EQ(1, parser.remb_item()->num_packets()); - EXPECT_EQ(261011, parser.remb_item()->last_bitrate_bps()); - std::vector ssrcs = parser.remb_item()->last_ssrc_list(); - EXPECT_EQ(kRemoteSsrc, ssrcs[0]); - EXPECT_EQ(kRemoteSsrc + 1, ssrcs[1]); - EXPECT_EQ(kRemoteSsrc + 2, ssrcs[2]); + + EXPECT_THAT(make_tuple(packet->Buffer(), packet->Length()), + ElementsAreArray(kPacket)); } + +TEST(RtcpPacketRembTest, Parse) { + Remb remb; + EXPECT_TRUE(ParseRemb(kPacket, kPacketLength, &remb)); + const Remb& parsed = remb; + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(kBitrateBps, parsed.bitrate_bps()); + EXPECT_THAT(parsed.ssrcs(), ElementsAreArray(kRemoteSsrcs)); +} + +TEST(RtcpPacketRembTest, CreateAndParseWithoutSsrcs) { + Remb remb; + remb.From(kSenderSsrc); + remb.WithBitrateBps(kBitrateBps); + rtc::scoped_ptr packet(remb.Build()); + + Remb parsed; + EXPECT_TRUE(ParseRemb(packet->Buffer(), packet->Length(), &parsed)); + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(kBitrateBps, parsed.bitrate_bps()); + EXPECT_THAT(parsed.ssrcs(), IsEmpty()); +} + +TEST(RtcpPacketRembTest, ParseFailsOnTooSmallPacketToBeRemb) { + uint8_t packet[kPacketLength]; + memcpy(packet, kPacket, kPacketLength); + packet[3] = 3; // Make it too small. + + Remb remb; + EXPECT_FALSE(ParseRemb(packet, (1 + 3) * 4, &remb)); +} + +TEST(RtcpPacketRembTest, ParseFailsWhenUniqueIdentifierIsNotRemb) { + uint8_t packet[kPacketLength]; + memcpy(packet, kPacket, kPacketLength); + packet[12] = 'N'; // Swap 'R' -> 'N' in the 'REMB' unique identifier. + + Remb remb; + EXPECT_FALSE(ParseRemb(packet, kPacketLength, &remb)); +} + +TEST(RtcpPacketRembTest, ParseFailsWhenSsrcCountMismatchLength) { + uint8_t packet[kPacketLength]; + memcpy(packet, kPacket, kPacketLength); + packet[16]++; // Swap 3 -> 4 in the ssrcs count. + + Remb remb; + EXPECT_FALSE(ParseRemb(packet, kPacketLength, &remb)); +} + +TEST(RtcpPacketRembTest, TooManySsrcs) { + const size_t kMax = 0xff; + Remb remb; + for (size_t i = 1; i <= kMax; ++i) + EXPECT_TRUE(remb.AppliesTo(kRemoteSsrcs[0] + i)); + EXPECT_FALSE(remb.AppliesTo(kRemoteSsrcs[0])); +} + +TEST(RtcpPacketRembTest, TooManySsrcsForBatchAssign) { + const uint32_t kRemoteSsrc = kRemoteSsrcs[0]; + const size_t kMax = 0xff; + const std::vector kAllButOneSsrc(kMax - 1, kRemoteSsrc); + const std::vector kTwoSsrcs(2, kRemoteSsrc); + + Remb remb; + EXPECT_TRUE(remb.AppliesToMany(kAllButOneSsrc)); + // Should be no place for 2 more. + EXPECT_FALSE(remb.AppliesToMany(kTwoSsrcs)); + // But enough place for 1 more. + EXPECT_TRUE(remb.AppliesTo(kRemoteSsrc)); + // But not for another one. + EXPECT_FALSE(remb.AppliesTo(kRemoteSsrc)); +} +} // namespace } // namespace webrtc