diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc index fd0219cf82..ec3b428039 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc @@ -10,63 +10,31 @@ #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h" +#include "webrtc/base/checks.h" #include "webrtc/base/logging.h" #include "webrtc/modules/rtp_rtcp/source/byte_io.h" -using webrtc::RTCPUtility::PT_RTPFB; -using webrtc::RTCPUtility::RTCPPacketRTPFBTMMBN; -using webrtc::RTCPUtility::RTCPPacketRTPFBTMMBRItem; +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); -} - -void CreateTmmbrItem(const RTCPPacketRTPFBTMMBRItem& tmmbr_item, - uint8_t* buffer, - size_t* pos) { - uint32_t bitrate_bps = tmmbr_item.MaxTotalMediaBitRate * 1000; - uint32_t mantissa = 0; - uint8_t exp = 0; - ComputeMantissaAnd6bitBase2Exponent(bitrate_bps, 17, &mantissa, &exp); - - AssignUWord32(buffer, pos, tmmbr_item.SSRC); - AssignUWord8(buffer, pos, (exp << 2) + ((mantissa >> 15) & 0x03)); - AssignUWord8(buffer, pos, mantissa >> 7); - AssignUWord8(buffer, pos, (mantissa << 1) + - ((tmmbr_item.MeasuredOverhead >> 8) & 0x01)); - AssignUWord8(buffer, pos, tmmbr_item.MeasuredOverhead); -} - +// RFC 4585: Feedback format. +// Common packet 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| FMT | PT | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of media source (unused) = 0 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : : // Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104). -// -// FCI: -// +// The Feedback Control Information (FCI) consists of zero, one, or more +// TMMBN FCI entries. // 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 // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ @@ -74,33 +42,37 @@ void CreateTmmbrItem(const RTCPPacketRTPFBTMMBRItem& tmmbr_item, // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ // | MxTBR Exp | MxTBR Mantissa |Measured Overhead| // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +bool Tmmbn::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_CHECK(header.packet_type == kPacketType); + RTC_CHECK(header.count_or_format == kFeedbackMessageType); -void CreateTmmbn(const RTCPPacketRTPFBTMMBN& tmmbn, - const std::vector& tmmbn_items, - uint8_t* buffer, - size_t* pos) { - AssignUWord32(buffer, pos, tmmbn.SenderSSRC); - AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); - for (uint8_t i = 0; i < tmmbn_items.size(); ++i) { - CreateTmmbrItem(tmmbn_items[i], buffer, pos); - } -} -} // namespace - -bool Tmmbn::WithTmmbr(uint32_t ssrc, uint32_t bitrate_kbps, uint16_t overhead) { - assert(overhead <= 0x1ff); - if (tmmbn_items_.size() >= kMaxNumberOfTmmbrs) { - LOG(LS_WARNING) << "Max TMMBN size reached."; + if (header.payload_size_bytes < kCommonFeedbackLength) { + LOG(LS_WARNING) << "Payload length " << header.payload_size_bytes + << " is too small for TMMBN."; return false; } - RTCPPacketRTPFBTMMBRItem tmmbn_item; - tmmbn_item.SSRC = ssrc; - tmmbn_item.MaxTotalMediaBitRate = bitrate_kbps; - tmmbn_item.MeasuredOverhead = overhead; - tmmbn_items_.push_back(tmmbn_item); + size_t items_size_bytes = header.payload_size_bytes - kCommonFeedbackLength; + if (items_size_bytes % TmmbItem::kLength != 0) { + LOG(LS_WARNING) << "Payload length " << header.payload_size_bytes + << " is not valid for TMMBN."; + return false; + } + ParseCommonFeedback(payload); + const uint8_t* next_item = payload + kCommonFeedbackLength; + + size_t number_of_items = items_size_bytes / TmmbItem::kLength; + items_.resize(number_of_items); + for (TmmbItem& item : items_) { + item.Parse(next_item); + next_item += TmmbItem::kLength; + } return true; } +void Tmmbn::WithTmmbr(const TmmbItem& item) { + items_.push_back(item); +} + bool Tmmbn::Create(uint8_t* packet, size_t* index, size_t max_length, @@ -109,11 +81,19 @@ bool Tmmbn::Create(uint8_t* packet, if (!OnBufferFull(packet, index, callback)) return false; } - const uint8_t kFmt = 4; - CreateHeader(kFmt, PT_RTPFB, HeaderLength(), packet, index); - CreateTmmbn(tmmbn_, tmmbn_items_, packet, index); + const size_t index_end = *index + BlockLength(); + + CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet, + index); + RTC_DCHECK_EQ(0u, Rtpfb::media_ssrc()); + CreateCommonFeedback(packet + *index); + *index += kCommonFeedbackLength; + for (const TmmbItem& item : items_) { + item.Create(packet + *index); + *index += TmmbItem::kLength; + } + RTC_CHECK_EQ(index_end, *index); return true; } - } // namespace rtcp } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h index 82bf9dd9e9..26a44082d3 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h @@ -6,34 +6,39 @@ * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. - * */ #ifndef WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_ #include + #include "webrtc/base/basictypes.h" -#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" namespace webrtc { namespace rtcp { - -// Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104). -class Tmmbn : public RtcpPacket { +// Temporary Maximum Media Stream Bit Rate Notification (TMMBN). +// RFC 5104, Section 4.2.2. +class Tmmbn : public Rtpfb { public: - Tmmbn() : RtcpPacket() { - memset(&tmmbn_, 0, sizeof(tmmbn_)); - } + static const uint8_t kFeedbackMessageType = 4; - virtual ~Tmmbn() {} + Tmmbn() {} + ~Tmmbn() override {} - void From(uint32_t ssrc) { - tmmbn_.SenderSSRC = 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 WithTmmbr(uint32_t ssrc, uint32_t bitrate_kbps, uint16_t overhead) { + WithTmmbr(TmmbItem(ssrc, bitrate_kbps * 1000, overhead)); } - // Max 50 TMMBR can be added per TMMBN. - bool WithTmmbr(uint32_t ssrc, uint32_t bitrate_kbps, uint16_t overhead); + void WithTmmbr(const TmmbItem& item); + + const std::vector& items() const { return items_; } protected: bool Create(uint8_t* packet, @@ -42,19 +47,19 @@ class Tmmbn : public RtcpPacket { RtcpPacket::PacketReadyCallback* callback) const override; private: - static const int kMaxNumberOfTmmbrs = 50; - - size_t BlockLength() const { - const size_t kFciLen = 8; - return kCommonFbFmtLength + kFciLen * tmmbn_items_.size(); + size_t BlockLength() const override { + return kHeaderLength + kCommonFeedbackLength + + TmmbItem::kLength * items_.size(); } - RTCPUtility::RTCPPacketRTPFBTMMBN tmmbn_; - std::vector tmmbn_items_; + // Media ssrc is unused, shadow base class setter and getter. + void To(uint32_t ssrc); + uint32_t media_ssrc() const; + + std::vector items_; RTC_DISALLOW_COPY_AND_ASSIGN(Tmmbn); }; - } // namespace rtcp } // namespace webrtc #endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.cc index 32d64a97b4..d65af46fd7 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.cc @@ -10,75 +10,108 @@ #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h" +#include "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" -#include "webrtc/modules/rtp_rtcp/source/byte_io.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::TmmbItem; using webrtc::rtcp::Tmmbn; -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 kBitrateBps = 312000; +const uint16_t kOverhead = 0x1fe; +const uint8_t kPacket[] = {0x84, 205, 0x00, 0x04, + 0x12, 0x34, 0x56, 0x78, + 0x00, 0x00, 0x00, 0x00, + 0x23, 0x45, 0x67, 0x89, + 0x0a, 0x61, 0x61, 0xfe}; -TEST(RtcpPacketTest, TmmbnWithNoItem) { +bool ParseTmmbn(const uint8_t* buffer, size_t length, Tmmbn* tmmbn) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(buffer, length, &header)); + EXPECT_EQ(length, header.BlockSize()); + return tmmbn->Parse(header, buffer + RtcpCommonHeader::kHeaderSizeBytes); +} +} // namespace + +TEST(RtcpPacketTmmbnTest, Create) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + tmmbn.WithTmmbr(TmmbItem(kRemoteSsrc, kBitrateBps, kOverhead)); + + rtc::scoped_ptr packet = tmmbn.Build(); + + EXPECT_THAT(make_tuple(packet->Buffer(), packet->Length()), + ElementsAreArray(kPacket)); +} + +TEST(RtcpPacketTmmbnTest, Parse) { + Tmmbn tmmbn; + EXPECT_TRUE(ParseTmmbn(kPacket, sizeof(kPacket), &tmmbn)); + + const Tmmbn& parsed = tmmbn; + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + ASSERT_EQ(1u, parsed.items().size()); + EXPECT_EQ(kRemoteSsrc, parsed.items().front().ssrc()); + EXPECT_EQ(kBitrateBps, parsed.items().front().bitrate_bps()); + EXPECT_EQ(kOverhead, parsed.items().front().packet_overhead()); +} + +TEST(RtcpPacketTmmbnTest, CreateAndParseWithoutItems) { Tmmbn tmmbn; tmmbn.From(kSenderSsrc); - rtc::scoped_ptr packet(tmmbn.Build()); - RtcpPacketParser parser; - parser.Parse(packet->Buffer(), packet->Length()); - EXPECT_EQ(1, parser.tmmbn()->num_packets()); - EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); - EXPECT_EQ(0, parser.tmmbn_items()->num_packets()); + rtc::scoped_ptr packet = tmmbn.Build(); + Tmmbn parsed; + EXPECT_TRUE(ParseTmmbn(packet->Buffer(), packet->Length(), &parsed)); + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_THAT(parsed.items(), IsEmpty()); } -TEST(RtcpPacketTest, TmmbnWithOneItem) { +TEST(RtcpPacketTmmbnTest, CreateAndParseWithTwoItems) { Tmmbn tmmbn; tmmbn.From(kSenderSsrc); - EXPECT_TRUE(tmmbn.WithTmmbr(kRemoteSsrc, 312, 60)); + tmmbn.WithTmmbr(TmmbItem(kRemoteSsrc, kBitrateBps, kOverhead)); + tmmbn.WithTmmbr(TmmbItem(kRemoteSsrc + 1, 4 * kBitrateBps, 40)); - rtc::scoped_ptr packet(tmmbn.Build()); - RtcpPacketParser parser; - parser.Parse(packet->Buffer(), packet->Length()); - EXPECT_EQ(1, parser.tmmbn()->num_packets()); - EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); - EXPECT_EQ(1, parser.tmmbn_items()->num_packets()); - EXPECT_EQ(kRemoteSsrc, parser.tmmbn_items()->Ssrc(0)); - EXPECT_EQ(312U, parser.tmmbn_items()->BitrateKbps(0)); - EXPECT_EQ(60U, parser.tmmbn_items()->Overhead(0)); + rtc::scoped_ptr packet = tmmbn.Build(); + Tmmbn parsed; + EXPECT_TRUE(ParseTmmbn(packet->Buffer(), packet->Length(), &parsed)); + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(2u, parsed.items().size()); + EXPECT_EQ(kRemoteSsrc, parsed.items()[0].ssrc()); + EXPECT_EQ(kBitrateBps, parsed.items()[0].bitrate_bps()); + EXPECT_EQ(kOverhead, parsed.items()[0].packet_overhead()); + EXPECT_EQ(kRemoteSsrc + 1, parsed.items()[1].ssrc()); + EXPECT_EQ(4 * kBitrateBps, parsed.items()[1].bitrate_bps()); + EXPECT_EQ(40U, parsed.items()[1].packet_overhead()); } -TEST(RtcpPacketTest, TmmbnWithTwoItems) { +TEST(RtcpPacketTmmbnTest, ParseFailsOnTooSmallPacket) { + const uint8_t kSmallPacket[] = {0x84, 205, 0x00, 0x01, + 0x12, 0x34, 0x56, 0x78}; Tmmbn tmmbn; - tmmbn.From(kSenderSsrc); - EXPECT_TRUE(tmmbn.WithTmmbr(kRemoteSsrc, 312, 60)); - EXPECT_TRUE(tmmbn.WithTmmbr(kRemoteSsrc + 1, 1288, 40)); - - rtc::scoped_ptr packet(tmmbn.Build()); - RtcpPacketParser parser; - parser.Parse(packet->Buffer(), packet->Length()); - EXPECT_EQ(1, parser.tmmbn()->num_packets()); - EXPECT_EQ(kSenderSsrc, parser.tmmbn()->Ssrc()); - EXPECT_EQ(2, parser.tmmbn_items()->num_packets()); - EXPECT_EQ(kRemoteSsrc, parser.tmmbn_items()->Ssrc(0)); - EXPECT_EQ(312U, parser.tmmbn_items()->BitrateKbps(0)); - EXPECT_EQ(60U, parser.tmmbn_items()->Overhead(0)); - EXPECT_EQ(kRemoteSsrc + 1, parser.tmmbn_items()->Ssrc(1)); - EXPECT_EQ(1288U, parser.tmmbn_items()->BitrateKbps(1)); - EXPECT_EQ(40U, parser.tmmbn_items()->Overhead(1)); + EXPECT_FALSE(ParseTmmbn(kSmallPacket, sizeof(kSmallPacket), &tmmbn)); } -TEST(RtcpPacketTest, TmmbnWithTooManyItems) { +TEST(RtcpPacketTmmbnTest, ParseFailsOnUnAlignedPacket) { + const uint8_t kUnalignedPacket[] = {0x84, 205, 0x00, 0x03, + 0x12, 0x34, 0x56, 0x78, + 0x00, 0x00, 0x00, 0x00, + 0x23, 0x45, 0x67, 0x89}; + Tmmbn tmmbn; - tmmbn.From(kSenderSsrc); - const int kMaxTmmbrItems = 50; - for (int i = 0; i < kMaxTmmbrItems; ++i) - EXPECT_TRUE(tmmbn.WithTmmbr(kRemoteSsrc + i, 312, 60)); - - EXPECT_FALSE(tmmbn.WithTmmbr(kRemoteSsrc + kMaxTmmbrItems, 312, 60)); + EXPECT_FALSE(ParseTmmbn(kUnalignedPacket, sizeof(kUnalignedPacket), &tmmbn)); } - } // namespace webrtc