uses standard types instead of RTCPUtility type to store data.

got member read accessors, got Parse function.

BUG=webrtc:5260
R=åsapersson

Review URL: https://codereview.webrtc.org/1552773002

Cr-Commit-Position: refs/heads/master@{#11324}
This commit is contained in:
danilchap 2016-01-20 12:08:51 -08:00 committed by Commit bot
parent 72c08edced
commit d8dccd57ea
3 changed files with 227 additions and 120 deletions

View File

@ -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<uint32_t>::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<uint32_t>::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<uint32_t>(payload[13] & 0x03) << 16) |
ByteReader<uint16_t>::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<uint32_t>::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<uint32_t>& 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<uint32_t>::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<uint16_t>::WriteBigEndian(packet + *index, mantissa & 0xffff);
*index += sizeof(uint16_t);
for (uint32_t ssrc : ssrcs_) {
ByteWriter<uint32_t>::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

View File

@ -12,30 +12,31 @@
#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMB_H_
#include <vector>
#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<uint32_t>& ssrcs);
void WithBitrateBps(uint32_t bitrate_bps) { bitrate_bps_ = bitrate_bps; }
uint32_t bitrate_bps() const { return bitrate_bps_; }
const std::vector<uint32_t>& 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<uint32_t> ssrcs_;
RTC_DISALLOW_COPY_AND_ASSIGN(Remb);
};

View File

@ -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<RawPacket> 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<uint32_t> 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<RawPacket> 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<uint32_t> kAllButOneSsrc(kMax - 1, kRemoteSsrc);
const std::vector<uint32_t> 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