diff options
Diffstat (limited to 'webrtc/modules/rtp_rtcp/source/rtcp_packet')
47 files changed, 3829 insertions, 1 deletions
diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/app.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/app.cc new file mode 100644 index 0000000000..a1ad8d6427 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/app.cc @@ -0,0 +1,79 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/app.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +using webrtc::RTCPUtility::RtcpCommonHeader; + +namespace webrtc { +namespace rtcp { + +// Application-Defined packet (APP) (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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| subtype | PT=APP=204 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 0 | SSRC/CSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | name (ASCII) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 8 | application-dependent data ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +bool App::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + + sub_type_ = header.count_or_format; + ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&payload[0]); + name_ = ByteReader<uint32_t>::ReadBigEndian(&payload[4]); + data_.SetData(&payload[8], header.payload_size_bytes - 8); + return true; +} + +void App::WithSubType(uint8_t subtype) { + RTC_DCHECK_LE(subtype, 0x1f); + sub_type_ = subtype; +} + +void App::WithData(const uint8_t* data, size_t data_length) { + RTC_DCHECK(data); + RTC_DCHECK_EQ(0u, data_length % 4) << "Data must be 32 bits aligned."; + RTC_DCHECK(data_length <= kMaxDataSize) << "App data size << " << data_length + << "exceed maximum of " + << kMaxDataSize << " bytes."; + data_.SetData(data, data_length); +} + +bool App::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + while (*index + BlockLength() > max_length) { + if (!OnBufferFull(packet, index, callback)) + return false; + } + const size_t index_end = *index + BlockLength(); + CreateHeader(sub_type_, kPacketType, HeaderLength(), packet, index); + + ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 0], ssrc_); + ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 4], name_); + memcpy(&packet[*index + 8], data_.data(), data_.size()); + *index += (8 + data_.size()); + RTC_DCHECK_EQ(index_end, *index); + return true; +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/app.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/app.h new file mode 100644 index 0000000000..16bd3fc2a2 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/app.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_APP_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_APP_H_ + +#include "webrtc/base/buffer.h" +#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +namespace webrtc { +namespace rtcp { + +class App : public RtcpPacket { + public: + static const uint8_t kPacketType = 204; + // 28 bytes for UDP header + // 12 bytes for RTCP app header + static const size_t kMaxDataSize = IP_PACKET_SIZE - 12 - 28; + App() : sub_type_(0), ssrc_(0), name_(0) {} + + virtual ~App() {} + + // 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 From(uint32_t ssrc) { ssrc_ = ssrc; } + void WithSubType(uint8_t subtype); + void WithName(uint32_t name) { name_ = name; } + void WithData(const uint8_t* data, size_t data_length); + + uint8_t sub_type() const { return sub_type_; } + uint32_t ssrc() const { return ssrc_; } + uint32_t name() const { return name_; } + size_t data_size() const { return data_.size(); } + const uint8_t* data() const { return data_.data(); } + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + private: + size_t BlockLength() const override { return 12 + data_.size(); } + + uint8_t sub_type_; + uint32_t ssrc_; + uint32_t name_; + rtc::Buffer data_; + + RTC_DISALLOW_COPY_AND_ASSIGN(App); +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_APP_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/app_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/app_unittest.cc new file mode 100644 index 0000000000..4451fe8fb5 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/app_unittest.cc @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/app.h" + +#include <limits> + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +using webrtc::rtcp::App; +using webrtc::rtcp::RawPacket; +using webrtc::RTCPUtility::RtcpCommonHeader; +using webrtc::RTCPUtility::RtcpParseCommonHeader; + +namespace webrtc { +namespace { + +const uint32_t kName = ((uint32_t)'n' << 24) | ((uint32_t)'a' << 16) | + ((uint32_t)'m' << 8) | (uint32_t)'e'; +const uint32_t kSenderSsrc = 0x12345678; + +class RtcpPacketAppTest : public ::testing::Test { + protected: + void BuildPacket() { packet = app.Build(); } + void ParsePacket() { + RtcpCommonHeader header; + EXPECT_TRUE( + RtcpParseCommonHeader(packet->Buffer(), packet->Length(), &header)); + // Check there is exactly one RTCP packet in the buffer. + EXPECT_EQ(header.BlockSize(), packet->Length()); + EXPECT_TRUE(parsed_.Parse( + header, packet->Buffer() + RtcpCommonHeader::kHeaderSizeBytes)); + } + + App app; + rtc::scoped_ptr<RawPacket> packet; + const App& parsed() { return parsed_; } + + private: + App parsed_; +}; + +TEST_F(RtcpPacketAppTest, WithNoData) { + app.WithSubType(30); + app.WithName(kName); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(30U, parsed().sub_type()); + EXPECT_EQ(kName, parsed().name()); + EXPECT_EQ(0u, parsed().data_size()); +} + +TEST_F(RtcpPacketAppTest, WithData) { + app.From(kSenderSsrc); + app.WithSubType(30); + app.WithName(kName); + const uint8_t kData[] = {'t', 'e', 's', 't', 'd', 'a', 't', 'a'}; + const size_t kDataLength = sizeof(kData) / sizeof(kData[0]); + app.WithData(kData, kDataLength); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(30U, parsed().sub_type()); + EXPECT_EQ(kName, parsed().name()); + EXPECT_EQ(kDataLength, parsed().data_size()); + EXPECT_EQ(0, memcmp(kData, parsed().data(), kDataLength)); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.cc new file mode 100644 index 0000000000..4cfc921ce5 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.cc @@ -0,0 +1,133 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +using webrtc::RTCPUtility::RtcpCommonHeader; + +namespace webrtc { +namespace rtcp { + +// Bye packet (BYE) (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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| SC | PT=BYE=203 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC/CSRC | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : ... : +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// (opt) | length | reason for leaving ... +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +Bye::Bye() : sender_ssrc_(0) {} + +bool Bye::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + + const uint8_t src_count = header.count_or_format; + // Validate packet. + if (header.payload_size_bytes < 4u * src_count) { + LOG(LS_WARNING) + << "Packet is too small to contain CSRCs it promise to have."; + return false; + } + bool has_reason = (header.payload_size_bytes > 4u * src_count); + uint8_t reason_length = 0; + if (has_reason) { + reason_length = payload[4u * src_count]; + if (header.payload_size_bytes - 4u * src_count < 1u + reason_length) { + LOG(LS_WARNING) << "Invalid reason length: " << reason_length; + return false; + } + } + // Once sure packet is valid, copy values. + if (src_count == 0) { // A count value of zero is valid, but useless. + sender_ssrc_ = 0; + csrcs_.clear(); + } else { + sender_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(payload); + csrcs_.resize(src_count - 1); + for (size_t i = 1; i < src_count; ++i) + csrcs_[i - 1] = ByteReader<uint32_t>::ReadBigEndian(&payload[4 * i]); + } + + if (has_reason) { + reason_.assign(reinterpret_cast<const char*>(&payload[4u * src_count + 1]), + reason_length); + } else { + reason_.clear(); + } + + return true; +} + +bool Bye::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + while (*index + BlockLength() > max_length) { + if (!OnBufferFull(packet, index, callback)) + return false; + } + const size_t index_end = *index + BlockLength(); + + CreateHeader(1 + csrcs_.size(), kPacketType, HeaderLength(), packet, index); + // Store srcs of the leaving clients. + ByteWriter<uint32_t>::WriteBigEndian(&packet[*index], sender_ssrc_); + *index += sizeof(uint32_t); + for (uint32_t csrc : csrcs_) { + ByteWriter<uint32_t>::WriteBigEndian(&packet[*index], csrc); + *index += sizeof(uint32_t); + } + // Store the reason to leave. + if (!reason_.empty()) { + uint8_t reason_length = reason_.size(); + packet[(*index)++] = reason_length; + memcpy(&packet[*index], reason_.data(), reason_length); + *index += reason_length; + // Add padding bytes if needed. + size_t bytes_to_pad = index_end - *index; + RTC_DCHECK_LE(bytes_to_pad, 3u); + if (bytes_to_pad > 0) { + memset(&packet[*index], 0, bytes_to_pad); + *index += bytes_to_pad; + } + } + RTC_DCHECK_EQ(index_end, *index); + return true; +} + +bool Bye::WithCsrc(uint32_t csrc) { + if (csrcs_.size() >= kMaxNumberOfCsrcs) { + LOG(LS_WARNING) << "Max CSRC size reached."; + return false; + } + csrcs_.push_back(csrc); + return true; +} + +void Bye::WithReason(const std::string& reason) { + RTC_DCHECK_LE(reason.size(), 0xffu); + reason_ = reason; +} + +size_t Bye::BlockLength() const { + size_t src_count = (1 + csrcs_.size()); + size_t reason_size_in_32bits = reason_.empty() ? 0 : (reason_.size() / 4 + 1); + return kHeaderLength + 4 * (src_count + reason_size_in_32bits); +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h new file mode 100644 index 0000000000..6b4a181330 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_BYE_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_BYE_H_ + +#include <string> +#include <vector> + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +namespace webrtc { +namespace rtcp { + +class Bye : public RtcpPacket { + public: + static const uint8_t kPacketType = 203; + + Bye(); + virtual ~Bye() {} + + // 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 From(uint32_t ssrc) { sender_ssrc_ = ssrc; } + bool WithCsrc(uint32_t csrc); + void WithReason(const std::string& reason); + + uint32_t sender_ssrc() const { return sender_ssrc_; } + const std::vector<uint32_t>& csrcs() const { return csrcs_; } + const std::string& reason() const { return reason_; } + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + private: + static const int kMaxNumberOfCsrcs = 0x1f - 1; // First item is sender SSRC. + + size_t BlockLength() const override; + + uint32_t sender_ssrc_; + std::vector<uint32_t> csrcs_; + std::string reason_; + + RTC_DISALLOW_COPY_AND_ASSIGN(Bye); +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_BYE_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye_unittest.cc new file mode 100644 index 0000000000..d2ae8ed782 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/bye_unittest.cc @@ -0,0 +1,173 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +using ::testing::ElementsAre; + +using webrtc::rtcp::Bye; +using webrtc::rtcp::RawPacket; +using webrtc::RTCPUtility::RtcpCommonHeader; +using webrtc::RTCPUtility::RtcpParseCommonHeader; + +namespace webrtc { +namespace { + +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kCsrc1 = 0x22232425; +const uint32_t kCsrc2 = 0x33343536; + +class RtcpPacketByeTest : public ::testing::Test { + protected: + void BuildPacket() { packet = bye.Build(); } + void ParsePacket() { + RtcpCommonHeader header; + EXPECT_TRUE( + RtcpParseCommonHeader(packet->Buffer(), packet->Length(), &header)); + // Check that there is exactly one RTCP packet in the buffer. + EXPECT_EQ(header.BlockSize(), packet->Length()); + EXPECT_TRUE(parsed_bye.Parse( + header, packet->Buffer() + RtcpCommonHeader::kHeaderSizeBytes)); + } + + Bye bye; + rtc::scoped_ptr<RawPacket> packet; + Bye parsed_bye; +}; + +TEST_F(RtcpPacketByeTest, Bye) { + bye.From(kSenderSsrc); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(kSenderSsrc, parsed_bye.sender_ssrc()); + EXPECT_TRUE(parsed_bye.csrcs().empty()); + EXPECT_TRUE(parsed_bye.reason().empty()); +} + +TEST_F(RtcpPacketByeTest, WithCsrcs) { + bye.From(kSenderSsrc); + EXPECT_TRUE(bye.WithCsrc(kCsrc1)); + EXPECT_TRUE(bye.WithCsrc(kCsrc2)); + EXPECT_TRUE(bye.reason().empty()); + + BuildPacket(); + EXPECT_EQ(16u, packet->Length()); // Header: 4, 3xSRCs: 12, Reason: 0. + + ParsePacket(); + + EXPECT_EQ(kSenderSsrc, parsed_bye.sender_ssrc()); + EXPECT_THAT(parsed_bye.csrcs(), ElementsAre(kCsrc1, kCsrc2)); + EXPECT_TRUE(parsed_bye.reason().empty()); +} + +TEST_F(RtcpPacketByeTest, WithCsrcsAndReason) { + const std::string kReason = "Some Reason"; + + bye.From(kSenderSsrc); + EXPECT_TRUE(bye.WithCsrc(kCsrc1)); + EXPECT_TRUE(bye.WithCsrc(kCsrc2)); + bye.WithReason(kReason); + + BuildPacket(); + EXPECT_EQ(28u, packet->Length()); // Header: 4, 3xSRCs: 12, Reason: 12. + + ParsePacket(); + + EXPECT_EQ(kSenderSsrc, parsed_bye.sender_ssrc()); + EXPECT_THAT(parsed_bye.csrcs(), ElementsAre(kCsrc1, kCsrc2)); + EXPECT_EQ(kReason, parsed_bye.reason()); +} + +TEST_F(RtcpPacketByeTest, WithTooManyCsrcs) { + bye.From(kSenderSsrc); + const int kMaxCsrcs = (1 << 5) - 2; // 5 bit len, first item is sender SSRC. + for (int i = 0; i < kMaxCsrcs; ++i) { + EXPECT_TRUE(bye.WithCsrc(i)); + } + EXPECT_FALSE(bye.WithCsrc(kMaxCsrcs)); +} + +TEST_F(RtcpPacketByeTest, WithAReason) { + const std::string kReason = "Some Random Reason"; + + bye.From(kSenderSsrc); + bye.WithReason(kReason); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(kSenderSsrc, parsed_bye.sender_ssrc()); + EXPECT_TRUE(parsed_bye.csrcs().empty()); + EXPECT_EQ(kReason, parsed_bye.reason()); +} + +TEST_F(RtcpPacketByeTest, WithReasons) { + // Test that packet creation/parsing behave with reasons of different length + // both when it require padding and when it does not. + for (size_t reminder = 0; reminder < 4; ++reminder) { + const std::string kReason(4 + reminder, 'a' + reminder); + bye.From(kSenderSsrc); + bye.WithReason(kReason); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(kReason, parsed_bye.reason()); + } +} + +TEST_F(RtcpPacketByeTest, ParseEmptyPacket) { + RtcpCommonHeader header; + header.packet_type = Bye::kPacketType; + header.count_or_format = 0; + header.payload_size_bytes = 0; + uint8_t empty_payload[1]; + + EXPECT_TRUE(parsed_bye.Parse(header, empty_payload + 1)); + EXPECT_EQ(0u, parsed_bye.sender_ssrc()); + EXPECT_TRUE(parsed_bye.csrcs().empty()); + EXPECT_TRUE(parsed_bye.reason().empty()); +} + +TEST_F(RtcpPacketByeTest, ParseFailOnInvalidSrcCount) { + bye.From(kSenderSsrc); + + BuildPacket(); + + RtcpCommonHeader header; + RtcpParseCommonHeader(packet->Buffer(), packet->Length(), &header); + header.count_or_format = 2; // Lie there are 2 ssrcs, not one. + + EXPECT_FALSE(parsed_bye.Parse( + header, packet->Buffer() + RtcpCommonHeader::kHeaderSizeBytes)); +} + +TEST_F(RtcpPacketByeTest, ParseFailOnInvalidReasonLength) { + bye.From(kSenderSsrc); + bye.WithReason("18 characters long"); + + BuildPacket(); + + RtcpCommonHeader header; + RtcpParseCommonHeader(packet->Buffer(), packet->Length(), &header); + header.payload_size_bytes -= 4; // Payload is usually 32bit aligned. + + EXPECT_FALSE(parsed_bye.Parse( + header, packet->Buffer() + RtcpCommonHeader::kHeaderSizeBytes)); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.cc new file mode 100644 index 0000000000..8f5afd5dd1 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.cc @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h" + +namespace webrtc { +namespace rtcp { + +bool CompoundPacket::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + return true; +} + +size_t CompoundPacket::BlockLength() const { + return 0; +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h new file mode 100644 index 0000000000..f2f49a8ffb --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h @@ -0,0 +1,41 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_COMPOUND_PACKET_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMPOUND_PACKET_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" + +namespace webrtc { +namespace rtcp { + +class CompoundPacket : public RtcpPacket { + public: + CompoundPacket() : RtcpPacket() {} + + virtual ~CompoundPacket() {} + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + size_t BlockLength() const override; + + private: + RTC_DISALLOW_COPY_AND_ASSIGN(CompoundPacket); +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMPOUND_PACKET_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc new file mode 100644 index 0000000000..83dc5f6ed3 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" +#include "webrtc/test/rtcp_packet_parser.h" + +using webrtc::rtcp::Bye; +using webrtc::rtcp::CompoundPacket; +using webrtc::rtcp::Fir; +using webrtc::rtcp::RawPacket; +using webrtc::rtcp::ReceiverReport; +using webrtc::rtcp::ReportBlock; +using webrtc::rtcp::SenderReport; +using webrtc::test::RtcpPacketParser; + +namespace webrtc { + +const uint32_t kSenderSsrc = 0x12345678; + +TEST(RtcpCompoundPacketTest, AppendPacket) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + EXPECT_TRUE(rr.WithReportBlock(rb)); + rr.Append(&fir); + + rtc::scoped_ptr<RawPacket> packet(rr.Build()); + RtcpPacketParser parser; + parser.Parse(packet->Buffer(), packet->Length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(kSenderSsrc, parser.receiver_report()->Ssrc()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpCompoundPacketTest, AppendPacketOnEmpty) { + CompoundPacket empty; + ReceiverReport rr; + rr.From(kSenderSsrc); + empty.Append(&rr); + + rtc::scoped_ptr<RawPacket> packet(empty.Build()); + RtcpPacketParser parser; + parser.Parse(packet->Buffer(), packet->Length()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(0, parser.report_block()->num_packets()); +} + +TEST(RtcpCompoundPacketTest, AppendPacketWithOwnAppendedPacket) { + Fir fir; + Bye bye; + ReportBlock rb; + + ReceiverReport rr; + EXPECT_TRUE(rr.WithReportBlock(rb)); + rr.Append(&fir); + + SenderReport sr; + sr.Append(&bye); + sr.Append(&rr); + + rtc::scoped_ptr<RawPacket> packet(sr.Build()); + RtcpPacketParser parser; + parser.Parse(packet->Buffer(), packet->Length()); + EXPECT_EQ(1, parser.sender_report()->num_packets()); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.bye()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); +} + +TEST(RtcpCompoundPacketTest, BuildWithInputBuffer) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + EXPECT_TRUE(rr.WithReportBlock(rb)); + rr.Append(&fir); + + const size_t kRrLength = 8; + const size_t kReportBlockLength = 24; + const size_t kFirLength = 20; + + class Verifier : public rtcp::RtcpPacket::PacketReadyCallback { + public: + void OnPacketReady(uint8_t* data, size_t length) override { + RtcpPacketParser parser; + parser.Parse(data, length); + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); + ++packets_created_; + } + + int packets_created_ = 0; + } verifier; + const size_t kBufferSize = kRrLength + kReportBlockLength + kFirLength; + uint8_t buffer[kBufferSize]; + EXPECT_TRUE(rr.BuildExternalBuffer(buffer, kBufferSize, &verifier)); + EXPECT_EQ(1, verifier.packets_created_); +} + +TEST(RtcpCompoundPacketTest, BuildWithTooSmallBuffer_FragmentedSend) { + Fir fir; + ReportBlock rb; + ReceiverReport rr; + rr.From(kSenderSsrc); + EXPECT_TRUE(rr.WithReportBlock(rb)); + rr.Append(&fir); + + const size_t kRrLength = 8; + const size_t kReportBlockLength = 24; + + class Verifier : public rtcp::RtcpPacket::PacketReadyCallback { + public: + void OnPacketReady(uint8_t* data, size_t length) override { + RtcpPacketParser parser; + parser.Parse(data, length); + switch (packets_created_++) { + case 0: + EXPECT_EQ(1, parser.receiver_report()->num_packets()); + EXPECT_EQ(1, parser.report_block()->num_packets()); + EXPECT_EQ(0, parser.fir()->num_packets()); + break; + case 1: + EXPECT_EQ(0, parser.receiver_report()->num_packets()); + EXPECT_EQ(0, parser.report_block()->num_packets()); + EXPECT_EQ(1, parser.fir()->num_packets()); + break; + default: + ADD_FAILURE() << "OnPacketReady not expected to be called " + << packets_created_ << " times."; + } + } + + int packets_created_ = 0; + } verifier; + const size_t kBufferSize = kRrLength + kReportBlockLength; + uint8_t buffer[kBufferSize]; + EXPECT_TRUE(rr.BuildExternalBuffer(buffer, kBufferSize, &verifier)); + EXPECT_EQ(2, verifier.packets_created_); +} + +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc new file mode 100644 index 0000000000..6d6c48fada --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc @@ -0,0 +1,100 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +namespace webrtc { +namespace rtcp { +// DLRR Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=5 | reserved | block length | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_1 (SSRC of first receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// | last RR (LRR) | 1 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | delay since last RR (DLRR) | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | SSRC_2 (SSRC of second receiver) | sub- +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ block +// : ... : 2 +bool Dlrr::Parse(const uint8_t* buffer, uint16_t block_length_32bits) { + RTC_DCHECK(buffer[0] == kBlockType); + // kReserved = buffer[1]; + RTC_DCHECK_EQ(block_length_32bits, + ByteReader<uint16_t>::ReadBigEndian(&buffer[2])); + if (block_length_32bits % 3 != 0) { + LOG(LS_WARNING) << "Invalid size for dlrr block."; + return false; + } + + size_t blocks_count = block_length_32bits / 3; + const uint8_t* read_at = buffer + kBlockHeaderLength; + sub_blocks_.resize(blocks_count); + for (SubBlock& sub_block : sub_blocks_) { + sub_block.ssrc = ByteReader<uint32_t>::ReadBigEndian(&read_at[0]); + sub_block.last_rr = ByteReader<uint32_t>::ReadBigEndian(&read_at[4]); + sub_block.delay_since_last_rr = + ByteReader<uint32_t>::ReadBigEndian(&read_at[8]); + read_at += kSubBlockLength; + } + return true; +} + +size_t Dlrr::BlockLength() const { + if (sub_blocks_.empty()) + return 0; + return kBlockHeaderLength + kSubBlockLength * sub_blocks_.size(); +} + +void Dlrr::Create(uint8_t* buffer) const { + if (sub_blocks_.empty()) // No subblocks, no need to write header either. + return; + // Create block header. + const uint8_t kReserved = 0; + buffer[0] = kBlockType; + buffer[1] = kReserved; + ByteWriter<uint16_t>::WriteBigEndian(&buffer[2], 3 * sub_blocks_.size()); + // Create sub blocks. + uint8_t* write_at = buffer + kBlockHeaderLength; + for (const SubBlock& sub_block : sub_blocks_) { + ByteWriter<uint32_t>::WriteBigEndian(&write_at[0], sub_block.ssrc); + ByteWriter<uint32_t>::WriteBigEndian(&write_at[4], sub_block.last_rr); + ByteWriter<uint32_t>::WriteBigEndian(&write_at[8], + sub_block.delay_since_last_rr); + write_at += kSubBlockLength; + } + RTC_DCHECK_EQ(buffer + BlockLength(), write_at); +} + +bool Dlrr::WithDlrrItem(uint32_t ssrc, + uint32_t last_rr, + uint32_t delay_last_rr) { + if (sub_blocks_.size() >= kMaxNumberOfDlrrItems) { + LOG(LS_WARNING) << "Max DLRR items reached."; + return false; + } + SubBlock block; + block.ssrc = ssrc; + block.last_rr = last_rr; + block.delay_since_last_rr = delay_last_rr; + sub_blocks_.push_back(block); + return true; +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h new file mode 100644 index 0000000000..9af2dedf3f --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_DLRR_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_DLRR_H_ + +#include <vector> + +#include "webrtc/base/basictypes.h" + +namespace webrtc { +namespace rtcp { + +// DLRR Report Block: Delay since the Last Receiver Report (RFC 3611). +class Dlrr { + public: + struct SubBlock { + // RFC 3611 4.5 + uint32_t ssrc; + uint32_t last_rr; + uint32_t delay_since_last_rr; + }; + + static const uint8_t kBlockType = 5; + static const size_t kMaxNumberOfDlrrItems = 100; + + Dlrr() {} + Dlrr(const Dlrr& other) = default; + ~Dlrr() {} + + Dlrr& operator=(const Dlrr& other) = default; + + // Second parameter is value read from block header, + // i.e. size of block in 32bits excluding block header itself. + bool Parse(const uint8_t* buffer, uint16_t block_length_32bits); + + size_t BlockLength() const; + // Fills buffer with the Dlrr. + // Consumes BlockLength() bytes. + void Create(uint8_t* buffer) const; + + // Max 100 DLRR Items can be added per DLRR report block. + bool WithDlrrItem(uint32_t ssrc, uint32_t last_rr, uint32_t delay_last_rr); + + const std::vector<SubBlock>& sub_blocks() const { return sub_blocks_; } + + private: + static const size_t kBlockHeaderLength = 4; + static const size_t kSubBlockLength = 12; + + std::vector<SubBlock> sub_blocks_; +}; +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_DLRR_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr_unittest.cc new file mode 100644 index 0000000000..c7c139c560 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr_unittest.cc @@ -0,0 +1,102 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h" + +#include "testing/gtest/include/gtest/gtest.h" + +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +using webrtc::rtcp::Dlrr; + +namespace webrtc { +namespace { + +const uint32_t kSsrc = 0x12345678; +const uint32_t kLastRR = 0x23344556; +const uint32_t kDelay = 0x33343536; +const uint8_t kBlock[] = {0x05, 0x00, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78, + 0x23, 0x34, 0x45, 0x56, 0x33, 0x34, 0x35, 0x36}; +const size_t kBlockSizeBytes = sizeof(kBlock); + +TEST(RtcpPacketDlrrTest, Empty) { + Dlrr dlrr; + + EXPECT_EQ(0u, dlrr.BlockLength()); +} + +TEST(RtcpPacketDlrrTest, Create) { + Dlrr dlrr; + EXPECT_TRUE(dlrr.WithDlrrItem(kSsrc, kLastRR, kDelay)); + + ASSERT_EQ(kBlockSizeBytes, dlrr.BlockLength()); + uint8_t buffer[kBlockSizeBytes]; + + dlrr.Create(buffer); + EXPECT_EQ(0, memcmp(buffer, kBlock, kBlockSizeBytes)); +} + +TEST(RtcpPacketDlrrTest, Parse) { + Dlrr dlrr; + uint16_t block_length = ByteReader<uint16_t>::ReadBigEndian(&kBlock[2]); + EXPECT_TRUE(dlrr.Parse(kBlock, block_length)); + + EXPECT_EQ(1u, dlrr.sub_blocks().size()); + const Dlrr::SubBlock& block = dlrr.sub_blocks().front(); + EXPECT_EQ(kSsrc, block.ssrc); + EXPECT_EQ(kLastRR, block.last_rr); + EXPECT_EQ(kDelay, block.delay_since_last_rr); +} + +TEST(RtcpPacketDlrrTest, ParseFailsOnBadSize) { + const size_t kBigBufferSize = 0x100; // More than enough. + uint8_t buffer[kBigBufferSize]; + buffer[0] = Dlrr::kBlockType; + buffer[1] = 0; // Reserved. + buffer[2] = 0; // Most significant size byte. + for (uint8_t size = 3; size < 6; ++size) { + buffer[3] = size; + Dlrr dlrr; + // Parse should be successful only when size is multiple of 3. + EXPECT_EQ(size % 3 == 0, dlrr.Parse(buffer, static_cast<uint16_t>(size))); + } +} + +TEST(RtcpPacketDlrrTest, FailsOnTooManySubBlocks) { + Dlrr dlrr; + for (size_t i = 1; i <= Dlrr::kMaxNumberOfDlrrItems; ++i) { + EXPECT_TRUE(dlrr.WithDlrrItem(kSsrc + i, kLastRR + i, kDelay + i)); + } + EXPECT_FALSE(dlrr.WithDlrrItem(kSsrc, kLastRR, kDelay)); +} + +TEST(RtcpPacketDlrrTest, CreateAndParseMaxSubBlocks) { + const size_t kBufferSize = 0x1000; // More than enough. + uint8_t buffer[kBufferSize]; + + // Create. + Dlrr dlrr; + for (size_t i = 1; i <= Dlrr::kMaxNumberOfDlrrItems; ++i) { + EXPECT_TRUE(dlrr.WithDlrrItem(kSsrc + i, kLastRR + i, kDelay + i)); + } + size_t used_buffer_size = dlrr.BlockLength(); + ASSERT_LE(used_buffer_size, kBufferSize); + dlrr.Create(buffer); + + // Parse. + Dlrr parsed; + uint16_t block_length = ByteReader<uint16_t>::ReadBigEndian(&buffer[2]); + EXPECT_EQ(used_buffer_size, (block_length + 1) * 4u); + EXPECT_TRUE(parsed.Parse(buffer, block_length)); + EXPECT_TRUE(parsed.sub_blocks().size() == Dlrr::kMaxNumberOfDlrrItems); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.cc new file mode 100644 index 0000000000..030f9f81fa --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.cc @@ -0,0 +1,95 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" +#include "webrtc/modules/rtp_rtcp/source/rtp_utility.h" + +using webrtc::RTCPUtility::RtcpCommonHeader; + +namespace webrtc { +namespace rtcp { + +// Transmission Time Offsets in RTP Streams (RFC 5450). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// hdr |V=2|P| RC | PT=IJ=195 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | inter-arrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// . . +// . . +// . . +// | inter-arrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// +// If present, this RTCP packet must be placed after a receiver report +// (inside a compound RTCP packet), and MUST have the same value for RC +// (reception report count) as the receiver report. + +bool ExtendedJitterReport::Parse(const RtcpCommonHeader& header, + const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + + const uint8_t jitters_count = header.count_or_format; + const size_t kJitterSizeBytes = 4u; + + if (header.payload_size_bytes < jitters_count * kJitterSizeBytes) { + LOG(LS_WARNING) << "Packet is too small to contain all the jitter."; + return false; + } + + inter_arrival_jitters_.resize(jitters_count); + for (size_t index = 0; index < jitters_count; ++index) { + inter_arrival_jitters_[index] = + ByteReader<uint32_t>::ReadBigEndian(&payload[index * kJitterSizeBytes]); + } + + return true; +} + +bool ExtendedJitterReport::WithJitter(uint32_t jitter) { + if (inter_arrival_jitters_.size() >= kMaxNumberOfJitters) { + LOG(LS_WARNING) << "Max inter-arrival jitter items reached."; + return false; + } + inter_arrival_jitters_.push_back(jitter); + return true; +} + +bool ExtendedJitterReport::Create( + uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + while (*index + BlockLength() > max_length) { + if (!OnBufferFull(packet, index, callback)) + return false; + } + const size_t index_end = *index + BlockLength(); + size_t length = inter_arrival_jitters_.size(); + CreateHeader(length, kPacketType, length, packet, index); + + for (uint32_t jitter : inter_arrival_jitters_) { + ByteWriter<uint32_t>::WriteBigEndian(packet + *index, jitter); + *index += sizeof(uint32_t); + } + // Sanity check. + RTC_DCHECK_EQ(index_end, *index); + return true; +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.h new file mode 100644 index 0000000000..49de7be1a8 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_EXTENDED_JITTER_REPORT_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_EXTENDED_JITTER_REPORT_H_ + +#include <vector> + +#include "webrtc/base/checks.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +namespace webrtc { +namespace rtcp { + +class ExtendedJitterReport : public RtcpPacket { + public: + static const uint8_t kPacketType = 195; + + ExtendedJitterReport() : RtcpPacket() {} + + virtual ~ExtendedJitterReport() {} + + // 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. + + bool WithJitter(uint32_t jitter); + + size_t jitters_count() const { return inter_arrival_jitters_.size(); } + uint32_t jitter(size_t index) const { + RTC_DCHECK_LT(index, jitters_count()); + return inter_arrival_jitters_[index]; + } + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + private: + static const int kMaxNumberOfJitters = 0x1f; + + size_t BlockLength() const override { + return kHeaderLength + 4 * inter_arrival_jitters_.size(); + } + + std::vector<uint32_t> inter_arrival_jitters_; + + RTC_DISALLOW_COPY_AND_ASSIGN(ExtendedJitterReport); +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_EXTENDED_JITTER_REPORT_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report_unittest.cc new file mode 100644 index 0000000000..09d7b6305f --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report_unittest.cc @@ -0,0 +1,98 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/extended_jitter_report.h" + +#include <limits> + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +using webrtc::rtcp::RawPacket; +using webrtc::rtcp::ExtendedJitterReport; +using webrtc::RTCPUtility::RtcpCommonHeader; +using webrtc::RTCPUtility::RtcpParseCommonHeader; + +namespace webrtc { +namespace { + +class RtcpPacketExtendedJitterReportTest : public ::testing::Test { + protected: + void BuildPacket() { packet = ij.Build(); } + void ParsePacket() { + RtcpCommonHeader header; + EXPECT_TRUE( + RtcpParseCommonHeader(packet->Buffer(), packet->Length(), &header)); + EXPECT_EQ(header.BlockSize(), packet->Length()); + EXPECT_TRUE(parsed_.Parse( + header, packet->Buffer() + RtcpCommonHeader::kHeaderSizeBytes)); + } + + ExtendedJitterReport ij; + rtc::scoped_ptr<RawPacket> packet; + const ExtendedJitterReport& parsed() { return parsed_; } + + private: + ExtendedJitterReport parsed_; +}; + +TEST_F(RtcpPacketExtendedJitterReportTest, NoItem) { + // No initialization because packet is empty. + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(0u, parsed().jitters_count()); +} + +TEST_F(RtcpPacketExtendedJitterReportTest, OneItem) { + EXPECT_TRUE(ij.WithJitter(0x11121314)); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(1u, parsed().jitters_count()); + EXPECT_EQ(0x11121314U, parsed().jitter(0)); +} + +TEST_F(RtcpPacketExtendedJitterReportTest, TwoItems) { + EXPECT_TRUE(ij.WithJitter(0x11121418)); + EXPECT_TRUE(ij.WithJitter(0x22242628)); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(2u, parsed().jitters_count()); + EXPECT_EQ(0x11121418U, parsed().jitter(0)); + EXPECT_EQ(0x22242628U, parsed().jitter(1)); +} + +TEST_F(RtcpPacketExtendedJitterReportTest, TooManyItems) { + const int kMaxIjItems = (1 << 5) - 1; + for (int i = 0; i < kMaxIjItems; ++i) { + EXPECT_TRUE(ij.WithJitter(i)); + } + EXPECT_FALSE(ij.WithJitter(kMaxIjItems)); +} + +TEST_F(RtcpPacketExtendedJitterReportTest, ParseFailWithTooManyItems) { + ij.WithJitter(0x11121418); + BuildPacket(); + RtcpCommonHeader header; + RtcpParseCommonHeader(packet->Buffer(), packet->Length(), &header); + header.count_or_format++; // Damage package. + + ExtendedJitterReport parsed; + + EXPECT_FALSE(parsed.Parse( + header, packet->Buffer() + RtcpCommonHeader::kHeaderSizeBytes)); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc new file mode 100644 index 0000000000..8b9b354a06 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h" + +#include <algorithm> + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +using webrtc::RTCPUtility::RtcpCommonHeader; + +namespace webrtc { +namespace rtcp { + +// 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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 0 | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : 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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +bool Nack::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + RTC_DCHECK(header.count_or_format == kFeedbackMessageType); + + if (header.payload_size_bytes < kCommonFeedbackLength + kNackItemLength) { + LOG(LS_WARNING) << "Payload length " << header.payload_size_bytes + << " is too small for a Nack."; + return false; + } + size_t nack_items = + (header.payload_size_bytes - kCommonFeedbackLength) / kNackItemLength; + + ParseCommonFeedback(payload); + const uint8_t* next_nack = payload + kCommonFeedbackLength; + + packet_ids_.clear(); + packed_.resize(nack_items); + for (size_t index = 0; index < nack_items; ++index) { + packed_[index].first_pid = ByteReader<uint16_t>::ReadBigEndian(next_nack); + packed_[index].bitmask = ByteReader<uint16_t>::ReadBigEndian(next_nack + 2); + next_nack += kNackItemLength; + } + Unpack(); + + return true; +} + +bool Nack::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + RTC_DCHECK(!packed_.empty()); + // If nack list can't fit in packet, try to fragment. + size_t nack_index = 0; + const size_t kCommonFbFmtLength = kHeaderLength + kCommonFeedbackLength; + do { + size_t bytes_left_in_buffer = max_length - *index; + if (bytes_left_in_buffer < kCommonFbFmtLength + kNackItemLength) { + if (!OnBufferFull(packet, index, callback)) + return false; + continue; + } + size_t num_nack_fields = + std::min((bytes_left_in_buffer - kCommonFbFmtLength) / kNackItemLength, + packed_.size() - nack_index); + + size_t size_bytes = + (num_nack_fields * kNackItemLength) + kCommonFbFmtLength; + size_t header_length = ((size_bytes + 3) / 4) - 1; // As 32bit words - 1 + CreateHeader(kFeedbackMessageType, kPacketType, header_length, packet, + index); + CreateCommonFeedback(packet + *index); + *index += kCommonFeedbackLength; + size_t end_index = nack_index + num_nack_fields; + for (; nack_index < end_index; ++nack_index) { + const auto& item = packed_[nack_index]; + ByteWriter<uint16_t>::WriteBigEndian(packet + *index + 0, item.first_pid); + ByteWriter<uint16_t>::WriteBigEndian(packet + *index + 2, item.bitmask); + *index += kNackItemLength; + } + RTC_DCHECK_LE(*index, max_length); + } while (nack_index < packed_.size()); + + return true; +} + +size_t Nack::BlockLength() const { + return (packed_.size() * kNackItemLength) + kCommonFeedbackLength + + kHeaderLength; +} + +void Nack::WithList(const uint16_t* nack_list, size_t length) { + RTC_DCHECK(nack_list); + RTC_DCHECK(packet_ids_.empty()); + RTC_DCHECK(packed_.empty()); + packet_ids_.assign(nack_list, nack_list + length); + Pack(); +} + +void Nack::Pack() { + RTC_DCHECK(!packet_ids_.empty()); + RTC_DCHECK(packed_.empty()); + auto it = packet_ids_.begin(); + const auto end = packet_ids_.end(); + while (it != end) { + PackedNack item; + item.first_pid = *it++; + // Bitmask specifies losses in any of the 16 packets following the pid. + item.bitmask = 0; + while (it != end) { + uint16_t shift = static_cast<uint16_t>(*it - item.first_pid - 1); + if (shift <= 15) { + item.bitmask |= (1 << shift); + ++it; + } else { + break; + } + } + packed_.push_back(item); + } +} + +void Nack::Unpack() { + RTC_DCHECK(packet_ids_.empty()); + RTC_DCHECK(!packed_.empty()); + for (const PackedNack& item : packed_) { + packet_ids_.push_back(item.first_pid); + uint16_t pid = item.first_pid + 1; + for (uint16_t bitmask = item.bitmask; bitmask != 0; bitmask >>= 1, ++pid) + if (bitmask & 1) + packet_ids_.push_back(pid); + } +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h new file mode 100644 index 0000000000..fb2be113a2 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h @@ -0,0 +1,63 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_NACK_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_NACK_H_ + +#include <vector> + +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +namespace webrtc { +namespace rtcp { + +class Nack : public Rtpfb { + public: + const uint8_t kFeedbackMessageType = 1; + Nack() {} + + virtual ~Nack() {} + + // 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 WithList(const uint16_t* nack_list, size_t length); + const std::vector<uint16_t>& packet_ids() const { return packet_ids_; } + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + size_t BlockLength() const override; + + private: + const size_t kNackItemLength = 4; + struct PackedNack { + uint16_t first_pid; + uint16_t bitmask; + }; + + void Pack(); // Fills packed_ using packed_ids_. (used in WithList). + void Unpack(); // Fills packet_ids_ using packed_. (used in Parse). + + std::vector<PackedNack> packed_; + std::vector<uint16_t> packet_ids_; + + RTC_DISALLOW_COPY_AND_ASSIGN(Nack); +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_NACK_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc new file mode 100644 index 0000000000..01e30f5644 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc @@ -0,0 +1,190 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using ::testing::_; +using ::testing::ElementsAreArray; +using ::testing::Invoke; +using ::testing::UnorderedElementsAreArray; + +using webrtc::rtcp::Nack; +using webrtc::rtcp::RawPacket; +using webrtc::RTCPUtility::RtcpCommonHeader; +using webrtc::RTCPUtility::RtcpParseCommonHeader; + +namespace webrtc { +namespace { + +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kRemoteSsrc = 0x23456789; + +const uint16_t kList[] = {0, 1, 3, 8, 16}; +const size_t kListLength = sizeof(kList) / sizeof(kList[0]); +const uint8_t kPacket[] = {0x81, 205, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78, + 0x23, 0x45, 0x67, 0x89, 0x00, 0x00, 0x80, 0x85}; +const size_t kPacketLength = sizeof(kPacket); + +const uint16_t kWrapList[] = {0xffdc, 0xffec, 0xfffe, 0xffff, 0x0000, + 0x0001, 0x0003, 0x0014, 0x0064}; +const size_t kWrapListLength = sizeof(kWrapList) / sizeof(kWrapList[0]); +const uint8_t kWrapPacket[] = {0x81, 205, 0x00, 0x06, 0x12, 0x34, 0x56, 0x78, + 0x23, 0x45, 0x67, 0x89, 0xff, 0xdc, 0x80, 0x00, + 0xff, 0xfe, 0x00, 0x17, 0x00, 0x14, 0x00, 0x00, + 0x00, 0x64, 0x00, 0x00}; +const size_t kWrapPacketLength = sizeof(kWrapPacket); + +TEST(RtcpPacketNackTest, Create) { + Nack nack; + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kList, kListLength); + + rtc::scoped_ptr<RawPacket> packet = nack.Build(); + + EXPECT_EQ(kPacketLength, packet->Length()); + EXPECT_EQ(0, memcmp(kPacket, packet->Buffer(), kPacketLength)); +} + +TEST(RtcpPacketNackTest, Parse) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(kPacket, kPacketLength, &header)); + EXPECT_EQ(kPacketLength, header.BlockSize()); + Nack parsed; + + EXPECT_TRUE( + parsed.Parse(header, kPacket + RtcpCommonHeader::kHeaderSizeBytes)); + const Nack& const_parsed = parsed; + + EXPECT_EQ(kSenderSsrc, const_parsed.sender_ssrc()); + EXPECT_EQ(kRemoteSsrc, const_parsed.media_ssrc()); + EXPECT_THAT(const_parsed.packet_ids(), ElementsAreArray(kList)); +} + +TEST(RtcpPacketNackTest, CreateWrap) { + Nack nack; + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kWrapList, kWrapListLength); + + rtc::scoped_ptr<RawPacket> packet = nack.Build(); + + EXPECT_EQ(kWrapPacketLength, packet->Length()); + EXPECT_EQ(0, memcmp(kWrapPacket, packet->Buffer(), kWrapPacketLength)); +} + +TEST(RtcpPacketNackTest, ParseWrap) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(kWrapPacket, kWrapPacketLength, &header)); + EXPECT_EQ(kWrapPacketLength, header.BlockSize()); + + Nack parsed; + EXPECT_TRUE( + parsed.Parse(header, kWrapPacket + RtcpCommonHeader::kHeaderSizeBytes)); + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(kRemoteSsrc, parsed.media_ssrc()); + EXPECT_THAT(parsed.packet_ids(), ElementsAreArray(kWrapList)); +} + +TEST(RtcpPacketNackTest, BadOrder) { + // Does not guarantee optimal packing, but should guarantee correctness. + const uint16_t kUnorderedList[] = {1, 25, 13, 12, 9, 27, 29}; + const size_t kUnorderedListLength = + sizeof(kUnorderedList) / sizeof(kUnorderedList[0]); + Nack nack; + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kUnorderedList, kUnorderedListLength); + + rtc::scoped_ptr<RawPacket> packet = nack.Build(); + + Nack parsed; + RtcpCommonHeader header; + EXPECT_TRUE( + RtcpParseCommonHeader(packet->Buffer(), packet->Length(), &header)); + EXPECT_TRUE(parsed.Parse( + header, packet->Buffer() + RtcpCommonHeader::kHeaderSizeBytes)); + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(kRemoteSsrc, parsed.media_ssrc()); + EXPECT_THAT(parsed.packet_ids(), UnorderedElementsAreArray(kUnorderedList)); +} + +TEST(RtcpPacketNackTest, CreateFragmented) { + Nack nack; + const uint16_t kList[] = {1, 100, 200, 300, 400}; + const uint16_t kListLength = sizeof(kList) / sizeof(kList[0]); + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kList, kListLength); + + class MockPacketReadyCallback : public rtcp::RtcpPacket::PacketReadyCallback { + public: + MOCK_METHOD2(OnPacketReady, void(uint8_t*, size_t)); + } verifier; + + class NackVerifier { + public: + explicit NackVerifier(std::vector<uint16_t> ids) : ids_(ids) {} + void operator()(uint8_t* data, size_t length) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(data, length, &header)); + EXPECT_EQ(length, header.BlockSize()); + Nack nack; + EXPECT_TRUE( + nack.Parse(header, data + RtcpCommonHeader::kHeaderSizeBytes)); + EXPECT_EQ(kSenderSsrc, nack.sender_ssrc()); + EXPECT_EQ(kRemoteSsrc, nack.media_ssrc()); + EXPECT_THAT(nack.packet_ids(), ElementsAreArray(ids_)); + } + std::vector<uint16_t> ids_; + } packet1({1, 100, 200}), packet2({300, 400}); + + EXPECT_CALL(verifier, OnPacketReady(_, _)) + .WillOnce(Invoke(packet1)) + .WillOnce(Invoke(packet2)); + const size_t kBufferSize = 12 + (3 * 4); // Fits common header + 3 nack items + uint8_t buffer[kBufferSize]; + EXPECT_TRUE(nack.BuildExternalBuffer(buffer, kBufferSize, &verifier)); +} + +TEST(RtcpPacketNackTest, CreateFailsWithTooSmallBuffer) { + const uint16_t kList[] = {1}; + const size_t kMinNackBlockSize = 16; + Nack nack; + nack.From(kSenderSsrc); + nack.To(kRemoteSsrc); + nack.WithList(kList, 1); + class Verifier : public rtcp::RtcpPacket::PacketReadyCallback { + public: + void OnPacketReady(uint8_t* data, size_t length) override { + ADD_FAILURE() << "Buffer should be too small."; + } + } verifier; + uint8_t buffer[kMinNackBlockSize - 1]; + EXPECT_FALSE( + nack.BuildExternalBuffer(buffer, kMinNackBlockSize - 1, &verifier)); +} + +TEST(RtcpPacketNackTest, ParseFailsWithTooSmallBuffer) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(kPacket, kPacketLength, &header)); + header.payload_size_bytes--; // Damage the packet + Nack parsed; + EXPECT_FALSE( + parsed.Parse(header, kPacket + RtcpCommonHeader::kHeaderSizeBytes)); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.cc new file mode 100644 index 0000000000..3673491058 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.cc @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" + +using webrtc::RTCPUtility::RtcpCommonHeader; + +namespace webrtc { +namespace rtcp { + +// 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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : : + +// +// Picture loss indication (PLI) (RFC 4585). +// FCI: no feedback control information. +bool Pli::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + RTC_DCHECK(header.count_or_format == kFeedbackMessageType); + + if (header.payload_size_bytes < kCommonFeedbackLength) { + LOG(LS_WARNING) << "Packet is too small to be a valid PLI packet"; + return false; + } + + ParseCommonFeedback(payload); + return true; +} + +bool Pli::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + while (*index + BlockLength() > max_length) { + if (!OnBufferFull(packet, index, callback)) + return false; + } + + CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet, + index); + CreateCommonFeedback(packet + *index); + *index += kCommonFeedbackLength; + return true; +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h new file mode 100644 index 0000000000..5567825830 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_PLI_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PLI_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h" + +namespace webrtc { +namespace rtcp { + +// Picture loss indication (PLI) (RFC 4585). +class Pli : public Psfb { + public: + static const uint8_t kFeedbackMessageType = 1; + + Pli() {} + virtual ~Pli() {} + + // 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. + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + private: + size_t BlockLength() const override { + return kHeaderLength + kCommonFeedbackLength; + } + + RTC_DISALLOW_COPY_AND_ASSIGN(Pli); +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PLI_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli_unittest.cc new file mode 100644 index 0000000000..1c47c3ffb1 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/pli_unittest.cc @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h" + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +using webrtc::rtcp::Pli; +using webrtc::rtcp::RawPacket; +using webrtc::RTCPUtility::RtcpCommonHeader; +using webrtc::RTCPUtility::RtcpParseCommonHeader; + +namespace webrtc { +namespace { + +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kRemoteSsrc = 0x23456789; +// Manually created Pli packet matching constants above. +const uint8_t kPacket[] = {0x81, 206, 0x00, 0x02, + 0x12, 0x34, 0x56, 0x78, + 0x23, 0x45, 0x67, 0x89}; +const size_t kPacketLength = sizeof(kPacket); + +TEST(RtcpPacketPliTest, Parse) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(kPacket, kPacketLength, &header)); + Pli mutable_parsed; + EXPECT_TRUE(mutable_parsed.Parse( + header, kPacket + RtcpCommonHeader::kHeaderSizeBytes)); + const Pli& parsed = mutable_parsed; // Read values from constant object. + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(kRemoteSsrc, parsed.media_ssrc()); +} + +TEST(RtcpPacketPliTest, Create) { + Pli pli; + pli.From(kSenderSsrc); + pli.To(kRemoteSsrc); + + rtc::scoped_ptr<RawPacket> packet(pli.Build()); + + ASSERT_EQ(kPacketLength, packet->Length()); + EXPECT_EQ(0, memcmp(kPacket, packet->Buffer(), kPacketLength)); +} + +TEST(RtcpPacketPliTest, ParseFailsOnTooSmallPacket) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(kPacket, kPacketLength, &header)); + header.payload_size_bytes--; + + Pli parsed; + EXPECT_FALSE( + parsed.Parse(header, kPacket + RtcpCommonHeader::kHeaderSizeBytes)); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.cc new file mode 100644 index 0000000000..d1ee401dab --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.cc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h" + +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +namespace webrtc { +namespace rtcp { + +// 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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 0 | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : : + +void Psfb::ParseCommonFeedback(const uint8_t* payload) { + sender_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&payload[0]); + media_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&payload[4]); +} + +void Psfb::CreateCommonFeedback(uint8_t* payload) const { + ByteWriter<uint32_t>::WriteBigEndian(&payload[0], sender_ssrc_); + ByteWriter<uint32_t>::WriteBigEndian(&payload[4], media_ssrc_); +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h new file mode 100644 index 0000000000..dddcdecba6 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_PSFB_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PSFB_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" + +namespace webrtc { +namespace rtcp { + +// PSFB: Payload-specific feedback message. +// RFC 4585, Section 6.3. +class Psfb : public RtcpPacket { + public: + static const uint8_t kPacketType = 206; + + Psfb() : sender_ssrc_(0), media_ssrc_(0) {} + virtual ~Psfb() {} + + void From(uint32_t ssrc) { sender_ssrc_ = ssrc; } + void To(uint32_t ssrc) { media_ssrc_ = ssrc; } + + uint32_t sender_ssrc() const { return sender_ssrc_; } + uint32_t media_ssrc() const { return media_ssrc_; } + + protected: + static const size_t kCommonFeedbackLength = 8; + void ParseCommonFeedback(const uint8_t* payload); + void CreateCommonFeedback(uint8_t* payload) const; + + private: + uint32_t sender_ssrc_; + uint32_t media_ssrc_; +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PSFB_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.cc new file mode 100644 index 0000000000..ef64b4f51b --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.cc @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +using webrtc::RTCPUtility::RtcpCommonHeader; + +namespace webrtc { +namespace rtcp { + +// +// RTCP receiver report (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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// |V=2|P| RC | PT=RR=201 | length | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | SSRC of packet sender | +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// | report block(s) | +// | .... | +bool ReceiverReport::Parse(const RTCPUtility::RtcpCommonHeader& header, + const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + + const uint8_t report_blocks_count = header.count_or_format; + + if (header.payload_size_bytes < + kRrBaseLength + report_blocks_count * ReportBlock::kLength) { + LOG(LS_WARNING) << "Packet is too small to contain all the data."; + return false; + } + + sender_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(payload); + + const uint8_t* next_report_block = payload + kRrBaseLength; + + report_blocks_.resize(report_blocks_count); + for (ReportBlock& block : report_blocks_) { + block.Parse(next_report_block, ReportBlock::kLength); + next_report_block += ReportBlock::kLength; + } + + RTC_DCHECK_LE(next_report_block, payload + header.payload_size_bytes); + return true; +} + +bool ReceiverReport::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + while (*index + BlockLength() > max_length) { + if (!OnBufferFull(packet, index, callback)) + return false; + } + CreateHeader(report_blocks_.size(), kPacketType, HeaderLength(), packet, + index); + ByteWriter<uint32_t>::WriteBigEndian(packet + *index, sender_ssrc_); + *index += kRrBaseLength; + for (const ReportBlock& block : report_blocks_) { + block.Create(packet + *index); + *index += ReportBlock::kLength; + } + return true; +} + +bool ReceiverReport::WithReportBlock(const ReportBlock& block) { + if (report_blocks_.size() >= kMaxNumberOfReportBlocks) { + LOG(LS_WARNING) << "Max report blocks reached."; + return false; + } + report_blocks_.push_back(block); + return true; +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h new file mode 100644 index 0000000000..172a84ea2f --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h @@ -0,0 +1,66 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_RECEIVER_REPORT_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RECEIVER_REPORT_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/report_block.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +namespace webrtc { +namespace rtcp { + +class ReceiverReport : public RtcpPacket { + public: + static const uint8_t kPacketType = 201; + ReceiverReport() : sender_ssrc_(0) {} + + virtual ~ReceiverReport() {} + + // 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 From(uint32_t ssrc) { sender_ssrc_ = ssrc; } + bool WithReportBlock(const ReportBlock& block); + + uint32_t sender_ssrc() const { return sender_ssrc_; } + const std::vector<ReportBlock>& report_blocks() const { + return report_blocks_; + } + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + private: + static const size_t kRrBaseLength = 4; + static const size_t kMaxNumberOfReportBlocks = 0x1F; + + size_t BlockLength() const { + return kHeaderLength + kRrBaseLength + + report_blocks_.size() * ReportBlock::kLength; + } + + uint32_t sender_ssrc_; + std::vector<ReportBlock> report_blocks_; + + RTC_DISALLOW_COPY_AND_ASSIGN(ReceiverReport); +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RECEIVER_REPORT_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report_unittest.cc new file mode 100644 index 0000000000..ff3da600a5 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report_unittest.cc @@ -0,0 +1,145 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using webrtc::rtcp::RawPacket; +using webrtc::rtcp::ReceiverReport; +using webrtc::rtcp::ReportBlock; +using webrtc::RTCPUtility::RtcpCommonHeader; +using webrtc::RTCPUtility::RtcpParseCommonHeader; + +namespace webrtc { +namespace { +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kRemoteSsrc = 0x23456789; +const uint8_t kFractionLost = 55; +const uint32_t kCumulativeLost = 0x111213; +const uint32_t kExtHighestSeqNum = 0x22232425; +const uint32_t kJitter = 0x33343536; +const uint32_t kLastSr = 0x44454647; +const uint32_t kDelayLastSr = 0x55565758; +// Manually created ReceiverReport with one ReportBlock matching constants +// above. +// Having this block allows to test Create and Parse separately. +const uint8_t kPacket[] = {0x81, 201, 0x00, 0x07, 0x12, 0x34, 0x56, 0x78, + 0x23, 0x45, 0x67, 0x89, 55, 0x11, 0x12, 0x13, + 0x22, 0x23, 0x24, 0x25, 0x33, 0x34, 0x35, 0x36, + 0x44, 0x45, 0x46, 0x47, 0x55, 0x56, 0x57, 0x58}; +const size_t kPacketLength = sizeof(kPacket); + +class RtcpPacketReceiverReportTest : public ::testing::Test { + protected: + void BuildPacket() { packet = rr.Build(); } + void ParsePacket() { + RtcpCommonHeader header; + EXPECT_TRUE( + RtcpParseCommonHeader(packet->Buffer(), packet->Length(), &header)); + EXPECT_EQ(header.BlockSize(), packet->Length()); + EXPECT_TRUE(parsed_.Parse( + header, packet->Buffer() + RtcpCommonHeader::kHeaderSizeBytes)); + } + + ReceiverReport rr; + rtc::scoped_ptr<RawPacket> packet; + const ReceiverReport& parsed() { return parsed_; } + + private: + ReceiverReport parsed_; +}; + +TEST_F(RtcpPacketReceiverReportTest, Parse) { + RtcpCommonHeader header; + RtcpParseCommonHeader(kPacket, kPacketLength, &header); + EXPECT_TRUE(rr.Parse(header, kPacket + RtcpCommonHeader::kHeaderSizeBytes)); + const ReceiverReport& parsed = rr; + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(1u, parsed.report_blocks().size()); + const ReportBlock& rb = parsed.report_blocks().front(); + EXPECT_EQ(kRemoteSsrc, rb.source_ssrc()); + EXPECT_EQ(kFractionLost, rb.fraction_lost()); + EXPECT_EQ(kCumulativeLost, rb.cumulative_lost()); + EXPECT_EQ(kExtHighestSeqNum, rb.extended_high_seq_num()); + EXPECT_EQ(kJitter, rb.jitter()); + EXPECT_EQ(kLastSr, rb.last_sr()); + EXPECT_EQ(kDelayLastSr, rb.delay_since_last_sr()); +} + +TEST_F(RtcpPacketReceiverReportTest, ParseFailsOnIncorrectSize) { + RtcpCommonHeader header; + RtcpParseCommonHeader(kPacket, kPacketLength, &header); + header.count_or_format++; // Damage the packet. + EXPECT_FALSE(rr.Parse(header, kPacket + RtcpCommonHeader::kHeaderSizeBytes)); +} + +TEST_F(RtcpPacketReceiverReportTest, Create) { + rr.From(kSenderSsrc); + ReportBlock rb; + rb.To(kRemoteSsrc); + rb.WithFractionLost(kFractionLost); + rb.WithCumulativeLost(kCumulativeLost); + rb.WithExtHighestSeqNum(kExtHighestSeqNum); + rb.WithJitter(kJitter); + rb.WithLastSr(kLastSr); + rb.WithDelayLastSr(kDelayLastSr); + rr.WithReportBlock(rb); + + BuildPacket(); + + ASSERT_EQ(kPacketLength, packet->Length()); + EXPECT_EQ(0, memcmp(kPacket, packet->Buffer(), kPacketLength)); +} + +TEST_F(RtcpPacketReceiverReportTest, WithoutReportBlocks) { + rr.From(kSenderSsrc); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(kSenderSsrc, parsed().sender_ssrc()); + EXPECT_EQ(0u, parsed().report_blocks().size()); +} + +TEST_F(RtcpPacketReceiverReportTest, WithTwoReportBlocks) { + ReportBlock rb1; + rb1.To(kRemoteSsrc); + ReportBlock rb2; + rb2.To(kRemoteSsrc + 1); + + rr.From(kSenderSsrc); + EXPECT_TRUE(rr.WithReportBlock(rb1)); + EXPECT_TRUE(rr.WithReportBlock(rb2)); + + BuildPacket(); + ParsePacket(); + + EXPECT_EQ(kSenderSsrc, parsed().sender_ssrc()); + EXPECT_EQ(2u, parsed().report_blocks().size()); + EXPECT_EQ(kRemoteSsrc, parsed().report_blocks()[0].source_ssrc()); + EXPECT_EQ(kRemoteSsrc + 1, parsed().report_blocks()[1].source_ssrc()); +} + +TEST_F(RtcpPacketReceiverReportTest, WithTooManyReportBlocks) { + rr.From(kSenderSsrc); + const size_t kMaxReportBlocks = (1 << 5) - 1; + ReportBlock rb; + for (size_t i = 0; i < kMaxReportBlocks; ++i) { + rb.To(kRemoteSsrc + i); + EXPECT_TRUE(rr.WithReportBlock(rb)); + } + rb.To(kRemoteSsrc + kMaxReportBlocks); + EXPECT_FALSE(rr.WithReportBlock(rb)); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.cc new file mode 100644 index 0000000000..4911dbf5b7 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.cc @@ -0,0 +1,89 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +namespace webrtc { +namespace rtcp { + +// From RFC 3550, RTP: A Transport Protocol for Real-Time Applications. +// +// RTCP report block (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 +// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +// 0 | SSRC_1 (SSRC of first source) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | fraction lost | cumulative number of packets lost | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 8 | extended highest sequence number received | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 12 | interarrival jitter | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 16 | last SR (LSR) | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 20 | delay since last SR (DLSR) | +// 24 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+ +ReportBlock::ReportBlock() + : source_ssrc_(0), + fraction_lost_(0), + cumulative_lost_(0), + extended_high_seq_num_(0), + jitter_(0), + last_sr_(0), + delay_since_last_sr_(0) {} + +bool ReportBlock::Parse(const uint8_t* buffer, size_t length) { + RTC_DCHECK(buffer != nullptr); + if (length < ReportBlock::kLength) { + LOG(LS_ERROR) << "Report Block should be 24 bytes long"; + return false; + } + + source_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[0]); + fraction_lost_ = buffer[4]; + cumulative_lost_ = ByteReader<uint32_t, 3>::ReadBigEndian(&buffer[5]); + extended_high_seq_num_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]); + jitter_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[12]); + last_sr_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[16]); + delay_since_last_sr_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[20]); + + return true; +} + +void ReportBlock::Create(uint8_t* buffer) const { + // Runtime check should be done while setting cumulative_lost. + RTC_DCHECK_LT(cumulative_lost(), (1u << 24)); // Have only 3 bytes for it. + + ByteWriter<uint32_t>::WriteBigEndian(&buffer[0], source_ssrc()); + ByteWriter<uint8_t>::WriteBigEndian(&buffer[4], fraction_lost()); + ByteWriter<uint32_t, 3>::WriteBigEndian(&buffer[5], cumulative_lost()); + ByteWriter<uint32_t>::WriteBigEndian(&buffer[8], extended_high_seq_num()); + ByteWriter<uint32_t>::WriteBigEndian(&buffer[12], jitter()); + ByteWriter<uint32_t>::WriteBigEndian(&buffer[16], last_sr()); + ByteWriter<uint32_t>::WriteBigEndian(&buffer[20], delay_since_last_sr()); +} + +bool ReportBlock::WithCumulativeLost(uint32_t cumulative_lost) { + if (cumulative_lost >= (1u << 24)) { // Have only 3 bytes to store it. + LOG(LS_WARNING) << "Cumulative lost is too big to fit into Report Block"; + return false; + } + cumulative_lost_ = cumulative_lost; + return true; +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h new file mode 100644 index 0000000000..ef99e17297 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h @@ -0,0 +1,67 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_REPORT_BLOCK_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REPORT_BLOCK_H_ + +#include "webrtc/base/basictypes.h" + +namespace webrtc { +namespace rtcp { + +class ReportBlock { + public: + static const size_t kLength = 24; + + ReportBlock(); + ~ReportBlock() {} + + bool Parse(const uint8_t* buffer, size_t length); + + // Fills buffer with the ReportBlock. + // Consumes ReportBlock::kLength bytes. + void Create(uint8_t* buffer) const; + + void To(uint32_t ssrc) { source_ssrc_ = ssrc; } + void WithFractionLost(uint8_t fraction_lost) { + fraction_lost_ = fraction_lost; + } + bool WithCumulativeLost(uint32_t cumulative_lost); + void WithExtHighestSeqNum(uint32_t ext_highest_seq_num) { + extended_high_seq_num_ = ext_highest_seq_num; + } + void WithJitter(uint32_t jitter) { jitter_ = jitter; } + void WithLastSr(uint32_t last_sr) { last_sr_ = last_sr; } + void WithDelayLastSr(uint32_t delay_last_sr) { + delay_since_last_sr_ = delay_last_sr; + } + + uint32_t source_ssrc() const { return source_ssrc_; } + uint8_t fraction_lost() const { return fraction_lost_; } + uint32_t cumulative_lost() const { return cumulative_lost_; } + uint32_t extended_high_seq_num() const { return extended_high_seq_num_; } + uint32_t jitter() const { return jitter_; } + uint32_t last_sr() const { return last_sr_; } + uint32_t delay_since_last_sr() const { return delay_since_last_sr_; } + + private: + uint32_t source_ssrc_; + uint8_t fraction_lost_; + uint32_t cumulative_lost_; + uint32_t extended_high_seq_num_; + uint32_t jitter_; + uint32_t last_sr_; + uint32_t delay_since_last_sr_; +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REPORT_BLOCK_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block_unittest.cc new file mode 100644 index 0000000000..85bbb404a4 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block_unittest.cc @@ -0,0 +1,86 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h" + +#include <limits> + +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/base/random.h" + +using webrtc::rtcp::ReportBlock; + +namespace webrtc { +namespace { + +const uint32_t kRemoteSsrc = 0x23456789; +const uint8_t kFractionLost = 55; +// Use values that are streamed differently LE and BE. +const uint32_t kCumulativeLost = 0x111213; +const uint32_t kExtHighestSeqNum = 0x22232425; +const uint32_t kJitter = 0x33343536; +const uint32_t kLastSr = 0x44454647; +const uint32_t kDelayLastSr = 0x55565758; +const size_t kBufferLength = ReportBlock::kLength; + +TEST(RtcpPacketReportBlockTest, ParseChecksLength) { + uint8_t buffer[kBufferLength]; + memset(buffer, 0, sizeof(buffer)); + + ReportBlock rb; + EXPECT_FALSE(rb.Parse(buffer, kBufferLength - 1)); + EXPECT_TRUE(rb.Parse(buffer, kBufferLength)); +} + +TEST(RtcpPacketReportBlockTest, ParseAnyData) { + uint8_t buffer[kBufferLength]; + // Fill buffer with semi-random data. + Random generator(0x256F8A285EC829ull); + for (size_t i = 0; i < kBufferLength; ++i) + buffer[i] = static_cast<uint8_t>(generator.Rand(0, 0xff)); + + ReportBlock rb; + EXPECT_TRUE(rb.Parse(buffer, kBufferLength)); +} + +TEST(RtcpPacketReportBlockTest, ParseMatchCreate) { + ReportBlock rb; + rb.To(kRemoteSsrc); + rb.WithFractionLost(kFractionLost); + rb.WithCumulativeLost(kCumulativeLost); + rb.WithExtHighestSeqNum(kExtHighestSeqNum); + rb.WithJitter(kJitter); + rb.WithLastSr(kLastSr); + rb.WithDelayLastSr(kDelayLastSr); + + uint8_t buffer[kBufferLength]; + rb.Create(buffer); + + ReportBlock parsed; + EXPECT_TRUE(parsed.Parse(buffer, kBufferLength)); + + EXPECT_EQ(kRemoteSsrc, parsed.source_ssrc()); + EXPECT_EQ(kFractionLost, parsed.fraction_lost()); + EXPECT_EQ(kCumulativeLost, parsed.cumulative_lost()); + EXPECT_EQ(kExtHighestSeqNum, parsed.extended_high_seq_num()); + EXPECT_EQ(kJitter, parsed.jitter()); + EXPECT_EQ(kLastSr, parsed.last_sr()); + EXPECT_EQ(kDelayLastSr, parsed.delay_since_last_sr()); +} + +TEST(RtcpPacketReportBlockTest, ValidateCumulativeLost) { + const uint32_t kMaxCumulativeLost = 0xffffff; + ReportBlock rb; + EXPECT_FALSE(rb.WithCumulativeLost(kMaxCumulativeLost + 1)); + EXPECT_TRUE(rb.WithCumulativeLost(kMaxCumulativeLost)); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.cc new file mode 100644 index 0000000000..db4ae67326 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.cc @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h" + +#include "webrtc/base/checks.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +namespace webrtc { +namespace rtcp { +// Receiver Reference Time Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | BT=4 | reserved | block length = 2 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, most significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | NTP timestamp, least significant word | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void Rrtr::Parse(const uint8_t* buffer) { + RTC_DCHECK(buffer[0] == kBlockType); + // reserved = buffer[1]; + RTC_DCHECK(ByteReader<uint16_t>::ReadBigEndian(&buffer[2]) == kBlockLength); + uint32_t seconds = ByteReader<uint32_t>::ReadBigEndian(&buffer[4]); + uint32_t fraction = ByteReader<uint32_t>::ReadBigEndian(&buffer[8]); + ntp_.Set(seconds, fraction); +} + +void Rrtr::Create(uint8_t* buffer) const { + const uint8_t kReserved = 0; + buffer[0] = kBlockType; + buffer[1] = kReserved; + ByteWriter<uint16_t>::WriteBigEndian(&buffer[2], kBlockLength); + ByteWriter<uint32_t>::WriteBigEndian(&buffer[4], ntp_.seconds()); + ByteWriter<uint32_t>::WriteBigEndian(&buffer[8], ntp_.fractions()); +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h new file mode 100644 index 0000000000..3354f61df6 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h @@ -0,0 +1,49 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_RRTR_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/system_wrappers/include/ntp_time.h" + +namespace webrtc { +namespace rtcp { + +class Rrtr { + public: + static const uint8_t kBlockType = 4; + static const uint16_t kBlockLength = 2; + static const size_t kLength = 4 * (kBlockLength + 1); // 12 + + Rrtr() {} + Rrtr(const Rrtr&) = default; + ~Rrtr() {} + + Rrtr& operator=(const Rrtr&) = default; + + void Parse(const uint8_t* buffer); + + // Fills buffer with the Rrtr. + // Consumes Rrtr::kLength bytes. + void Create(uint8_t* buffer) const; + + void WithNtp(const NtpTime& ntp) { ntp_ = ntp; } + + NtpTime ntp() const { return ntp_; } + + private: + NtpTime ntp_; +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr_unittest.cc new file mode 100644 index 0000000000..6536e06186 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr_unittest.cc @@ -0,0 +1,51 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h" + +#include "testing/gtest/include/gtest/gtest.h" + +using webrtc::rtcp::Rrtr; + +namespace webrtc { +namespace { + +const uint32_t kNtpSec = 0x12345678; +const uint32_t kNtpFrac = 0x23456789; +const uint8_t kBlock[] = {0x04, 0x00, 0x00, 0x02, + 0x12, 0x34, 0x56, 0x78, + 0x23, 0x45, 0x67, 0x89}; +const size_t kBlockSizeBytes = sizeof(kBlock); +static_assert( + kBlockSizeBytes == Rrtr::kLength, + "Size of manually created Rrtr block should match class constant"); + +TEST(RtcpPacketRrtrTest, Create) { + uint8_t buffer[Rrtr::kLength]; + Rrtr rrtr; + rrtr.WithNtp(NtpTime(kNtpSec, kNtpFrac)); + + rrtr.Create(buffer); + EXPECT_EQ(0, memcmp(buffer, kBlock, kBlockSizeBytes)); +} + +TEST(RtcpPacketRrtrTest, Parse) { + Rrtr read_rrtr; + read_rrtr.Parse(kBlock); + + // Run checks on const object to ensure all accessors have const modifier. + const Rrtr& parsed = read_rrtr; + + EXPECT_EQ(kNtpSec, parsed.ntp().seconds()); + EXPECT_EQ(kNtpFrac, parsed.ntp().fractions()); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.cc new file mode 100644 index 0000000000..b5571d45a3 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.cc @@ -0,0 +1,45 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h" + +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +namespace webrtc { +namespace rtcp { + +// RFC 4585, Section 6.1: 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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 0 | SSRC of packet sender | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | SSRC of media source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : : + +void Rtpfb::ParseCommonFeedback(const uint8_t* payload) { + sender_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&payload[0]); + media_ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&payload[4]); +} + +void Rtpfb::CreateCommonFeedback(uint8_t* payload) const { + ByteWriter<uint32_t>::WriteBigEndian(&payload[0], sender_ssrc_); + ByteWriter<uint32_t>::WriteBigEndian(&payload[4], media_ssrc_); +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h new file mode 100644 index 0000000000..801aa085c4 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h @@ -0,0 +1,48 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_RTPFB_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RTPFB_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" + +namespace webrtc { +namespace rtcp { + +// RTPFB: Transport layer feedback message. +// RFC4585, Section 6.2 +class Rtpfb : public RtcpPacket { + public: + static const uint8_t kPacketType = 205; + + Rtpfb() : sender_ssrc_(0), media_ssrc_(0) {} + virtual ~Rtpfb() {} + + void From(uint32_t ssrc) { sender_ssrc_ = ssrc; } + void To(uint32_t ssrc) { media_ssrc_ = ssrc; } + + uint32_t sender_ssrc() const { return sender_ssrc_; } + uint32_t media_ssrc() const { return media_ssrc_; } + + protected: + static const size_t kCommonFeedbackLength = 8; + void ParseCommonFeedback(const uint8_t* payload); + void CreateCommonFeedback(uint8_t* payload) const; + + private: + uint32_t sender_ssrc_; + uint32_t media_ssrc_; +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RTPFB_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli.cc new file mode 100644 index 0000000000..829f3a9db9 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli.cc @@ -0,0 +1,108 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sli.h" + +#include "webrtc/base/checks.h" +#include "webrtc/base/logging.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +using webrtc::RTCPUtility::RtcpCommonHeader; + +namespace webrtc { +namespace rtcp { +// 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 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// : Feedback Control Information (FCI) : +// : : +// +// Slice loss indication (SLI) (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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// | First | Number | PictureID | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +Sli::Macroblocks::Macroblocks(uint8_t picture_id, + uint16_t first, + uint16_t number) { + RTC_DCHECK_LE(first, 0x1fff); + RTC_DCHECK_LE(number, 0x1fff); + RTC_DCHECK_LE(picture_id, 0x3f); + item_ = (first << 19) | (number << 6) | picture_id; +} + +void Sli::Macroblocks::Parse(const uint8_t* buffer) { + item_ = ByteReader<uint32_t>::ReadBigEndian(buffer); +} + +void Sli::Macroblocks::Create(uint8_t* buffer) const { + ByteWriter<uint32_t>::WriteBigEndian(buffer, item_); +} + +bool Sli::Parse(const RtcpCommonHeader& header, const uint8_t* payload) { + RTC_DCHECK(header.packet_type == kPacketType); + RTC_DCHECK(header.count_or_format == kFeedbackMessageType); + + if (header.payload_size_bytes < + kCommonFeedbackLength + Macroblocks::kLength) { + LOG(LS_WARNING) << "Packet is too small to be a valid SLI packet"; + return false; + } + + size_t number_of_items = + (header.payload_size_bytes - kCommonFeedbackLength) / + Macroblocks::kLength; + + ParseCommonFeedback(payload); + items_.resize(number_of_items); + + const uint8_t* next_item = payload + kCommonFeedbackLength; + for (Macroblocks& item : items_) { + item.Parse(next_item); + next_item += Macroblocks::kLength; + } + + return true; +} + +bool Sli::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; + } + CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet, + index); + CreateCommonFeedback(packet + *index); + *index += kCommonFeedbackLength; + for (const Macroblocks& item : items_) { + item.Create(packet + *index); + *index += Macroblocks::kLength; + } + return true; +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli.h new file mode 100644 index 0000000000..5d9e6c93e9 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli.h @@ -0,0 +1,81 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_SLI_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SLI_H_ + +#include <vector> + +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_utility.h" + +namespace webrtc { +namespace rtcp { + +// Slice loss indication (SLI) (RFC 4585). +class Sli : public Psfb { + public: + static const uint8_t kFeedbackMessageType = 2; + class Macroblocks { + public: + static const size_t kLength = 4; + Macroblocks() : item_(0) {} + Macroblocks(uint8_t picture_id, uint16_t first, uint16_t number); + ~Macroblocks() {} + + void Parse(const uint8_t* buffer); + void Create(uint8_t* buffer) const; + + uint16_t first() const { return item_ >> 19; } + uint16_t number() const { return (item_ >> 6) & 0x1fff; } + uint8_t picture_id() const { return (item_ & 0x3f); } + + private: + uint32_t item_; + }; + + Sli() {} + virtual ~Sli() {} + + // 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 WithPictureId(uint8_t picture_id, + uint16_t first_macroblock = 0, + uint16_t number_macroblocks = 0x1fff) { + items_.push_back( + Macroblocks(picture_id, first_macroblock, number_macroblocks)); + } + + const std::vector<Macroblocks>& macroblocks() const { return items_; } + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + private: + size_t BlockLength() const override { + return RtcpPacket::kHeaderLength + Psfb::kCommonFeedbackLength + + items_.size() * Macroblocks::kLength; + } + + std::vector<Macroblocks> items_; + + RTC_DISALLOW_COPY_AND_ASSIGN(Sli); +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SLI_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli_unittest.cc new file mode 100644 index 0000000000..c2be16846b --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/sli_unittest.cc @@ -0,0 +1,91 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/sli.h" + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" + +using testing::ElementsAreArray; +using testing::make_tuple; +using webrtc::rtcp::RawPacket; +using webrtc::rtcp::Sli; +using webrtc::RTCPUtility::RtcpCommonHeader; +using webrtc::RTCPUtility::RtcpParseCommonHeader; + +namespace webrtc { +namespace { + +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kRemoteSsrc = 0x23456789; + +const uint8_t kPictureId = 0x3f; +const uint16_t kFirstMb = 0x1e61; +const uint16_t kNumberOfMb = 0x1a0a; +const uint32_t kSliItem = (static_cast<uint32_t>(kFirstMb) << 19) | + (static_cast<uint32_t>(kNumberOfMb) << 6) | + static_cast<uint32_t>(kPictureId); + +// Manually created Sli packet matching constants above. +const uint8_t kPacket[] = {0x82, 206, 0x00, 0x03, + 0x12, 0x34, 0x56, 0x78, + 0x23, 0x45, 0x67, 0x89, + (kSliItem >> 24) & 0xff, + (kSliItem >> 16) & 0xff, + (kSliItem >> 8) & 0xff, + kSliItem & 0xff}; +const size_t kPacketLength = sizeof(kPacket); + +bool ParseSli(const uint8_t* buffer, size_t length, Sli* sli) { + RtcpCommonHeader header; + EXPECT_TRUE(RtcpParseCommonHeader(buffer, length, &header)); + EXPECT_EQ(length, header.BlockSize()); + return sli->Parse(header, buffer + RtcpCommonHeader::kHeaderSizeBytes); +} + +TEST(RtcpPacketSliTest, Create) { + Sli sli; + sli.From(kSenderSsrc); + sli.To(kRemoteSsrc); + sli.WithPictureId(kPictureId, kFirstMb, kNumberOfMb); + + rtc::scoped_ptr<RawPacket> packet(sli.Build()); + + EXPECT_THAT(make_tuple(packet->Buffer(), packet->Length()), + ElementsAreArray(kPacket)); +} + +TEST(RtcpPacketSliTest, Parse) { + Sli mutable_parsed; + EXPECT_TRUE(ParseSli(kPacket, kPacketLength, &mutable_parsed)); + const Sli& parsed = mutable_parsed; // Read values from constant object. + + EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc()); + EXPECT_EQ(kRemoteSsrc, parsed.media_ssrc()); + EXPECT_EQ(1u, parsed.macroblocks().size()); + EXPECT_EQ(kFirstMb, parsed.macroblocks()[0].first()); + EXPECT_EQ(kNumberOfMb, parsed.macroblocks()[0].number()); + EXPECT_EQ(kPictureId, parsed.macroblocks()[0].picture_id()); +} + +TEST(RtcpPacketSliTest, ParseFailsOnTooSmallPacket) { + Sli sli; + sli.From(kSenderSsrc); + sli.To(kRemoteSsrc); + sli.WithPictureId(kPictureId, kFirstMb, kNumberOfMb); + + rtc::scoped_ptr<RawPacket> packet(sli.Build()); + packet->MutableBuffer()[3]--; // Decrease size by 1 word (4 bytes). + + EXPECT_FALSE(ParseSli(packet->Buffer(), packet->Length() - 4, &sli)); +} + +} // namespace +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc new file mode 100644 index 0000000000..fd0219cf82 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc @@ -0,0 +1,119 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.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; + +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); +} + +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); +} + +// Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104). +// +// 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| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +void CreateTmmbn(const RTCPPacketRTPFBTMMBN& tmmbn, + const std::vector<RTCPPacketRTPFBTMMBRItem>& 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."; + return false; + } + RTCPPacketRTPFBTMMBRItem tmmbn_item; + tmmbn_item.SSRC = ssrc; + tmmbn_item.MaxTotalMediaBitRate = bitrate_kbps; + tmmbn_item.MeasuredOverhead = overhead; + tmmbn_items_.push_back(tmmbn_item); + return true; +} + +bool Tmmbn::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + while (*index + BlockLength() > max_length) { + 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); + 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 new file mode 100644 index 0000000000..82bf9dd9e9 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h @@ -0,0 +1,60 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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 <vector> +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.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 { + public: + Tmmbn() : RtcpPacket() { + memset(&tmmbn_, 0, sizeof(tmmbn_)); + } + + virtual ~Tmmbn() {} + + void From(uint32_t ssrc) { + tmmbn_.SenderSSRC = ssrc; + } + // Max 50 TMMBR can be added per TMMBN. + bool WithTmmbr(uint32_t ssrc, uint32_t bitrate_kbps, uint16_t overhead); + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + 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(); + } + + RTCPUtility::RTCPPacketRTPFBTMMBN tmmbn_; + std::vector<RTCPUtility::RTCPPacketRTPFBTMMBRItem> tmmbn_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 new file mode 100644 index 0000000000..32d64a97b4 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.cc @@ -0,0 +1,84 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.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 webrtc::rtcp::RawPacket; +using webrtc::rtcp::Tmmbn; +using webrtc::test::RtcpPacketParser; + +namespace webrtc { +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kRemoteSsrc = 0x23456789; + +TEST(RtcpPacketTest, TmmbnWithNoItem) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + + rtc::scoped_ptr<RawPacket> 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()); +} + +TEST(RtcpPacketTest, TmmbnWithOneItem) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + EXPECT_TRUE(tmmbn.WithTmmbr(kRemoteSsrc, 312, 60)); + + rtc::scoped_ptr<RawPacket> 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)); +} + +TEST(RtcpPacketTest, TmmbnWithTwoItems) { + Tmmbn tmmbn; + tmmbn.From(kSenderSsrc); + EXPECT_TRUE(tmmbn.WithTmmbr(kRemoteSsrc, 312, 60)); + EXPECT_TRUE(tmmbn.WithTmmbr(kRemoteSsrc + 1, 1288, 40)); + + rtc::scoped_ptr<RawPacket> 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)); +} + +TEST(RtcpPacketTest, TmmbnWithTooManyItems) { + 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)); +} + +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc new file mode 100644 index 0000000000..4df167de79 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc @@ -0,0 +1,105 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.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; + +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); +} + +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); +} + +// Temporary Maximum Media Stream Bit Rate Request (TMMBR) (RFC 5104). +// +// 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| +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ + +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); +} +} // namespace + +bool Tmmbr::Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const { + 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); + 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 new file mode 100644 index 0000000000..84a4180ad3 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h @@ -0,0 +1,64 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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 "webrtc/base/basictypes.h" +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet.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 { + public: + Tmmbr() : RtcpPacket() { + memset(&tmmbr_, 0, sizeof(tmmbr_)); + memset(&tmmbr_item_, 0, sizeof(tmmbr_item_)); + } + + virtual ~Tmmbr() {} + + 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; + } + + protected: + bool Create(uint8_t* packet, + size_t* index, + size_t max_length, + RtcpPacket::PacketReadyCallback* callback) const override; + + private: + size_t BlockLength() const { + const size_t kFciLen = 8; + return kCommonFbFmtLength + kFciLen; + } + + RTCPUtility::RTCPPacketRTPFBTMMBR tmmbr_; + RTCPUtility::RTCPPacketRTPFBTMMBRItem tmmbr_item_; + + RTC_DISALLOW_COPY_AND_ASSIGN(Tmmbr); +}; +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBR_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc new file mode 100644 index 0000000000..6d71caa251 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc @@ -0,0 +1,43 @@ +/* + * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.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 webrtc::rtcp::RawPacket; +using webrtc::rtcp::Tmmbr; +using webrtc::test::RtcpPacketParser; + +namespace webrtc { +const uint32_t kSenderSsrc = 0x12345678; +const uint32_t kRemoteSsrc = 0x23456789; + +TEST(RtcpPacketTest, Tmmbr) { + Tmmbr tmmbr; + tmmbr.From(kSenderSsrc); + tmmbr.To(kRemoteSsrc); + tmmbr.WithBitrateKbps(312); + tmmbr.WithOverhead(60); + + rtc::scoped_ptr<RawPacket> 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()); +} + +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h index 4cc1f38479..ad6fd166f2 100644 --- a/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h @@ -15,7 +15,7 @@ #include <vector> #include "webrtc/base/constructormagic.h" -#include "webrtc/modules/interface/module_common_types.h" +#include "webrtc/modules/include/module_common_types.h" #include "webrtc/modules/rtp_rtcp/source/rtcp_packet.h" namespace webrtc { diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.cc new file mode 100644 index 0000000000..a79d48e1ca --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.cc @@ -0,0 +1,107 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.h" + +#include "webrtc/base/checks.h" +#include "webrtc/modules/rtp_rtcp/source/byte_io.h" + +namespace webrtc { +namespace rtcp { +// VoIP Metrics Report Block (RFC 3611). +// +// 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 +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 0 | BT=7 | reserved | block length = 8 | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 4 | SSRC of source | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 8 | loss rate | discard rate | burst density | gap density | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 12 | burst duration | gap duration | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 16 | round trip delay | end system delay | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 20 | signal level | noise level | RERL | Gmin | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 24 | R factor | ext. R factor | MOS-LQ | MOS-CQ | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 28 | RX config | reserved | JB nominal | +// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +// 32 | JB maximum | JB abs max | +// 36 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+ +VoipMetric::VoipMetric() : ssrc_(0) { + memset(&voip_metric_, 0, sizeof(voip_metric_)); +} + +void VoipMetric::Parse(const uint8_t* buffer) { + RTC_DCHECK(buffer[0] == kBlockType); + // reserved = buffer[1]; + RTC_DCHECK(ByteReader<uint16_t>::ReadBigEndian(&buffer[2]) == kBlockLength); + ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[4]); + voip_metric_.lossRate = buffer[8]; + voip_metric_.discardRate = buffer[9]; + voip_metric_.burstDensity = buffer[10]; + voip_metric_.gapDensity = buffer[11]; + voip_metric_.burstDuration = ByteReader<uint16_t>::ReadBigEndian(&buffer[12]); + voip_metric_.gapDuration = ByteReader<uint16_t>::ReadBigEndian(&buffer[14]); + voip_metric_.roundTripDelay = + ByteReader<uint16_t>::ReadBigEndian(&buffer[16]); + voip_metric_.endSystemDelay = + ByteReader<uint16_t>::ReadBigEndian(&buffer[18]); + voip_metric_.signalLevel = buffer[20]; + voip_metric_.noiseLevel = buffer[21]; + voip_metric_.RERL = buffer[22]; + voip_metric_.Gmin = buffer[23]; + voip_metric_.Rfactor = buffer[24]; + voip_metric_.extRfactor = buffer[25]; + voip_metric_.MOSLQ = buffer[26]; + voip_metric_.MOSCQ = buffer[27]; + voip_metric_.RXconfig = buffer[28]; + // reserved = buffer[29]; + voip_metric_.JBnominal = ByteReader<uint16_t>::ReadBigEndian(&buffer[30]); + voip_metric_.JBmax = ByteReader<uint16_t>::ReadBigEndian(&buffer[32]); + voip_metric_.JBabsMax = ByteReader<uint16_t>::ReadBigEndian(&buffer[34]); +} + +void VoipMetric::Create(uint8_t* buffer) const { + const uint8_t kReserved = 0; + buffer[0] = kBlockType; + buffer[1] = kReserved; + ByteWriter<uint16_t>::WriteBigEndian(&buffer[2], kBlockLength); + ByteWriter<uint32_t>::WriteBigEndian(&buffer[4], ssrc_); + buffer[8] = voip_metric_.lossRate; + buffer[9] = voip_metric_.discardRate; + buffer[10] = voip_metric_.burstDensity; + buffer[11] = voip_metric_.gapDensity; + ByteWriter<uint16_t>::WriteBigEndian(&buffer[12], voip_metric_.burstDuration); + ByteWriter<uint16_t>::WriteBigEndian(&buffer[14], voip_metric_.gapDuration); + ByteWriter<uint16_t>::WriteBigEndian(&buffer[16], + voip_metric_.roundTripDelay); + ByteWriter<uint16_t>::WriteBigEndian(&buffer[18], + voip_metric_.endSystemDelay); + buffer[20] = voip_metric_.signalLevel; + buffer[21] = voip_metric_.noiseLevel; + buffer[22] = voip_metric_.RERL; + buffer[23] = voip_metric_.Gmin; + buffer[24] = voip_metric_.Rfactor; + buffer[25] = voip_metric_.extRfactor; + buffer[26] = voip_metric_.MOSLQ; + buffer[27] = voip_metric_.MOSCQ; + buffer[28] = voip_metric_.RXconfig; + buffer[29] = kReserved; + ByteWriter<uint16_t>::WriteBigEndian(&buffer[30], voip_metric_.JBnominal); + ByteWriter<uint16_t>::WriteBigEndian(&buffer[32], voip_metric_.JBmax); + ByteWriter<uint16_t>::WriteBigEndian(&buffer[34], voip_metric_.JBabsMax); +} + +} // namespace rtcp +} // namespace webrtc diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.h b/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.h new file mode 100644 index 0000000000..9e3e41995a --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.h @@ -0,0 +1,53 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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_VOIP_METRIC_H_ +#define WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_VOIP_METRIC_H_ + +#include "webrtc/base/basictypes.h" +#include "webrtc/modules/include/module_common_types.h" + +namespace webrtc { +namespace rtcp { + +class VoipMetric { + public: + static const uint8_t kBlockType = 7; + static const uint16_t kBlockLength = 8; + static const size_t kLength = 4 * (kBlockLength + 1); // 36 + VoipMetric(); + VoipMetric(const VoipMetric&) = default; + ~VoipMetric() {} + + VoipMetric& operator=(const VoipMetric&) = default; + + void Parse(const uint8_t* buffer); + + // Fills buffer with the VoipMetric. + // Consumes VoipMetric::kLength bytes. + void Create(uint8_t* buffer) const; + + void To(uint32_t ssrc) { ssrc_ = ssrc; } + void WithVoipMetric(const RTCPVoIPMetric& voip_metric) { + voip_metric_ = voip_metric; + } + + uint32_t ssrc() const { return ssrc_; } + const RTCPVoIPMetric& voip_metric() const { return voip_metric_; } + + private: + uint32_t ssrc_; + RTCPVoIPMetric voip_metric_; +}; + +} // namespace rtcp +} // namespace webrtc +#endif // WEBRTC_MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_VOIP_METRIC_H_ diff --git a/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric_unittest.cc b/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric_unittest.cc new file mode 100644 index 0000000000..44c82d67a9 --- /dev/null +++ b/webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric_unittest.cc @@ -0,0 +1,93 @@ +/* + * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. + * + * Use of this source code is governed by a BSD-style license + * that can be found in the LICENSE file in the root of the source + * 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. + */ + +#include "webrtc/modules/rtp_rtcp/source/rtcp_packet/voip_metric.h" + +#include "testing/gtest/include/gtest/gtest.h" + +namespace webrtc { +namespace rtcp { +namespace { + +const uint32_t kRemoteSsrc = 0x23456789; +const uint8_t kBlock[] = {0x07, 0x00, 0x00, 0x08, 0x23, 0x45, 0x67, 0x89, + 0x01, 0x02, 0x03, 0x04, 0x11, 0x12, 0x22, 0x23, + 0x33, 0x34, 0x44, 0x45, 0x05, 0x06, 0x07, 0x08, + 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x00, 0x55, 0x56, + 0x66, 0x67, 0x77, 0x78}; +const size_t kBlockSizeBytes = sizeof(kBlock); +static_assert( + kBlockSizeBytes == VoipMetric::kLength, + "Size of manually created Voip Metric block should match class constant"); + +TEST(RtcpPacketVoipMetricTest, Create) { + uint8_t buffer[VoipMetric::kLength]; + RTCPVoIPMetric metric; + metric.lossRate = 1; + metric.discardRate = 2; + metric.burstDensity = 3; + metric.gapDensity = 4; + metric.burstDuration = 0x1112; + metric.gapDuration = 0x2223; + metric.roundTripDelay = 0x3334; + metric.endSystemDelay = 0x4445; + metric.signalLevel = 5; + metric.noiseLevel = 6; + metric.RERL = 7; + metric.Gmin = 8; + metric.Rfactor = 9; + metric.extRfactor = 10; + metric.MOSLQ = 11; + metric.MOSCQ = 12; + metric.RXconfig = 13; + metric.JBnominal = 0x5556; + metric.JBmax = 0x6667; + metric.JBabsMax = 0x7778; + VoipMetric metric_block; + metric_block.To(kRemoteSsrc); + metric_block.WithVoipMetric(metric); + + metric_block.Create(buffer); + EXPECT_EQ(0, memcmp(buffer, kBlock, kBlockSizeBytes)); +} + +TEST(RtcpPacketVoipMetricTest, Parse) { + VoipMetric read_metric; + read_metric.Parse(kBlock); + + // Run checks on const object to ensure all accessors have const modifier. + const VoipMetric& parsed = read_metric; + + EXPECT_EQ(kRemoteSsrc, parsed.ssrc()); + EXPECT_EQ(1, parsed.voip_metric().lossRate); + EXPECT_EQ(2, parsed.voip_metric().discardRate); + EXPECT_EQ(3, parsed.voip_metric().burstDensity); + EXPECT_EQ(4, parsed.voip_metric().gapDensity); + EXPECT_EQ(0x1112, parsed.voip_metric().burstDuration); + EXPECT_EQ(0x2223, parsed.voip_metric().gapDuration); + EXPECT_EQ(0x3334, parsed.voip_metric().roundTripDelay); + EXPECT_EQ(0x4445, parsed.voip_metric().endSystemDelay); + EXPECT_EQ(5, parsed.voip_metric().signalLevel); + EXPECT_EQ(6, parsed.voip_metric().noiseLevel); + EXPECT_EQ(7, parsed.voip_metric().RERL); + EXPECT_EQ(8, parsed.voip_metric().Gmin); + EXPECT_EQ(9, parsed.voip_metric().Rfactor); + EXPECT_EQ(10, parsed.voip_metric().extRfactor); + EXPECT_EQ(11, parsed.voip_metric().MOSLQ); + EXPECT_EQ(12, parsed.voip_metric().MOSCQ); + EXPECT_EQ(13, parsed.voip_metric().RXconfig); + EXPECT_EQ(0x5556, parsed.voip_metric().JBnominal); + EXPECT_EQ(0x6667, parsed.voip_metric().JBmax); + EXPECT_EQ(0x7778, parsed.voip_metric().JBabsMax); +} + +} // namespace +} // namespace rtcp +} // namespace webrtc |