diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc index 4df167de79..b19fa08b3b 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc @@ -10,96 +10,92 @@ #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.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::RTCPPacketRTPFBTMMBR; -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 Request (TMMBR) (RFC 5104). -// +// The Feedback Control Information (FCI) for the TMMBR +// consists of one or more FCI entries. // 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 -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | SSRC | -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ -// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| -// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | MxTBR Exp | MxTBR Mantissa |Measured Overhead| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +bool Tmmbr::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_CHECK(header.packet_type == kPacketType); + RTC_CHECK(header.count_or_format == kFeedbackMessageType); -void CreateTmmbr(const RTCPPacketRTPFBTMMBR& tmmbr, - const RTCPPacketRTPFBTMMBRItem& tmmbr_item, - uint8_t* buffer, - size_t* pos) { - AssignUWord32(buffer, pos, tmmbr.SenderSSRC); - AssignUWord32(buffer, pos, kUnusedMediaSourceSsrc0); - CreateTmmbrItem(tmmbr_item, buffer, pos); + if (header.payload_size_bytes < kCommonFeedbackLength + TmmbItem::kLength) { + LOG(LS_WARNING) << "Payload length " << header.payload_size_bytes + << " is too small for a TMMBR."; + return false; + } + 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 a TMMBR."; + 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 Tmmbr::WithTmmbr(const TmmbItem& item) { + items_.push_back(item); } -} // namespace bool Tmmbr::Create(uint8_t* packet, size_t* index, size_t max_length, RtcpPacket::PacketReadyCallback* callback) const { + RTC_DCHECK(!items_.empty()); while (*index + BlockLength() > max_length) { if (!OnBufferFull(packet, index, callback)) return false; } - const uint8_t kFmt = 3; - CreateHeader(kFmt, PT_RTPFB, HeaderLength(), packet, index); - CreateTmmbr(tmmbr_, tmmbr_item_, 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/tmmbr.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h index 84a4180ad3..4028563d07 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h @@ -6,41 +6,36 @@ * 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_TMMBR_H_ #define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBR_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 Request (TMMBR) (RFC 5104). -class Tmmbr : public RtcpPacket { +// Temporary Maximum Media Stream Bit Rate Request (TMMBR). +// RFC 5104, Section 4.2.1. +class Tmmbr : public Rtpfb { public: - Tmmbr() : RtcpPacket() { - memset(&tmmbr_, 0, sizeof(tmmbr_)); - memset(&tmmbr_item_, 0, sizeof(tmmbr_item_)); - } + static const uint8_t kFeedbackMessageType = 3; - virtual ~Tmmbr() {} + Tmmbr() {} + ~Tmmbr() override {} - void From(uint32_t ssrc) { - tmmbr_.SenderSSRC = ssrc; - } - void To(uint32_t ssrc) { - tmmbr_item_.SSRC = ssrc; - } - void WithBitrateKbps(uint32_t bitrate_kbps) { - tmmbr_item_.MaxTotalMediaBitRate = bitrate_kbps; - } - void WithOverhead(uint16_t overhead) { - assert(overhead <= 0x1ff); - tmmbr_item_.MeasuredOverhead = overhead; - } + // 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(const TmmbItem& item); + + const std::vector& requests() const { return items_; } protected: bool Create(uint8_t* packet, @@ -49,13 +44,16 @@ class Tmmbr : public RtcpPacket { RtcpPacket::PacketReadyCallback* callback) const override; private: - size_t BlockLength() const { - const size_t kFciLen = 8; - return kCommonFbFmtLength + kFciLen; + size_t BlockLength() const override { + return kHeaderLength + kCommonFeedbackLength + + TmmbItem::kLength * items_.size(); } - RTCPUtility::RTCPPacketRTPFBTMMBR tmmbr_; - RTCPUtility::RTCPPacketRTPFBTMMBRItem tmmbr_item_; + // 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(Tmmbr); }; diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc index 6d71caa251..9303f48e54 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc @@ -10,34 +10,96 @@ #include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.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::Tmmbr; -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[] = {0x83, 205, 0x00, 0x04, + 0x12, 0x34, 0x56, 0x78, + 0x00, 0x00, 0x00, 0x00, + 0x23, 0x45, 0x67, 0x89, + 0x0a, 0x61, 0x61, 0xfe}; -TEST(RtcpPacketTest, Tmmbr) { +bool ParseTmmbr(const uint8_t* buffer, size_t length, Tmmbr* tmmbr) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(buffer, length, &header)); + EXPECT_EQ(length, header.BlockSize()); + return tmmbr->Parse(header, buffer + RtcpCommonHeader::kHeaderSizeBytes); +} +} // namespace + +TEST(RtcpPacketTmmbrTest, Create) { Tmmbr tmmbr; tmmbr.From(kSenderSsrc); - tmmbr.To(kRemoteSsrc); - tmmbr.WithBitrateKbps(312); - tmmbr.WithOverhead(60); + tmmbr.WithTmmbr(TmmbItem(kRemoteSsrc, kBitrateBps, kOverhead)); - rtc::scoped_ptr packet(tmmbr.Build()); - RtcpPacketParser parser; - parser.Parse(packet->Buffer(), packet->Length()); - EXPECT_EQ(1, parser.tmmbr()->num_packets()); - EXPECT_EQ(kSenderSsrc, parser.tmmbr()->Ssrc()); - EXPECT_EQ(1, parser.tmmbr_item()->num_packets()); - EXPECT_EQ(312U, parser.tmmbr_item()->BitrateKbps()); - EXPECT_EQ(60U, parser.tmmbr_item()->Overhead()); + rtc::scoped_ptr packet = tmmbr.Build(); + + EXPECT_THAT(make_tuple(packet->Buffer(), packet->Length()), + ElementsAreArray(kPacket)); } +TEST(RtcpPacketTmmbrTest, Parse) { + Tmmbr tmmbr; + EXPECT_TRUE(ParseTmmbr(kPacket, sizeof(kPacket), &tmmbr)); + const Tmmbr& parsed = tmmbr; + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + ASSERT_EQ(1u, parsed.requests().size()); + EXPECT_EQ(kRemoteSsrc, parsed.requests().front().ssrc()); + EXPECT_EQ(kBitrateBps, parsed.requests().front().bitrate_bps()); + EXPECT_EQ(kOverhead, parsed.requests().front().packet_overhead()); +} + +TEST(RtcpPacketTmmbrTest, CreateAndParseWithTwoEntries) { + Tmmbr tmmbr; + tmmbr.From(kSenderSsrc); + tmmbr.WithTmmbr(TmmbItem(kRemoteSsrc, kBitrateBps, kOverhead)); + tmmbr.WithTmmbr(TmmbItem(kRemoteSsrc + 1, 4 * kBitrateBps, kOverhead + 1)); + + rtc::scoped_ptr packet = tmmbr.Build(); + + Tmmbr parsed; + EXPECT_TRUE(ParseTmmbr(packet->Buffer(), packet->Length(), &parsed)); + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(2u, parsed.requests().size()); + EXPECT_EQ(kRemoteSsrc, parsed.requests()[0].ssrc()); + EXPECT_EQ(kRemoteSsrc + 1, parsed.requests()[1].ssrc()); +} + +TEST(RtcpPacketTmmbrTest, ParseFailsWithoutItems) { + const uint8_t kZeroItemsPacket[] = {0x83, 205, 0x00, 0x02, + 0x12, 0x34, 0x56, 0x78, + 0x00, 0x00, 0x00, 0x00}; + + Tmmbr tmmbr; + EXPECT_FALSE(ParseTmmbr(kZeroItemsPacket, sizeof(kZeroItemsPacket), &tmmbr)); +} + +TEST(RtcpPacketTmmbrTest, ParseFailsOnUnAlignedPacket) { + const uint8_t kUnalignedPacket[] = {0x83, 205, 0x00, 0x05, + 0x12, 0x34, 0x56, 0x78, + 0x00, 0x00, 0x00, 0x00, + 0x23, 0x45, 0x67, 0x89, + 0x0a, 0x61, 0x61, 0xfe, + 0x34, 0x56, 0x78, 0x9a}; + + Tmmbr tmmbr; + EXPECT_FALSE(ParseTmmbr(kUnalignedPacket, sizeof(kUnalignedPacket), &tmmbr)); +} } // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc index e0c70c2b5c..4ea4dd8c46 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_receiver_unittest.cc @@ -869,8 +869,7 @@ TEST_F(RtcpReceiverTest, TmmbrPacketAccepted) { rtcp::Tmmbr tmmbr; tmmbr.From(kSenderSsrc); - tmmbr.To(kMediaFlowSsrc); - tmmbr.WithBitrateKbps(30); + tmmbr.WithTmmbr(rtcp::TmmbItem(kMediaFlowSsrc, 30000, 0)); rtcp::SenderReport sr; sr.From(kSenderSsrc); @@ -892,8 +891,8 @@ TEST_F(RtcpReceiverTest, TmmbrPacketNotForUsIgnored) { rtcp::Tmmbr tmmbr; tmmbr.From(kSenderSsrc); - tmmbr.To(kMediaFlowSsrc + 1); // This SSRC is not what we are sending. - tmmbr.WithBitrateKbps(30); + // This SSRC is not what we are sending. + tmmbr.WithTmmbr(rtcp::TmmbItem(kMediaFlowSsrc + 1, 30000, 0)); rtcp::SenderReport sr; sr.From(kSenderSsrc); @@ -916,8 +915,7 @@ TEST_F(RtcpReceiverTest, TmmbrPacketZeroRateIgnored) { rtcp::Tmmbr tmmbr; tmmbr.From(kSenderSsrc); - tmmbr.To(kMediaFlowSsrc); - tmmbr.WithBitrateKbps(0); + tmmbr.WithTmmbr(rtcp::TmmbItem(kMediaFlowSsrc, 0, 0)); rtcp::SenderReport sr; sr.From(kSenderSsrc); @@ -940,8 +938,7 @@ TEST_F(RtcpReceiverTest, TmmbrThreeConstraintsTimeOut) { for (uint32_t ssrc = kSenderSsrc; ssrc < kSenderSsrc + 3; ++ssrc) { rtcp::Tmmbr tmmbr; tmmbr.From(ssrc); - tmmbr.To(kMediaFlowSsrc); - tmmbr.WithBitrateKbps(30); + tmmbr.WithTmmbr(rtcp::TmmbItem(kMediaFlowSsrc, 30000, 0)); rtcp::SenderReport sr; sr.From(ssrc); diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc index b8ef58fd22..5b42fa1971 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc +++ b/webrtc/modules/rtp_rtcp/source/rtcp_sender.cc @@ -678,9 +678,11 @@ rtc::scoped_ptr RTCPSender::BuildTMMBR( rtcp::Tmmbr* tmmbr = new rtcp::Tmmbr(); tmmbr->From(ssrc_); - tmmbr->To(remote_ssrc_); - tmmbr->WithBitrateKbps(tmmbr_send_); - tmmbr->WithOverhead(packet_oh_send_); + rtcp::TmmbItem request; + request.set_ssrc(remote_ssrc_); + request.set_bitrate_bps(tmmbr_send_ * 1000); + request.set_packet_overhead(packet_oh_send_); + tmmbr->WithTmmbr(request); return rtc::scoped_ptr(tmmbr); }