summaryrefslogtreecommitdiffstats
path: root/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet
diff options
context:
space:
mode:
Diffstat (limited to 'third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet')
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app.cc103
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app.h67
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app_unittest.cc110
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye.cc141
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h57
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye_unittest.cc147
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.cc89
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h52
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header_unittest.cc103
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.cc50
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h47
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc155
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc94
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h80
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr_unittest.cc92
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.cc195
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h73
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc169
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir.cc113
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir.h62
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir_unittest.cc93
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification.cc133
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h82
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification_unittest.cc136
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc176
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h59
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc171
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli.cc79
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h39
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli_unittest.cc58
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.cc47
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h48
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.cc68
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h40
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request_unittest.cc64
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.cc112
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h60
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report_unittest.cc161
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb.cc143
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h59
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb_unittest.cc141
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc148
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h59
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate_unittest.cc56
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.cc91
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h70
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block_unittest.cc106
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.cc49
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h59
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr_unittest.cc50
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.cc45
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h47
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.cc199
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.h56
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes_unittest.cc244
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.cc141
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.h81
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report_unittest.cc142
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.cc127
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h63
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate_unittest.cc96
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.cc71
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h52
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc109
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h55
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.cc105
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc111
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h54
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc89
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc737
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h185
-rw-r--r--third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc667
72 files changed, 8132 insertions, 0 deletions
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app.cc
new file mode 100644
index 0000000000..d5734c6dd5
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app.cc
@@ -0,0 +1,103 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/app.h"
+
+#include <string.h>
+
+#include <cstdint>
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t App::kPacketType;
+constexpr size_t App::kMaxDataSize;
+// 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 ...
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+App::App() : sub_type_(0), name_(0) {}
+
+App::~App() = default;
+
+bool App::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ if (packet.payload_size_bytes() < kAppBaseLength) {
+ RTC_LOG(LS_WARNING) << "Packet is too small to be a valid APP packet";
+ return false;
+ }
+ if (packet.payload_size_bytes() % 4 != 0) {
+ RTC_LOG(LS_WARNING)
+ << "Packet payload must be 32 bits aligned to make a valid APP packet";
+ return false;
+ }
+ sub_type_ = packet.fmt();
+ SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(&packet.payload()[0]));
+ name_ = ByteReader<uint32_t>::ReadBigEndian(&packet.payload()[4]);
+ data_.SetData(packet.payload() + kAppBaseLength,
+ packet.payload_size_bytes() - kAppBaseLength);
+ return true;
+}
+
+void App::SetSubType(uint8_t subtype) {
+ RTC_DCHECK_LE(subtype, 0x1f);
+ sub_type_ = subtype;
+}
+
+void App::SetData(const uint8_t* data, size_t data_length) {
+ RTC_DCHECK(data);
+ RTC_DCHECK_EQ(data_length % 4, 0) << "Data must be 32 bits aligned.";
+ RTC_DCHECK_LE(data_length, kMaxDataSize)
+ << "App data size " << data_length << " exceed maximum of "
+ << kMaxDataSize << " bytes.";
+ data_.SetData(data, data_length);
+}
+
+size_t App::BlockLength() const {
+ return kHeaderLength + kAppBaseLength + data_.size();
+}
+
+bool App::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ 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], sender_ssrc());
+ ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 4], name_);
+ if (!data_.empty()) {
+ 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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app.h
new file mode 100644
index 0000000000..4518792e5a
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app.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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_APP_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_APP_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+#include "rtc_base/buffer.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+class App : public RtcpPacket {
+ public:
+ static constexpr uint8_t kPacketType = 204;
+ App();
+ App(App&&) = default;
+ ~App() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ void SetSubType(uint8_t subtype);
+ void SetName(uint32_t name) { name_ = name; }
+ void SetData(const uint8_t* data, size_t data_length);
+
+ uint8_t sub_type() const { return sub_type_; }
+ uint32_t name() const { return name_; }
+ size_t data_size() const { return data_.size(); }
+ const uint8_t* data() const { return data_.data(); }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ static inline constexpr uint32_t NameToInt(const char name[5]) {
+ return static_cast<uint32_t>(name[0]) << 24 |
+ static_cast<uint32_t>(name[1]) << 16 |
+ static_cast<uint32_t>(name[2]) << 8 | static_cast<uint32_t>(name[3]);
+ }
+
+ private:
+ static constexpr size_t kAppBaseLength = 8; // Ssrc and Name.
+ static constexpr size_t kMaxDataSize = 0xffff * 4 - kAppBaseLength;
+
+ uint8_t sub_type_;
+ uint32_t name_;
+ rtc::Buffer data_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_APP_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app_unittest.cc
new file mode 100644
index 0000000000..8690e8e5a0
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/app_unittest.cc
@@ -0,0 +1,110 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/app.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+namespace webrtc {
+namespace {
+
+using ::testing::ElementsAreArray;
+using ::testing::make_tuple;
+using ::webrtc::rtcp::App;
+
+constexpr uint32_t kName = ((uint32_t)'n' << 24) | ((uint32_t)'a' << 16) |
+ ((uint32_t)'m' << 8) | (uint32_t)'e';
+constexpr uint8_t kSubtype = 0x1e;
+constexpr uint32_t kSenderSsrc = 0x12345678;
+constexpr uint8_t kData[] = {'t', 'e', 's', 't', 'd', 'a', 't', 'a'};
+constexpr uint8_t kVersionBits = 2 << 6;
+constexpr uint8_t kPaddingBit = 1 << 5;
+// clang-format off
+constexpr uint8_t kPacketWithoutData[] = {
+ kVersionBits | kSubtype, App::kPacketType, 0x00, 0x02,
+ 0x12, 0x34, 0x56, 0x78,
+ 'n', 'a', 'm', 'e'};
+constexpr uint8_t kPacketWithData[] = {
+ kVersionBits | kSubtype, App::kPacketType, 0x00, 0x04,
+ 0x12, 0x34, 0x56, 0x78,
+ 'n', 'a', 'm', 'e',
+ 't', 'e', 's', 't',
+ 'd', 'a', 't', 'a'};
+constexpr uint8_t kTooSmallPacket[] = {
+ kVersionBits | kSubtype, App::kPacketType, 0x00, 0x01,
+ 0x12, 0x34, 0x56, 0x78};
+constexpr uint8_t kPaddingSize = 1;
+constexpr uint8_t kPacketWithUnalignedPayload[] = {
+ kVersionBits | kPaddingBit | kSubtype, App::kPacketType, 0x00, 0x03,
+ 0x12, 0x34, 0x56, 0x78,
+ 'n', 'a', 'm', 'e',
+ 'd', 'a', 't', kPaddingSize};
+// clang-format on
+} // namespace
+
+TEST(RtcpPacketAppTest, CreateWithoutData) {
+ App app;
+ app.SetSenderSsrc(kSenderSsrc);
+ app.SetSubType(kSubtype);
+ app.SetName(kName);
+
+ rtc::Buffer raw = app.Build();
+
+ EXPECT_THAT(make_tuple(raw.data(), raw.size()),
+ ElementsAreArray(kPacketWithoutData));
+}
+
+TEST(RtcpPacketAppTest, ParseWithoutData) {
+ App parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacketWithoutData, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(kSubtype, parsed.sub_type());
+ EXPECT_EQ(kName, parsed.name());
+ EXPECT_EQ(0u, parsed.data_size());
+}
+
+TEST(RtcpPacketAppTest, CreateWithData) {
+ App app;
+ app.SetSenderSsrc(kSenderSsrc);
+ app.SetSubType(kSubtype);
+ app.SetName(kName);
+ app.SetData(kData, sizeof(kData));
+
+ rtc::Buffer raw = app.Build();
+
+ EXPECT_THAT(make_tuple(raw.data(), raw.size()),
+ ElementsAreArray(kPacketWithData));
+}
+
+TEST(RtcpPacketAppTest, ParseWithData) {
+ App parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacketWithData, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(kSubtype, parsed.sub_type());
+ EXPECT_EQ(kName, parsed.name());
+ EXPECT_THAT(make_tuple(parsed.data(), parsed.data_size()),
+ ElementsAreArray(kData));
+}
+
+TEST(RtcpPacketAppTest, ParseFailsOnTooSmallPacket) {
+ App parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kTooSmallPacket, &parsed));
+}
+
+TEST(RtcpPacketAppTest, ParseFailsOnUnalignedPayload) {
+ App parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kPacketWithUnalignedPayload, &parsed));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye.cc
new file mode 100644
index 0000000000..a6471772b1
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye.cc
@@ -0,0 +1,141 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/bye.h"
+
+#include <string.h>
+
+#include <cstdint>
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Bye::kPacketType;
+// 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() = default;
+
+Bye::~Bye() = default;
+
+bool Bye::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+
+ const uint8_t src_count = packet.count();
+ // Validate packet.
+ if (packet.payload_size_bytes() < 4u * src_count) {
+ RTC_LOG(LS_WARNING)
+ << "Packet is too small to contain CSRCs it promise to have.";
+ return false;
+ }
+ const uint8_t* const payload = packet.payload();
+ bool has_reason = packet.payload_size_bytes() > 4u * src_count;
+ uint8_t reason_length = 0;
+ if (has_reason) {
+ reason_length = payload[4u * src_count];
+ if (packet.payload_size_bytes() - 4u * src_count < 1u + reason_length) {
+ RTC_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.
+ SetSenderSsrc(0);
+ csrcs_.clear();
+ } else {
+ SetSenderSsrc(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,
+ 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 = static_cast<uint8_t>(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, 3);
+ 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::SetCsrcs(std::vector<uint32_t> csrcs) {
+ if (csrcs.size() > kMaxNumberOfCsrcs) {
+ RTC_LOG(LS_WARNING) << "Too many CSRCs for Bye packet.";
+ return false;
+ }
+ csrcs_ = std::move(csrcs);
+ return true;
+}
+
+void Bye::SetReason(absl::string_view reason) {
+ RTC_DCHECK_LE(reason.size(), 0xffu);
+ reason_ = std::string(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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h
new file mode 100644
index 0000000000..d31205793a
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye.h
@@ -0,0 +1,57 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_BYE_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_BYE_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+class Bye : public RtcpPacket {
+ public:
+ static constexpr uint8_t kPacketType = 203;
+
+ Bye();
+ ~Bye() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ bool SetCsrcs(std::vector<uint32_t> csrcs);
+ void SetReason(absl::string_view reason);
+
+ const std::vector<uint32_t>& csrcs() const { return csrcs_; }
+ const std::string& reason() const { return reason_; }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ static const int kMaxNumberOfCsrcs = 0x1f - 1; // First item is sender SSRC.
+
+ std::vector<uint32_t> csrcs_;
+ std::string reason_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_BYE_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye_unittest.cc
new file mode 100644
index 0000000000..448c2d4194
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/bye_unittest.cc
@@ -0,0 +1,147 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/bye.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAre;
+using webrtc::rtcp::Bye;
+
+namespace webrtc {
+namespace {
+const uint32_t kSenderSsrc = 0x12345678;
+const uint32_t kCsrc1 = 0x22232425;
+const uint32_t kCsrc2 = 0x33343536;
+} // namespace
+
+TEST(RtcpPacketByeTest, CreateAndParseWithoutReason) {
+ Bye bye;
+ bye.SetSenderSsrc(kSenderSsrc);
+
+ rtc::Buffer raw = bye.Build();
+ Bye parsed_bye;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed_bye));
+
+ EXPECT_EQ(kSenderSsrc, parsed_bye.sender_ssrc());
+ EXPECT_TRUE(parsed_bye.csrcs().empty());
+ EXPECT_TRUE(parsed_bye.reason().empty());
+}
+
+TEST(RtcpPacketByeTest, CreateAndParseWithCsrcs) {
+ Bye bye;
+ bye.SetSenderSsrc(kSenderSsrc);
+ EXPECT_TRUE(bye.SetCsrcs({kCsrc1, kCsrc2}));
+ EXPECT_TRUE(bye.reason().empty());
+
+ rtc::Buffer raw = bye.Build();
+ Bye parsed_bye;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed_bye));
+
+ EXPECT_EQ(kSenderSsrc, parsed_bye.sender_ssrc());
+ EXPECT_THAT(parsed_bye.csrcs(), ElementsAre(kCsrc1, kCsrc2));
+ EXPECT_TRUE(parsed_bye.reason().empty());
+}
+
+TEST(RtcpPacketByeTest, CreateAndParseWithCsrcsAndAReason) {
+ Bye bye;
+ const std::string kReason = "Some Reason";
+
+ bye.SetSenderSsrc(kSenderSsrc);
+ EXPECT_TRUE(bye.SetCsrcs({kCsrc1, kCsrc2}));
+ bye.SetReason(kReason);
+
+ rtc::Buffer raw = bye.Build();
+ Bye parsed_bye;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed_bye));
+
+ EXPECT_EQ(kSenderSsrc, parsed_bye.sender_ssrc());
+ EXPECT_THAT(parsed_bye.csrcs(), ElementsAre(kCsrc1, kCsrc2));
+ EXPECT_EQ(kReason, parsed_bye.reason());
+}
+
+TEST(RtcpPacketByeTest, CreateWithTooManyCsrcs) {
+ Bye bye;
+ bye.SetSenderSsrc(kSenderSsrc);
+ const int kMaxCsrcs = (1 << 5) - 2; // 5 bit len, first item is sender SSRC.
+ EXPECT_TRUE(bye.SetCsrcs(std::vector<uint32_t>(kMaxCsrcs, kCsrc1)));
+ EXPECT_FALSE(bye.SetCsrcs(std::vector<uint32_t>(kMaxCsrcs + 1, kCsrc1)));
+}
+
+TEST(RtcpPacketByeTest, CreateAndParseWithAReason) {
+ Bye bye;
+ const std::string kReason = "Some Random Reason";
+
+ bye.SetSenderSsrc(kSenderSsrc);
+ bye.SetReason(kReason);
+
+ rtc::Buffer raw = bye.Build();
+ Bye parsed_bye;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed_bye));
+
+ EXPECT_EQ(kSenderSsrc, parsed_bye.sender_ssrc());
+ EXPECT_TRUE(parsed_bye.csrcs().empty());
+ EXPECT_EQ(kReason, parsed_bye.reason());
+}
+
+TEST(RtcpPacketByeTest, CreateAndParseWithReasons) {
+ // 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 bye;
+ bye.SetSenderSsrc(kSenderSsrc);
+ bye.SetReason(kReason);
+
+ rtc::Buffer raw = bye.Build();
+ Bye parsed_bye;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed_bye));
+
+ EXPECT_EQ(kReason, parsed_bye.reason());
+ }
+}
+
+TEST(RtcpPacketByeTest, ParseEmptyPacket) {
+ uint8_t kEmptyPacket[] = {0x80, Bye::kPacketType, 0, 0};
+ Bye parsed_bye;
+ EXPECT_TRUE(test::ParseSinglePacket(kEmptyPacket, &parsed_bye));
+ EXPECT_EQ(0u, parsed_bye.sender_ssrc());
+ EXPECT_TRUE(parsed_bye.csrcs().empty());
+ EXPECT_TRUE(parsed_bye.reason().empty());
+}
+
+TEST(RtcpPacketByeTest, ParseFailOnInvalidSrcCount) {
+ Bye bye;
+ bye.SetSenderSsrc(kSenderSsrc);
+
+ rtc::Buffer raw = bye.Build();
+ raw[0]++; // Damage the packet: increase ssrc count by one.
+
+ Bye parsed_bye;
+ EXPECT_FALSE(test::ParseSinglePacket(raw, &parsed_bye));
+}
+
+TEST(RtcpPacketByeTest, ParseFailOnInvalidReasonLength) {
+ Bye bye;
+ bye.SetSenderSsrc(kSenderSsrc);
+ bye.SetReason("18 characters long");
+
+ rtc::Buffer raw = bye.Build();
+ // Damage the packet: decrease payload size by 4 bytes
+ raw[3]--;
+ raw.SetSize(raw.size() - 4);
+
+ Bye parsed_bye;
+ EXPECT_FALSE(test::ParseSinglePacket(raw, &parsed_bye));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.cc
new file mode 100644
index 0000000000..5b54982220
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.cc
@@ -0,0 +1,89 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr size_t CommonHeader::kHeaderSizeBytes;
+// 0 1 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 |V=2|P| C/F |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 1 | Packet Type |
+// ----------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 2 | length |
+// --------------------------------+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// Common header for all RTCP packets, 4 octets.
+bool CommonHeader::Parse(const uint8_t* buffer, size_t size_bytes) {
+ const uint8_t kVersion = 2;
+
+ if (size_bytes < kHeaderSizeBytes) {
+ RTC_LOG(LS_WARNING)
+ << "Too little data (" << size_bytes << " byte"
+ << (size_bytes != 1 ? "s" : "")
+ << ") remaining in buffer to parse RTCP header (4 bytes).";
+ return false;
+ }
+
+ uint8_t version = buffer[0] >> 6;
+ if (version != kVersion) {
+ RTC_LOG(LS_WARNING) << "Invalid RTCP header: Version must be "
+ << static_cast<int>(kVersion) << " but was "
+ << static_cast<int>(version);
+ return false;
+ }
+
+ bool has_padding = (buffer[0] & 0x20) != 0;
+ count_or_format_ = buffer[0] & 0x1F;
+ packet_type_ = buffer[1];
+ payload_size_ = ByteReader<uint16_t>::ReadBigEndian(&buffer[2]) * 4;
+ payload_ = buffer + kHeaderSizeBytes;
+ padding_size_ = 0;
+
+ if (size_bytes < kHeaderSizeBytes + payload_size_) {
+ RTC_LOG(LS_WARNING) << "Buffer too small (" << size_bytes
+ << " bytes) to fit an RtcpPacket with a header and "
+ << payload_size_ << " bytes.";
+ return false;
+ }
+
+ if (has_padding) {
+ if (payload_size_ == 0) {
+ RTC_LOG(LS_WARNING)
+ << "Invalid RTCP header: Padding bit set but 0 payload "
+ "size specified.";
+ return false;
+ }
+
+ padding_size_ = payload_[payload_size_ - 1];
+ if (padding_size_ == 0) {
+ RTC_LOG(LS_WARNING)
+ << "Invalid RTCP header: Padding bit set but 0 padding "
+ "size specified.";
+ return false;
+ }
+ if (padding_size_ > payload_size_) {
+ RTC_LOG(LS_WARNING) << "Invalid RTCP header: Too many padding bytes ("
+ << padding_size_ << ") for a packet payload size of "
+ << payload_size_ << " bytes.";
+ return false;
+ }
+ payload_size_ -= padding_size_;
+ }
+ return true;
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h
new file mode 100644
index 0000000000..5416406091
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header.h
@@ -0,0 +1,52 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader {
+ public:
+ static constexpr size_t kHeaderSizeBytes = 4;
+
+ CommonHeader() {}
+ CommonHeader(const CommonHeader&) = default;
+ CommonHeader& operator=(const CommonHeader&) = default;
+
+ bool Parse(const uint8_t* buffer, size_t size_bytes);
+
+ uint8_t type() const { return packet_type_; }
+ // Depending on packet type same header field can be used either as count or
+ // as feedback message type (fmt). Caller expected to know how it is used.
+ uint8_t fmt() const { return count_or_format_; }
+ uint8_t count() const { return count_or_format_; }
+ size_t payload_size_bytes() const { return payload_size_; }
+ const uint8_t* payload() const { return payload_; }
+ size_t packet_size() const {
+ return kHeaderSizeBytes + payload_size_ + padding_size_;
+ }
+ // Returns pointer to the next RTCP packet in compound packet.
+ const uint8_t* NextPacket() const {
+ return payload_ + payload_size_ + padding_size_;
+ }
+
+ private:
+ uint8_t packet_type_ = 0;
+ uint8_t count_or_format_ = 0;
+ uint8_t padding_size_ = 0;
+ uint32_t payload_size_ = 0;
+ const uint8_t* payload_ = nullptr;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMMON_HEADER_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header_unittest.cc
new file mode 100644
index 0000000000..e8b4c52c68
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/common_header_unittest.cc
@@ -0,0 +1,103 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+
+#include "test/gtest.h"
+
+using webrtc::rtcp::CommonHeader;
+
+namespace webrtc {
+
+TEST(RtcpCommonHeaderTest, TooSmallBuffer) {
+ uint8_t buffer[] = {0x80, 0x00, 0x00, 0x00};
+ CommonHeader header;
+ // Buffer needs to be able to hold the header.
+ EXPECT_FALSE(header.Parse(buffer, 0));
+ EXPECT_FALSE(header.Parse(buffer, 1));
+ EXPECT_FALSE(header.Parse(buffer, 2));
+ EXPECT_FALSE(header.Parse(buffer, 3));
+ EXPECT_TRUE(header.Parse(buffer, 4));
+}
+
+TEST(RtcpCommonHeaderTest, Version) {
+ uint8_t buffer[] = {0x00, 0x00, 0x00, 0x00};
+ CommonHeader header;
+ // Version 2 is the only allowed.
+ buffer[0] = 0 << 6;
+ EXPECT_FALSE(header.Parse(buffer, sizeof(buffer)));
+ buffer[0] = 1 << 6;
+ EXPECT_FALSE(header.Parse(buffer, sizeof(buffer)));
+ buffer[0] = 2 << 6;
+ EXPECT_TRUE(header.Parse(buffer, sizeof(buffer)));
+ buffer[0] = 3 << 6;
+ EXPECT_FALSE(header.Parse(buffer, sizeof(buffer)));
+}
+
+TEST(RtcpCommonHeaderTest, PacketSize) {
+ uint8_t buffer[] = {0x80, 0x00, 0x00, 0x02, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ CommonHeader header;
+ EXPECT_FALSE(header.Parse(buffer, sizeof(buffer) - 1));
+ EXPECT_TRUE(header.Parse(buffer, sizeof(buffer)));
+ EXPECT_EQ(8u, header.payload_size_bytes());
+ EXPECT_EQ(buffer + sizeof(buffer), header.NextPacket());
+ EXPECT_EQ(sizeof(buffer), header.packet_size());
+}
+
+TEST(RtcpCommonHeaderTest, PaddingAndPayloadSize) {
+ // Set v = 2, p = 1, but leave fmt, pt as 0.
+ uint8_t buffer[] = {0xa0, 0x00, 0x00, 0x00, 0x00, 0x00,
+ 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
+ CommonHeader header;
+ // Padding bit set, but no byte for padding (can't specify padding length).
+ EXPECT_FALSE(header.Parse(buffer, 4));
+
+ buffer[3] = 2; // Set payload size to 2x32bit.
+ const size_t kPayloadSizeBytes = buffer[3] * 4;
+ const size_t kPaddingAddress =
+ CommonHeader::kHeaderSizeBytes + kPayloadSizeBytes - 1;
+
+ // Padding one byte larger than possible.
+ buffer[kPaddingAddress] = kPayloadSizeBytes + 1;
+ EXPECT_FALSE(header.Parse(buffer, sizeof(buffer)));
+
+ // Invalid zero padding size.
+ buffer[kPaddingAddress] = 0;
+ EXPECT_FALSE(header.Parse(buffer, sizeof(buffer)));
+
+ // Pure padding packet.
+ buffer[kPaddingAddress] = kPayloadSizeBytes;
+ EXPECT_TRUE(header.Parse(buffer, sizeof(buffer)));
+ EXPECT_EQ(0u, header.payload_size_bytes());
+ EXPECT_EQ(buffer + sizeof(buffer), header.NextPacket());
+ EXPECT_EQ(header.payload(), buffer + CommonHeader::kHeaderSizeBytes);
+ EXPECT_EQ(header.packet_size(), sizeof(buffer));
+
+ // Single byte of actual data.
+ buffer[kPaddingAddress] = kPayloadSizeBytes - 1;
+ EXPECT_TRUE(header.Parse(buffer, sizeof(buffer)));
+ EXPECT_EQ(1u, header.payload_size_bytes());
+ EXPECT_EQ(buffer + sizeof(buffer), header.NextPacket());
+ EXPECT_EQ(header.packet_size(), sizeof(buffer));
+}
+
+TEST(RtcpCommonHeaderTest, FormatAndPayloadType) {
+ uint8_t buffer[] = {0x9e, 0xab, 0x00, 0x00};
+ CommonHeader header;
+ EXPECT_TRUE(header.Parse(buffer, sizeof(buffer)));
+
+ EXPECT_EQ(header.count(), 0x1e);
+ EXPECT_EQ(header.fmt(), 0x1e);
+ EXPECT_EQ(header.type(), 0xab);
+ EXPECT_EQ(header.payload_size_bytes(), 0u);
+ EXPECT_EQ(header.payload(), buffer + CommonHeader::kHeaderSizeBytes);
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.cc
new file mode 100644
index 0000000000..54f3555fc6
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.cc
@@ -0,0 +1,50 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h"
+
+#include <memory>
+#include <utility>
+
+#include "rtc_base/checks.h"
+
+namespace webrtc {
+namespace rtcp {
+
+CompoundPacket::CompoundPacket() = default;
+
+CompoundPacket::~CompoundPacket() = default;
+
+void CompoundPacket::Append(std::unique_ptr<RtcpPacket> packet) {
+ RTC_CHECK(packet);
+ appended_packets_.push_back(std::move(packet));
+}
+
+bool CompoundPacket::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ for (const auto& appended : appended_packets_) {
+ if (!appended->Create(packet, index, max_length, callback))
+ return false;
+ }
+ return true;
+}
+
+size_t CompoundPacket::BlockLength() const {
+ size_t block_length = 0;
+ for (const auto& appended : appended_packets_) {
+ block_length += appended->BlockLength();
+ }
+ return block_length;
+}
+
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h
new file mode 100644
index 0000000000..d98dbd088d
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet.h
@@ -0,0 +1,47 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMPOUND_PACKET_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMPOUND_PACKET_H_
+
+#include <memory>
+#include <vector>
+
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+
+namespace webrtc {
+namespace rtcp {
+
+class CompoundPacket : public RtcpPacket {
+ public:
+ CompoundPacket();
+ ~CompoundPacket() override;
+
+ CompoundPacket(const CompoundPacket&) = delete;
+ CompoundPacket& operator=(const CompoundPacket&) = delete;
+
+ void Append(std::unique_ptr<RtcpPacket> packet);
+
+ // Size of this packet in bytes (i.e. total size of nested packets).
+ size_t BlockLength() const override;
+ // Returns true if all calls to Create succeeded.
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ protected:
+ std::vector<std::unique_ptr<RtcpPacket>> appended_packets_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_COMPOUND_PACKET_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc
new file mode 100644
index 0000000000..ba7c241215
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/compound_packet_unittest.cc
@@ -0,0 +1,155 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/compound_packet.h"
+
+#include <memory>
+#include <utility>
+
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/bye.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/fir.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::MockFunction;
+using webrtc::rtcp::Bye;
+using webrtc::rtcp::CompoundPacket;
+using webrtc::rtcp::Fir;
+using webrtc::rtcp::ReceiverReport;
+using webrtc::rtcp::ReportBlock;
+using webrtc::rtcp::SenderReport;
+using webrtc::test::RtcpPacketParser;
+
+namespace webrtc {
+
+const uint32_t kSenderSsrc = 0x12345678;
+const uint32_t kRemoteSsrc = 0x23456789;
+const uint8_t kSeqNo = 13;
+
+TEST(RtcpCompoundPacketTest, AppendPacket) {
+ CompoundPacket compound;
+ auto fir = std::make_unique<Fir>();
+ fir->AddRequestTo(kRemoteSsrc, kSeqNo);
+ ReportBlock rb;
+ auto rr = std::make_unique<ReceiverReport>();
+ rr->SetSenderSsrc(kSenderSsrc);
+ EXPECT_TRUE(rr->AddReportBlock(rb));
+ compound.Append(std::move(rr));
+ compound.Append(std::move(fir));
+
+ rtc::Buffer packet = compound.Build();
+ RtcpPacketParser parser;
+ parser.Parse(packet);
+ EXPECT_EQ(1, parser.receiver_report()->num_packets());
+ EXPECT_EQ(kSenderSsrc, parser.receiver_report()->sender_ssrc());
+ EXPECT_EQ(1u, parser.receiver_report()->report_blocks().size());
+ EXPECT_EQ(1, parser.fir()->num_packets());
+}
+
+TEST(RtcpCompoundPacketTest, AppendPacketWithOwnAppendedPacket) {
+ CompoundPacket root;
+ auto leaf = std::make_unique<CompoundPacket>();
+
+ auto fir = std::make_unique<Fir>();
+ fir->AddRequestTo(kRemoteSsrc, kSeqNo);
+ auto bye = std::make_unique<Bye>();
+ ReportBlock rb;
+
+ auto rr = std::make_unique<ReceiverReport>();
+ EXPECT_TRUE(rr->AddReportBlock(rb));
+ leaf->Append(std::move(rr));
+ leaf->Append(std::move(fir));
+
+ auto sr = std::make_unique<SenderReport>();
+ root.Append(std::move(sr));
+ root.Append(std::move(bye));
+ root.Append(std::move(leaf));
+
+ rtc::Buffer packet = root.Build();
+ RtcpPacketParser parser;
+ parser.Parse(packet);
+ EXPECT_EQ(1, parser.sender_report()->num_packets());
+ EXPECT_EQ(1, parser.receiver_report()->num_packets());
+ EXPECT_EQ(1u, parser.receiver_report()->report_blocks().size());
+ EXPECT_EQ(1, parser.bye()->num_packets());
+ EXPECT_EQ(1, parser.fir()->num_packets());
+}
+
+TEST(RtcpCompoundPacketTest, BuildWithInputBuffer) {
+ CompoundPacket compound;
+ auto fir = std::make_unique<Fir>();
+ fir->AddRequestTo(kRemoteSsrc, kSeqNo);
+ ReportBlock rb;
+ auto rr = std::make_unique<ReceiverReport>();
+ rr->SetSenderSsrc(kSenderSsrc);
+ EXPECT_TRUE(rr->AddReportBlock(rb));
+ compound.Append(std::move(rr));
+ compound.Append(std::move(fir));
+
+ const size_t kRrLength = 8;
+ const size_t kReportBlockLength = 24;
+ const size_t kFirLength = 20;
+
+ const size_t kBufferSize = kRrLength + kReportBlockLength + kFirLength;
+ MockFunction<void(rtc::ArrayView<const uint8_t>)> callback;
+ EXPECT_CALL(callback, Call(_))
+ .WillOnce(Invoke([&](rtc::ArrayView<const uint8_t> packet) {
+ RtcpPacketParser parser;
+ parser.Parse(packet);
+ EXPECT_EQ(1, parser.receiver_report()->num_packets());
+ EXPECT_EQ(1u, parser.receiver_report()->report_blocks().size());
+ EXPECT_EQ(1, parser.fir()->num_packets());
+ }));
+
+ EXPECT_TRUE(compound.Build(kBufferSize, callback.AsStdFunction()));
+}
+
+TEST(RtcpCompoundPacketTest, BuildWithTooSmallBuffer_FragmentedSend) {
+ CompoundPacket compound;
+ auto fir = std::make_unique<Fir>();
+ fir->AddRequestTo(kRemoteSsrc, kSeqNo);
+ ReportBlock rb;
+ auto rr = std::make_unique<ReceiverReport>();
+ rr->SetSenderSsrc(kSenderSsrc);
+ EXPECT_TRUE(rr->AddReportBlock(rb));
+ compound.Append(std::move(rr));
+ compound.Append(std::move(fir));
+
+ const size_t kRrLength = 8;
+ const size_t kReportBlockLength = 24;
+
+ const size_t kBufferSize = kRrLength + kReportBlockLength;
+ MockFunction<void(rtc::ArrayView<const uint8_t>)> callback;
+ EXPECT_CALL(callback, Call(_))
+ .WillOnce(Invoke([&](rtc::ArrayView<const uint8_t> packet) {
+ RtcpPacketParser parser;
+ parser.Parse(packet);
+ EXPECT_EQ(1, parser.receiver_report()->num_packets());
+ EXPECT_EQ(1U, parser.receiver_report()->report_blocks().size());
+ EXPECT_EQ(0, parser.fir()->num_packets());
+ }))
+ .WillOnce(Invoke([&](rtc::ArrayView<const uint8_t> packet) {
+ RtcpPacketParser parser;
+ parser.Parse(packet);
+ EXPECT_EQ(0, parser.receiver_report()->num_packets());
+ EXPECT_EQ(0U, parser.receiver_report()->report_blocks().size());
+ EXPECT_EQ(1, parser.fir()->num_packets());
+ }));
+
+ EXPECT_TRUE(compound.Build(kBufferSize, callback.AsStdFunction()));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc
new file mode 100644
index 0000000000..6863def2fe
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.cc
@@ -0,0 +1,94 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/numerics/safe_conversions.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
+
+Dlrr::Dlrr() = default;
+
+Dlrr::Dlrr(const Dlrr& other) = default;
+
+Dlrr::~Dlrr() = default;
+
+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) {
+ RTC_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 (ReceiveTimeInfo& 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], rtc::dchecked_cast<uint16_t>(3 * sub_blocks_.size()));
+ // Create sub blocks.
+ uint8_t* write_at = buffer + kBlockHeaderLength;
+ for (const ReceiveTimeInfo& 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);
+}
+
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h
new file mode 100644
index 0000000000..ad91dfdcc6
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr.h
@@ -0,0 +1,80 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_DLRR_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_DLRR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+namespace webrtc {
+namespace rtcp {
+struct ReceiveTimeInfo {
+ // RFC 3611 4.5
+ ReceiveTimeInfo() : ssrc(0), last_rr(0), delay_since_last_rr(0) {}
+ ReceiveTimeInfo(uint32_t ssrc, uint32_t last_rr, uint32_t delay)
+ : ssrc(ssrc), last_rr(last_rr), delay_since_last_rr(delay) {}
+
+ uint32_t ssrc;
+ uint32_t last_rr;
+ uint32_t delay_since_last_rr;
+};
+
+inline bool operator==(const ReceiveTimeInfo& lhs, const ReceiveTimeInfo& rhs) {
+ return lhs.ssrc == rhs.ssrc && lhs.last_rr == rhs.last_rr &&
+ lhs.delay_since_last_rr == rhs.delay_since_last_rr;
+}
+
+inline bool operator!=(const ReceiveTimeInfo& lhs, const ReceiveTimeInfo& rhs) {
+ return !(lhs == rhs);
+}
+
+// DLRR Report Block: Delay since the Last Receiver Report (RFC 3611).
+class Dlrr {
+ public:
+ static const uint8_t kBlockType = 5;
+
+ Dlrr();
+ Dlrr(const Dlrr& other);
+ ~Dlrr();
+
+ Dlrr& operator=(const Dlrr& other) = default;
+
+ // Dlrr without items treated same as no dlrr block.
+ explicit operator bool() const { return !sub_blocks_.empty(); }
+
+ // 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;
+
+ void ClearItems() { sub_blocks_.clear(); }
+ void AddDlrrItem(const ReceiveTimeInfo& time_info) {
+ sub_blocks_.push_back(time_info);
+ }
+
+ const std::vector<ReceiveTimeInfo>& sub_blocks() const { return sub_blocks_; }
+
+ private:
+ static const size_t kBlockHeaderLength = 4;
+ static const size_t kSubBlockLength = 12;
+
+ std::vector<ReceiveTimeInfo> sub_blocks_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_DLRR_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr_unittest.cc
new file mode 100644
index 0000000000..408d0011b8
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/dlrr_unittest.cc
@@ -0,0 +1,92 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "test/gtest.h"
+
+using webrtc::rtcp::Dlrr;
+using webrtc::rtcp::ReceiveTimeInfo;
+
+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);
+} // namespace
+
+TEST(RtcpPacketDlrrTest, Empty) {
+ Dlrr dlrr;
+
+ EXPECT_EQ(0u, dlrr.BlockLength());
+}
+
+TEST(RtcpPacketDlrrTest, Create) {
+ Dlrr dlrr;
+ dlrr.AddDlrrItem(ReceiveTimeInfo(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 ReceiveTimeInfo& 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, CreateAndParseManySubBlocks) {
+ const size_t kBufferSize = 0x1000; // More than enough.
+ const size_t kManyDlrrItems = 50;
+ uint8_t buffer[kBufferSize];
+
+ // Create.
+ Dlrr dlrr;
+ for (size_t i = 1; i <= kManyDlrrItems; ++i)
+ dlrr.AddDlrrItem(ReceiveTimeInfo(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_EQ(kManyDlrrItems, parsed.sub_blocks().size());
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.cc
new file mode 100644
index 0000000000..ce57bd5a88
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.cc
@@ -0,0 +1,195 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
+
+#include <vector>
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t ExtendedReports::kPacketType;
+constexpr size_t ExtendedReports::kMaxNumberOfDlrrItems;
+// From RFC 3611: RTP Control Protocol Extended Reports (RTCP XR).
+//
+// Format for XR packets:
+//
+// 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|reserved | PT=XR=207 | length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : report blocks :
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// Extended report block:
+// 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
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Block Type | reserved | block length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : type-specific block contents :
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+ExtendedReports::ExtendedReports() = default;
+ExtendedReports::ExtendedReports(const ExtendedReports& xr) = default;
+ExtendedReports::~ExtendedReports() = default;
+
+bool ExtendedReports::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+
+ if (packet.payload_size_bytes() < kXrBaseLength) {
+ RTC_LOG(LS_WARNING)
+ << "Packet is too small to be an ExtendedReports packet.";
+ return false;
+ }
+
+ SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(packet.payload()));
+ rrtr_block_.reset();
+ dlrr_block_.ClearItems();
+ target_bitrate_ = absl::nullopt;
+
+ const uint8_t* current_block = packet.payload() + kXrBaseLength;
+ const uint8_t* const packet_end =
+ packet.payload() + packet.payload_size_bytes();
+ constexpr size_t kBlockHeaderSizeBytes = 4;
+ while (current_block + kBlockHeaderSizeBytes <= packet_end) {
+ uint8_t block_type = ByteReader<uint8_t>::ReadBigEndian(current_block);
+ uint16_t block_length =
+ ByteReader<uint16_t>::ReadBigEndian(current_block + 2);
+ const uint8_t* next_block =
+ current_block + kBlockHeaderSizeBytes + block_length * 4;
+ if (next_block > packet_end) {
+ RTC_LOG(LS_WARNING)
+ << "Report block in extended report packet is too big.";
+ return false;
+ }
+ switch (block_type) {
+ case Rrtr::kBlockType:
+ ParseRrtrBlock(current_block, block_length);
+ break;
+ case Dlrr::kBlockType:
+ ParseDlrrBlock(current_block, block_length);
+ break;
+ case TargetBitrate::kBlockType:
+ ParseTargetBitrateBlock(current_block, block_length);
+ break;
+ default:
+ // Unknown block, ignore.
+ RTC_LOG(LS_WARNING)
+ << "Unknown extended report block type " << block_type;
+ break;
+ }
+ current_block = next_block;
+ }
+
+ return true;
+}
+
+void ExtendedReports::SetRrtr(const Rrtr& rrtr) {
+ if (rrtr_block_)
+ RTC_LOG(LS_WARNING) << "Rrtr already set, overwriting.";
+ rrtr_block_.emplace(rrtr);
+}
+
+bool ExtendedReports::AddDlrrItem(const ReceiveTimeInfo& time_info) {
+ if (dlrr_block_.sub_blocks().size() >= kMaxNumberOfDlrrItems) {
+ RTC_LOG(LS_WARNING) << "Reached maximum number of DLRR items.";
+ return false;
+ }
+ dlrr_block_.AddDlrrItem(time_info);
+ return true;
+}
+
+void ExtendedReports::SetTargetBitrate(const TargetBitrate& bitrate) {
+ if (target_bitrate_)
+ RTC_LOG(LS_WARNING) << "TargetBitrate already set, overwriting.";
+
+ target_bitrate_ = bitrate;
+}
+
+size_t ExtendedReports::BlockLength() const {
+ return kHeaderLength + kXrBaseLength + RrtrLength() + DlrrLength() +
+ TargetBitrateLength();
+}
+
+bool ExtendedReports::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ while (*index + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ }
+ size_t index_end = *index + BlockLength();
+ const uint8_t kReserved = 0;
+ CreateHeader(kReserved, kPacketType, HeaderLength(), packet, index);
+ ByteWriter<uint32_t>::WriteBigEndian(packet + *index, sender_ssrc());
+ *index += sizeof(uint32_t);
+ if (rrtr_block_) {
+ rrtr_block_->Create(packet + *index);
+ *index += Rrtr::kLength;
+ }
+ if (dlrr_block_) {
+ dlrr_block_.Create(packet + *index);
+ *index += dlrr_block_.BlockLength();
+ }
+ if (target_bitrate_) {
+ target_bitrate_->Create(packet + *index);
+ *index += target_bitrate_->BlockLength();
+ }
+ RTC_CHECK_EQ(*index, index_end);
+ return true;
+}
+
+size_t ExtendedReports::TargetBitrateLength() const {
+ if (target_bitrate_)
+ return target_bitrate_->BlockLength();
+ return 0;
+}
+
+void ExtendedReports::ParseRrtrBlock(const uint8_t* block,
+ uint16_t block_length) {
+ if (block_length != Rrtr::kBlockLength) {
+ RTC_LOG(LS_WARNING) << "Incorrect rrtr block size " << block_length
+ << " Should be " << Rrtr::kBlockLength;
+ return;
+ }
+ if (rrtr_block_) {
+ RTC_LOG(LS_WARNING)
+ << "Two rrtr blocks found in same Extended Report packet";
+ return;
+ }
+ rrtr_block_.emplace();
+ rrtr_block_->Parse(block);
+}
+
+void ExtendedReports::ParseDlrrBlock(const uint8_t* block,
+ uint16_t block_length) {
+ if (dlrr_block_) {
+ RTC_LOG(LS_WARNING)
+ << "Two Dlrr blocks found in same Extended Report packet";
+ return;
+ }
+ dlrr_block_.Parse(block, block_length);
+}
+
+void ExtendedReports::ParseTargetBitrateBlock(const uint8_t* block,
+ uint16_t block_length) {
+ target_bitrate_.emplace();
+ target_bitrate_->Parse(block, block_length);
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h
new file mode 100644
index 0000000000..6c804bbc7b
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports.h
@@ -0,0 +1,73 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_EXTENDED_REPORTS_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_EXTENDED_REPORTS_H_
+
+#include <vector>
+
+#include "absl/types/optional.h"
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/dlrr.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/rrtr.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+// From RFC 3611: RTP Control Protocol Extended Reports (RTCP XR).
+class ExtendedReports : public RtcpPacket {
+ public:
+ static constexpr uint8_t kPacketType = 207;
+ static constexpr size_t kMaxNumberOfDlrrItems = 50;
+
+ ExtendedReports();
+ ExtendedReports(const ExtendedReports& xr);
+ ~ExtendedReports() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ void SetRrtr(const Rrtr& rrtr);
+ bool AddDlrrItem(const ReceiveTimeInfo& time_info);
+ void SetTargetBitrate(const TargetBitrate& target_bitrate);
+
+ const absl::optional<Rrtr>& rrtr() const { return rrtr_block_; }
+ const Dlrr& dlrr() const { return dlrr_block_; }
+ const absl::optional<TargetBitrate>& target_bitrate() const {
+ return target_bitrate_;
+ }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ static constexpr size_t kXrBaseLength = 4;
+
+ size_t RrtrLength() const { return rrtr_block_ ? Rrtr::kLength : 0; }
+ size_t DlrrLength() const { return dlrr_block_.BlockLength(); }
+ size_t TargetBitrateLength() const;
+
+ void ParseRrtrBlock(const uint8_t* block, uint16_t block_length);
+ void ParseDlrrBlock(const uint8_t* block, uint16_t block_length);
+ void ParseTargetBitrateBlock(const uint8_t* block, uint16_t block_length);
+
+ absl::optional<Rrtr> rrtr_block_;
+ Dlrr dlrr_block_; // Dlrr without items treated same as no dlrr block.
+ absl::optional<TargetBitrate> target_bitrate_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_EXTENDED_REPORTS_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc
new file mode 100644
index 0000000000..3d9a2a3408
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/extended_reports_unittest.cc
@@ -0,0 +1,169 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
+
+#include "rtc_base/random.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::make_tuple;
+using ::testing::SizeIs;
+using webrtc::rtcp::ExtendedReports;
+using webrtc::rtcp::ReceiveTimeInfo;
+using webrtc::rtcp::Rrtr;
+
+namespace webrtc {
+namespace {
+constexpr uint32_t kSenderSsrc = 0x12345678;
+constexpr uint8_t kEmptyPacket[] = {0x80, 207, 0x00, 0x01,
+ 0x12, 0x34, 0x56, 0x78};
+} // namespace
+
+class RtcpPacketExtendedReportsTest : public ::testing::Test {
+ public:
+ RtcpPacketExtendedReportsTest() : random_(0x123456789) {}
+
+ protected:
+ template <typename T>
+ T Rand() {
+ return random_.Rand<T>();
+ }
+
+ private:
+ Random random_;
+};
+
+template <>
+ReceiveTimeInfo RtcpPacketExtendedReportsTest::Rand<ReceiveTimeInfo>() {
+ uint32_t ssrc = Rand<uint32_t>();
+ uint32_t last_rr = Rand<uint32_t>();
+ uint32_t delay_since_last_rr = Rand<uint32_t>();
+ return ReceiveTimeInfo(ssrc, last_rr, delay_since_last_rr);
+}
+
+template <>
+NtpTime RtcpPacketExtendedReportsTest::Rand<NtpTime>() {
+ uint32_t secs = Rand<uint32_t>();
+ uint32_t frac = Rand<uint32_t>();
+ return NtpTime(secs, frac);
+}
+
+template <>
+Rrtr RtcpPacketExtendedReportsTest::Rand<Rrtr>() {
+ Rrtr rrtr;
+ rrtr.SetNtp(Rand<NtpTime>());
+ return rrtr;
+}
+
+TEST_F(RtcpPacketExtendedReportsTest, CreateWithoutReportBlocks) {
+ ExtendedReports xr;
+ xr.SetSenderSsrc(kSenderSsrc);
+
+ rtc::Buffer packet = xr.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kEmptyPacket));
+}
+
+TEST_F(RtcpPacketExtendedReportsTest, ParseWithoutReportBlocks) {
+ ExtendedReports parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kEmptyPacket, &parsed));
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_FALSE(parsed.rrtr());
+ EXPECT_FALSE(parsed.dlrr());
+}
+
+TEST_F(RtcpPacketExtendedReportsTest, CreateAndParseWithRrtrBlock) {
+ const Rrtr kRrtr = Rand<Rrtr>();
+ ExtendedReports xr;
+ xr.SetSenderSsrc(kSenderSsrc);
+ xr.SetRrtr(kRrtr);
+ rtc::Buffer packet = xr.Build();
+
+ ExtendedReports mparsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &mparsed));
+ const ExtendedReports& parsed = mparsed;
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(kRrtr, parsed.rrtr());
+}
+
+TEST_F(RtcpPacketExtendedReportsTest, CreateAndParseWithDlrrWithOneSubBlock) {
+ const ReceiveTimeInfo kTimeInfo = Rand<ReceiveTimeInfo>();
+ ExtendedReports xr;
+ xr.SetSenderSsrc(kSenderSsrc);
+ xr.AddDlrrItem(kTimeInfo);
+
+ rtc::Buffer packet = xr.Build();
+
+ ExtendedReports mparsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &mparsed));
+ const ExtendedReports& parsed = mparsed;
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_THAT(parsed.dlrr().sub_blocks(), ElementsAre(kTimeInfo));
+}
+
+TEST_F(RtcpPacketExtendedReportsTest, CreateAndParseWithDlrrWithTwoSubBlocks) {
+ const ReceiveTimeInfo kTimeInfo1 = Rand<ReceiveTimeInfo>();
+ const ReceiveTimeInfo kTimeInfo2 = Rand<ReceiveTimeInfo>();
+ ExtendedReports xr;
+ xr.SetSenderSsrc(kSenderSsrc);
+ xr.AddDlrrItem(kTimeInfo1);
+ xr.AddDlrrItem(kTimeInfo2);
+
+ rtc::Buffer packet = xr.Build();
+
+ ExtendedReports mparsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &mparsed));
+ const ExtendedReports& parsed = mparsed;
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_THAT(parsed.dlrr().sub_blocks(), ElementsAre(kTimeInfo1, kTimeInfo2));
+}
+
+TEST_F(RtcpPacketExtendedReportsTest, CreateLimitsTheNumberOfDlrrSubBlocks) {
+ const ReceiveTimeInfo kTimeInfo = Rand<ReceiveTimeInfo>();
+ ExtendedReports xr;
+
+ for (size_t i = 0; i < ExtendedReports::kMaxNumberOfDlrrItems; ++i)
+ EXPECT_TRUE(xr.AddDlrrItem(kTimeInfo));
+ EXPECT_FALSE(xr.AddDlrrItem(kTimeInfo));
+
+ EXPECT_THAT(xr.dlrr().sub_blocks(),
+ SizeIs(ExtendedReports::kMaxNumberOfDlrrItems));
+}
+
+TEST_F(RtcpPacketExtendedReportsTest, CreateAndParseWithMaximumReportBlocks) {
+ const Rrtr kRrtr = Rand<Rrtr>();
+
+ ExtendedReports xr;
+ xr.SetSenderSsrc(kSenderSsrc);
+ xr.SetRrtr(kRrtr);
+ for (size_t i = 0; i < ExtendedReports::kMaxNumberOfDlrrItems; ++i)
+ xr.AddDlrrItem(Rand<ReceiveTimeInfo>());
+
+ rtc::Buffer packet = xr.Build();
+
+ ExtendedReports mparsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &mparsed));
+ const ExtendedReports& parsed = mparsed;
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(kRrtr, parsed.rrtr());
+ EXPECT_THAT(parsed.dlrr().sub_blocks(),
+ ElementsAreArray(xr.dlrr().sub_blocks()));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir.cc
new file mode 100644
index 0000000000..fd4a4c947a
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir.cc
@@ -0,0 +1,113 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/fir.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Fir::kFeedbackMessageType;
+// RFC 4585: Feedback format.
+// Common packet format:
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |V=2|P| FMT | PT | length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of packet sender |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of media source (unused) = 0 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : Feedback Control Information (FCI) :
+// : :
+// Full intra request (FIR) (RFC 5104).
+// The Feedback Control Information (FCI) for the Full Intra Request
+// consists of one or more FCI entries.
+// FCI:
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | Seq nr. | Reserved = 0 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Fir::Fir() = default;
+
+Fir::Fir(const Fir& fir) = default;
+
+Fir::~Fir() = default;
+
+bool Fir::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
+
+ // The FCI field MUST contain one or more FIR entries.
+ if (packet.payload_size_bytes() < kCommonFeedbackLength + kFciLength) {
+ RTC_LOG(LS_WARNING) << "Packet is too small to be a valid FIR packet.";
+ return false;
+ }
+
+ if ((packet.payload_size_bytes() - kCommonFeedbackLength) % kFciLength != 0) {
+ RTC_LOG(LS_WARNING) << "Invalid size for a valid FIR packet.";
+ return false;
+ }
+
+ ParseCommonFeedback(packet.payload());
+
+ size_t number_of_fci_items =
+ (packet.payload_size_bytes() - kCommonFeedbackLength) / kFciLength;
+ const uint8_t* next_fci = packet.payload() + kCommonFeedbackLength;
+ items_.resize(number_of_fci_items);
+ for (Request& request : items_) {
+ request.ssrc = ByteReader<uint32_t>::ReadBigEndian(next_fci);
+ request.seq_nr = ByteReader<uint8_t>::ReadBigEndian(next_fci + 4);
+ next_fci += kFciLength;
+ }
+ return true;
+}
+
+size_t Fir::BlockLength() const {
+ return kHeaderLength + kCommonFeedbackLength + kFciLength * items_.size();
+}
+
+bool Fir::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ RTC_DCHECK(!items_.empty());
+ while (*index + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ }
+ size_t index_end = *index + BlockLength();
+ CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
+ index);
+ RTC_DCHECK_EQ(Psfb::media_ssrc(), 0);
+ CreateCommonFeedback(packet + *index);
+ *index += kCommonFeedbackLength;
+
+ constexpr uint32_t kReserved = 0;
+ for (const Request& request : items_) {
+ ByteWriter<uint32_t>::WriteBigEndian(packet + *index, request.ssrc);
+ ByteWriter<uint8_t>::WriteBigEndian(packet + *index + 4, request.seq_nr);
+ ByteWriter<uint32_t, 3>::WriteBigEndian(packet + *index + 5, kReserved);
+ *index += kFciLength;
+ }
+ RTC_CHECK_EQ(*index, index_end);
+ return true;
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir.h
new file mode 100644
index 0000000000..383dc96114
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir.h
@@ -0,0 +1,62 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_FIR_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_FIR_H_
+
+#include <vector>
+
+#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+// Full intra request (FIR) (RFC 5104).
+class Fir : public Psfb {
+ public:
+ static constexpr uint8_t kFeedbackMessageType = 4;
+ struct Request {
+ Request() : ssrc(0), seq_nr(0) {}
+ Request(uint32_t ssrc, uint8_t seq_nr) : ssrc(ssrc), seq_nr(seq_nr) {}
+ uint32_t ssrc;
+ uint8_t seq_nr;
+ };
+
+ Fir();
+ Fir(const Fir& fir);
+ ~Fir() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ void AddRequestTo(uint32_t ssrc, uint8_t seq_num) {
+ items_.emplace_back(ssrc, seq_num);
+ }
+ const std::vector<Request>& requests() const { return items_; }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ static constexpr size_t kFciLength = 8;
+
+ // SSRC of media source is not used in FIR packet. Shadow base functions.
+ void SetMediaSsrc(uint32_t ssrc);
+ uint32_t media_ssrc() const;
+
+ std::vector<Request> items_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_FIR_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir_unittest.cc
new file mode 100644
index 0000000000..01593e12ba
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/fir_unittest.cc
@@ -0,0 +1,93 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/fir.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::AllOf;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::Field;
+using ::testing::make_tuple;
+using webrtc::rtcp::Fir;
+
+namespace webrtc {
+namespace {
+
+constexpr uint32_t kSenderSsrc = 0x12345678;
+constexpr uint32_t kRemoteSsrc = 0x23456789;
+constexpr uint8_t kSeqNr = 13;
+// Manually created Fir packet matching constants above.
+constexpr uint8_t kPacket[] = {0x84, 206, 0x00, 0x04, 0x12, 0x34, 0x56,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45,
+ 0x67, 0x89, 0x0d, 0x00, 0x00, 0x00};
+} // namespace
+
+TEST(RtcpPacketFirTest, Parse) {
+ Fir mutable_parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &mutable_parsed));
+ const Fir& parsed = mutable_parsed; // Read values from constant object.
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_THAT(parsed.requests(),
+ ElementsAre(AllOf(Field(&Fir::Request::ssrc, Eq(kRemoteSsrc)),
+ Field(&Fir::Request::seq_nr, Eq(kSeqNr)))));
+}
+
+TEST(RtcpPacketFirTest, Create) {
+ Fir fir;
+ fir.SetSenderSsrc(kSenderSsrc);
+ fir.AddRequestTo(kRemoteSsrc, kSeqNr);
+
+ rtc::Buffer packet = fir.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketFirTest, TwoFciEntries) {
+ Fir fir;
+ fir.SetSenderSsrc(kSenderSsrc);
+ fir.AddRequestTo(kRemoteSsrc, kSeqNr);
+ fir.AddRequestTo(kRemoteSsrc + 1, kSeqNr + 1);
+
+ rtc::Buffer packet = fir.Build();
+ Fir parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_THAT(parsed.requests(),
+ ElementsAre(AllOf(Field(&Fir::Request::ssrc, Eq(kRemoteSsrc)),
+ Field(&Fir::Request::seq_nr, Eq(kSeqNr))),
+ AllOf(Field(&Fir::Request::ssrc, Eq(kRemoteSsrc + 1)),
+ Field(&Fir::Request::seq_nr, Eq(kSeqNr + 1)))));
+}
+
+TEST(RtcpPacketFirTest, ParseFailsOnZeroFciEntries) {
+ constexpr uint8_t kPacketWithoutFci[] = {0x84, 206, 0x00, 0x02, 0x12, 0x34,
+ 0x56, 0x78, 0x00, 0x00, 0x00, 0x00};
+ Fir parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kPacketWithoutFci, &parsed));
+}
+
+TEST(RtcpPacketFirTest, ParseFailsOnFractionalFciEntries) {
+ constexpr uint8_t kPacketWithOneAndHalfFci[] = {
+ 0x84, 206, 0x00, 0x05, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x45, 0x67, 0x89, 0x0d, 0x00, 0x00, 0x00, 'h', 'a', 'l', 'f'};
+
+ Fir parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kPacketWithOneAndHalfFci, &parsed));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification.cc
new file mode 100644
index 0000000000..0817846f95
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification.cc
@@ -0,0 +1,133 @@
+/*
+ * Copyright (c) 2019 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 "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+
+// Loss Notification
+// -----------------
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |V=2|P| FMT=15 | PT=206 | length |
+// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+// 0 | SSRC of packet sender |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 4 | SSRC of media source |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 8 | Unique identifier 'L' 'N' 'T' 'F' |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 12 | Last Decoded Sequence Number | Last Received SeqNum Delta |D|
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+LossNotification::LossNotification()
+ : last_decoded_(0), last_received_(0), decodability_flag_(false) {}
+
+LossNotification::LossNotification(uint16_t last_decoded,
+ uint16_t last_received,
+ bool decodability_flag)
+ : last_decoded_(last_decoded),
+ last_received_(last_received),
+ decodability_flag_(decodability_flag) {}
+
+LossNotification::LossNotification(const LossNotification& rhs) = default;
+
+LossNotification::~LossNotification() = default;
+
+size_t LossNotification::BlockLength() const {
+ return kHeaderLength + kCommonFeedbackLength + kLossNotificationPayloadLength;
+}
+
+bool LossNotification::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ while (*index + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ }
+
+ const size_t index_end = *index + BlockLength();
+
+ // Note: `index` updated by the function below.
+ CreateHeader(Psfb::kAfbMessageType, kPacketType, HeaderLength(), packet,
+ index);
+
+ CreateCommonFeedback(packet + *index);
+ *index += kCommonFeedbackLength;
+
+ ByteWriter<uint32_t>::WriteBigEndian(packet + *index, kUniqueIdentifier);
+ *index += sizeof(uint32_t);
+
+ ByteWriter<uint16_t>::WriteBigEndian(packet + *index, last_decoded_);
+ *index += sizeof(uint16_t);
+
+ const uint16_t last_received_delta = last_received_ - last_decoded_;
+ RTC_DCHECK_LE(last_received_delta, 0x7fff);
+ const uint16_t last_received_delta_and_decodability =
+ (last_received_delta << 1) | (decodability_flag_ ? 0x0001 : 0x0000);
+
+ ByteWriter<uint16_t>::WriteBigEndian(packet + *index,
+ last_received_delta_and_decodability);
+ *index += sizeof(uint16_t);
+
+ RTC_DCHECK_EQ(index_end, *index);
+ return true;
+}
+
+bool LossNotification::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), Psfb::kAfbMessageType);
+
+ if (packet.payload_size_bytes() <
+ kCommonFeedbackLength + kLossNotificationPayloadLength) {
+ return false;
+ }
+
+ const uint8_t* const payload = packet.payload();
+
+ if (ByteReader<uint32_t>::ReadBigEndian(&payload[8]) != kUniqueIdentifier) {
+ return false;
+ }
+
+ ParseCommonFeedback(payload);
+
+ last_decoded_ = ByteReader<uint16_t>::ReadBigEndian(&payload[12]);
+
+ const uint16_t last_received_delta_and_decodability =
+ ByteReader<uint16_t>::ReadBigEndian(&payload[14]);
+ last_received_ = last_decoded_ + (last_received_delta_and_decodability >> 1);
+ decodability_flag_ = (last_received_delta_and_decodability & 0x0001);
+
+ return true;
+}
+
+bool LossNotification::Set(uint16_t last_decoded,
+ uint16_t last_received,
+ bool decodability_flag) {
+ const uint16_t delta = last_received - last_decoded;
+ if (delta > 0x7fff) {
+ return false;
+ }
+ last_received_ = last_received;
+ last_decoded_ = last_decoded;
+ decodability_flag_ = decodability_flag;
+ return true;
+}
+
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h
new file mode 100644
index 0000000000..0f70cf75c3
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification.h
@@ -0,0 +1,82 @@
+/*
+ * Copyright (c) 2019 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_LOSS_NOTIFICATION_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_LOSS_NOTIFICATION_H_
+
+#include "absl/base/attributes.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
+
+namespace webrtc {
+namespace rtcp {
+
+class LossNotification : public Psfb {
+ public:
+ LossNotification();
+ LossNotification(uint16_t last_decoded,
+ uint16_t last_received,
+ bool decodability_flag);
+ LossNotification(const LossNotification& other);
+ ~LossNotification() override;
+
+ size_t BlockLength() const override;
+
+ ABSL_MUST_USE_RESULT
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ // Parse assumes header is already parsed and validated.
+ ABSL_MUST_USE_RESULT
+ bool Parse(const CommonHeader& packet);
+
+ // Set all of the values transmitted by the loss notification message.
+ // If the values may not be represented by a loss notification message,
+ // false is returned, and no change is made to the object; this happens
+ // when `last_received` is ahead of `last_decoded` by more than 0x7fff.
+ // This is because `last_received` is represented on the wire as a delta,
+ // and only 15 bits are available for that delta.
+ ABSL_MUST_USE_RESULT
+ bool Set(uint16_t last_decoded,
+ uint16_t last_received,
+ bool decodability_flag);
+
+ // RTP sequence number of the first packet belong to the last decoded
+ // non-discardable frame.
+ uint16_t last_decoded() const { return last_decoded_; }
+
+ // RTP sequence number of the last received packet.
+ uint16_t last_received() const { return last_received_; }
+
+ // A decodability flag, whose specific meaning depends on the last-received
+ // RTP sequence number. The decodability flag is true if and only if all of
+ // the frame's dependencies are known to be decodable, and the frame itself
+ // is not yet known to be unassemblable.
+ // * Clarification #1: In a multi-packet frame, the first packet's
+ // dependencies are known, but it is not yet known whether all parts
+ // of the current frame will be received.
+ // * Clarification #2: In a multi-packet frame, the dependencies would be
+ // unknown if the first packet was not received. Then, the packet will
+ // be known-unassemblable.
+ bool decodability_flag() const { return decodability_flag_; }
+
+ private:
+ static constexpr uint32_t kUniqueIdentifier = 0x4C4E5446; // 'L' 'N' 'T' 'F'.
+ static constexpr size_t kLossNotificationPayloadLength = 8;
+
+ uint16_t last_decoded_;
+ uint16_t last_received_;
+ bool decodability_flag_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_LOSS_NOTIFICATION_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification_unittest.cc
new file mode 100644
index 0000000000..c38e7f4438
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/loss_notification_unittest.cc
@@ -0,0 +1,136 @@
+/*
+ * Copyright (c) 2019 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 "modules/rtp_rtcp/source/rtcp_packet/loss_notification.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+namespace webrtc {
+
+using ::testing::ElementsAreArray;
+using ::testing::make_tuple;
+using ::webrtc::rtcp::LossNotification;
+
+TEST(RtcpPacketLossNotificationTest, SetWithIllegalValuesFails) {
+ constexpr uint16_t kLastDecoded = 0x3c7b;
+ constexpr uint16_t kLastReceived = kLastDecoded + 0x7fff + 1;
+ constexpr bool kDecodabilityFlag = true;
+ LossNotification loss_notification;
+ EXPECT_FALSE(
+ loss_notification.Set(kLastDecoded, kLastReceived, kDecodabilityFlag));
+}
+
+TEST(RtcpPacketLossNotificationTest, SetWithLegalValuesSucceeds) {
+ constexpr uint16_t kLastDecoded = 0x3c7b;
+ constexpr uint16_t kLastReceived = kLastDecoded + 0x7fff;
+ constexpr bool kDecodabilityFlag = true;
+ LossNotification loss_notification;
+ EXPECT_TRUE(
+ loss_notification.Set(kLastDecoded, kLastReceived, kDecodabilityFlag));
+}
+
+TEST(RtcpPacketLossNotificationTest, CreateProducesExpectedWireFormat) {
+ // Note that (0x6542 >> 1) is used just to make the pattern in kPacket
+ // more apparent; there's nothing truly special about the value,
+ // it's only an implementation detail that last-received is represented
+ // as a delta from last-decoded, and that this delta is shifted before
+ // it's put on the wire.
+ constexpr uint16_t kLastDecoded = 0x3c7b;
+ constexpr uint16_t kLastReceived = kLastDecoded + (0x6542 >> 1);
+ constexpr bool kDecodabilityFlag = true;
+
+ const uint8_t kPacket[] = {0x8f, 206, 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, //
+ 0xab, 0xcd, 0xef, 0x01, 'L', 'N', 'T', 'F', //
+ 0x3c, 0x7b, 0x65, 0x43};
+
+ LossNotification loss_notification;
+ loss_notification.SetSenderSsrc(0x12345678);
+ loss_notification.SetMediaSsrc(0xabcdef01);
+ ASSERT_TRUE(
+ loss_notification.Set(kLastDecoded, kLastReceived, kDecodabilityFlag));
+
+ rtc::Buffer packet = loss_notification.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketLossNotificationTest,
+ ParseFailsOnTooSmallPacketToBeLossNotification) {
+ uint8_t packet[] = {0x8f, 206, 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, //
+ 0xab, 0xcd, 0xef, 0x01, 'L', 'N', 'T', 'F', //
+ 0x3c, 0x7b, 0x65, 0x43};
+ size_t packet_length_bytes = sizeof(packet);
+
+ LossNotification loss_notification;
+
+ // First, prove that the failure we're expecting to see happens because of
+ // the length, by showing that before the modification to the length,
+ // the packet was correctly parsed.
+ ASSERT_TRUE(
+ test::ParseSinglePacket(packet, packet_length_bytes, &loss_notification));
+
+ // Show that after shaving off a word, the packet is no longer parsable.
+ packet[3] -= 1; // Change the `length` field of the RTCP packet.
+ packet_length_bytes -= 4; // Effectively forget the last 32-bit word.
+ EXPECT_FALSE(
+ test::ParseSinglePacket(packet, packet_length_bytes, &loss_notification));
+}
+
+TEST(RtcpPacketLossNotificationTest,
+ ParseFailsWhenUniqueIdentifierIsNotLossNotification) {
+ uint8_t packet[] = {0x8f, 206, 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, //
+ 0xab, 0xcd, 0xef, 0x01, 'L', 'N', 'T', 'F', //
+ 0x3c, 0x7b, 0x65, 0x43};
+
+ LossNotification loss_notification;
+
+ // First, prove that the failure we're expecting to see happens because of
+ // the identifier, by showing that before the modification to the identifier,
+ // the packet was correctly parsed.
+ ASSERT_TRUE(test::ParseSinglePacket(packet, &loss_notification));
+
+ // Show that after changing the identifier, the packet is no longer parsable.
+ RTC_DCHECK_EQ(packet[12], 'L');
+ RTC_DCHECK_EQ(packet[13], 'N');
+ RTC_DCHECK_EQ(packet[14], 'T');
+ RTC_DCHECK_EQ(packet[15], 'F');
+ packet[14] = 'x';
+ EXPECT_FALSE(test::ParseSinglePacket(packet, &loss_notification));
+}
+
+TEST(RtcpPacketLossNotificationTest,
+ ParseLegalLossNotificationMessagesCorrectly) {
+ // Note that (0x6542 >> 1) is used just to make the pattern in kPacket
+ // more apparent; there's nothing truly special about the value,
+ // it's only an implementation detail that last-received is represented
+ // as a delta from last-decoded, and that this delta is shifted before
+ // it's put on the wire.
+ constexpr uint16_t kLastDecoded = 0x3c7b;
+ constexpr uint16_t kLastReceived = kLastDecoded + (0x6542 >> 1);
+ constexpr bool kDecodabilityFlag = true;
+
+ const uint8_t kPacket[] = {0x8f, 206, 0x00, 0x04, 0x12, 0x34, 0x56, 0x78, //
+ 0xab, 0xcd, 0xef, 0x01, 'L', 'N', 'T', 'F', //
+ 0x3c, 0x7b, 0x65, 0x43};
+
+ LossNotification loss_notification;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &loss_notification));
+
+ EXPECT_EQ(loss_notification.sender_ssrc(), 0x12345678u);
+ EXPECT_EQ(loss_notification.media_ssrc(), 0xabcdef01u);
+ EXPECT_EQ(loss_notification.last_decoded(), kLastDecoded);
+ EXPECT_EQ(loss_notification.last_received(), kLastReceived);
+ EXPECT_EQ(loss_notification.decodability_flag(), kDecodabilityFlag);
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc
new file mode 100644
index 0000000000..6fe7eade62
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack.cc
@@ -0,0 +1,176 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/nack.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <utility>
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Nack::kFeedbackMessageType;
+constexpr size_t Nack::kNackItemLength;
+// 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 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+Nack::Nack() = default;
+Nack::Nack(const Nack& rhs) = default;
+Nack::~Nack() = default;
+
+bool Nack::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
+
+ if (packet.payload_size_bytes() < kCommonFeedbackLength + kNackItemLength) {
+ RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
+ << " is too small for a Nack.";
+ return false;
+ }
+ size_t nack_items =
+ (packet.payload_size_bytes() - kCommonFeedbackLength) / kNackItemLength;
+
+ ParseCommonFeedback(packet.payload());
+ const uint8_t* next_nack = packet.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;
+}
+
+size_t Nack::BlockLength() const {
+ return kHeaderLength + kCommonFeedbackLength +
+ packed_.size() * kNackItemLength;
+}
+
+bool Nack::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ RTC_DCHECK(!packed_.empty());
+ // If nack list can't fit in packet, try to fragment.
+ constexpr size_t kNackHeaderLength = kHeaderLength + kCommonFeedbackLength;
+ for (size_t nack_index = 0; nack_index < packed_.size();) {
+ size_t bytes_left_in_buffer = max_length - *index;
+ if (bytes_left_in_buffer < kNackHeaderLength + kNackItemLength) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ continue;
+ }
+ size_t num_nack_fields =
+ std::min((bytes_left_in_buffer - kNackHeaderLength) / kNackItemLength,
+ packed_.size() - nack_index);
+
+ size_t payload_size_bytes =
+ kCommonFeedbackLength + (num_nack_fields * kNackItemLength);
+ size_t payload_size_32bits =
+ rtc::CheckedDivExact<size_t>(payload_size_bytes, 4);
+ CreateHeader(kFeedbackMessageType, kPacketType, payload_size_32bits, packet,
+ index);
+
+ CreateCommonFeedback(packet + *index);
+ *index += kCommonFeedbackLength;
+
+ size_t nack_end_index = nack_index + num_nack_fields;
+ for (; nack_index < nack_end_index; ++nack_index) {
+ const PackedNack& 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);
+ }
+
+ return true;
+}
+
+void Nack::SetPacketIds(const uint16_t* nack_list, size_t length) {
+ RTC_DCHECK(nack_list);
+ SetPacketIds(std::vector<uint16_t>(nack_list, nack_list + length));
+}
+
+void Nack::SetPacketIds(std::vector<uint16_t> nack_list) {
+ RTC_DCHECK(packet_ids_.empty());
+ RTC_DCHECK(packed_.empty());
+ packet_ids_ = std::move(nack_list);
+ 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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h
new file mode 100644
index 0000000000..9153733fb9
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack.h
@@ -0,0 +1,59 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_NACK_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_NACK_H_
+
+#include <vector>
+
+#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+class Nack : public Rtpfb {
+ public:
+ static constexpr uint8_t kFeedbackMessageType = 1;
+ Nack();
+ Nack(const Nack&);
+ ~Nack() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ void SetPacketIds(const uint16_t* nack_list, size_t length);
+ void SetPacketIds(std::vector<uint16_t> nack_list);
+ const std::vector<uint16_t>& packet_ids() const { return packet_ids_; }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ static constexpr size_t kNackItemLength = 4;
+ struct PackedNack {
+ uint16_t first_pid;
+ uint16_t bitmask;
+ };
+
+ void Pack(); // Fills packed_ using packed_ids_. (used in SetPacketIds).
+ void Unpack(); // Fills packet_ids_ using packed_. (used in Parse).
+
+ std::vector<PackedNack> packed_;
+ std::vector<uint16_t> packet_ids_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_NACK_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc
new file mode 100644
index 0000000000..aabae0dc48
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/nack_unittest.cc
@@ -0,0 +1,171 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/nack.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+namespace webrtc {
+namespace {
+
+using ::testing::_;
+using ::testing::ElementsAre;
+using ::testing::ElementsAreArray;
+using ::testing::Invoke;
+using ::testing::make_tuple;
+using ::testing::MockFunction;
+using ::testing::UnorderedElementsAreArray;
+using ::webrtc::rtcp::Nack;
+
+constexpr uint32_t kSenderSsrc = 0x12345678;
+constexpr uint32_t kRemoteSsrc = 0x23456789;
+
+constexpr uint16_t kList[] = {0, 1, 3, 8, 16};
+constexpr size_t kListLength = sizeof(kList) / sizeof(kList[0]);
+constexpr uint8_t kVersionBits = 2 << 6;
+// clang-format off
+constexpr uint8_t kPacket[] = {
+ kVersionBits | Nack::kFeedbackMessageType, Nack::kPacketType, 0, 3,
+ 0x12, 0x34, 0x56, 0x78,
+ 0x23, 0x45, 0x67, 0x89,
+ 0x00, 0x00, 0x80, 0x85};
+
+constexpr uint16_t kWrapList[] = {0xffdc, 0xffec, 0xfffe, 0xffff, 0x0000,
+ 0x0001, 0x0003, 0x0014, 0x0064};
+constexpr size_t kWrapListLength = sizeof(kWrapList) / sizeof(kWrapList[0]);
+constexpr uint8_t kWrapPacket[] = {
+ kVersionBits | Nack::kFeedbackMessageType, Nack::kPacketType, 0, 6,
+ 0x12, 0x34, 0x56, 0x78,
+ 0x23, 0x45, 0x67, 0x89,
+ 0xff, 0xdc, 0x80, 0x00,
+ 0xff, 0xfe, 0x00, 0x17,
+ 0x00, 0x14, 0x00, 0x00,
+ 0x00, 0x64, 0x00, 0x00};
+constexpr uint8_t kTooSmallPacket[] = {
+ kVersionBits | Nack::kFeedbackMessageType, Nack::kPacketType, 0, 2,
+ 0x12, 0x34, 0x56, 0x78,
+ 0x23, 0x45, 0x67, 0x89};
+// clang-format on
+} // namespace
+
+TEST(RtcpPacketNackTest, Create) {
+ Nack nack;
+ nack.SetSenderSsrc(kSenderSsrc);
+ nack.SetMediaSsrc(kRemoteSsrc);
+ nack.SetPacketIds(kList, kListLength);
+
+ rtc::Buffer packet = nack.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketNackTest, Parse) {
+ Nack parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &parsed));
+ 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.SetSenderSsrc(kSenderSsrc);
+ nack.SetMediaSsrc(kRemoteSsrc);
+ nack.SetPacketIds(kWrapList, kWrapListLength);
+
+ rtc::Buffer packet = nack.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kWrapPacket));
+}
+
+TEST(RtcpPacketNackTest, ParseWrap) {
+ Nack parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kWrapPacket, &parsed));
+
+ 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.SetSenderSsrc(kSenderSsrc);
+ nack.SetMediaSsrc(kRemoteSsrc);
+ nack.SetPacketIds(kUnorderedList, kUnorderedListLength);
+
+ rtc::Buffer packet = nack.Build();
+
+ Nack parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+
+ 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.SetSenderSsrc(kSenderSsrc);
+ nack.SetMediaSsrc(kRemoteSsrc);
+ nack.SetPacketIds(kList, kListLength);
+
+ const size_t kBufferSize = 12 + (3 * 4); // Fits common header + 3 nack items
+
+ MockFunction<void(rtc::ArrayView<const uint8_t>)> callback;
+ EXPECT_CALL(callback, Call(_))
+ .WillOnce(Invoke([&](rtc::ArrayView<const uint8_t> packet) {
+ Nack nack;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &nack));
+ EXPECT_EQ(kSenderSsrc, nack.sender_ssrc());
+ EXPECT_EQ(kRemoteSsrc, nack.media_ssrc());
+ EXPECT_THAT(nack.packet_ids(), ElementsAre(1, 100, 200));
+ }))
+ .WillOnce(Invoke([&](rtc::ArrayView<const uint8_t> packet) {
+ Nack nack;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &nack));
+ EXPECT_EQ(kSenderSsrc, nack.sender_ssrc());
+ EXPECT_EQ(kRemoteSsrc, nack.media_ssrc());
+ EXPECT_THAT(nack.packet_ids(), ElementsAre(300, 400));
+ }));
+
+ EXPECT_TRUE(nack.Build(kBufferSize, callback.AsStdFunction()));
+}
+
+TEST(RtcpPacketNackTest, CreateFailsWithTooSmallBuffer) {
+ const uint16_t kList[] = {1};
+ const size_t kMinNackBlockSize = 16;
+ Nack nack;
+ nack.SetSenderSsrc(kSenderSsrc);
+ nack.SetMediaSsrc(kRemoteSsrc);
+ nack.SetPacketIds(kList, 1);
+
+ MockFunction<void(rtc::ArrayView<const uint8_t>)> callback;
+ EXPECT_CALL(callback, Call(_)).Times(0);
+ EXPECT_FALSE(nack.Build(kMinNackBlockSize - 1, callback.AsStdFunction()));
+}
+
+TEST(RtcpPacketNackTest, ParseFailsWithTooSmallBuffer) {
+ Nack parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kTooSmallPacket, &parsed));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli.cc
new file mode 100644
index 0000000000..5b41aa5c2c
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli.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 "modules/rtp_rtcp/source/rtcp_packet/pli.h"
+
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Pli::kFeedbackMessageType;
+// 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) :
+// : :
+
+Pli::Pli() = default;
+
+Pli::Pli(const Pli& pli) = default;
+
+Pli::~Pli() = default;
+
+//
+// Picture loss indication (PLI) (RFC 4585).
+// FCI: no feedback control information.
+bool Pli::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
+
+ if (packet.payload_size_bytes() < kCommonFeedbackLength) {
+ RTC_LOG(LS_WARNING) << "Packet is too small to be a valid PLI packet";
+ return false;
+ }
+
+ ParseCommonFeedback(packet.payload());
+ return true;
+}
+
+size_t Pli::BlockLength() const {
+ return kHeaderLength + kCommonFeedbackLength;
+}
+
+bool Pli::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ 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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h
new file mode 100644
index 0000000000..b9b9c45a9c
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli.h
@@ -0,0 +1,39 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PLI_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PLI_H_
+
+#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+// Picture loss indication (PLI) (RFC 4585).
+class Pli : public Psfb {
+ public:
+ static constexpr uint8_t kFeedbackMessageType = 1;
+
+ Pli();
+ Pli(const Pli& pli);
+ ~Pli() override;
+
+ bool Parse(const CommonHeader& packet);
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PLI_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli_unittest.cc
new file mode 100644
index 0000000000..c971e22bc1
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/pli_unittest.cc
@@ -0,0 +1,58 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/pli.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAreArray;
+using ::testing::make_tuple;
+using webrtc::rtcp::Pli;
+
+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};
+} // namespace
+
+TEST(RtcpPacketPliTest, Parse) {
+ Pli mutable_parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &mutable_parsed));
+ 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.SetSenderSsrc(kSenderSsrc);
+ pli.SetMediaSsrc(kRemoteSsrc);
+
+ rtc::Buffer packet = pli.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketPliTest, ParseFailsOnTooSmallPacket) {
+ const uint8_t kTooSmallPacket[] = {0x81, 206, 0x00, 0x01,
+ 0x12, 0x34, 0x56, 0x78};
+
+ Pli parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kTooSmallPacket, &parsed));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.cc
new file mode 100644
index 0000000000..384d8ba811
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.cc
@@ -0,0 +1,47 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Psfb::kPacketType;
+constexpr uint8_t Psfb::kAfbMessageType;
+constexpr size_t Psfb::kCommonFeedbackLength;
+// 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) {
+ SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(&payload[0]));
+ SetMediaSsrc(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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/psfb.h
new file mode 100644
index 0000000000..d6b8bca7c4
--- /dev/null
+++ b/third_party/libwebrtc/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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PSFB_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PSFB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "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 constexpr uint8_t kPacketType = 206;
+ static constexpr uint8_t kAfbMessageType = 15;
+
+ Psfb() = default;
+ ~Psfb() override = default;
+
+ void SetMediaSsrc(uint32_t ssrc) { media_ssrc_ = ssrc; }
+
+ uint32_t media_ssrc() const { return media_ssrc_; }
+
+ protected:
+ static constexpr size_t kCommonFeedbackLength = 8;
+ void ParseCommonFeedback(const uint8_t* payload);
+ void CreateCommonFeedback(uint8_t* payload) const;
+
+ private:
+ uint32_t media_ssrc_ = 0;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_PSFB_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.cc
new file mode 100644
index 0000000000..8563c28373
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.cc
@@ -0,0 +1,68 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h"
+
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t RapidResyncRequest::kFeedbackMessageType;
+// RFC 4585: Feedback format.
+// Rapid Resynchronisation Request (draft-perkins-avt-rapid-rtp-sync-03).
+//
+// 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=5 | PT=205 | length=2 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of packet sender |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of media source |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+bool RapidResyncRequest::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
+
+ if (packet.payload_size_bytes() != kCommonFeedbackLength) {
+ RTC_LOG(LS_WARNING) << "Packet payload size should be "
+ << kCommonFeedbackLength << " instead of "
+ << packet.payload_size_bytes()
+ << " to be a valid Rapid Resynchronisation Request";
+ return false;
+ }
+
+ ParseCommonFeedback(packet.payload());
+ return true;
+}
+
+size_t RapidResyncRequest::BlockLength() const {
+ return kHeaderLength + kCommonFeedbackLength;
+}
+
+bool RapidResyncRequest::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ 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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h
new file mode 100644
index 0000000000..1955b98f5c
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h
@@ -0,0 +1,40 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RAPID_RESYNC_REQUEST_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RAPID_RESYNC_REQUEST_H_
+
+#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+// draft-perkins-avt-rapid-rtp-sync-03
+class RapidResyncRequest : public Rtpfb {
+ public:
+ static constexpr uint8_t kFeedbackMessageType = 5;
+
+ RapidResyncRequest() {}
+ ~RapidResyncRequest() override {}
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& header);
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RAPID_RESYNC_REQUEST_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request_unittest.cc
new file mode 100644
index 0000000000..d0e40fd83d
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request_unittest.cc
@@ -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.
+ */
+
+#include "modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAreArray;
+using ::testing::make_tuple;
+using webrtc::rtcp::RapidResyncRequest;
+
+namespace webrtc {
+namespace {
+const uint32_t kSenderSsrc = 0x12345678;
+const uint32_t kRemoteSsrc = 0x23456789;
+// Manually created packet matching constants above.
+const uint8_t kPacket[] = {0x85, 205, 0x00, 0x02, 0x12, 0x34,
+ 0x56, 0x78, 0x23, 0x45, 0x67, 0x89};
+} // namespace
+
+TEST(RtcpPacketRapidResyncRequestTest, Parse) {
+ RapidResyncRequest mutable_parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &mutable_parsed));
+ const RapidResyncRequest& parsed = mutable_parsed;
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(kRemoteSsrc, parsed.media_ssrc());
+}
+
+TEST(RtcpPacketRapidResyncRequestTest, Create) {
+ RapidResyncRequest rrr;
+ rrr.SetSenderSsrc(kSenderSsrc);
+ rrr.SetMediaSsrc(kRemoteSsrc);
+
+ rtc::Buffer packet = rrr.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketRapidResyncRequestTest, ParseFailsOnTooSmallPacket) {
+ const uint8_t kTooSmallPacket[] = {0x85, 205, 0x00, 0x01,
+ 0x12, 0x34, 0x56, 0x78};
+ RapidResyncRequest parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kTooSmallPacket, &parsed));
+}
+
+TEST(RtcpPacketRapidResyncRequestTest, ParseFailsOnTooLargePacket) {
+ const uint8_t kTooLargePacket[] = {0x85, 205, 0x00, 0x03, 0x12, 0x34,
+ 0x56, 0x78, 0x32, 0x21, 0x65, 0x87,
+ 0x23, 0x45, 0x67, 0x89};
+ RapidResyncRequest parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kTooLargePacket, &parsed));
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.cc
new file mode 100644
index 0000000000..185011dff1
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.cc
@@ -0,0 +1,112 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
+
+#include <utility>
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t ReceiverReport::kPacketType;
+constexpr size_t ReceiverReport::kMaxNumberOfReportBlocks;
+// 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) |
+// | .... |
+
+ReceiverReport::ReceiverReport() = default;
+
+ReceiverReport::ReceiverReport(const ReceiverReport& rhs) = default;
+
+ReceiverReport::~ReceiverReport() = default;
+
+bool ReceiverReport::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+
+ const uint8_t report_blocks_count = packet.count();
+
+ if (packet.payload_size_bytes() <
+ kRrBaseLength + report_blocks_count * ReportBlock::kLength) {
+ RTC_LOG(LS_WARNING) << "Packet is too small to contain all the data.";
+ return false;
+ }
+
+ SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(packet.payload()));
+
+ const uint8_t* next_report_block = packet.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 - packet.payload(),
+ static_cast<ptrdiff_t>(packet.payload_size_bytes()));
+ return true;
+}
+
+size_t ReceiverReport::BlockLength() const {
+ return kHeaderLength + kRrBaseLength +
+ report_blocks_.size() * ReportBlock::kLength;
+}
+
+bool ReceiverReport::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ 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::AddReportBlock(const ReportBlock& block) {
+ if (report_blocks_.size() >= kMaxNumberOfReportBlocks) {
+ RTC_LOG(LS_WARNING) << "Max report blocks reached.";
+ return false;
+ }
+ report_blocks_.push_back(block);
+ return true;
+}
+
+bool ReceiverReport::SetReportBlocks(std::vector<ReportBlock> blocks) {
+ if (blocks.size() > kMaxNumberOfReportBlocks) {
+ RTC_LOG(LS_WARNING) << "Too many report blocks (" << blocks.size()
+ << ") for receiver report.";
+ return false;
+ }
+ report_blocks_ = std::move(blocks);
+ return true;
+}
+
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h
new file mode 100644
index 0000000000..b9c1c466c7
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report.h
@@ -0,0 +1,60 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RECEIVER_REPORT_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RECEIVER_REPORT_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+class ReceiverReport : public RtcpPacket {
+ public:
+ static constexpr uint8_t kPacketType = 201;
+ static constexpr size_t kMaxNumberOfReportBlocks = 0x1f;
+
+ ReceiverReport();
+ ReceiverReport(const ReceiverReport&);
+ ~ReceiverReport() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ bool AddReportBlock(const ReportBlock& block);
+ bool SetReportBlocks(std::vector<ReportBlock> blocks);
+
+ const std::vector<ReportBlock>& report_blocks() const {
+ return report_blocks_;
+ }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ static const size_t kRrBaseLength = 4;
+
+ std::vector<ReportBlock> report_blocks_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RECEIVER_REPORT_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report_unittest.cc
new file mode 100644
index 0000000000..47f8eb13cb
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/receiver_report_unittest.cc
@@ -0,0 +1,161 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
+
+#include <utility>
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAreArray;
+using ::testing::IsEmpty;
+using ::testing::make_tuple;
+using webrtc::rtcp::ReceiverReport;
+using webrtc::rtcp::ReportBlock;
+
+namespace webrtc {
+namespace {
+const uint32_t kSenderSsrc = 0x12345678;
+const uint32_t kRemoteSsrc = 0x23456789;
+const uint8_t kFractionLost = 55;
+const int32_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};
+} // namespace
+
+TEST(RtcpPacketReceiverReportTest, ParseWithOneReportBlock) {
+ ReceiverReport rr;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &rr));
+ 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(RtcpPacketReceiverReportTest, ParseFailsOnIncorrectSize) {
+ rtc::Buffer damaged_packet(kPacket);
+ damaged_packet[0]++; // Damage the packet: increase count field.
+ ReceiverReport rr;
+ EXPECT_FALSE(test::ParseSinglePacket(damaged_packet, &rr));
+}
+
+TEST(RtcpPacketReceiverReportTest, CreateWithOneReportBlock) {
+ ReceiverReport rr;
+ rr.SetSenderSsrc(kSenderSsrc);
+ ReportBlock rb;
+ rb.SetMediaSsrc(kRemoteSsrc);
+ rb.SetFractionLost(kFractionLost);
+ rb.SetCumulativeLost(kCumulativeLost);
+ rb.SetExtHighestSeqNum(kExtHighestSeqNum);
+ rb.SetJitter(kJitter);
+ rb.SetLastSr(kLastSr);
+ rb.SetDelayLastSr(kDelayLastSr);
+ rr.AddReportBlock(rb);
+
+ rtc::Buffer raw = rr.Build();
+
+ EXPECT_THAT(make_tuple(raw.data(), raw.size()), ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketReceiverReportTest, CreateAndParseWithoutReportBlocks) {
+ ReceiverReport rr;
+ rr.SetSenderSsrc(kSenderSsrc);
+
+ rtc::Buffer raw = rr.Build();
+ ReceiverReport parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_THAT(parsed.report_blocks(), IsEmpty());
+}
+
+TEST(RtcpPacketReceiverReportTest, CreateAndParseWithTwoReportBlocks) {
+ ReceiverReport rr;
+ ReportBlock rb1;
+ rb1.SetMediaSsrc(kRemoteSsrc);
+ ReportBlock rb2;
+ rb2.SetMediaSsrc(kRemoteSsrc + 1);
+
+ rr.SetSenderSsrc(kSenderSsrc);
+ EXPECT_TRUE(rr.AddReportBlock(rb1));
+ EXPECT_TRUE(rr.AddReportBlock(rb2));
+
+ rtc::Buffer raw = rr.Build();
+ ReceiverReport parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed));
+
+ 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(RtcpPacketReceiverReportTest, CreateWithTooManyReportBlocks) {
+ ReceiverReport rr;
+ rr.SetSenderSsrc(kSenderSsrc);
+ ReportBlock rb;
+ for (size_t i = 0; i < ReceiverReport::kMaxNumberOfReportBlocks; ++i) {
+ rb.SetMediaSsrc(kRemoteSsrc + i);
+ EXPECT_TRUE(rr.AddReportBlock(rb));
+ }
+ rb.SetMediaSsrc(kRemoteSsrc + ReceiverReport::kMaxNumberOfReportBlocks);
+ EXPECT_FALSE(rr.AddReportBlock(rb));
+}
+
+TEST(RtcpPacketReceiverReportTest, SetReportBlocksOverwritesOldBlocks) {
+ ReceiverReport rr;
+ ReportBlock report_block;
+ // Use jitter field of the report blocks to distinguish them.
+ report_block.SetJitter(1001u);
+ rr.AddReportBlock(report_block);
+ ASSERT_EQ(rr.report_blocks().size(), 1u);
+ ASSERT_EQ(rr.report_blocks()[0].jitter(), 1001u);
+
+ std::vector<ReportBlock> blocks(3u);
+ blocks[0].SetJitter(2001u);
+ blocks[1].SetJitter(3001u);
+ blocks[2].SetJitter(4001u);
+ EXPECT_TRUE(rr.SetReportBlocks(blocks));
+ ASSERT_EQ(rr.report_blocks().size(), 3u);
+ EXPECT_EQ(rr.report_blocks()[0].jitter(), 2001u);
+ EXPECT_EQ(rr.report_blocks()[1].jitter(), 3001u);
+ EXPECT_EQ(rr.report_blocks()[2].jitter(), 4001u);
+}
+
+TEST(RtcpPacketReceiverReportTest, SetReportBlocksMaxLimit) {
+ ReceiverReport rr;
+ std::vector<ReportBlock> max_blocks(ReceiverReport::kMaxNumberOfReportBlocks);
+ EXPECT_TRUE(rr.SetReportBlocks(std::move(max_blocks)));
+
+ std::vector<ReportBlock> one_too_many_blocks(
+ ReceiverReport::kMaxNumberOfReportBlocks + 1);
+ EXPECT_FALSE(rr.SetReportBlocks(std::move(one_too_many_blocks)));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb.cc
new file mode 100644
index 0000000000..1389ca7836
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb.cc
@@ -0,0 +1,143 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/remb.h"
+
+#include <cstdint>
+#include <utility>
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb).
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |V=2|P| FMT=15 | PT=206 | length |
+// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+// 0 | SSRC of packet sender |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 4 | Unused = 0 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 8 | Unique identifier 'R' 'E' 'M' 'B' |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 12 | Num SSRC | BR Exp | BR Mantissa |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 16 | SSRC feedback |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : ... :
+
+Remb::Remb() : bitrate_bps_(0) {}
+
+Remb::Remb(const Remb& rhs) = default;
+
+Remb::~Remb() = default;
+
+bool Remb::Parse(const CommonHeader& packet) {
+ RTC_DCHECK(packet.type() == kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), Psfb::kAfbMessageType);
+
+ if (packet.payload_size_bytes() < 16) {
+ RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
+ << " is too small for Remb packet.";
+ return false;
+ }
+ const uint8_t* const payload = packet.payload();
+ if (kUniqueIdentifier != ByteReader<uint32_t>::ReadBigEndian(&payload[8])) {
+ return false;
+ }
+ uint8_t number_of_ssrcs = payload[12];
+ if (packet.payload_size_bytes() !=
+ kCommonFeedbackLength + (2 + number_of_ssrcs) * 4) {
+ RTC_LOG(LS_WARNING) << "Payload size " << packet.payload_size_bytes()
+ << " does not match " << number_of_ssrcs << " ssrcs.";
+ return false;
+ }
+
+ ParseCommonFeedback(payload);
+ uint8_t exponent = payload[13] >> 2;
+ uint64_t mantissa = (static_cast<uint32_t>(payload[13] & 0x03) << 16) |
+ ByteReader<uint16_t>::ReadBigEndian(&payload[14]);
+ bitrate_bps_ = (mantissa << exponent);
+ bool shift_overflow =
+ (static_cast<uint64_t>(bitrate_bps_) >> exponent) != mantissa;
+ if (bitrate_bps_ < 0 || shift_overflow) {
+ RTC_LOG(LS_ERROR) << "Invalid remb bitrate value : " << mantissa << "*2^"
+ << static_cast<int>(exponent);
+ return false;
+ }
+
+ const uint8_t* next_ssrc = payload + 16;
+ ssrcs_.clear();
+ ssrcs_.reserve(number_of_ssrcs);
+ for (uint8_t i = 0; i < number_of_ssrcs; ++i) {
+ ssrcs_.push_back(ByteReader<uint32_t>::ReadBigEndian(next_ssrc));
+ next_ssrc += sizeof(uint32_t);
+ }
+
+ return true;
+}
+
+bool Remb::SetSsrcs(std::vector<uint32_t> ssrcs) {
+ if (ssrcs.size() > kMaxNumberOfSsrcs) {
+ RTC_LOG(LS_WARNING) << "Not enough space for all given SSRCs.";
+ return false;
+ }
+ ssrcs_ = std::move(ssrcs);
+ return true;
+}
+
+size_t Remb::BlockLength() const {
+ return kHeaderLength + kCommonFeedbackLength + (2 + ssrcs_.size()) * 4;
+}
+
+bool Remb::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ while (*index + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ }
+ size_t index_end = *index + BlockLength();
+ CreateHeader(Psfb::kAfbMessageType, kPacketType, HeaderLength(), packet,
+ index);
+ RTC_DCHECK_EQ(0, Psfb::media_ssrc());
+ CreateCommonFeedback(packet + *index);
+ *index += kCommonFeedbackLength;
+
+ ByteWriter<uint32_t>::WriteBigEndian(packet + *index, kUniqueIdentifier);
+ *index += sizeof(uint32_t);
+ const uint32_t kMaxMantissa = 0x3ffff; // 18 bits.
+ uint64_t mantissa = bitrate_bps_;
+ uint8_t exponenta = 0;
+ while (mantissa > kMaxMantissa) {
+ mantissa >>= 1;
+ ++exponenta;
+ }
+ packet[(*index)++] = static_cast<uint8_t>(ssrcs_.size());
+ packet[(*index)++] = (exponenta << 2) | (mantissa >> 16);
+ ByteWriter<uint16_t>::WriteBigEndian(packet + *index, mantissa & 0xffff);
+ *index += sizeof(uint16_t);
+
+ for (uint32_t ssrc : ssrcs_) {
+ ByteWriter<uint32_t>::WriteBigEndian(packet + *index, ssrc);
+ *index += sizeof(uint32_t);
+ }
+ RTC_DCHECK_EQ(index_end, *index);
+ return true;
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h
new file mode 100644
index 0000000000..b7075c0f23
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb.h
@@ -0,0 +1,59 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMB_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMB_H_
+
+#include <vector>
+
+#include "modules/rtp_rtcp/source/rtcp_packet/psfb.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+// Receiver Estimated Max Bitrate (REMB) (draft-alvestrand-rmcat-remb).
+class Remb : public Psfb {
+ public:
+ static constexpr size_t kMaxNumberOfSsrcs = 0xff;
+
+ Remb();
+ Remb(const Remb&);
+ ~Remb() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ bool SetSsrcs(std::vector<uint32_t> ssrcs);
+ void SetBitrateBps(int64_t bitrate_bps) { bitrate_bps_ = bitrate_bps; }
+
+ int64_t bitrate_bps() const { return bitrate_bps_; }
+ const std::vector<uint32_t>& ssrcs() const { return ssrcs_; }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ static constexpr uint32_t kUniqueIdentifier = 0x52454D42; // 'R' 'E' 'M' 'B'.
+
+ // Media ssrc is unused, shadow base class setter and getter.
+ void SetMediaSsrc(uint32_t);
+ uint32_t media_ssrc() const;
+
+ int64_t bitrate_bps_;
+ std::vector<uint32_t> ssrcs_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMB_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb_unittest.cc
new file mode 100644
index 0000000000..c439d9c5f6
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remb_unittest.cc
@@ -0,0 +1,141 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/remb.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAreArray;
+using ::testing::IsEmpty;
+using ::testing::make_tuple;
+using webrtc::rtcp::Remb;
+
+namespace webrtc {
+namespace {
+const uint32_t kSenderSsrc = 0x12345678;
+const uint32_t kRemoteSsrcs[] = {0x23456789, 0x2345678a, 0x2345678b};
+const uint32_t kBitrateBps = 0x3fb93 * 2; // 522022;
+const int64_t kBitrateBps64bit = int64_t{0x3fb93} << 30;
+const uint8_t kPacket[] = {0x8f, 206, 0x00, 0x07, 0x12, 0x34, 0x56, 0x78,
+ 0x00, 0x00, 0x00, 0x00, 'R', 'E', 'M', 'B',
+ 0x03, 0x07, 0xfb, 0x93, 0x23, 0x45, 0x67, 0x89,
+ 0x23, 0x45, 0x67, 0x8a, 0x23, 0x45, 0x67, 0x8b};
+const size_t kPacketLength = sizeof(kPacket);
+} // namespace
+
+TEST(RtcpPacketRembTest, Create) {
+ Remb remb;
+ remb.SetSenderSsrc(kSenderSsrc);
+ remb.SetSsrcs(
+ std::vector<uint32_t>(std::begin(kRemoteSsrcs), std::end(kRemoteSsrcs)));
+ remb.SetBitrateBps(kBitrateBps);
+
+ rtc::Buffer packet = remb.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketRembTest, Parse) {
+ Remb remb;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &remb));
+ const Remb& parsed = remb;
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(kBitrateBps, parsed.bitrate_bps());
+ EXPECT_THAT(parsed.ssrcs(), ElementsAreArray(kRemoteSsrcs));
+}
+
+TEST(RtcpPacketRembTest, CreateAndParseWithoutSsrcs) {
+ Remb remb;
+ remb.SetSenderSsrc(kSenderSsrc);
+ remb.SetBitrateBps(kBitrateBps);
+ rtc::Buffer packet = remb.Build();
+
+ Remb parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(kBitrateBps, parsed.bitrate_bps());
+ EXPECT_THAT(parsed.ssrcs(), IsEmpty());
+}
+
+TEST(RtcpPacketRembTest, CreateAndParse64bitBitrate) {
+ Remb remb;
+ remb.SetBitrateBps(kBitrateBps64bit);
+ rtc::Buffer packet = remb.Build();
+
+ Remb parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+ EXPECT_EQ(kBitrateBps64bit, parsed.bitrate_bps());
+}
+
+TEST(RtcpPacketRembTest, ParseFailsOnTooSmallPacketToBeRemb) {
+ // Make it too small.
+ constexpr size_t kTooSmallSize = (1 + 3) * 4;
+ uint8_t packet[kTooSmallSize];
+ memcpy(packet, kPacket, kTooSmallSize);
+ packet[3] = 3;
+
+ Remb remb;
+ EXPECT_FALSE(test::ParseSinglePacket(packet, &remb));
+}
+
+TEST(RtcpPacketRembTest, ParseFailsWhenUniqueIdentifierIsNotRemb) {
+ uint8_t packet[kPacketLength];
+ memcpy(packet, kPacket, kPacketLength);
+ packet[12] = 'N'; // Swap 'R' -> 'N' in the 'REMB' unique identifier.
+
+ Remb remb;
+ EXPECT_FALSE(test::ParseSinglePacket(packet, &remb));
+}
+
+TEST(RtcpPacketRembTest, ParseFailsWhenBitrateDoNotFitIn64bits) {
+ uint8_t packet[kPacketLength];
+ memcpy(packet, kPacket, kPacketLength);
+ packet[17] |= 0xfc; // Set exponent component to maximum of 63.
+ packet[19] |= 0x02; // Ensure mantissa is at least 2.
+
+ Remb remb;
+ EXPECT_FALSE(test::ParseSinglePacket(packet, &remb));
+}
+
+TEST(RtcpPacketRembTest, ParseFailsWhenBitrateDoNotFitIn63bits) {
+ uint8_t packet[kPacketLength];
+ memcpy(packet, kPacket, kPacketLength);
+ packet[17] = 56 << 2; // Set exponent component to 56.
+ packet[18] = 0; // Set mantissa to 200 > 128
+ packet[19] = 200;
+
+ // Result value 200 * 2^56 can't be represented with int64_t and thus should
+ // be rejected.
+ Remb remb;
+ EXPECT_FALSE(test::ParseSinglePacket(packet, &remb));
+}
+
+TEST(RtcpPacketRembTest, ParseFailsWhenSsrcCountMismatchLength) {
+ uint8_t packet[kPacketLength];
+ memcpy(packet, kPacket, kPacketLength);
+ packet[16]++; // Swap 3 -> 4 in the ssrcs count.
+
+ Remb remb;
+ EXPECT_FALSE(test::ParseSinglePacket(packet, &remb));
+}
+
+TEST(RtcpPacketRembTest, TooManySsrcs) {
+ Remb remb;
+ EXPECT_FALSE(remb.SetSsrcs(
+ std::vector<uint32_t>(Remb::kMaxNumberOfSsrcs + 1, kRemoteSsrcs[0])));
+ EXPECT_TRUE(remb.SetSsrcs(
+ std::vector<uint32_t>(Remb::kMaxNumberOfSsrcs, kRemoteSsrcs[0])));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc
new file mode 100644
index 0000000000..ca59791248
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc
@@ -0,0 +1,148 @@
+/*
+ * Copyright (c) 2019 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 "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
+
+#include <algorithm>
+#include <cmath>
+#include <type_traits>
+#include <utility>
+#include <vector>
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+namespace {
+
+static constexpr int kFieldValueSize = 3;
+static constexpr int kFieldSize = 1 + kFieldValueSize;
+static constexpr DataRate kDataRateResolution = DataRate::KilobitsPerSec(1);
+constexpr int64_t kMaxEncoded = (1 << (kFieldValueSize * 8)) - 1;
+
+class DataRateSerializer {
+ public:
+ DataRateSerializer(
+ uint8_t id,
+ std::function<DataRate*(NetworkStateEstimate*)> field_getter)
+ : id_(id), field_getter_(field_getter) {}
+
+ uint8_t id() const { return id_; }
+
+ void Read(const uint8_t* src, NetworkStateEstimate* target) const {
+ int64_t scaled = ByteReader<uint32_t, kFieldValueSize>::ReadBigEndian(src);
+ if (scaled == kMaxEncoded) {
+ *field_getter_(target) = DataRate::PlusInfinity();
+ } else {
+ *field_getter_(target) = kDataRateResolution * scaled;
+ }
+ }
+
+ bool Write(const NetworkStateEstimate& src, uint8_t* target) const {
+ auto value = *field_getter_(const_cast<NetworkStateEstimate*>(&src));
+ if (value.IsMinusInfinity()) {
+ RTC_LOG(LS_WARNING) << "Trying to serialize MinusInfinity";
+ return false;
+ }
+ ByteWriter<uint8_t>::WriteBigEndian(target++, id_);
+ int64_t scaled;
+ if (value.IsPlusInfinity()) {
+ scaled = kMaxEncoded;
+ } else {
+ scaled = value / kDataRateResolution;
+ if (scaled >= kMaxEncoded) {
+ scaled = kMaxEncoded;
+ RTC_LOG(LS_WARNING) << ToString(value) << " is larger than max ("
+ << ToString(kMaxEncoded * kDataRateResolution)
+ << "), encoded as PlusInfinity.";
+ }
+ }
+ ByteWriter<uint32_t, kFieldValueSize>::WriteBigEndian(target, scaled);
+ return true;
+ }
+
+ private:
+ const uint8_t id_;
+ const std::function<DataRate*(NetworkStateEstimate*)> field_getter_;
+};
+
+class RemoteEstimateSerializerImpl : public RemoteEstimateSerializer {
+ public:
+ explicit RemoteEstimateSerializerImpl(std::vector<DataRateSerializer> fields)
+ : fields_(fields) {}
+
+ rtc::Buffer Serialize(const NetworkStateEstimate& src) const override {
+ size_t max_size = fields_.size() * kFieldSize;
+ size_t size = 0;
+ rtc::Buffer buf(max_size);
+ for (const auto& field : fields_) {
+ if (field.Write(src, buf.data() + size)) {
+ size += kFieldSize;
+ }
+ }
+ buf.SetSize(size);
+ return buf;
+ }
+
+ bool Parse(rtc::ArrayView<const uint8_t> src,
+ NetworkStateEstimate* target) const override {
+ if (src.size() % kFieldSize != 0)
+ return false;
+ RTC_DCHECK_EQ(src.size() % kFieldSize, 0);
+ for (const uint8_t* data_ptr = src.data(); data_ptr < src.end();
+ data_ptr += kFieldSize) {
+ uint8_t field_id = ByteReader<uint8_t>::ReadBigEndian(data_ptr);
+ for (const auto& field : fields_) {
+ if (field.id() == field_id) {
+ field.Read(data_ptr + 1, target);
+ break;
+ }
+ }
+ }
+ return true;
+ }
+
+ private:
+ const std::vector<DataRateSerializer> fields_;
+};
+
+} // namespace
+
+const RemoteEstimateSerializer* GetRemoteEstimateSerializer() {
+ using E = NetworkStateEstimate;
+ static auto* serializer = new RemoteEstimateSerializerImpl({
+ {1, [](E* e) { return &e->link_capacity_lower; }},
+ {2, [](E* e) { return &e->link_capacity_upper; }},
+ });
+ return serializer;
+}
+
+RemoteEstimate::RemoteEstimate() : serializer_(GetRemoteEstimateSerializer()) {
+ SetSubType(kSubType);
+ SetName(kName);
+ SetSenderSsrc(0);
+}
+
+RemoteEstimate::RemoteEstimate(App&& app)
+ : App(std::move(app)), serializer_(GetRemoteEstimateSerializer()) {}
+
+bool RemoteEstimate::ParseData() {
+ return serializer_->Parse({data(), data_size()}, &estimate_);
+}
+
+void RemoteEstimate::SetEstimate(NetworkStateEstimate estimate) {
+ estimate_ = estimate;
+ auto buf = serializer_->Serialize(estimate);
+ SetData(buf.data(), buf.size());
+}
+
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h
new file mode 100644
index 0000000000..3400274568
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (c) 2019 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMOTE_ESTIMATE_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMOTE_ESTIMATE_H_
+
+#include <memory>
+#include <vector>
+
+#include "api/transport/network_types.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/app.h"
+
+namespace webrtc {
+namespace rtcp {
+
+class CommonHeader;
+class RemoteEstimateSerializer {
+ public:
+ virtual bool Parse(rtc::ArrayView<const uint8_t> src,
+ NetworkStateEstimate* target) const = 0;
+ virtual rtc::Buffer Serialize(const NetworkStateEstimate& src) const = 0;
+ virtual ~RemoteEstimateSerializer() = default;
+};
+
+// Using a static global implementation to avoid incurring initialization
+// overhead of the serializer every time RemoteEstimate is created.
+const RemoteEstimateSerializer* GetRemoteEstimateSerializer();
+
+// The RemoteEstimate packet provides network estimation results from the
+// receive side. This functionality is experimental and subject to change
+// without notice.
+class RemoteEstimate : public App {
+ public:
+ RemoteEstimate();
+ explicit RemoteEstimate(App&& app);
+ // Note, sub type must be unique among all app messages with "goog" name.
+ static constexpr uint8_t kSubType = 13;
+ static constexpr uint32_t kName = NameToInt("goog");
+ static TimeDelta GetTimestampPeriod();
+
+ bool ParseData();
+ void SetEstimate(NetworkStateEstimate estimate);
+ NetworkStateEstimate estimate() const { return estimate_; }
+
+ private:
+ NetworkStateEstimate estimate_;
+ const RemoteEstimateSerializer* const serializer_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REMOTE_ESTIMATE_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate_unittest.cc
new file mode 100644
index 0000000000..bf0e0e2610
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/remote_estimate_unittest.cc
@@ -0,0 +1,56 @@
+/*
+ * Copyright (c) 2019 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 "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
+
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace rtcp {
+TEST(RemoteEstimateTest, EncodesCapacityBounds) {
+ NetworkStateEstimate src;
+ src.link_capacity_lower = DataRate::KilobitsPerSec(10);
+ src.link_capacity_upper = DataRate::KilobitsPerSec(1000000);
+ rtc::Buffer data = GetRemoteEstimateSerializer()->Serialize(src);
+ NetworkStateEstimate dst;
+ EXPECT_TRUE(GetRemoteEstimateSerializer()->Parse(data, &dst));
+ EXPECT_EQ(src.link_capacity_lower, dst.link_capacity_lower);
+ EXPECT_EQ(src.link_capacity_upper, dst.link_capacity_upper);
+}
+
+TEST(RemoteEstimateTest, ExpandsToPlusInfinity) {
+ NetworkStateEstimate src;
+ // White box testing: We know that the value is stored in an unsigned 24 int
+ // with kbps resolution. We expected it be represented as plus infinity.
+ src.link_capacity_lower = DataRate::KilobitsPerSec(2 << 24);
+ src.link_capacity_upper = DataRate::PlusInfinity();
+ rtc::Buffer data = GetRemoteEstimateSerializer()->Serialize(src);
+
+ NetworkStateEstimate dst;
+ EXPECT_TRUE(GetRemoteEstimateSerializer()->Parse(data, &dst));
+ EXPECT_TRUE(dst.link_capacity_lower.IsPlusInfinity());
+ EXPECT_TRUE(dst.link_capacity_upper.IsPlusInfinity());
+}
+
+TEST(RemoteEstimateTest, DoesNotEncodeNegative) {
+ NetworkStateEstimate src;
+ src.link_capacity_lower = DataRate::MinusInfinity();
+ src.link_capacity_upper = DataRate::MinusInfinity();
+ rtc::Buffer data = GetRemoteEstimateSerializer()->Serialize(src);
+ // Since MinusInfinity can't be represented, the buffer should be empty.
+ EXPECT_EQ(data.size(), 0u);
+ NetworkStateEstimate dst;
+ dst.link_capacity_lower = DataRate::KilobitsPerSec(300);
+ EXPECT_TRUE(GetRemoteEstimateSerializer()->Parse(data, &dst));
+ // The fields will be left unchanged by the parser as they were not encoded.
+ EXPECT_EQ(dst.link_capacity_lower, DataRate::KilobitsPerSec(300));
+ EXPECT_TRUE(dst.link_capacity_upper.IsMinusInfinity());
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.cc
new file mode 100644
index 0000000000..e7e92d2bf1
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.cc
@@ -0,0 +1,91 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.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) {
+ RTC_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<int32_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(), (1 << 23)); // Have only 3 bytes for it.
+
+ ByteWriter<uint32_t>::WriteBigEndian(&buffer[0], source_ssrc());
+ ByteWriter<uint8_t>::WriteBigEndian(&buffer[4], fraction_lost());
+ ByteWriter<int32_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::SetCumulativeLost(int32_t cumulative_lost) {
+ // We have only 3 bytes to store it, and it's a signed value.
+ if (cumulative_lost >= (1 << 23) || cumulative_lost < -(1 << 23)) {
+ RTC_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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h
new file mode 100644
index 0000000000..b49219eceb
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block.h
@@ -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.
+ *
+ */
+
+#ifndef MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REPORT_BLOCK_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REPORT_BLOCK_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace webrtc {
+namespace rtcp {
+
+// A ReportBlock represents the Sender Report packet from
+// RFC 3550 section 6.4.1.
+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 SetMediaSsrc(uint32_t ssrc) { source_ssrc_ = ssrc; }
+ void SetFractionLost(uint8_t fraction_lost) {
+ fraction_lost_ = fraction_lost;
+ }
+ bool SetCumulativeLost(int32_t cumulative_lost);
+ void SetExtHighestSeqNum(uint32_t ext_highest_seq_num) {
+ extended_high_seq_num_ = ext_highest_seq_num;
+ }
+ void SetJitter(uint32_t jitter) { jitter_ = jitter; }
+ void SetLastSr(uint32_t last_sr) { last_sr_ = last_sr; }
+ void SetDelayLastSr(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_; }
+ int32_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_; // 32 bits
+ uint8_t fraction_lost_; // 8 bits representing a fixed point value 0..1
+ int32_t cumulative_lost_; // Signed 24-bit value
+ uint32_t extended_high_seq_num_; // 32 bits
+ uint32_t jitter_; // 32 bits
+ uint32_t last_sr_; // 32 bits
+ uint32_t delay_since_last_sr_; // 32 bits, units of 1/65536 seconds
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_REPORT_BLOCK_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block_unittest.cc
new file mode 100644
index 0000000000..11031a059a
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/report_block_unittest.cc
@@ -0,0 +1,106 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
+
+#include <limits>
+
+#include "rtc_base/random.h"
+#include "test/gtest.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 int32_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.SetMediaSsrc(kRemoteSsrc);
+ rb.SetFractionLost(kFractionLost);
+ rb.SetCumulativeLost(kCumulativeLost);
+ rb.SetExtHighestSeqNum(kExtHighestSeqNum);
+ rb.SetJitter(kJitter);
+ rb.SetLastSr(kLastSr);
+ rb.SetDelayLastSr(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) {
+ // CumulativeLost is a signed 24-bit integer.
+ const int32_t kMaxCumulativeLost = 0x7fffff;
+ const int32_t kMinCumulativeLost = -0x800000;
+ ReportBlock rb;
+ EXPECT_FALSE(rb.SetCumulativeLost(kMaxCumulativeLost + 1));
+ EXPECT_TRUE(rb.SetCumulativeLost(kMaxCumulativeLost));
+ EXPECT_FALSE(rb.SetCumulativeLost(kMinCumulativeLost - 1));
+ EXPECT_TRUE(rb.SetCumulativeLost(kMinCumulativeLost));
+ EXPECT_EQ(rb.cumulative_lost(), kMinCumulativeLost);
+}
+
+TEST(RtcpPacketReportBlockTest, ParseNegativeCumulativeLost) {
+ // CumulativeLost is a signed 24-bit integer.
+ const int32_t kNegativeCumulativeLost = -123;
+ ReportBlock rb;
+ EXPECT_TRUE(rb.SetCumulativeLost(kNegativeCumulativeLost));
+
+ uint8_t buffer[kBufferLength];
+ rb.Create(buffer);
+
+ ReportBlock parsed;
+ EXPECT_TRUE(parsed.Parse(buffer, kBufferLength));
+
+ EXPECT_EQ(kNegativeCumulativeLost, parsed.cumulative_lost());
+}
+
+} // namespace
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.cc
new file mode 100644
index 0000000000..95fc890b19
--- /dev/null
+++ b/third_party/libwebrtc/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 "modules/rtp_rtcp/source/rtcp_packet/rrtr.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "rtc_base/checks.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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h
new file mode 100644
index 0000000000..827bd74399
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr.h
@@ -0,0 +1,59 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "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 SetNtp(NtpTime ntp) { ntp_ = ntp; }
+
+ NtpTime ntp() const { return ntp_; }
+
+ private:
+ NtpTime ntp_;
+};
+
+inline bool operator==(const Rrtr& rrtr1, const Rrtr& rrtr2) {
+ return rrtr1.ntp() == rrtr2.ntp();
+}
+
+inline bool operator!=(const Rrtr& rrtr1, const Rrtr& rrtr2) {
+ return !(rrtr1 == rrtr2);
+}
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RRTR_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr_unittest.cc
new file mode 100644
index 0000000000..56622ea81a
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rrtr_unittest.cc
@@ -0,0 +1,50 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/rrtr.h"
+
+#include "test/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.SetNtp(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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.cc
new file mode 100644
index 0000000000..18097de330
--- /dev/null
+++ b/third_party/libwebrtc/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 "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Rtpfb::kPacketType;
+// 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) {
+ SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(&payload[0]));
+ SetMediaSsrc(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/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h
new file mode 100644
index 0000000000..973b429a2d
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/rtpfb.h
@@ -0,0 +1,47 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RTPFB_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RTPFB_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include "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 constexpr uint8_t kPacketType = 205;
+
+ Rtpfb() = default;
+ ~Rtpfb() override = default;
+
+ void SetMediaSsrc(uint32_t ssrc) { media_ssrc_ = ssrc; }
+
+ uint32_t media_ssrc() const { return media_ssrc_; }
+
+ protected:
+ static constexpr size_t kCommonFeedbackLength = 8;
+ void ParseCommonFeedback(const uint8_t* payload);
+ void CreateCommonFeedback(uint8_t* payload) const;
+
+ private:
+ uint32_t media_ssrc_ = 0;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_RTPFB_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.cc
new file mode 100644
index 0000000000..f244ec5f37
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.cc
@@ -0,0 +1,199 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
+
+#include <string.h>
+
+#include <utility>
+
+#include "absl/strings/string_view.h"
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Sdes::kPacketType;
+constexpr size_t Sdes::kMaxNumberOfChunks;
+// Source Description (SDES) (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
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// header |V=2|P| SC | PT=SDES=202 | length |
+// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+// chunk | SSRC/CSRC_1 |
+// 1 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SDES items |
+// | ... |
+// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+// chunk | SSRC/CSRC_2 |
+// 2 +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SDES items |
+// | ... |
+// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// Canonical End-Point Identifier SDES Item (CNAME)
+//
+// 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
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | CNAME=1 | length | user and domain name ...
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+namespace {
+const uint8_t kTerminatorTag = 0;
+const uint8_t kCnameTag = 1;
+
+size_t ChunkSize(const Sdes::Chunk& chunk) {
+ // Chunk:
+ // SSRC/CSRC (4 bytes) | CNAME=1 (1 byte) | length (1 byte) | cname | padding.
+ size_t chunk_payload_size = 4 + 1 + 1 + chunk.cname.size();
+ size_t padding_size = 4 - (chunk_payload_size % 4); // Minimum 1.
+ return chunk_payload_size + padding_size;
+}
+} // namespace
+
+Sdes::Sdes() : block_length_(RtcpPacket::kHeaderLength) {}
+
+Sdes::~Sdes() {}
+
+bool Sdes::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+
+ uint8_t number_of_chunks = packet.count();
+ std::vector<Chunk> chunks; // Read chunk into temporary array, so that in
+ // case of an error original array would stay
+ // unchanged.
+ size_t block_length = kHeaderLength;
+
+ if (packet.payload_size_bytes() % 4 != 0) {
+ RTC_LOG(LS_WARNING) << "Invalid payload size "
+ << packet.payload_size_bytes()
+ << " bytes for a valid Sdes packet. Size should be"
+ " multiple of 4 bytes";
+ }
+ const uint8_t* const payload_end =
+ packet.payload() + packet.payload_size_bytes();
+ const uint8_t* looking_at = packet.payload();
+ chunks.resize(number_of_chunks);
+ for (size_t i = 0; i < number_of_chunks;) {
+ // Each chunk consumes at least 8 bytes.
+ if (payload_end - looking_at < 8) {
+ RTC_LOG(LS_WARNING) << "Not enough space left for chunk #" << (i + 1);
+ return false;
+ }
+ chunks[i].ssrc = ByteReader<uint32_t>::ReadBigEndian(looking_at);
+ looking_at += sizeof(uint32_t);
+ bool cname_found = false;
+
+ uint8_t item_type;
+ while ((item_type = *(looking_at++)) != kTerminatorTag) {
+ if (looking_at >= payload_end) {
+ RTC_LOG(LS_WARNING)
+ << "Unexpected end of packet while reading chunk #" << (i + 1)
+ << ". Expected to find size of the text.";
+ return false;
+ }
+ uint8_t item_length = *(looking_at++);
+ const size_t kTerminatorSize = 1;
+ if (looking_at + item_length + kTerminatorSize > payload_end) {
+ RTC_LOG(LS_WARNING)
+ << "Unexpected end of packet while reading chunk #" << (i + 1)
+ << ". Expected to find text of size " << item_length;
+ return false;
+ }
+ if (item_type == kCnameTag) {
+ if (cname_found) {
+ RTC_LOG(LS_WARNING)
+ << "Found extra CNAME for same ssrc in chunk #" << (i + 1);
+ return false;
+ }
+ cname_found = true;
+ chunks[i].cname.assign(reinterpret_cast<const char*>(looking_at),
+ item_length);
+ }
+ looking_at += item_length;
+ }
+ if (cname_found) {
+ // block_length calculates length of the packet that would be generated by
+ // Build/Create functions. Adjust it same way WithCName function does.
+ block_length += ChunkSize(chunks[i]);
+ ++i;
+ } else {
+ // RFC states CNAME item is mandatory.
+ // But same time it allows chunk without items.
+ // So while parsing, ignore all chunks without cname,
+ // but do not fail the parse.
+ RTC_LOG(LS_WARNING) << "CNAME not found for ssrc " << chunks[i].ssrc;
+ --number_of_chunks;
+ chunks.resize(number_of_chunks);
+ }
+ // Adjust to 32bit boundary.
+ looking_at += (payload_end - looking_at) % 4;
+ }
+
+ chunks_ = std::move(chunks);
+ block_length_ = block_length;
+ return true;
+}
+
+bool Sdes::AddCName(uint32_t ssrc, absl::string_view cname) {
+ RTC_DCHECK_LE(cname.length(), 0xffu);
+ if (chunks_.size() >= kMaxNumberOfChunks) {
+ RTC_LOG(LS_WARNING) << "Max SDES chunks reached.";
+ return false;
+ }
+ Chunk chunk;
+ chunk.ssrc = ssrc;
+ chunk.cname = std::string(cname);
+ chunks_.push_back(chunk);
+ block_length_ += ChunkSize(chunk);
+ return true;
+}
+
+size_t Sdes::BlockLength() const {
+ return block_length_;
+}
+
+bool Sdes::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ while (*index + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ }
+ const size_t index_end = *index + BlockLength();
+ CreateHeader(chunks_.size(), kPacketType, HeaderLength(), packet, index);
+
+ for (const Sdes::Chunk& chunk : chunks_) {
+ ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 0], chunk.ssrc);
+ ByteWriter<uint8_t>::WriteBigEndian(&packet[*index + 4], kCnameTag);
+ ByteWriter<uint8_t>::WriteBigEndian(
+ &packet[*index + 5], static_cast<uint8_t>(chunk.cname.size()));
+ memcpy(&packet[*index + 6], chunk.cname.data(), chunk.cname.size());
+ *index += (6 + chunk.cname.size());
+
+ // In each chunk, the list of items must be terminated by one or more null
+ // octets. The next chunk must start on a 32-bit boundary.
+ // CNAME (1 byte) | length (1 byte) | name | padding.
+ size_t padding_size = 4 - ((6 + chunk.cname.size()) % 4);
+ const int kPadding = 0;
+ memset(packet + *index, kPadding, padding_size);
+ *index += padding_size;
+ }
+
+ RTC_CHECK_EQ(*index, index_end);
+ return true;
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.h
new file mode 100644
index 0000000000..36b63ba29f
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes.h
@@ -0,0 +1,56 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SDES_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SDES_H_
+
+#include <string>
+#include <vector>
+
+#include "absl/strings/string_view.h"
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+// Source Description (SDES) (RFC 3550).
+class Sdes : public RtcpPacket {
+ public:
+ struct Chunk {
+ uint32_t ssrc;
+ std::string cname;
+ };
+ static constexpr uint8_t kPacketType = 202;
+ static constexpr size_t kMaxNumberOfChunks = 0x1f;
+
+ Sdes();
+ ~Sdes() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ bool AddCName(uint32_t ssrc, absl::string_view cname);
+
+ const std::vector<Chunk>& chunks() const { return chunks_; }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ std::vector<Chunk> chunks_;
+ size_t block_length_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SDES_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes_unittest.cc
new file mode 100644
index 0000000000..15a39efe87
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sdes_unittest.cc
@@ -0,0 +1,244 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
+
+#include "rtc_base/strings/string_builder.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using webrtc::rtcp::Sdes;
+
+namespace webrtc {
+namespace {
+const uint32_t kSenderSsrc = 0x12345678;
+const uint8_t kPadding = 0;
+const uint8_t kTerminatorTag = 0;
+const uint8_t kCnameTag = 1;
+const uint8_t kNameTag = 2;
+const uint8_t kEmailTag = 3;
+} // namespace
+
+TEST(RtcpPacketSdesTest, CreateAndParseWithoutChunks) {
+ Sdes sdes;
+
+ rtc::Buffer packet = sdes.Build();
+ Sdes parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+
+ EXPECT_EQ(0u, parsed.chunks().size());
+}
+
+TEST(RtcpPacketSdesTest, CreateAndParseWithOneChunk) {
+ const std::string kCname = "alice@host";
+
+ Sdes sdes;
+ EXPECT_TRUE(sdes.AddCName(kSenderSsrc, kCname));
+
+ rtc::Buffer packet = sdes.Build();
+ Sdes sdes_parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &sdes_parsed));
+ const Sdes& parsed = sdes_parsed; // Ensure accessors are const.
+
+ EXPECT_EQ(1u, parsed.chunks().size());
+ EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc);
+ EXPECT_EQ(kCname, parsed.chunks()[0].cname);
+}
+
+TEST(RtcpPacketSdesTest, CreateAndParseWithMultipleChunks) {
+ Sdes sdes;
+ EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 0, "a"));
+ EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 1, "ab"));
+ EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 2, "abc"));
+ EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 3, "abcd"));
+ EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 4, "abcde"));
+ EXPECT_TRUE(sdes.AddCName(kSenderSsrc + 5, "abcdef"));
+
+ rtc::Buffer packet = sdes.Build();
+ Sdes parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+
+ EXPECT_EQ(6u, parsed.chunks().size());
+ EXPECT_EQ(kSenderSsrc + 5, parsed.chunks()[5].ssrc);
+ EXPECT_EQ("abcdef", parsed.chunks()[5].cname);
+}
+
+TEST(RtcpPacketSdesTest, CreateWithTooManyChunks) {
+ const size_t kMaxChunks = (1 << 5) - 1;
+ Sdes sdes;
+ for (size_t i = 0; i < kMaxChunks; ++i) {
+ uint32_t ssrc = kSenderSsrc + i;
+ rtc::StringBuilder oss;
+ oss << "cname" << i;
+ EXPECT_TRUE(sdes.AddCName(ssrc, oss.str()));
+ }
+ EXPECT_FALSE(sdes.AddCName(kSenderSsrc + kMaxChunks, "foo"));
+}
+
+TEST(RtcpPacketSdesTest, CreateAndParseCnameItemWithEmptyString) {
+ Sdes sdes;
+ EXPECT_TRUE(sdes.AddCName(kSenderSsrc, ""));
+
+ rtc::Buffer packet = sdes.Build();
+ Sdes parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+
+ EXPECT_EQ(1u, parsed.chunks().size());
+ EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc);
+ EXPECT_EQ("", parsed.chunks()[0].cname);
+}
+
+TEST(RtcpPacketSdesTest, ParseSkipsNonCNameField) {
+ const uint8_t kName[] = "abc";
+ const uint8_t kCname[] = "de";
+ const uint8_t kValidPacket[] = {
+ 0x81, 202, 0x00, 0x04, 0x12, 0x34, 0x56,
+ 0x78, kNameTag, 3, kName[0], kName[1], kName[2], kCnameTag,
+ 2, kCname[0], kCname[1], kTerminatorTag, kPadding, kPadding};
+ // Sanity checks packet was assembled correctly.
+ ASSERT_EQ(0u, sizeof(kValidPacket) % 4);
+ ASSERT_EQ(kValidPacket[3] + 1u, sizeof(kValidPacket) / 4);
+
+ Sdes parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kValidPacket, &parsed));
+
+ EXPECT_EQ(1u, parsed.chunks().size());
+ EXPECT_EQ(kSenderSsrc, parsed.chunks()[0].ssrc);
+ EXPECT_EQ("de", parsed.chunks()[0].cname);
+}
+
+TEST(RtcpPacketSdesTest, ParseSkipsChunksWithoutCName) {
+ const uint8_t kName[] = "ab";
+ const uint8_t kEmail[] = "de";
+ const uint8_t kCname[] = "def";
+ const uint8_t kPacket[] = {
+ 0x82, 202, 0x00, 0x07, 0x12,
+ 0x34, 0x56, 0x78, // 1st chunk.
+ kNameTag, 3, kName[0], kName[1], kName[2],
+ kEmailTag, 2, kEmail[0], kEmail[1], kTerminatorTag,
+ kPadding, kPadding, 0x23, 0x45, 0x67,
+ 0x89, // 2nd chunk.
+ kCnameTag, 3, kCname[0], kCname[1], kCname[2],
+ kTerminatorTag, kPadding, kPadding};
+ // Sanity checks packet was assembled correctly.
+ ASSERT_EQ(0u, sizeof(kPacket) % 4);
+ ASSERT_EQ(kPacket[3] + 1u, sizeof(kPacket) / 4);
+
+ Sdes parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &parsed));
+ ASSERT_EQ(1u, parsed.chunks().size());
+ EXPECT_EQ(0x23456789u, parsed.chunks()[0].ssrc);
+ EXPECT_EQ("def", parsed.chunks()[0].cname);
+}
+
+TEST(RtcpPacketSdesTest, ParseFailsWithoutChunkItemTerminator) {
+ const uint8_t kName[] = "abc";
+ const uint8_t kCname[] = "d";
+ // No place for next chunk item.
+ const uint8_t kInvalidPacket[] = {
+ 0x81, 202, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78,
+ kNameTag, 3, kName[0], kName[1], kName[2], kCnameTag, 1, kCname[0]};
+ // Sanity checks packet was assembled correctly.
+ ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
+ ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);
+
+ Sdes parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
+}
+
+TEST(RtcpPacketSdesTest, ParseFailsWithDamagedChunkItem) {
+ const uint8_t kName[] = "ab";
+ const uint8_t kCname[] = "d";
+ // Next chunk item has non-terminator type, but not the size.
+ const uint8_t kInvalidPacket[] = {
+ 0x81, 202, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78,
+ kNameTag, 2, kName[0], kName[1], kCnameTag, 1, kCname[0], kEmailTag};
+ // Sanity checks packet was assembled correctly.
+ ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
+ ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);
+
+ Sdes parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
+}
+
+TEST(RtcpPacketSdesTest, ParseFailsWithTooLongChunkItem) {
+ const uint8_t kName[] = "abc";
+ const uint8_t kCname[] = "d";
+ // Last chunk item has length that goes beyond the buffer end.
+ const uint8_t kInvalidPacket[] = {
+ 0x81, 202, 0x00, 0x03, 0x12, 0x34, 0x56, 0x78,
+ kNameTag, 3, kName[0], kName[1], kName[2], kCnameTag, 2, kCname[0]};
+ // Sanity checks packet was assembled correctly.
+ ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
+ ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);
+
+ Sdes parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
+}
+
+TEST(RtcpPacketSdesTest, ParseFailsWithTwoCNames) {
+ const uint8_t kCname1[] = "a";
+ const uint8_t kCname2[] = "de";
+ const uint8_t kInvalidPacket[] = {
+ 0x81, 202, 0x00, 0x03, 0x12, 0x34, 0x56,
+ 0x78, kCnameTag, 1, kCname1[0], kCnameTag, 2, kCname2[0],
+ kCname2[1], kTerminatorTag};
+ // Sanity checks packet was assembled correctly.
+ ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
+ ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);
+
+ Sdes parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
+}
+
+TEST(RtcpPacketSdesTest, ParseFailsWithTooLittleSpaceForNextChunk) {
+ const uint8_t kCname[] = "a";
+ const uint8_t kEmail[] = "de";
+ // Two chunks are promised in the header, but no place for the second chunk.
+ const uint8_t kInvalidPacket[] = {
+ 0x82, 202, 0x00, 0x04, 0x12, 0x34, 0x56,
+ 0x78, // 1st chunk.
+ kCnameTag, 1, kCname[0], kEmailTag, 2, kEmail[0], kEmail[1],
+ kTerminatorTag, 0x23, 0x45, 0x67, 0x89}; // 2nd chunk.
+ // Sanity checks packet was assembled correctly.
+ ASSERT_EQ(0u, sizeof(kInvalidPacket) % 4);
+ ASSERT_EQ(kInvalidPacket[3] + 1u, sizeof(kInvalidPacket) / 4);
+
+ Sdes parsed;
+ EXPECT_FALSE(test::ParseSinglePacket(kInvalidPacket, &parsed));
+}
+
+TEST(RtcpPacketSdesTest, ParsedSdesCanBeReusedForBuilding) {
+ Sdes source;
+ const std::string kAlice = "alice@host";
+ const std::string kBob = "bob@host";
+ source.AddCName(kSenderSsrc, kAlice);
+
+ rtc::Buffer packet1 = source.Build();
+ Sdes middle;
+ test::ParseSinglePacket(packet1, &middle);
+
+ EXPECT_EQ(source.BlockLength(), middle.BlockLength());
+
+ middle.AddCName(kSenderSsrc + 1, kBob);
+
+ rtc::Buffer packet2 = middle.Build();
+ Sdes destination;
+ test::ParseSinglePacket(packet2, &destination);
+
+ EXPECT_EQ(middle.BlockLength(), destination.BlockLength());
+
+ EXPECT_EQ(2u, destination.chunks().size());
+ EXPECT_EQ(kSenderSsrc, destination.chunks()[0].ssrc);
+ EXPECT_EQ(kAlice, destination.chunks()[0].cname);
+ EXPECT_EQ(kSenderSsrc + 1, destination.chunks()[1].ssrc);
+ EXPECT_EQ(kBob, destination.chunks()[1].cname);
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.cc
new file mode 100644
index 0000000000..73738376c3
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.cc
@@ -0,0 +1,141 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
+
+#include <utility>
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t SenderReport::kPacketType;
+constexpr size_t SenderReport::kMaxNumberOfReportBlocks;
+constexpr size_t SenderReport::kSenderBaseLength;
+// Sender report (SR) (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=SR=200 | length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 0 | SSRC of sender |
+// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+// 4 | NTP timestamp, most significant word |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 8 | NTP timestamp, least significant word |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 12 | RTP timestamp |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 16 | sender's packet count |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 20 | sender's octet count |
+// 24 +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+
+SenderReport::SenderReport()
+ : rtp_timestamp_(0), sender_packet_count_(0), sender_octet_count_(0) {}
+
+SenderReport::SenderReport(const SenderReport&) = default;
+SenderReport::SenderReport(SenderReport&&) = default;
+SenderReport& SenderReport::operator=(const SenderReport&) = default;
+SenderReport& SenderReport::operator=(SenderReport&&) = default;
+SenderReport::~SenderReport() = default;
+
+bool SenderReport::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+
+ const uint8_t report_block_count = packet.count();
+ if (packet.payload_size_bytes() <
+ kSenderBaseLength + report_block_count * ReportBlock::kLength) {
+ RTC_LOG(LS_WARNING) << "Packet is too small to contain all the data.";
+ return false;
+ }
+ // Read SenderReport header.
+ const uint8_t* const payload = packet.payload();
+ SetSenderSsrc(ByteReader<uint32_t>::ReadBigEndian(&payload[0]));
+ uint32_t secs = ByteReader<uint32_t>::ReadBigEndian(&payload[4]);
+ uint32_t frac = ByteReader<uint32_t>::ReadBigEndian(&payload[8]);
+ ntp_.Set(secs, frac);
+ rtp_timestamp_ = ByteReader<uint32_t>::ReadBigEndian(&payload[12]);
+ sender_packet_count_ = ByteReader<uint32_t>::ReadBigEndian(&payload[16]);
+ sender_octet_count_ = ByteReader<uint32_t>::ReadBigEndian(&payload[20]);
+ report_blocks_.resize(report_block_count);
+ const uint8_t* next_block = payload + kSenderBaseLength;
+ for (ReportBlock& block : report_blocks_) {
+ bool block_parsed = block.Parse(next_block, ReportBlock::kLength);
+ RTC_DCHECK(block_parsed);
+ next_block += ReportBlock::kLength;
+ }
+ // Double check we didn't read beyond provided buffer.
+ RTC_DCHECK_LE(next_block - payload,
+ static_cast<ptrdiff_t>(packet.payload_size_bytes()));
+ return true;
+}
+
+size_t SenderReport::BlockLength() const {
+ return kHeaderLength + kSenderBaseLength +
+ report_blocks_.size() * ReportBlock::kLength;
+}
+
+bool SenderReport::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ while (*index + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ }
+ const size_t index_end = *index + BlockLength();
+
+ CreateHeader(report_blocks_.size(), kPacketType, HeaderLength(), packet,
+ index);
+ // Write SenderReport header.
+ ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 0], sender_ssrc());
+ ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 4], ntp_.seconds());
+ ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 8], ntp_.fractions());
+ ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 12], rtp_timestamp_);
+ ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 16],
+ sender_packet_count_);
+ ByteWriter<uint32_t>::WriteBigEndian(&packet[*index + 20],
+ sender_octet_count_);
+ *index += kSenderBaseLength;
+ // Write report blocks.
+ for (const ReportBlock& block : report_blocks_) {
+ block.Create(packet + *index);
+ *index += ReportBlock::kLength;
+ }
+ // Ensure bytes written match expected.
+ RTC_DCHECK_EQ(*index, index_end);
+ return true;
+}
+
+bool SenderReport::AddReportBlock(const ReportBlock& block) {
+ if (report_blocks_.size() >= kMaxNumberOfReportBlocks) {
+ RTC_LOG(LS_WARNING) << "Max report blocks reached.";
+ return false;
+ }
+ report_blocks_.push_back(block);
+ return true;
+}
+
+bool SenderReport::SetReportBlocks(std::vector<ReportBlock> blocks) {
+ if (blocks.size() > kMaxNumberOfReportBlocks) {
+ RTC_LOG(LS_WARNING) << "Too many report blocks (" << blocks.size()
+ << ") for sender report.";
+ return false;
+ }
+ report_blocks_ = std::move(blocks);
+ return true;
+}
+
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.h
new file mode 100644
index 0000000000..66ced31721
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report.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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SENDER_REPORT_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SENDER_REPORT_H_
+
+#include <vector>
+
+#include "modules/rtp_rtcp/source/rtcp_packet.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/report_block.h"
+#include "system_wrappers/include/ntp_time.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+class SenderReport : public RtcpPacket {
+ public:
+ static constexpr uint8_t kPacketType = 200;
+ static constexpr size_t kMaxNumberOfReportBlocks = 0x1f;
+
+ SenderReport();
+ SenderReport(const SenderReport&);
+ SenderReport(SenderReport&&);
+ SenderReport& operator=(const SenderReport&);
+ SenderReport& operator=(SenderReport&&);
+ ~SenderReport() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ void SetNtp(NtpTime ntp) { ntp_ = ntp; }
+ void SetRtpTimestamp(uint32_t rtp_timestamp) {
+ rtp_timestamp_ = rtp_timestamp;
+ }
+ void SetPacketCount(uint32_t packet_count) {
+ sender_packet_count_ = packet_count;
+ }
+ void SetOctetCount(uint32_t octet_count) {
+ sender_octet_count_ = octet_count;
+ }
+ bool AddReportBlock(const ReportBlock& block);
+ bool SetReportBlocks(std::vector<ReportBlock> blocks);
+ void ClearReportBlocks() { report_blocks_.clear(); }
+
+ NtpTime ntp() const { return ntp_; }
+ uint32_t rtp_timestamp() const { return rtp_timestamp_; }
+ uint32_t sender_packet_count() const { return sender_packet_count_; }
+ uint32_t sender_octet_count() const { return sender_octet_count_; }
+
+ const std::vector<ReportBlock>& report_blocks() const {
+ return report_blocks_;
+ }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ static constexpr size_t kSenderBaseLength = 24;
+
+ NtpTime ntp_;
+ uint32_t rtp_timestamp_;
+ uint32_t sender_packet_count_;
+ uint32_t sender_octet_count_;
+ std::vector<ReportBlock> report_blocks_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_SENDER_REPORT_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report_unittest.cc
new file mode 100644
index 0000000000..37f268e6b4
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/sender_report_unittest.cc
@@ -0,0 +1,142 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
+
+#include <utility>
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAreArray;
+using ::testing::make_tuple;
+using webrtc::rtcp::ReportBlock;
+using webrtc::rtcp::SenderReport;
+
+namespace webrtc {
+namespace {
+const uint32_t kSenderSsrc = 0x12345678;
+const uint32_t kRemoteSsrc = 0x23456789;
+const NtpTime kNtp(0x11121418, 0x22242628);
+const uint32_t kRtpTimestamp = 0x33343536;
+const uint32_t kPacketCount = 0x44454647;
+const uint32_t kOctetCount = 0x55565758;
+const uint8_t kPacket[] = {0x80, 200, 0x00, 0x06, 0x12, 0x34, 0x56,
+ 0x78, 0x11, 0x12, 0x14, 0x18, 0x22, 0x24,
+ 0x26, 0x28, 0x33, 0x34, 0x35, 0x36, 0x44,
+ 0x45, 0x46, 0x47, 0x55, 0x56, 0x57, 0x58};
+} // namespace
+
+TEST(RtcpPacketSenderReportTest, CreateWithoutReportBlocks) {
+ SenderReport sr;
+ sr.SetSenderSsrc(kSenderSsrc);
+ sr.SetNtp(kNtp);
+ sr.SetRtpTimestamp(kRtpTimestamp);
+ sr.SetPacketCount(kPacketCount);
+ sr.SetOctetCount(kOctetCount);
+
+ rtc::Buffer raw = sr.Build();
+ EXPECT_THAT(make_tuple(raw.data(), raw.size()), ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketSenderReportTest, ParseWithoutReportBlocks) {
+ SenderReport parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(kNtp, parsed.ntp());
+ EXPECT_EQ(kRtpTimestamp, parsed.rtp_timestamp());
+ EXPECT_EQ(kPacketCount, parsed.sender_packet_count());
+ EXPECT_EQ(kOctetCount, parsed.sender_octet_count());
+ EXPECT_TRUE(parsed.report_blocks().empty());
+}
+
+TEST(RtcpPacketSenderReportTest, CreateAndParseWithOneReportBlock) {
+ ReportBlock rb;
+ rb.SetMediaSsrc(kRemoteSsrc);
+
+ SenderReport sr;
+ sr.SetSenderSsrc(kSenderSsrc);
+ EXPECT_TRUE(sr.AddReportBlock(rb));
+
+ rtc::Buffer raw = sr.Build();
+ SenderReport parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(1u, parsed.report_blocks().size());
+ EXPECT_EQ(kRemoteSsrc, parsed.report_blocks()[0].source_ssrc());
+}
+
+TEST(RtcpPacketSenderReportTest, CreateAndParseWithTwoReportBlocks) {
+ ReportBlock rb1;
+ rb1.SetMediaSsrc(kRemoteSsrc);
+ ReportBlock rb2;
+ rb2.SetMediaSsrc(kRemoteSsrc + 1);
+
+ SenderReport sr;
+ sr.SetSenderSsrc(kSenderSsrc);
+ EXPECT_TRUE(sr.AddReportBlock(rb1));
+ EXPECT_TRUE(sr.AddReportBlock(rb2));
+
+ rtc::Buffer raw = sr.Build();
+ SenderReport parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(raw, &parsed));
+
+ 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(RtcpPacketSenderReportTest, CreateWithTooManyReportBlocks) {
+ SenderReport sr;
+ sr.SetSenderSsrc(kSenderSsrc);
+ ReportBlock rb;
+ for (size_t i = 0; i < SenderReport::kMaxNumberOfReportBlocks; ++i) {
+ rb.SetMediaSsrc(kRemoteSsrc + i);
+ EXPECT_TRUE(sr.AddReportBlock(rb));
+ }
+ rb.SetMediaSsrc(kRemoteSsrc + SenderReport::kMaxNumberOfReportBlocks);
+ EXPECT_FALSE(sr.AddReportBlock(rb));
+}
+
+TEST(RtcpPacketSenderReportTest, SetReportBlocksOverwritesOldBlocks) {
+ SenderReport sr;
+ ReportBlock report_block;
+ // Use jitter field of the report blocks to distinguish them.
+ report_block.SetJitter(1001u);
+ sr.AddReportBlock(report_block);
+ ASSERT_EQ(sr.report_blocks().size(), 1u);
+ ASSERT_EQ(sr.report_blocks()[0].jitter(), 1001u);
+
+ std::vector<ReportBlock> blocks(3u);
+ blocks[0].SetJitter(2001u);
+ blocks[1].SetJitter(3001u);
+ blocks[2].SetJitter(4001u);
+ EXPECT_TRUE(sr.SetReportBlocks(blocks));
+ ASSERT_EQ(sr.report_blocks().size(), 3u);
+ EXPECT_EQ(sr.report_blocks()[0].jitter(), 2001u);
+ EXPECT_EQ(sr.report_blocks()[1].jitter(), 3001u);
+ EXPECT_EQ(sr.report_blocks()[2].jitter(), 4001u);
+}
+
+TEST(RtcpPacketSenderReportTest, SetReportBlocksMaxLimit) {
+ SenderReport sr;
+ std::vector<ReportBlock> max_blocks(SenderReport::kMaxNumberOfReportBlocks);
+ EXPECT_TRUE(sr.SetReportBlocks(std::move(max_blocks)));
+
+ std::vector<ReportBlock> one_too_many_blocks(
+ SenderReport::kMaxNumberOfReportBlocks + 1);
+ EXPECT_FALSE(sr.SetReportBlocks(std::move(one_too_many_blocks)));
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.cc
new file mode 100644
index 0000000000..601b24fe94
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.cc
@@ -0,0 +1,127 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/numerics/safe_conversions.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr size_t kTargetBitrateHeaderSizeBytes = 4;
+constexpr uint8_t TargetBitrate::kBlockType;
+const size_t TargetBitrate::kBitrateItemSizeBytes = 4;
+
+TargetBitrate::BitrateItem::BitrateItem()
+ : spatial_layer(0), temporal_layer(0), target_bitrate_kbps(0) {}
+
+TargetBitrate::BitrateItem::BitrateItem(uint8_t spatial_layer,
+ uint8_t temporal_layer,
+ uint32_t target_bitrate_kbps)
+ : spatial_layer(spatial_layer),
+ temporal_layer(temporal_layer),
+ target_bitrate_kbps(target_bitrate_kbps) {}
+
+// 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
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | BT=42 | reserved | block length |
+// +=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
+//
+// Target bitrate item (repeat as many times as necessary).
+//
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | S | T | Target Bitrate |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : ... :
+//
+// Spatial Layer (S): 4 bits
+// Indicates which temporal layer this bitrate concerns.
+//
+// Temporal Layer (T): 4 bits
+// Indicates which temporal layer this bitrate concerns.
+//
+// Target Bitrate: 24 bits
+// The encoder target bitrate for this layer, in kbps.
+//
+// As an example of how S and T are intended to be used, VP8 simulcast will
+// use a separate TargetBitrate message per stream, since they are transmitted
+// on separate SSRCs, with temporal layers grouped by stream.
+// If VP9 SVC is used, there will be only one SSRC, so each spatial and
+// temporal layer combo used shall be specified in the TargetBitrate packet.
+
+TargetBitrate::TargetBitrate() = default;
+TargetBitrate::TargetBitrate(const TargetBitrate&) = default;
+TargetBitrate& TargetBitrate::operator=(const TargetBitrate&) = default;
+TargetBitrate::~TargetBitrate() = default;
+
+void TargetBitrate::Parse(const uint8_t* block, uint16_t block_length) {
+ // Validate block header (should already have been parsed and checked).
+ RTC_DCHECK_EQ(block[0], kBlockType);
+ RTC_DCHECK_EQ(block_length, ByteReader<uint16_t>::ReadBigEndian(&block[2]));
+
+ // Header specifies block length - 1, but since we ignore the header, which
+ // occupies exactly on block, we can just treat this as payload length.
+ const size_t payload_bytes = block_length * 4;
+ const size_t num_items = payload_bytes / kBitrateItemSizeBytes;
+ size_t index = kTargetBitrateHeaderSizeBytes;
+ bitrates_.clear();
+ for (size_t i = 0; i < num_items; ++i) {
+ uint8_t layers = block[index];
+ uint32_t bitrate_kbps =
+ ByteReader<uint32_t, 3>::ReadBigEndian(&block[index + 1]);
+ index += kBitrateItemSizeBytes;
+ AddTargetBitrate((layers >> 4) & 0x0F, layers & 0x0F, bitrate_kbps);
+ }
+}
+
+void TargetBitrate::AddTargetBitrate(uint8_t spatial_layer,
+ uint8_t temporal_layer,
+ uint32_t target_bitrate_kbps) {
+ RTC_DCHECK_LE(spatial_layer, 0x0F);
+ RTC_DCHECK_LE(temporal_layer, 0x0F);
+ RTC_DCHECK_LE(target_bitrate_kbps, 0x00FFFFFFU);
+ bitrates_.push_back(
+ BitrateItem(spatial_layer, temporal_layer, target_bitrate_kbps));
+}
+
+const std::vector<TargetBitrate::BitrateItem>&
+TargetBitrate::GetTargetBitrates() const {
+ return bitrates_;
+}
+
+size_t TargetBitrate::BlockLength() const {
+ return kTargetBitrateHeaderSizeBytes +
+ bitrates_.size() * kBitrateItemSizeBytes;
+}
+
+void TargetBitrate::Create(uint8_t* buffer) const {
+ buffer[0] = kBlockType;
+ buffer[1] = 0; // Reserved.
+ uint16_t block_length_words =
+ rtc::dchecked_cast<uint16_t>((BlockLength() / 4) - 1);
+ ByteWriter<uint16_t>::WriteBigEndian(&buffer[2], block_length_words);
+
+ size_t index = kTargetBitrateHeaderSizeBytes;
+ for (const BitrateItem& item : bitrates_) {
+ buffer[index] = (item.spatial_layer << 4) | item.temporal_layer;
+ ByteWriter<uint32_t, 3>::WriteBigEndian(&buffer[index + 1],
+ item.target_bitrate_kbps);
+ index += kBitrateItemSizeBytes;
+ }
+}
+
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h
new file mode 100644
index 0000000000..07e5da1a49
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h
@@ -0,0 +1,63 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+#include <vector>
+
+namespace webrtc {
+namespace rtcp {
+
+class TargetBitrate {
+ public:
+ // TODO(sprang): This block type is just a place holder. We need to get an
+ // id assigned by IANA.
+ static constexpr uint8_t kBlockType = 42;
+ static const size_t kBitrateItemSizeBytes;
+
+ struct BitrateItem {
+ BitrateItem();
+ BitrateItem(uint8_t spatial_layer,
+ uint8_t temporal_layer,
+ uint32_t target_bitrate_kbps);
+
+ uint8_t spatial_layer;
+ uint8_t temporal_layer;
+ uint32_t target_bitrate_kbps;
+ };
+
+ TargetBitrate();
+ TargetBitrate(const TargetBitrate&);
+ TargetBitrate& operator=(const TargetBitrate&);
+ ~TargetBitrate();
+
+ void AddTargetBitrate(uint8_t spatial_layer,
+ uint8_t temporal_layer,
+ uint32_t target_bitrate_kbps);
+
+ const std::vector<BitrateItem>& GetTargetBitrates() const;
+
+ void Parse(const uint8_t* block, uint16_t block_length);
+
+ size_t BlockLength() const;
+
+ void Create(uint8_t* buffer) const;
+
+ private:
+ std::vector<BitrateItem> bitrates_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TARGET_BITRATE_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate_unittest.cc
new file mode 100644
index 0000000000..b16bb5beaa
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/target_bitrate_unittest.cc
@@ -0,0 +1,96 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/target_bitrate.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/extended_reports.h"
+#include "rtc_base/buffer.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+namespace webrtc {
+namespace {
+using BitrateItem = rtcp::TargetBitrate::BitrateItem;
+using rtcp::TargetBitrate;
+using test::ParseSinglePacket;
+
+constexpr uint32_t kSsrc = 0x12345678;
+
+// clang-format off
+const uint8_t kPacket[] = { TargetBitrate::kBlockType, // Block ID.
+ 0x00, // Reserved.
+ 0x00, 0x04, // Length = 4 words.
+ 0x00, 0x01, 0x02, 0x03, // S0T0 0x010203 kbps.
+ 0x01, 0x02, 0x03, 0x04, // S0T1 0x020304 kbps.
+ 0x10, 0x03, 0x04, 0x05, // S1T0 0x030405 kbps.
+ 0x11, 0x04, 0x05, 0x06 }; // S1T1 0x040506 kbps.
+constexpr size_t kPacketLengthBlocks = ((sizeof(kPacket) + 3) / 4) - 1;
+// clang-format on
+
+void ExpectBirateItemEquals(const BitrateItem& expected,
+ const BitrateItem& actual) {
+ EXPECT_EQ(expected.spatial_layer, actual.spatial_layer);
+ EXPECT_EQ(expected.temporal_layer, actual.temporal_layer);
+ EXPECT_EQ(expected.target_bitrate_kbps, actual.target_bitrate_kbps);
+}
+
+void CheckBitrateItems(const std::vector<BitrateItem>& bitrates) {
+ EXPECT_EQ(4U, bitrates.size());
+ ExpectBirateItemEquals(BitrateItem(0, 0, 0x010203), bitrates[0]);
+ ExpectBirateItemEquals(BitrateItem(0, 1, 0x020304), bitrates[1]);
+ ExpectBirateItemEquals(BitrateItem(1, 0, 0x030405), bitrates[2]);
+ ExpectBirateItemEquals(BitrateItem(1, 1, 0x040506), bitrates[3]);
+}
+
+} // namespace
+
+TEST(TargetBitrateTest, Parse) {
+ TargetBitrate target_bitrate;
+ target_bitrate.Parse(kPacket, kPacketLengthBlocks);
+ CheckBitrateItems(target_bitrate.GetTargetBitrates());
+}
+
+TEST(TargetBitrateTest, FullPacket) {
+ const size_t kXRHeaderSize = 8; // RTCP header (4) + SSRC (4).
+ const size_t kTotalSize = kXRHeaderSize + sizeof(kPacket);
+ uint8_t kRtcpPacket[kTotalSize] = {2 << 6, 207, 0x00, (kTotalSize / 4) - 1,
+ 0x12, 0x34, 0x56, 0x78}; // SSRC.
+ memcpy(&kRtcpPacket[kXRHeaderSize], kPacket, sizeof(kPacket));
+ rtcp::ExtendedReports xr;
+ EXPECT_TRUE(ParseSinglePacket(kRtcpPacket, &xr));
+ EXPECT_EQ(kSsrc, xr.sender_ssrc());
+ const absl::optional<TargetBitrate>& target_bitrate = xr.target_bitrate();
+ ASSERT_TRUE(static_cast<bool>(target_bitrate));
+ CheckBitrateItems(target_bitrate->GetTargetBitrates());
+}
+
+TEST(TargetBitrateTest, Create) {
+ TargetBitrate target_bitrate;
+ target_bitrate.AddTargetBitrate(0, 0, 0x010203);
+ target_bitrate.AddTargetBitrate(0, 1, 0x020304);
+ target_bitrate.AddTargetBitrate(1, 0, 0x030405);
+ target_bitrate.AddTargetBitrate(1, 1, 0x040506);
+
+ uint8_t buffer[sizeof(kPacket)] = {};
+ ASSERT_EQ(sizeof(kPacket), target_bitrate.BlockLength());
+ target_bitrate.Create(buffer);
+
+ EXPECT_EQ(0, memcmp(kPacket, buffer, sizeof(kPacket)));
+}
+
+TEST(TargetBitrateTest, ParseNullBitratePacket) {
+ const uint8_t kNullPacket[] = {TargetBitrate::kBlockType, 0x00, 0x00, 0x00};
+ TargetBitrate target_bitrate;
+ target_bitrate.Parse(kNullPacket, 0);
+ EXPECT_TRUE(target_bitrate.GetTargetBitrates().empty());
+}
+
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.cc
new file mode 100644
index 0000000000..810e1e267a
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.cc
@@ -0,0 +1,71 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
+
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+TmmbItem::TmmbItem(uint32_t ssrc, uint64_t bitrate_bps, uint16_t overhead)
+ : ssrc_(ssrc), bitrate_bps_(bitrate_bps), packet_overhead_(overhead) {
+ RTC_DCHECK_LE(overhead, 0x1ffu);
+}
+
+// 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 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 4 | MxTBR Exp | MxTBR Mantissa |Measured Overhead|
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+bool TmmbItem::Parse(const uint8_t* buffer) {
+ ssrc_ = ByteReader<uint32_t>::ReadBigEndian(&buffer[0]);
+ // Read 4 bytes into 1 block.
+ uint32_t compact = ByteReader<uint32_t>::ReadBigEndian(&buffer[4]);
+ // Split 1 block into 3 components.
+ uint8_t exponent = compact >> 26; // 6 bits.
+ uint64_t mantissa = (compact >> 9) & 0x1ffff; // 17 bits.
+ uint16_t overhead = compact & 0x1ff; // 9 bits.
+ // Combine 3 components into 2 values.
+ bitrate_bps_ = (mantissa << exponent);
+
+ bool shift_overflow = (bitrate_bps_ >> exponent) != mantissa;
+ if (shift_overflow) {
+ RTC_LOG(LS_ERROR) << "Invalid tmmb bitrate value : " << mantissa << "*2^"
+ << static_cast<int>(exponent);
+ return false;
+ }
+ packet_overhead_ = overhead;
+ return true;
+}
+
+void TmmbItem::Create(uint8_t* buffer) const {
+ constexpr uint64_t kMaxMantissa = 0x1ffff; // 17 bits.
+ uint64_t mantissa = bitrate_bps_;
+ uint32_t exponent = 0;
+ while (mantissa > kMaxMantissa) {
+ mantissa >>= 1;
+ ++exponent;
+ }
+
+ ByteWriter<uint32_t>::WriteBigEndian(&buffer[0], ssrc_);
+ uint32_t compact = (exponent << 26) | (mantissa << 9) | packet_overhead_;
+ ByteWriter<uint32_t>::WriteBigEndian(&buffer[4], compact);
+}
+
+void TmmbItem::set_packet_overhead(uint16_t overhead) {
+ RTC_DCHECK_LE(overhead, 0x1ffu);
+ packet_overhead_ = overhead;
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h
new file mode 100644
index 0000000000..dc5d1b2c2d
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h
@@ -0,0 +1,52 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMB_ITEM_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMB_ITEM_H_
+
+#include <stddef.h>
+#include <stdint.h>
+
+namespace webrtc {
+namespace rtcp {
+// RFC5104, Section 3.5.4
+// Temporary Maximum Media Stream Bitrate Request/Notification.
+// Used both by TMMBR and TMMBN rtcp packets.
+class TmmbItem {
+ public:
+ static const size_t kLength = 8;
+
+ TmmbItem() : ssrc_(0), bitrate_bps_(0), packet_overhead_(0) {}
+ TmmbItem(uint32_t ssrc, uint64_t bitrate_bps, uint16_t overhead);
+
+ bool Parse(const uint8_t* buffer);
+ void Create(uint8_t* buffer) const;
+
+ void set_ssrc(uint32_t ssrc) { ssrc_ = ssrc; }
+ void set_bitrate_bps(uint64_t bitrate_bps) { bitrate_bps_ = bitrate_bps; }
+ void set_packet_overhead(uint16_t overhead);
+
+ uint32_t ssrc() const { return ssrc_; }
+ uint64_t bitrate_bps() const { return bitrate_bps_; }
+ uint16_t packet_overhead() const { return packet_overhead_; }
+
+ private:
+ // Media stream id.
+ uint32_t ssrc_;
+ // Maximum total media bit rate that the media receiver is
+ // currently prepared to accept for this media stream.
+ uint64_t bitrate_bps_;
+ // Per-packet overhead that the media receiver has observed
+ // for this media stream at its chosen reference protocol layer.
+ uint16_t packet_overhead_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMB_ITEM_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc
new file mode 100644
index 0000000000..f57e5749c2
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.cc
@@ -0,0 +1,109 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/tmmbn.h"
+
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Tmmbn::kFeedbackMessageType;
+// RFC 4585: Feedback format.
+// Common packet format:
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |V=2|P| FMT | PT | length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of packet sender |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of media source (unused) = 0 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : Feedback Control Information (FCI) :
+// : :
+// Temporary Maximum Media Stream Bit Rate Notification (TMMBN) (RFC 5104).
+// The Feedback Control Information (FCI) consists of zero, one, or more
+// TMMBN FCI entries.
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | MxTBR Exp | MxTBR Mantissa |Measured Overhead|
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Tmmbn::Tmmbn() = default;
+
+Tmmbn::~Tmmbn() = default;
+
+bool Tmmbn::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
+
+ if (packet.payload_size_bytes() < kCommonFeedbackLength) {
+ RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
+ << " is too small for TMMBN.";
+ return false;
+ }
+ size_t items_size_bytes = packet.payload_size_bytes() - kCommonFeedbackLength;
+ if (items_size_bytes % TmmbItem::kLength != 0) {
+ RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
+ << " is not valid for TMMBN.";
+ return false;
+ }
+ ParseCommonFeedback(packet.payload());
+ const uint8_t* next_item = packet.payload() + kCommonFeedbackLength;
+
+ size_t number_of_items = items_size_bytes / TmmbItem::kLength;
+ items_.resize(number_of_items);
+ for (TmmbItem& item : items_) {
+ if (!item.Parse(next_item))
+ return false;
+ next_item += TmmbItem::kLength;
+ }
+ return true;
+}
+
+void Tmmbn::AddTmmbr(const TmmbItem& item) {
+ items_.push_back(item);
+}
+
+size_t Tmmbn::BlockLength() const {
+ return kHeaderLength + kCommonFeedbackLength +
+ TmmbItem::kLength * items_.size();
+}
+
+bool Tmmbn::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ while (*index + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ }
+ const size_t index_end = *index + BlockLength();
+
+ CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
+ index);
+ RTC_DCHECK_EQ(0, Rtpfb::media_ssrc());
+ CreateCommonFeedback(packet + *index);
+ *index += kCommonFeedbackLength;
+ for (const TmmbItem& item : items_) {
+ item.Create(packet + *index);
+ *index += TmmbItem::kLength;
+ }
+ RTC_CHECK_EQ(index_end, *index);
+ return true;
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h
new file mode 100644
index 0000000000..ff7779d8ac
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn.h
@@ -0,0 +1,55 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_
+
+#include <vector>
+
+#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+// Temporary Maximum Media Stream Bit Rate Notification (TMMBN).
+// RFC 5104, Section 4.2.2.
+class Tmmbn : public Rtpfb {
+ public:
+ static constexpr uint8_t kFeedbackMessageType = 4;
+
+ Tmmbn();
+ ~Tmmbn() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ void AddTmmbr(const TmmbItem& item);
+
+ const std::vector<TmmbItem>& items() const { return items_; }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ // Media ssrc is unused, shadow base class setter and getter.
+ void SetMediaSsrc(uint32_t ssrc);
+ uint32_t media_ssrc() const;
+
+ std::vector<TmmbItem> items_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBN_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.cc
new file mode 100644
index 0000000000..3a37bb1c0e
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbn_unittest.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 "modules/rtp_rtcp/source/rtcp_packet/tmmbn.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAreArray;
+using ::testing::IsEmpty;
+using ::testing::make_tuple;
+using webrtc::rtcp::TmmbItem;
+using webrtc::rtcp::Tmmbn;
+
+namespace webrtc {
+namespace {
+const uint32_t kSenderSsrc = 0x12345678;
+const uint32_t kRemoteSsrc = 0x23456789;
+const uint32_t kBitrateBps = 312000;
+const uint16_t kOverhead = 0x1fe;
+const uint8_t kPacket[] = {0x84, 205, 0x00, 0x04, 0x12, 0x34, 0x56,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45,
+ 0x67, 0x89, 0x0a, 0x61, 0x61, 0xfe};
+} // namespace
+
+TEST(RtcpPacketTmmbnTest, Create) {
+ Tmmbn tmmbn;
+ tmmbn.SetSenderSsrc(kSenderSsrc);
+ tmmbn.AddTmmbr(TmmbItem(kRemoteSsrc, kBitrateBps, kOverhead));
+
+ rtc::Buffer packet = tmmbn.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketTmmbnTest, Parse) {
+ Tmmbn tmmbn;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &tmmbn));
+
+ const Tmmbn& parsed = tmmbn;
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ ASSERT_EQ(1u, parsed.items().size());
+ EXPECT_EQ(kRemoteSsrc, parsed.items().front().ssrc());
+ EXPECT_EQ(kBitrateBps, parsed.items().front().bitrate_bps());
+ EXPECT_EQ(kOverhead, parsed.items().front().packet_overhead());
+}
+
+TEST(RtcpPacketTmmbnTest, CreateAndParseWithoutItems) {
+ Tmmbn tmmbn;
+ tmmbn.SetSenderSsrc(kSenderSsrc);
+
+ rtc::Buffer packet = tmmbn.Build();
+ Tmmbn parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_THAT(parsed.items(), IsEmpty());
+}
+
+TEST(RtcpPacketTmmbnTest, CreateAndParseWithTwoItems) {
+ Tmmbn tmmbn;
+ tmmbn.SetSenderSsrc(kSenderSsrc);
+ tmmbn.AddTmmbr(TmmbItem(kRemoteSsrc, kBitrateBps, kOverhead));
+ tmmbn.AddTmmbr(TmmbItem(kRemoteSsrc + 1, 4 * kBitrateBps, 40));
+
+ rtc::Buffer packet = tmmbn.Build();
+ Tmmbn parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(2u, parsed.items().size());
+ EXPECT_EQ(kRemoteSsrc, parsed.items()[0].ssrc());
+ EXPECT_EQ(kBitrateBps, parsed.items()[0].bitrate_bps());
+ EXPECT_EQ(kOverhead, parsed.items()[0].packet_overhead());
+ EXPECT_EQ(kRemoteSsrc + 1, parsed.items()[1].ssrc());
+ EXPECT_EQ(4 * kBitrateBps, parsed.items()[1].bitrate_bps());
+ EXPECT_EQ(40U, parsed.items()[1].packet_overhead());
+}
+
+TEST(RtcpPacketTmmbnTest, ParseFailsOnTooSmallPacket) {
+ const uint8_t kSmallPacket[] = {0x84, 205, 0x00, 0x01,
+ 0x12, 0x34, 0x56, 0x78};
+ Tmmbn tmmbn;
+ EXPECT_FALSE(test::ParseSinglePacket(kSmallPacket, &tmmbn));
+}
+
+TEST(RtcpPacketTmmbnTest, ParseFailsOnUnAlignedPacket) {
+ const uint8_t kUnalignedPacket[] = {0x84, 205, 0x00, 0x03, 0x12, 0x34,
+ 0x56, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x45, 0x67, 0x89};
+
+ Tmmbn tmmbn;
+ EXPECT_FALSE(test::ParseSinglePacket(kUnalignedPacket, &tmmbn));
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc
new file mode 100644
index 0000000000..9dc745e509
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.cc
@@ -0,0 +1,111 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/tmmbr.h"
+
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace rtcp {
+constexpr uint8_t Tmmbr::kFeedbackMessageType;
+// RFC 4585: Feedback format.
+// Common packet format:
+//
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |V=2|P| FMT | PT | length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of packet sender |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC of media source (unused) = 0 |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// : Feedback Control Information (FCI) :
+// : :
+// Temporary Maximum Media Stream Bit Rate Request (TMMBR) (RFC 5104).
+// The Feedback Control Information (FCI) for the TMMBR
+// consists of one or more FCI entries.
+// FCI:
+// 0 1 2 3
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | SSRC |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | MxTBR Exp | MxTBR Mantissa |Measured Overhead|
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+
+Tmmbr::Tmmbr() = default;
+
+Tmmbr::~Tmmbr() = default;
+
+bool Tmmbr::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
+
+ if (packet.payload_size_bytes() < kCommonFeedbackLength + TmmbItem::kLength) {
+ RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
+ << " is too small for a TMMBR.";
+ return false;
+ }
+ size_t items_size_bytes = packet.payload_size_bytes() - kCommonFeedbackLength;
+ if (items_size_bytes % TmmbItem::kLength != 0) {
+ RTC_LOG(LS_WARNING) << "Payload length " << packet.payload_size_bytes()
+ << " is not valid for a TMMBR.";
+ return false;
+ }
+ ParseCommonFeedback(packet.payload());
+
+ const uint8_t* next_item = packet.payload() + kCommonFeedbackLength;
+ size_t number_of_items = items_size_bytes / TmmbItem::kLength;
+ items_.resize(number_of_items);
+ for (TmmbItem& item : items_) {
+ if (!item.Parse(next_item))
+ return false;
+ next_item += TmmbItem::kLength;
+ }
+ return true;
+}
+
+void Tmmbr::AddTmmbr(const TmmbItem& item) {
+ items_.push_back(item);
+}
+
+size_t Tmmbr::BlockLength() const {
+ return kHeaderLength + kCommonFeedbackLength +
+ TmmbItem::kLength * items_.size();
+}
+
+bool Tmmbr::Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ RTC_DCHECK(!items_.empty());
+ while (*index + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, index, callback))
+ return false;
+ }
+ const size_t index_end = *index + BlockLength();
+
+ CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), packet,
+ index);
+ RTC_DCHECK_EQ(0, Rtpfb::media_ssrc());
+ CreateCommonFeedback(packet + *index);
+ *index += kCommonFeedbackLength;
+ for (const TmmbItem& item : items_) {
+ item.Create(packet + *index);
+ *index += TmmbItem::kLength;
+ }
+ RTC_CHECK_EQ(index_end, *index);
+ return true;
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h
new file mode 100644
index 0000000000..7482cb75cc
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr.h
@@ -0,0 +1,54 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBR_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBR_H_
+
+#include <vector>
+
+#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/tmmb_item.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+// Temporary Maximum Media Stream Bit Rate Request (TMMBR).
+// RFC 5104, Section 4.2.1.
+class Tmmbr : public Rtpfb {
+ public:
+ static constexpr uint8_t kFeedbackMessageType = 3;
+
+ Tmmbr();
+ ~Tmmbr() override;
+
+ // Parse assumes header is already parsed and validated.
+ bool Parse(const CommonHeader& packet);
+
+ void AddTmmbr(const TmmbItem& item);
+
+ const std::vector<TmmbItem>& requests() const { return items_; }
+
+ size_t BlockLength() const override;
+
+ bool Create(uint8_t* packet,
+ size_t* index,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ // Media ssrc is unused, shadow base class setter.
+ void SetMediaSsrc(uint32_t ssrc);
+
+ std::vector<TmmbItem> items_;
+};
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TMMBR_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc
new file mode 100644
index 0000000000..1bac808aa9
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/tmmbr_unittest.cc
@@ -0,0 +1,89 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/tmmbr.h"
+
+#include "test/gmock.h"
+#include "test/gtest.h"
+#include "test/rtcp_packet_parser.h"
+
+using ::testing::ElementsAreArray;
+using ::testing::make_tuple;
+using webrtc::rtcp::TmmbItem;
+using webrtc::rtcp::Tmmbr;
+
+namespace webrtc {
+namespace {
+const uint32_t kSenderSsrc = 0x12345678;
+const uint32_t kRemoteSsrc = 0x23456789;
+const uint32_t kBitrateBps = 312000;
+const uint16_t kOverhead = 0x1fe;
+const uint8_t kPacket[] = {0x83, 205, 0x00, 0x04, 0x12, 0x34, 0x56,
+ 0x78, 0x00, 0x00, 0x00, 0x00, 0x23, 0x45,
+ 0x67, 0x89, 0x0a, 0x61, 0x61, 0xfe};
+} // namespace
+
+TEST(RtcpPacketTmmbrTest, Create) {
+ Tmmbr tmmbr;
+ tmmbr.SetSenderSsrc(kSenderSsrc);
+ tmmbr.AddTmmbr(TmmbItem(kRemoteSsrc, kBitrateBps, kOverhead));
+
+ rtc::Buffer packet = tmmbr.Build();
+
+ EXPECT_THAT(make_tuple(packet.data(), packet.size()),
+ ElementsAreArray(kPacket));
+}
+
+TEST(RtcpPacketTmmbrTest, Parse) {
+ Tmmbr tmmbr;
+ EXPECT_TRUE(test::ParseSinglePacket(kPacket, &tmmbr));
+ const Tmmbr& parsed = tmmbr;
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ ASSERT_EQ(1u, parsed.requests().size());
+ EXPECT_EQ(kRemoteSsrc, parsed.requests().front().ssrc());
+ EXPECT_EQ(kBitrateBps, parsed.requests().front().bitrate_bps());
+ EXPECT_EQ(kOverhead, parsed.requests().front().packet_overhead());
+}
+
+TEST(RtcpPacketTmmbrTest, CreateAndParseWithTwoEntries) {
+ Tmmbr tmmbr;
+ tmmbr.SetSenderSsrc(kSenderSsrc);
+ tmmbr.AddTmmbr(TmmbItem(kRemoteSsrc, kBitrateBps, kOverhead));
+ tmmbr.AddTmmbr(TmmbItem(kRemoteSsrc + 1, 4 * kBitrateBps, kOverhead + 1));
+
+ rtc::Buffer packet = tmmbr.Build();
+
+ Tmmbr parsed;
+ EXPECT_TRUE(test::ParseSinglePacket(packet, &parsed));
+
+ EXPECT_EQ(kSenderSsrc, parsed.sender_ssrc());
+ EXPECT_EQ(2u, parsed.requests().size());
+ EXPECT_EQ(kRemoteSsrc, parsed.requests()[0].ssrc());
+ EXPECT_EQ(kRemoteSsrc + 1, parsed.requests()[1].ssrc());
+}
+
+TEST(RtcpPacketTmmbrTest, ParseFailsWithoutItems) {
+ const uint8_t kZeroItemsPacket[] = {0x83, 205, 0x00, 0x02, 0x12, 0x34,
+ 0x56, 0x78, 0x00, 0x00, 0x00, 0x00};
+
+ Tmmbr tmmbr;
+ EXPECT_FALSE(test::ParseSinglePacket(kZeroItemsPacket, &tmmbr));
+}
+
+TEST(RtcpPacketTmmbrTest, ParseFailsOnUnAlignedPacket) {
+ const uint8_t kUnalignedPacket[] = {
+ 0x83, 205, 0x00, 0x05, 0x12, 0x34, 0x56, 0x78, 0x00, 0x00, 0x00, 0x00,
+ 0x23, 0x45, 0x67, 0x89, 0x0a, 0x61, 0x61, 0xfe, 0x34, 0x56, 0x78, 0x9a};
+
+ Tmmbr tmmbr;
+ EXPECT_FALSE(test::ParseSinglePacket(kUnalignedPacket, &tmmbr));
+}
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc
new file mode 100644
index 0000000000..003effad29
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.cc
@@ -0,0 +1,737 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
+
+#include <algorithm>
+#include <cstdint>
+#include <numeric>
+#include <utility>
+
+#include "absl/algorithm/container.h"
+#include "modules/include/module_common_types_public.h"
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "rtc_base/checks.h"
+#include "rtc_base/logging.h"
+#include "rtc_base/trace_event.h"
+
+namespace webrtc {
+namespace rtcp {
+namespace {
+// Header size:
+// * 4 bytes Common RTCP Packet Header
+// * 8 bytes Common Packet Format for RTCP Feedback Messages
+// * 8 bytes FeedbackPacket header
+constexpr size_t kTransportFeedbackHeaderSizeBytes = 4 + 8 + 8;
+constexpr size_t kChunkSizeBytes = 2;
+// TODO(sprang): Add support for dynamic max size for easier fragmentation,
+// eg. set it to what's left in the buffer or IP_PACKET_SIZE.
+// Size constraint imposed by RTCP common header: 16bit size field interpreted
+// as number of four byte words minus the first header word.
+constexpr size_t kMaxSizeBytes = (1 << 16) * 4;
+// Payload size:
+// * 8 bytes Common Packet Format for RTCP Feedback Messages
+// * 8 bytes FeedbackPacket header.
+// * 2 bytes for one chunk.
+constexpr size_t kMinPayloadSizeBytes = 8 + 8 + 2;
+constexpr TimeDelta kBaseTimeTick = TransportFeedback::kDeltaTick * (1 << 8);
+constexpr TimeDelta kTimeWrapPeriod = kBaseTimeTick * (1 << 24);
+
+// Message 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=15 | PT=205 | length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 0 | SSRC of packet sender |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 4 | SSRC of media source |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 8 | base sequence number | packet status count |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 12 | reference time | fb pkt. count |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// 16 | packet chunk | packet chunk |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// . .
+// . .
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | packet chunk | recv delta | recv delta |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// . .
+// . .
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// | recv delta | recv delta | zero padding |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+} // namespace
+constexpr uint8_t TransportFeedback::kFeedbackMessageType;
+constexpr size_t TransportFeedback::kMaxReportedPackets;
+
+constexpr size_t TransportFeedback::LastChunk::kMaxRunLengthCapacity;
+constexpr size_t TransportFeedback::LastChunk::kMaxOneBitCapacity;
+constexpr size_t TransportFeedback::LastChunk::kMaxTwoBitCapacity;
+constexpr size_t TransportFeedback::LastChunk::kMaxVectorCapacity;
+
+TransportFeedback::LastChunk::LastChunk() {
+ Clear();
+}
+
+bool TransportFeedback::LastChunk::Empty() const {
+ return size_ == 0;
+}
+
+void TransportFeedback::LastChunk::Clear() {
+ size_ = 0;
+ all_same_ = true;
+ has_large_delta_ = false;
+}
+
+bool TransportFeedback::LastChunk::CanAdd(DeltaSize delta_size) const {
+ RTC_DCHECK_LE(delta_size, 2);
+ if (size_ < kMaxTwoBitCapacity)
+ return true;
+ if (size_ < kMaxOneBitCapacity && !has_large_delta_ && delta_size != kLarge)
+ return true;
+ if (size_ < kMaxRunLengthCapacity && all_same_ &&
+ delta_sizes_[0] == delta_size)
+ return true;
+ return false;
+}
+
+void TransportFeedback::LastChunk::Add(DeltaSize delta_size) {
+ RTC_DCHECK(CanAdd(delta_size));
+ if (size_ < kMaxVectorCapacity)
+ delta_sizes_[size_] = delta_size;
+ size_++;
+ all_same_ = all_same_ && delta_size == delta_sizes_[0];
+ has_large_delta_ = has_large_delta_ || delta_size == kLarge;
+}
+
+void TransportFeedback::LastChunk::AddMissingPackets(size_t num_missing) {
+ RTC_DCHECK_EQ(size_, 0);
+ RTC_DCHECK(all_same_);
+ RTC_DCHECK(!has_large_delta_);
+ RTC_DCHECK_LT(num_missing, kMaxRunLengthCapacity);
+ absl::c_fill(delta_sizes_, DeltaSize(0));
+ size_ = num_missing;
+}
+
+uint16_t TransportFeedback::LastChunk::Emit() {
+ RTC_DCHECK(!CanAdd(0) || !CanAdd(1) || !CanAdd(2));
+ if (all_same_) {
+ uint16_t chunk = EncodeRunLength();
+ Clear();
+ return chunk;
+ }
+ if (size_ == kMaxOneBitCapacity) {
+ uint16_t chunk = EncodeOneBit();
+ Clear();
+ return chunk;
+ }
+ RTC_DCHECK_GE(size_, kMaxTwoBitCapacity);
+ uint16_t chunk = EncodeTwoBit(kMaxTwoBitCapacity);
+ // Remove `kMaxTwoBitCapacity` encoded delta sizes:
+ // Shift remaining delta sizes and recalculate all_same_ && has_large_delta_.
+ size_ -= kMaxTwoBitCapacity;
+ all_same_ = true;
+ has_large_delta_ = false;
+ for (size_t i = 0; i < size_; ++i) {
+ DeltaSize delta_size = delta_sizes_[kMaxTwoBitCapacity + i];
+ delta_sizes_[i] = delta_size;
+ all_same_ = all_same_ && delta_size == delta_sizes_[0];
+ has_large_delta_ = has_large_delta_ || delta_size == kLarge;
+ }
+
+ return chunk;
+}
+
+uint16_t TransportFeedback::LastChunk::EncodeLast() const {
+ RTC_DCHECK_GT(size_, 0);
+ if (all_same_)
+ return EncodeRunLength();
+ if (size_ <= kMaxTwoBitCapacity)
+ return EncodeTwoBit(size_);
+ return EncodeOneBit();
+}
+
+// Appends content of the Lastchunk to `deltas`.
+void TransportFeedback::LastChunk::AppendTo(
+ std::vector<DeltaSize>* deltas) const {
+ if (all_same_) {
+ deltas->insert(deltas->end(), size_, delta_sizes_[0]);
+ } else {
+ deltas->insert(deltas->end(), delta_sizes_.begin(),
+ delta_sizes_.begin() + size_);
+ }
+}
+
+void TransportFeedback::LastChunk::Decode(uint16_t chunk, size_t max_size) {
+ if ((chunk & 0x8000) == 0) {
+ DecodeRunLength(chunk, max_size);
+ } else if ((chunk & 0x4000) == 0) {
+ DecodeOneBit(chunk, max_size);
+ } else {
+ DecodeTwoBit(chunk, max_size);
+ }
+}
+
+// One Bit Status Vector Chunk
+//
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |T|S| symbol list |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// T = 1
+// S = 0
+// Symbol list = 14 entries where 0 = not received, 1 = received 1-byte delta.
+uint16_t TransportFeedback::LastChunk::EncodeOneBit() const {
+ RTC_DCHECK(!has_large_delta_);
+ RTC_DCHECK_LE(size_, kMaxOneBitCapacity);
+ uint16_t chunk = 0x8000;
+ for (size_t i = 0; i < size_; ++i)
+ chunk |= delta_sizes_[i] << (kMaxOneBitCapacity - 1 - i);
+ return chunk;
+}
+
+void TransportFeedback::LastChunk::DecodeOneBit(uint16_t chunk,
+ size_t max_size) {
+ RTC_DCHECK_EQ(chunk & 0xc000, 0x8000);
+ size_ = std::min(kMaxOneBitCapacity, max_size);
+ has_large_delta_ = false;
+ all_same_ = false;
+ for (size_t i = 0; i < size_; ++i)
+ delta_sizes_[i] = (chunk >> (kMaxOneBitCapacity - 1 - i)) & 0x01;
+}
+
+// Two Bit Status Vector Chunk
+//
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |T|S| symbol list |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// T = 1
+// S = 1
+// symbol list = 7 entries of two bits each.
+uint16_t TransportFeedback::LastChunk::EncodeTwoBit(size_t size) const {
+ RTC_DCHECK_LE(size, size_);
+ uint16_t chunk = 0xc000;
+ for (size_t i = 0; i < size; ++i)
+ chunk |= delta_sizes_[i] << 2 * (kMaxTwoBitCapacity - 1 - i);
+ return chunk;
+}
+
+void TransportFeedback::LastChunk::DecodeTwoBit(uint16_t chunk,
+ size_t max_size) {
+ RTC_DCHECK_EQ(chunk & 0xc000, 0xc000);
+ size_ = std::min(kMaxTwoBitCapacity, max_size);
+ has_large_delta_ = true;
+ all_same_ = false;
+ for (size_t i = 0; i < size_; ++i)
+ delta_sizes_[i] = (chunk >> 2 * (kMaxTwoBitCapacity - 1 - i)) & 0x03;
+}
+
+// Run Length Status Vector Chunk
+//
+// 0 1
+// 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+// |T| S | Run Length |
+// +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
+//
+// T = 0
+// S = symbol
+// Run Length = Unsigned integer denoting the run length of the symbol
+uint16_t TransportFeedback::LastChunk::EncodeRunLength() const {
+ RTC_DCHECK(all_same_);
+ RTC_DCHECK_LE(size_, kMaxRunLengthCapacity);
+ return (delta_sizes_[0] << 13) | static_cast<uint16_t>(size_);
+}
+
+void TransportFeedback::LastChunk::DecodeRunLength(uint16_t chunk,
+ size_t max_count) {
+ RTC_DCHECK_EQ(chunk & 0x8000, 0);
+ size_ = std::min<size_t>(chunk & 0x1fff, max_count);
+ DeltaSize delta_size = (chunk >> 13) & 0x03;
+ has_large_delta_ = delta_size >= kLarge;
+ all_same_ = true;
+ // To make it consistent with Add function, populate delta_sizes_ beyond 1st.
+ for (size_t i = 0; i < std::min<size_t>(size_, kMaxVectorCapacity); ++i)
+ delta_sizes_[i] = delta_size;
+}
+
+TransportFeedback::TransportFeedback()
+ : TransportFeedback(/*include_timestamps=*/true) {}
+
+TransportFeedback::TransportFeedback(bool include_timestamps)
+ : base_seq_no_(0),
+ num_seq_no_(0),
+ base_time_ticks_(0),
+ feedback_seq_(0),
+ include_timestamps_(include_timestamps),
+ last_timestamp_(Timestamp::Zero()),
+ size_bytes_(kTransportFeedbackHeaderSizeBytes) {}
+
+TransportFeedback::TransportFeedback(const TransportFeedback&) = default;
+
+TransportFeedback::TransportFeedback(TransportFeedback&& other)
+ : base_seq_no_(other.base_seq_no_),
+ num_seq_no_(other.num_seq_no_),
+ base_time_ticks_(other.base_time_ticks_),
+ feedback_seq_(other.feedback_seq_),
+ include_timestamps_(other.include_timestamps_),
+ last_timestamp_(other.last_timestamp_),
+ received_packets_(std::move(other.received_packets_)),
+ all_packets_(std::move(other.all_packets_)),
+ encoded_chunks_(std::move(other.encoded_chunks_)),
+ last_chunk_(other.last_chunk_),
+ size_bytes_(other.size_bytes_) {
+ other.Clear();
+}
+
+TransportFeedback::~TransportFeedback() {}
+
+void TransportFeedback::SetBase(uint16_t base_sequence,
+ Timestamp ref_timestamp) {
+ RTC_DCHECK_EQ(num_seq_no_, 0);
+ base_seq_no_ = base_sequence;
+ base_time_ticks_ =
+ (ref_timestamp.us() % kTimeWrapPeriod.us()) / kBaseTimeTick.us();
+ last_timestamp_ = BaseTime();
+}
+
+void TransportFeedback::SetFeedbackSequenceNumber(uint8_t feedback_sequence) {
+ feedback_seq_ = feedback_sequence;
+}
+
+bool TransportFeedback::AddReceivedPacket(uint16_t sequence_number,
+ Timestamp timestamp) {
+ // Set delta to zero if timestamps are not included, this will simplify the
+ // encoding process.
+ int16_t delta = 0;
+ if (include_timestamps_) {
+ // Convert to ticks and round.
+ if (last_timestamp_ > timestamp) {
+ timestamp += (last_timestamp_ - timestamp).RoundUpTo(kTimeWrapPeriod);
+ }
+ RTC_DCHECK_GE(timestamp, last_timestamp_);
+ int64_t delta_full =
+ (timestamp - last_timestamp_).us() % kTimeWrapPeriod.us();
+ if (delta_full > kTimeWrapPeriod.us() / 2) {
+ delta_full -= kTimeWrapPeriod.us();
+ delta_full -= kDeltaTick.us() / 2;
+ } else {
+ delta_full += kDeltaTick.us() / 2;
+ }
+ delta_full /= kDeltaTick.us();
+
+ delta = static_cast<int16_t>(delta_full);
+ // If larger than 16bit signed, we can't represent it - need new fb packet.
+ if (delta != delta_full) {
+ RTC_LOG(LS_WARNING) << "Delta value too large ( >= 2^16 ticks )";
+ return false;
+ }
+ }
+
+ uint16_t next_seq_no = base_seq_no_ + num_seq_no_;
+ if (sequence_number != next_seq_no) {
+ uint16_t last_seq_no = next_seq_no - 1;
+ if (!IsNewerSequenceNumber(sequence_number, last_seq_no))
+ return false;
+ uint16_t num_missing_packets = sequence_number - next_seq_no;
+ if (!AddMissingPackets(num_missing_packets))
+ return false;
+ }
+
+ DeltaSize delta_size = (delta >= 0 && delta <= 0xff) ? 1 : 2;
+ if (!AddDeltaSize(delta_size))
+ return false;
+
+ received_packets_.emplace_back(sequence_number, delta);
+ last_timestamp_ += delta * kDeltaTick;
+ if (include_timestamps_) {
+ size_bytes_ += delta_size;
+ }
+ return true;
+}
+
+const std::vector<TransportFeedback::ReceivedPacket>&
+TransportFeedback::GetReceivedPackets() const {
+ return received_packets_;
+}
+
+void TransportFeedback::ForAllPackets(
+ rtc::FunctionView<void(uint16_t, TimeDelta)> handler) const {
+ TimeDelta delta_since_base = TimeDelta::Zero();
+ auto received_it = received_packets_.begin();
+ const uint16_t last_seq_num = base_seq_no_ + num_seq_no_;
+ for (uint16_t seq_num = base_seq_no_; seq_num != last_seq_num; ++seq_num) {
+ if (received_it != received_packets_.end() &&
+ received_it->sequence_number() == seq_num) {
+ delta_since_base += received_it->delta();
+ handler(seq_num, delta_since_base);
+ ++received_it;
+ } else {
+ handler(seq_num, TimeDelta::PlusInfinity());
+ }
+ }
+ RTC_DCHECK(received_it == received_packets_.end());
+}
+
+uint16_t TransportFeedback::GetBaseSequence() const {
+ return base_seq_no_;
+}
+
+Timestamp TransportFeedback::BaseTime() const {
+ // Add an extra kTimeWrapPeriod to allow add received packets arrived earlier
+ // than the first added packet (and thus allow to record negative deltas)
+ // even when base_time_ticks_ == 0.
+ return Timestamp::Zero() + kTimeWrapPeriod +
+ int64_t{base_time_ticks_} * kBaseTimeTick;
+}
+
+TimeDelta TransportFeedback::GetBaseDelta(Timestamp prev_timestamp) const {
+ TimeDelta delta = BaseTime() - prev_timestamp;
+ // Compensate for wrap around.
+ if ((delta - kTimeWrapPeriod).Abs() < delta.Abs()) {
+ delta -= kTimeWrapPeriod; // Wrap backwards.
+ } else if ((delta + kTimeWrapPeriod).Abs() < delta.Abs()) {
+ delta += kTimeWrapPeriod; // Wrap forwards.
+ }
+ return delta;
+}
+
+// De-serialize packet.
+bool TransportFeedback::Parse(const CommonHeader& packet) {
+ RTC_DCHECK_EQ(packet.type(), kPacketType);
+ RTC_DCHECK_EQ(packet.fmt(), kFeedbackMessageType);
+ TRACE_EVENT0("webrtc", "TransportFeedback::Parse");
+
+ if (packet.payload_size_bytes() < kMinPayloadSizeBytes) {
+ RTC_LOG(LS_WARNING) << "Buffer too small (" << packet.payload_size_bytes()
+ << " bytes) to fit a "
+ "FeedbackPacket. Minimum size = "
+ << kMinPayloadSizeBytes;
+ return false;
+ }
+
+ const uint8_t* const payload = packet.payload();
+ ParseCommonFeedback(payload);
+
+ base_seq_no_ = ByteReader<uint16_t>::ReadBigEndian(&payload[8]);
+ uint16_t status_count = ByteReader<uint16_t>::ReadBigEndian(&payload[10]);
+ base_time_ticks_ = ByteReader<uint32_t, 3>::ReadBigEndian(&payload[12]);
+ feedback_seq_ = payload[15];
+ Clear();
+ size_t index = 16;
+ const size_t end_index = packet.payload_size_bytes();
+
+ if (status_count == 0) {
+ RTC_LOG(LS_WARNING) << "Empty feedback messages not allowed.";
+ return false;
+ }
+
+ std::vector<uint8_t> delta_sizes;
+ delta_sizes.reserve(status_count);
+ while (delta_sizes.size() < status_count) {
+ if (index + kChunkSizeBytes > end_index) {
+ RTC_LOG(LS_WARNING) << "Buffer overflow while parsing packet.";
+ Clear();
+ return false;
+ }
+
+ uint16_t chunk = ByteReader<uint16_t>::ReadBigEndian(&payload[index]);
+ index += kChunkSizeBytes;
+ encoded_chunks_.push_back(chunk);
+ last_chunk_.Decode(chunk, status_count - delta_sizes.size());
+ last_chunk_.AppendTo(&delta_sizes);
+ }
+ // Last chunk is stored in the `last_chunk_`.
+ encoded_chunks_.pop_back();
+ RTC_DCHECK_EQ(delta_sizes.size(), status_count);
+ num_seq_no_ = status_count;
+
+ uint16_t seq_no = base_seq_no_;
+ size_t recv_delta_size = absl::c_accumulate(delta_sizes, 0);
+
+ // Determine if timestamps, that is, recv_delta are included in the packet.
+ if (end_index >= index + recv_delta_size) {
+ for (size_t delta_size : delta_sizes) {
+ RTC_DCHECK_LE(index + delta_size, end_index);
+ switch (delta_size) {
+ case 0:
+ break;
+ case 1: {
+ int16_t delta = payload[index];
+ received_packets_.emplace_back(seq_no, delta);
+ last_timestamp_ += delta * kDeltaTick;
+ index += delta_size;
+ break;
+ }
+ case 2: {
+ int16_t delta = ByteReader<int16_t>::ReadBigEndian(&payload[index]);
+ received_packets_.emplace_back(seq_no, delta);
+ last_timestamp_ += delta * kDeltaTick;
+ index += delta_size;
+ break;
+ }
+ case 3:
+ Clear();
+ RTC_LOG(LS_WARNING) << "Invalid delta_size for seq_no " << seq_no;
+
+ return false;
+ default:
+ RTC_DCHECK_NOTREACHED();
+ break;
+ }
+ ++seq_no;
+ }
+ } else {
+ // The packet does not contain receive deltas.
+ include_timestamps_ = false;
+ for (size_t delta_size : delta_sizes) {
+ // Use delta sizes to detect if packet was received.
+ if (delta_size > 0) {
+ received_packets_.emplace_back(seq_no, 0);
+ }
+ ++seq_no;
+ }
+ }
+ size_bytes_ = RtcpPacket::kHeaderLength + index;
+ RTC_DCHECK_LE(index, end_index);
+ return true;
+}
+
+std::unique_ptr<TransportFeedback> TransportFeedback::ParseFrom(
+ const uint8_t* buffer,
+ size_t length) {
+ CommonHeader header;
+ if (!header.Parse(buffer, length))
+ return nullptr;
+ if (header.type() != kPacketType || header.fmt() != kFeedbackMessageType)
+ return nullptr;
+ std::unique_ptr<TransportFeedback> parsed(new TransportFeedback);
+ if (!parsed->Parse(header))
+ return nullptr;
+ return parsed;
+}
+
+bool TransportFeedback::IsConsistent() const {
+ size_t packet_size = kTransportFeedbackHeaderSizeBytes;
+ std::vector<DeltaSize> delta_sizes;
+ LastChunk chunk_decoder;
+ for (uint16_t chunk : encoded_chunks_) {
+ chunk_decoder.Decode(chunk, kMaxReportedPackets);
+ chunk_decoder.AppendTo(&delta_sizes);
+ packet_size += kChunkSizeBytes;
+ }
+ if (!last_chunk_.Empty()) {
+ last_chunk_.AppendTo(&delta_sizes);
+ packet_size += kChunkSizeBytes;
+ }
+ if (num_seq_no_ != delta_sizes.size()) {
+ RTC_LOG(LS_ERROR) << delta_sizes.size() << " packets encoded. Expected "
+ << num_seq_no_;
+ return false;
+ }
+ Timestamp timestamp = BaseTime();
+ auto packet_it = received_packets_.begin();
+ uint16_t seq_no = base_seq_no_;
+ for (DeltaSize delta_size : delta_sizes) {
+ if (delta_size > 0) {
+ if (packet_it == received_packets_.end()) {
+ RTC_LOG(LS_ERROR) << "Failed to find delta for seq_no " << seq_no;
+ return false;
+ }
+ if (packet_it->sequence_number() != seq_no) {
+ RTC_LOG(LS_ERROR) << "Expected to find delta for seq_no " << seq_no
+ << ". Next delta is for "
+ << packet_it->sequence_number();
+ return false;
+ }
+ if (delta_size == 1 &&
+ (packet_it->delta_ticks() < 0 || packet_it->delta_ticks() > 0xff)) {
+ RTC_LOG(LS_ERROR) << "Delta " << packet_it->delta_ticks()
+ << " for seq_no " << seq_no
+ << " doesn't fit into one byte";
+ return false;
+ }
+ timestamp += packet_it->delta();
+ ++packet_it;
+ }
+ if (include_timestamps_) {
+ packet_size += delta_size;
+ }
+ ++seq_no;
+ }
+ if (packet_it != received_packets_.end()) {
+ RTC_LOG(LS_ERROR) << "Unencoded delta for seq_no "
+ << packet_it->sequence_number();
+ return false;
+ }
+ if (timestamp != last_timestamp_) {
+ RTC_LOG(LS_ERROR) << "Last timestamp mismatch. Calculated: "
+ << ToLogString(timestamp)
+ << ". Saved: " << ToLogString(last_timestamp_);
+ return false;
+ }
+ if (size_bytes_ != packet_size) {
+ RTC_LOG(LS_ERROR) << "Rtcp packet size mismatch. Calculated: "
+ << packet_size << ". Saved: " << size_bytes_;
+ return false;
+ }
+ return true;
+}
+
+size_t TransportFeedback::BlockLength() const {
+ // Round size_bytes_ up to multiple of 32bits.
+ return (size_bytes_ + 3) & (~static_cast<size_t>(3));
+}
+
+size_t TransportFeedback::PaddingLength() const {
+ return BlockLength() - size_bytes_;
+}
+
+// Serialize packet.
+bool TransportFeedback::Create(uint8_t* packet,
+ size_t* position,
+ size_t max_length,
+ PacketReadyCallback callback) const {
+ if (num_seq_no_ == 0)
+ return false;
+
+ while (*position + BlockLength() > max_length) {
+ if (!OnBufferFull(packet, position, callback))
+ return false;
+ }
+ const size_t position_end = *position + BlockLength();
+ const size_t padding_length = PaddingLength();
+ bool has_padding = padding_length > 0;
+ CreateHeader(kFeedbackMessageType, kPacketType, HeaderLength(), has_padding,
+ packet, position);
+ CreateCommonFeedback(packet + *position);
+ *position += kCommonFeedbackLength;
+
+ ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], base_seq_no_);
+ *position += 2;
+
+ ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], num_seq_no_);
+ *position += 2;
+
+ ByteWriter<uint32_t, 3>::WriteBigEndian(&packet[*position], base_time_ticks_);
+ *position += 3;
+
+ packet[(*position)++] = feedback_seq_;
+
+ for (uint16_t chunk : encoded_chunks_) {
+ ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], chunk);
+ *position += 2;
+ }
+ if (!last_chunk_.Empty()) {
+ uint16_t chunk = last_chunk_.EncodeLast();
+ ByteWriter<uint16_t>::WriteBigEndian(&packet[*position], chunk);
+ *position += 2;
+ }
+
+ if (include_timestamps_) {
+ for (const auto& received_packet : received_packets_) {
+ int16_t delta = received_packet.delta_ticks();
+ if (delta >= 0 && delta <= 0xFF) {
+ packet[(*position)++] = delta;
+ } else {
+ ByteWriter<int16_t>::WriteBigEndian(&packet[*position], delta);
+ *position += 2;
+ }
+ }
+ }
+
+ if (padding_length > 0) {
+ for (size_t i = 0; i < padding_length - 1; ++i) {
+ packet[(*position)++] = 0;
+ }
+ packet[(*position)++] = padding_length;
+ }
+ RTC_DCHECK_EQ(*position, position_end);
+ return true;
+}
+
+void TransportFeedback::Clear() {
+ num_seq_no_ = 0;
+ last_timestamp_ = BaseTime();
+ received_packets_.clear();
+ all_packets_.clear();
+ encoded_chunks_.clear();
+ last_chunk_.Clear();
+ size_bytes_ = kTransportFeedbackHeaderSizeBytes;
+}
+
+bool TransportFeedback::AddDeltaSize(DeltaSize delta_size) {
+ if (num_seq_no_ == kMaxReportedPackets)
+ return false;
+ size_t add_chunk_size = last_chunk_.Empty() ? kChunkSizeBytes : 0;
+ if (size_bytes_ + delta_size + add_chunk_size > kMaxSizeBytes)
+ return false;
+
+ if (last_chunk_.CanAdd(delta_size)) {
+ size_bytes_ += add_chunk_size;
+ last_chunk_.Add(delta_size);
+ ++num_seq_no_;
+ return true;
+ }
+ if (size_bytes_ + delta_size + kChunkSizeBytes > kMaxSizeBytes)
+ return false;
+
+ encoded_chunks_.push_back(last_chunk_.Emit());
+ size_bytes_ += kChunkSizeBytes;
+ last_chunk_.Add(delta_size);
+ ++num_seq_no_;
+ return true;
+}
+
+bool TransportFeedback::AddMissingPackets(size_t num_missing_packets) {
+ size_t new_num_seq_no = num_seq_no_ + num_missing_packets;
+ if (new_num_seq_no > kMaxReportedPackets) {
+ return false;
+ }
+
+ if (!last_chunk_.Empty()) {
+ while (num_missing_packets > 0 && last_chunk_.CanAdd(0)) {
+ last_chunk_.Add(0);
+ --num_missing_packets;
+ }
+ if (num_missing_packets == 0) {
+ num_seq_no_ = new_num_seq_no;
+ return true;
+ }
+ encoded_chunks_.push_back(last_chunk_.Emit());
+ }
+ RTC_DCHECK(last_chunk_.Empty());
+ size_t full_chunks = num_missing_packets / LastChunk::kMaxRunLengthCapacity;
+ size_t partial_chunk = num_missing_packets % LastChunk::kMaxRunLengthCapacity;
+ size_t num_chunks = full_chunks + (partial_chunk > 0 ? 1 : 0);
+ if (size_bytes_ + kChunkSizeBytes * num_chunks > kMaxSizeBytes) {
+ num_seq_no_ = (new_num_seq_no - num_missing_packets);
+ return false;
+ }
+ size_bytes_ += kChunkSizeBytes * num_chunks;
+ // T = 0, S = 0, run length = kMaxRunLengthCapacity, see EncodeRunLength().
+ encoded_chunks_.insert(encoded_chunks_.end(), full_chunks,
+ LastChunk::kMaxRunLengthCapacity);
+ last_chunk_.AddMissingPackets(partial_chunk);
+ num_seq_no_ = new_num_seq_no;
+ return true;
+}
+} // namespace rtcp
+} // namespace webrtc
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h
new file mode 100644
index 0000000000..4d17b54c3a
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h
@@ -0,0 +1,185 @@
+/*
+ * 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 MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
+#define MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
+
+#include <array>
+#include <memory>
+#include <vector>
+
+#include "absl/base/attributes.h"
+#include "api/function_view.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/rtpfb.h"
+
+namespace webrtc {
+namespace rtcp {
+class CommonHeader;
+
+class TransportFeedback : public Rtpfb {
+ public:
+ class ReceivedPacket {
+ public:
+ ReceivedPacket(uint16_t sequence_number, int16_t delta_ticks)
+ : sequence_number_(sequence_number), delta_ticks_(delta_ticks) {}
+ ReceivedPacket(const ReceivedPacket&) = default;
+ ReceivedPacket& operator=(const ReceivedPacket&) = default;
+
+ uint16_t sequence_number() const { return sequence_number_; }
+ int16_t delta_ticks() const { return delta_ticks_; }
+ TimeDelta delta() const { return delta_ticks_ * kDeltaTick; }
+
+ private:
+ uint16_t sequence_number_;
+ int16_t delta_ticks_;
+ };
+ // TODO(sprang): IANA reg?
+ static constexpr uint8_t kFeedbackMessageType = 15;
+ // Convert to multiples of 0.25ms.
+ static constexpr TimeDelta kDeltaTick = TimeDelta::Micros(250);
+ // Maximum number of packets (including missing) TransportFeedback can report.
+ static constexpr size_t kMaxReportedPackets = 0xffff;
+
+ TransportFeedback();
+
+ // If `include_timestamps` is set to false, the created packet will not
+ // contain the receive delta block.
+ explicit TransportFeedback(bool include_timestamps);
+ TransportFeedback(const TransportFeedback&);
+ TransportFeedback(TransportFeedback&&);
+
+ ~TransportFeedback() override;
+
+ void SetBase(uint16_t base_sequence, // Seq# of first packet in this msg.
+ Timestamp ref_timestamp); // Reference timestamp for this msg.
+
+ void SetFeedbackSequenceNumber(uint8_t feedback_sequence);
+ // NOTE: This method requires increasing sequence numbers (excepting wraps).
+ bool AddReceivedPacket(uint16_t sequence_number, Timestamp timestamp);
+ const std::vector<ReceivedPacket>& GetReceivedPackets() const;
+
+ // Calls `handler` for all packets this feedback describes.
+ // For received packets pass receieve time as `delta_since_base` since the
+ // `BaseTime()`. For missed packets calls `handler` with `delta_since_base =
+ // PlusInfinity()`.
+ void ForAllPackets(
+ rtc::FunctionView<void(uint16_t sequence_number,
+ TimeDelta delta_since_base)> handler) const;
+
+ uint16_t GetBaseSequence() const;
+
+ // Returns number of packets (including missing) this feedback describes.
+ size_t GetPacketStatusCount() const { return num_seq_no_; }
+
+ // Get the reference time including any precision loss.
+ Timestamp BaseTime() const;
+
+ // Get the unwrapped delta between current base time and `prev_timestamp`.
+ TimeDelta GetBaseDelta(Timestamp prev_timestamp) const;
+
+ // Does the feedback packet contain timestamp information?
+ bool IncludeTimestamps() const { return include_timestamps_; }
+
+ bool Parse(const CommonHeader& packet);
+ static std::unique_ptr<TransportFeedback> ParseFrom(const uint8_t* buffer,
+ size_t length);
+ // Pre and postcondition for all public methods. Should always return true.
+ // This function is for tests.
+ bool IsConsistent() const;
+
+ size_t BlockLength() const override;
+ size_t PaddingLength() const;
+
+ bool Create(uint8_t* packet,
+ size_t* position,
+ size_t max_length,
+ PacketReadyCallback callback) const override;
+
+ private:
+ // Size in bytes of a delta time in rtcp packet.
+ // Valid values are 0 (packet wasn't received), 1 or 2.
+ using DeltaSize = uint8_t;
+ // Keeps DeltaSizes that can be encoded into single chunk if it is last chunk.
+ class LastChunk {
+ public:
+ using DeltaSize = TransportFeedback::DeltaSize;
+ static constexpr size_t kMaxRunLengthCapacity = 0x1fff;
+
+ LastChunk();
+
+ bool Empty() const;
+ void Clear();
+ // Return if delta sizes still can be encoded into single chunk with added
+ // `delta_size`.
+ bool CanAdd(DeltaSize delta_size) const;
+ // Add `delta_size`, assumes `CanAdd(delta_size)`,
+ void Add(DeltaSize delta_size);
+ // Equivalent to calling Add(0) `num_missing` times. Assumes `Empty()`.
+ void AddMissingPackets(size_t num_missing);
+
+ // Encode chunk as large as possible removing encoded delta sizes.
+ // Assume CanAdd() == false for some valid delta_size.
+ uint16_t Emit();
+ // Encode all stored delta_sizes into single chunk, pad with 0s if needed.
+ uint16_t EncodeLast() const;
+
+ // Decode up to `max_size` delta sizes from `chunk`.
+ void Decode(uint16_t chunk, size_t max_size);
+ // Appends content of the Lastchunk to `deltas`.
+ void AppendTo(std::vector<DeltaSize>* deltas) const;
+
+ private:
+ static constexpr size_t kMaxOneBitCapacity = 14;
+ static constexpr size_t kMaxTwoBitCapacity = 7;
+ static constexpr size_t kMaxVectorCapacity = kMaxOneBitCapacity;
+ static constexpr DeltaSize kLarge = 2;
+
+ uint16_t EncodeOneBit() const;
+ void DecodeOneBit(uint16_t chunk, size_t max_size);
+
+ uint16_t EncodeTwoBit(size_t size) const;
+ void DecodeTwoBit(uint16_t chunk, size_t max_size);
+
+ uint16_t EncodeRunLength() const;
+ void DecodeRunLength(uint16_t chunk, size_t max_size);
+
+ std::array<DeltaSize, kMaxVectorCapacity> delta_sizes_;
+ size_t size_;
+ bool all_same_;
+ bool has_large_delta_;
+ };
+
+ // Reset packet to consistent empty state.
+ void Clear();
+
+ bool AddDeltaSize(DeltaSize delta_size);
+ // Adds `num_missing_packets` deltas of size 0.
+ bool AddMissingPackets(size_t num_missing_packets);
+
+ uint16_t base_seq_no_;
+ uint16_t num_seq_no_;
+ uint32_t base_time_ticks_;
+ uint8_t feedback_seq_;
+ bool include_timestamps_;
+
+ Timestamp last_timestamp_;
+ std::vector<ReceivedPacket> received_packets_;
+ std::vector<ReceivedPacket> all_packets_;
+ // All but last encoded packet chunks.
+ std::vector<uint16_t> encoded_chunks_;
+ LastChunk last_chunk_;
+ size_t size_bytes_;
+};
+
+} // namespace rtcp
+} // namespace webrtc
+#endif // MODULES_RTP_RTCP_SOURCE_RTCP_PACKET_TRANSPORT_FEEDBACK_H_
diff --git a/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc
new file mode 100644
index 0000000000..356d7a2340
--- /dev/null
+++ b/third_party/libwebrtc/modules/rtp_rtcp/source/rtcp_packet/transport_feedback_unittest.cc
@@ -0,0 +1,667 @@
+/*
+ * 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 "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
+
+#include <limits>
+#include <memory>
+#include <utility>
+
+#include "api/array_view.h"
+#include "api/units/time_delta.h"
+#include "api/units/timestamp.h"
+#include "modules/rtp_rtcp/source/byte_io.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/common_header.h"
+#include "test/gmock.h"
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace {
+
+using rtcp::TransportFeedback;
+using ::testing::AllOf;
+using ::testing::Each;
+using ::testing::ElementsAreArray;
+using ::testing::Eq;
+using ::testing::InSequence;
+using ::testing::MockFunction;
+using ::testing::Ne;
+using ::testing::Property;
+using ::testing::SizeIs;
+
+constexpr int kHeaderSize = 20;
+constexpr int kStatusChunkSize = 2;
+constexpr int kSmallDeltaSize = 1;
+constexpr int kLargeDeltaSize = 2;
+
+constexpr TimeDelta kDeltaLimit = 0xFF * TransportFeedback::kDeltaTick;
+constexpr TimeDelta kBaseTimeTick = TransportFeedback::kDeltaTick * (1 << 8);
+constexpr TimeDelta kBaseTimeWrapPeriod = kBaseTimeTick * (1 << 24);
+
+MATCHER_P2(Near, value, max_abs_error, "") {
+ return value - max_abs_error <= arg && arg <= value + max_abs_error;
+}
+
+MATCHER(IsValidFeedback, "") {
+ rtcp::CommonHeader rtcp_header;
+ TransportFeedback feedback;
+ return rtcp_header.Parse(std::data(arg), std::size(arg)) &&
+ rtcp_header.type() == TransportFeedback::kPacketType &&
+ rtcp_header.fmt() == TransportFeedback::kFeedbackMessageType &&
+ feedback.Parse(rtcp_header);
+}
+
+TransportFeedback Parse(rtc::ArrayView<const uint8_t> buffer) {
+ rtcp::CommonHeader header;
+ EXPECT_TRUE(header.Parse(buffer.data(), buffer.size()));
+ EXPECT_EQ(header.type(), TransportFeedback::kPacketType);
+ EXPECT_EQ(header.fmt(), TransportFeedback::kFeedbackMessageType);
+ TransportFeedback feedback;
+ EXPECT_TRUE(feedback.Parse(header));
+ return feedback;
+}
+
+class FeedbackTester {
+ public:
+ FeedbackTester() : FeedbackTester(true) {}
+ explicit FeedbackTester(bool include_timestamps)
+ : expected_size_(kAnySize),
+ default_delta_(TransportFeedback::kDeltaTick * 4),
+ include_timestamps_(include_timestamps) {}
+
+ void WithExpectedSize(size_t expected_size) {
+ expected_size_ = expected_size;
+ }
+
+ void WithDefaultDelta(TimeDelta delta) { default_delta_ = delta; }
+
+ void WithInput(rtc::ArrayView<const uint16_t> received_seq,
+ rtc::ArrayView<const Timestamp> received_ts = {}) {
+ std::vector<Timestamp> temp_timestamps;
+ if (received_ts.empty()) {
+ temp_timestamps = GenerateReceiveTimestamps(received_seq);
+ received_ts = temp_timestamps;
+ }
+ ASSERT_EQ(received_seq.size(), received_ts.size());
+
+ expected_deltas_.clear();
+ feedback_.emplace(include_timestamps_);
+ feedback_->SetBase(received_seq[0], received_ts[0]);
+ ASSERT_TRUE(feedback_->IsConsistent());
+ // First delta is special: it doesn't represent the delta between two times,
+ // but a compensation for the reduced precision of the base time.
+ EXPECT_TRUE(feedback_->AddReceivedPacket(received_seq[0], received_ts[0]));
+ // GetBaseDelta suppose to return balanced diff between base time of the new
+ // feedback message (stored internally) and base time of the old feedback
+ // message (passed as parameter), but first delta is the difference between
+ // 1st timestamp (passed as parameter) and base time (stored internally),
+ // thus to get the first delta need to negate whatever GetBaseDelta returns.
+ expected_deltas_.push_back(-feedback_->GetBaseDelta(received_ts[0]));
+
+ for (size_t i = 1; i < received_ts.size(); ++i) {
+ EXPECT_TRUE(
+ feedback_->AddReceivedPacket(received_seq[i], received_ts[i]));
+ expected_deltas_.push_back(received_ts[i] - received_ts[i - 1]);
+ }
+ ASSERT_TRUE(feedback_->IsConsistent());
+ expected_seq_.assign(received_seq.begin(), received_seq.end());
+ }
+
+ void VerifyPacket() {
+ ASSERT_TRUE(feedback_->IsConsistent());
+ serialized_ = feedback_->Build();
+ VerifyInternal();
+
+ feedback_.emplace(Parse(serialized_));
+ ASSERT_TRUE(feedback_->IsConsistent());
+ EXPECT_EQ(include_timestamps_, feedback_->IncludeTimestamps());
+ VerifyInternal();
+ }
+
+ static constexpr size_t kAnySize = static_cast<size_t>(0) - 1;
+
+ private:
+ void VerifyInternal() {
+ if (expected_size_ != kAnySize) {
+ // Round up to whole 32-bit words.
+ size_t expected_size_words = (expected_size_ + 3) / 4;
+ size_t expected_size_bytes = expected_size_words * 4;
+ EXPECT_EQ(expected_size_bytes, serialized_.size());
+ }
+
+ std::vector<uint16_t> actual_seq_nos;
+ std::vector<TimeDelta> actual_deltas;
+ for (const auto& packet : feedback_->GetReceivedPackets()) {
+ actual_seq_nos.push_back(packet.sequence_number());
+ actual_deltas.push_back(packet.delta());
+ }
+ EXPECT_THAT(actual_seq_nos, ElementsAreArray(expected_seq_));
+ if (include_timestamps_) {
+ EXPECT_THAT(actual_deltas, ElementsAreArray(expected_deltas_));
+ }
+ }
+
+ std::vector<Timestamp> GenerateReceiveTimestamps(
+ rtc::ArrayView<const uint16_t> seq_nums) {
+ RTC_CHECK(!seq_nums.empty());
+ uint16_t last_seq = seq_nums[0];
+ Timestamp time = Timestamp::Zero();
+ std::vector<Timestamp> result;
+
+ for (uint16_t seq : seq_nums) {
+ if (seq < last_seq)
+ time += 0x10000 * default_delta_;
+ last_seq = seq;
+
+ result.push_back(time + last_seq * default_delta_);
+ }
+ return result;
+ }
+
+ std::vector<uint16_t> expected_seq_;
+ std::vector<TimeDelta> expected_deltas_;
+ size_t expected_size_;
+ TimeDelta default_delta_;
+ absl::optional<TransportFeedback> feedback_;
+ rtc::Buffer serialized_;
+ bool include_timestamps_;
+};
+
+// The following tests use FeedbackTester that simulates received packets as
+// specified by the parameters `received_seq[]` and `received_ts[]` (optional).
+// The following is verified in these tests:
+// - Expected size of serialized packet.
+// - Expected sequence numbers and receive deltas.
+// - Sequence numbers and receive deltas are persistent after serialization
+// followed by parsing.
+// - The internal state of a feedback packet is consistent.
+TEST(RtcpPacketTest, TransportFeedbackOneBitVector) {
+ const uint16_t kReceived[] = {1, 2, 7, 8, 9, 10, 13};
+ const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + (kLength * kSmallDeltaSize);
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackOneBitVectorNoRecvDelta) {
+ const uint16_t kReceived[] = {1, 2, 7, 8, 9, 10, 13};
+ const size_t kExpectedSizeBytes = kHeaderSize + kStatusChunkSize;
+
+ FeedbackTester test(/*include_timestamps=*/false);
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackFullOneBitVector) {
+ const uint16_t kReceived[] = {1, 2, 7, 8, 9, 10, 13, 14};
+ const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + (kLength * kSmallDeltaSize);
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackOneBitVectorWrapReceived) {
+ const uint16_t kMax = 0xFFFF;
+ const uint16_t kReceived[] = {kMax - 2, kMax - 1, kMax, 0, 1, 2};
+ const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + (kLength * kSmallDeltaSize);
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackOneBitVectorWrapMissing) {
+ const uint16_t kMax = 0xFFFF;
+ const uint16_t kReceived[] = {kMax - 2, kMax - 1, 1, 2};
+ const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + (kLength * kSmallDeltaSize);
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackTwoBitVector) {
+ const uint16_t kReceived[] = {1, 2, 6, 7};
+ const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + (kLength * kLargeDeltaSize);
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithDefaultDelta(kDeltaLimit + TransportFeedback::kDeltaTick);
+ test.WithInput(kReceived);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackTwoBitVectorFull) {
+ const uint16_t kReceived[] = {1, 2, 6, 7, 8};
+ const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + (2 * kStatusChunkSize) + (kLength * kLargeDeltaSize);
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithDefaultDelta(kDeltaLimit + TransportFeedback::kDeltaTick);
+ test.WithInput(kReceived);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackWithLargeBaseTimeIsConsistent) {
+ TransportFeedback tb;
+ constexpr Timestamp kTimestamp =
+ Timestamp::Zero() + int64_t{0x7fff'ffff} * TransportFeedback::kDeltaTick;
+ tb.SetBase(/*base_sequence=*/0, /*ref_timestamp=*/kTimestamp);
+ tb.AddReceivedPacket(/*base_sequence=*/0, /*ref_timestamp=*/kTimestamp);
+ EXPECT_TRUE(tb.IsConsistent());
+}
+
+TEST(RtcpPacketTest, TransportFeedbackLargeAndNegativeDeltas) {
+ const uint16_t kReceived[] = {1, 2, 6, 7, 8};
+ const Timestamp kReceiveTimes[] = {
+ Timestamp::Millis(2), Timestamp::Millis(1), Timestamp::Millis(4),
+ Timestamp::Millis(3),
+ Timestamp::Millis(3) + TransportFeedback::kDeltaTick * (1 << 8)};
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + (3 * kLargeDeltaSize) + kSmallDeltaSize;
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived, kReceiveTimes);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackMaxRle) {
+ // Expected chunks created:
+ // * 1-bit vector chunk (1xreceived + 13xdropped)
+ // * RLE chunk of max length for dropped symbol
+ // * 1-bit vector chunk (1xreceived + 13xdropped)
+
+ const size_t kPacketCount = (1 << 13) - 1 + 14;
+ const uint16_t kReceived[] = {0, kPacketCount};
+ const Timestamp kReceiveTimes[] = {Timestamp::Millis(1),
+ Timestamp::Millis(2)};
+ const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + (3 * kStatusChunkSize) + (kLength * kSmallDeltaSize);
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived, kReceiveTimes);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackMinRle) {
+ // Expected chunks created:
+ // * 1-bit vector chunk (1xreceived + 13xdropped)
+ // * RLE chunk of length 15 for dropped symbol
+ // * 1-bit vector chunk (1xreceived + 13xdropped)
+
+ const uint16_t kReceived[] = {0, (14 * 2) + 1};
+ const Timestamp kReceiveTimes[] = {Timestamp::Millis(1),
+ Timestamp::Millis(2)};
+ const size_t kLength = sizeof(kReceived) / sizeof(uint16_t);
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + (3 * kStatusChunkSize) + (kLength * kSmallDeltaSize);
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived, kReceiveTimes);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackOneToTwoBitVector) {
+ const size_t kTwoBitVectorCapacity = 7;
+ const uint16_t kReceived[] = {0, kTwoBitVectorCapacity - 1};
+ const Timestamp kReceiveTimes[] = {
+ Timestamp::Zero(),
+ Timestamp::Zero() + kDeltaLimit + TransportFeedback::kDeltaTick};
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + kSmallDeltaSize + kLargeDeltaSize;
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived, kReceiveTimes);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackOneToTwoBitVectorSimpleSplit) {
+ const size_t kTwoBitVectorCapacity = 7;
+ const uint16_t kReceived[] = {0, kTwoBitVectorCapacity};
+ const Timestamp kReceiveTimes[] = {
+ Timestamp::Zero(),
+ Timestamp::Zero() + kDeltaLimit + TransportFeedback::kDeltaTick};
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + (kStatusChunkSize * 2) + kSmallDeltaSize + kLargeDeltaSize;
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived, kReceiveTimes);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackOneToTwoBitVectorSplit) {
+ // With received small delta = S, received large delta = L, use input
+ // SSSSSSSSLSSSSSSSSSSSS. This will cause a 1:2 split at the L.
+ // After split there will be two symbols in symbol_vec: SL.
+
+ const TimeDelta kLargeDelta = TransportFeedback::kDeltaTick * (1 << 8);
+ const size_t kNumPackets = (3 * 7) + 1;
+ const size_t kExpectedSizeBytes = kHeaderSize + (kStatusChunkSize * 3) +
+ (kSmallDeltaSize * (kNumPackets - 1)) +
+ (kLargeDeltaSize * 1);
+
+ uint16_t kReceived[kNumPackets];
+ for (size_t i = 0; i < kNumPackets; ++i)
+ kReceived[i] = i;
+
+ std::vector<Timestamp> receive_times;
+ receive_times.reserve(kNumPackets);
+ receive_times.push_back(Timestamp::Millis(1));
+ for (size_t i = 1; i < kNumPackets; ++i) {
+ TimeDelta delta = (i == 8) ? kLargeDelta : TimeDelta::Millis(1);
+ receive_times.push_back(receive_times.back() + delta);
+ }
+
+ FeedbackTester test;
+ test.WithExpectedSize(kExpectedSizeBytes);
+ test.WithInput(kReceived, receive_times);
+ test.VerifyPacket();
+}
+
+TEST(RtcpPacketTest, TransportFeedbackAliasing) {
+ TransportFeedback feedback;
+ feedback.SetBase(0, Timestamp::Zero());
+
+ const int kSamples = 100;
+ const TimeDelta kTooSmallDelta = TransportFeedback::kDeltaTick / 3;
+
+ for (int i = 0; i < kSamples; ++i)
+ feedback.AddReceivedPacket(i, Timestamp::Zero() + i * kTooSmallDelta);
+
+ feedback.Build();
+
+ TimeDelta accumulated_delta = TimeDelta::Zero();
+ int num_samples = 0;
+ for (const auto& packet : feedback.GetReceivedPackets()) {
+ accumulated_delta += packet.delta();
+ TimeDelta expected_time = num_samples * kTooSmallDelta;
+ ++num_samples;
+
+ EXPECT_THAT(accumulated_delta,
+ Near(expected_time, TransportFeedback::kDeltaTick / 2));
+ }
+}
+
+TEST(RtcpPacketTest, TransportFeedbackLimits) {
+ // Sequence number wrap above 0x8000.
+ std::unique_ptr<TransportFeedback> packet(new TransportFeedback());
+ packet->SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(packet->AddReceivedPacket(0x0, Timestamp::Zero()));
+ EXPECT_TRUE(packet->AddReceivedPacket(0x8000, Timestamp::Millis(1)));
+
+ packet.reset(new TransportFeedback());
+ packet->SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(packet->AddReceivedPacket(0x0, Timestamp::Zero()));
+ EXPECT_FALSE(packet->AddReceivedPacket(0x8000 + 1, Timestamp::Millis(1)));
+
+ // Packet status count max 0xFFFF.
+ packet.reset(new TransportFeedback());
+ packet->SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(packet->AddReceivedPacket(0x0, Timestamp::Zero()));
+ EXPECT_TRUE(packet->AddReceivedPacket(0x8000, Timestamp::Millis(1)));
+ EXPECT_TRUE(packet->AddReceivedPacket(0xFFFE, Timestamp::Millis(2)));
+ EXPECT_FALSE(packet->AddReceivedPacket(0xFFFF, Timestamp::Millis(3)));
+
+ // Too large delta.
+ packet.reset(new TransportFeedback());
+ packet->SetBase(0, Timestamp::Zero());
+ TimeDelta kMaxPositiveTimeDelta =
+ std::numeric_limits<int16_t>::max() * TransportFeedback::kDeltaTick;
+ EXPECT_FALSE(packet->AddReceivedPacket(1, Timestamp::Zero() +
+ kMaxPositiveTimeDelta +
+ TransportFeedback::kDeltaTick));
+ EXPECT_TRUE(
+ packet->AddReceivedPacket(1, Timestamp::Zero() + kMaxPositiveTimeDelta));
+
+ // Too large negative delta.
+ packet.reset(new TransportFeedback());
+ TimeDelta kMaxNegativeTimeDelta =
+ std::numeric_limits<int16_t>::min() * TransportFeedback::kDeltaTick;
+ // Use larger base time to avoid kBaseTime + kNegativeDelta to be negative.
+ Timestamp kBaseTime = Timestamp::Seconds(1'000'000);
+ packet->SetBase(0, kBaseTime);
+ EXPECT_FALSE(packet->AddReceivedPacket(
+ 1, kBaseTime + kMaxNegativeTimeDelta - TransportFeedback::kDeltaTick));
+ EXPECT_TRUE(packet->AddReceivedPacket(1, kBaseTime + kMaxNegativeTimeDelta));
+
+ // TODO(sprang): Once we support max length lower than RTCP length limit,
+ // add back test for max size in bytes.
+}
+
+TEST(RtcpPacketTest, BaseTimeIsConsistentAcrossMultiplePackets) {
+ constexpr Timestamp kMaxBaseTime =
+ Timestamp::Zero() + kBaseTimeWrapPeriod - kBaseTimeTick;
+
+ TransportFeedback packet1;
+ packet1.SetBase(0, kMaxBaseTime);
+ packet1.AddReceivedPacket(0, kMaxBaseTime);
+ // Build and parse packet to simulate sending it over the wire.
+ TransportFeedback parsed_packet1 = Parse(packet1.Build());
+
+ TransportFeedback packet2;
+ packet2.SetBase(1, kMaxBaseTime + kBaseTimeTick);
+ packet2.AddReceivedPacket(1, kMaxBaseTime + kBaseTimeTick);
+ TransportFeedback parsed_packet2 = Parse(packet2.Build());
+
+ EXPECT_EQ(parsed_packet2.GetBaseDelta(parsed_packet1.BaseTime()),
+ kBaseTimeTick);
+}
+
+TEST(RtcpPacketTest, SupportsMaximumNumberOfNegativeDeltas) {
+ TransportFeedback feedback;
+ // Use large base time to avoid hitting zero limit while filling the feedback,
+ // but use multiple of kBaseTimeWrapPeriod to hit edge case where base time
+ // is recorded as zero in the raw rtcp packet.
+ Timestamp time = Timestamp::Zero() + 1'000 * kBaseTimeWrapPeriod;
+ feedback.SetBase(0, time);
+ static constexpr TimeDelta kMinDelta =
+ TransportFeedback::kDeltaTick * std::numeric_limits<int16_t>::min();
+ uint16_t num_received_rtp_packets = 0;
+ time += kMinDelta;
+ while (feedback.AddReceivedPacket(++num_received_rtp_packets, time)) {
+ ASSERT_GE(time, Timestamp::Zero() - kMinDelta);
+ time += kMinDelta;
+ }
+ // Subtract one last packet that failed to add.
+ --num_received_rtp_packets;
+ EXPECT_TRUE(feedback.IsConsistent());
+
+ TransportFeedback parsed = Parse(feedback.Build());
+ EXPECT_EQ(parsed.GetReceivedPackets().size(), num_received_rtp_packets);
+ EXPECT_THAT(parsed.GetReceivedPackets(),
+ AllOf(SizeIs(num_received_rtp_packets),
+ Each(Property(&TransportFeedback::ReceivedPacket::delta,
+ Eq(kMinDelta)))));
+ EXPECT_GE(parsed.BaseTime(),
+ Timestamp::Zero() - kMinDelta * num_received_rtp_packets);
+}
+
+TEST(RtcpPacketTest, TransportFeedbackPadding) {
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + kSmallDeltaSize;
+ const size_t kExpectedSizeWords = (kExpectedSizeBytes + 3) / 4;
+ const size_t kExpectedPaddingSizeBytes =
+ 4 * kExpectedSizeWords - kExpectedSizeBytes;
+
+ TransportFeedback feedback;
+ feedback.SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(feedback.AddReceivedPacket(0, Timestamp::Zero()));
+
+ rtc::Buffer packet = feedback.Build();
+ EXPECT_EQ(kExpectedSizeWords * 4, packet.size());
+ ASSERT_GT(kExpectedSizeWords * 4, kExpectedSizeBytes);
+ for (size_t i = kExpectedSizeBytes; i < (kExpectedSizeWords * 4 - 1); ++i)
+ EXPECT_EQ(0u, packet[i]);
+
+ EXPECT_EQ(kExpectedPaddingSizeBytes, packet[kExpectedSizeWords * 4 - 1]);
+
+ // Modify packet by adding 4 bytes of padding at the end. Not currently used
+ // when we're sending, but need to be able to handle it when receiving.
+
+ const int kPaddingBytes = 4;
+ const size_t kExpectedSizeWithPadding =
+ (kExpectedSizeWords * 4) + kPaddingBytes;
+ uint8_t mod_buffer[kExpectedSizeWithPadding];
+ memcpy(mod_buffer, packet.data(), kExpectedSizeWords * 4);
+ memset(&mod_buffer[kExpectedSizeWords * 4], 0, kPaddingBytes - 1);
+ mod_buffer[kExpectedSizeWithPadding - 1] =
+ kPaddingBytes + kExpectedPaddingSizeBytes;
+ const uint8_t padding_flag = 1 << 5;
+ mod_buffer[0] |= padding_flag;
+ ByteWriter<uint16_t>::WriteBigEndian(
+ &mod_buffer[2], ByteReader<uint16_t>::ReadBigEndian(&mod_buffer[2]) +
+ ((kPaddingBytes + 3) / 4));
+
+ EXPECT_THAT(mod_buffer, IsValidFeedback());
+}
+
+TEST(RtcpPacketTest, TransportFeedbackPaddingBackwardsCompatibility) {
+ const size_t kExpectedSizeBytes =
+ kHeaderSize + kStatusChunkSize + kSmallDeltaSize;
+ const size_t kExpectedSizeWords = (kExpectedSizeBytes + 3) / 4;
+ const size_t kExpectedPaddingSizeBytes =
+ 4 * kExpectedSizeWords - kExpectedSizeBytes;
+
+ TransportFeedback feedback;
+ feedback.SetBase(0, Timestamp::Zero());
+ EXPECT_TRUE(feedback.AddReceivedPacket(0, Timestamp::Zero()));
+
+ rtc::Buffer packet = feedback.Build();
+ EXPECT_EQ(kExpectedSizeWords * 4, packet.size());
+ ASSERT_GT(kExpectedSizeWords * 4, kExpectedSizeBytes);
+ for (size_t i = kExpectedSizeBytes; i < (kExpectedSizeWords * 4 - 1); ++i)
+ EXPECT_EQ(0u, packet[i]);
+
+ EXPECT_GT(kExpectedPaddingSizeBytes, 0u);
+ EXPECT_EQ(kExpectedPaddingSizeBytes, packet[kExpectedSizeWords * 4 - 1]);
+
+ // Modify packet by removing padding bit and writing zero at the last padding
+ // byte to verify that we can parse packets from old clients, where zero
+ // padding of up to three bytes was used without the padding bit being set.
+ uint8_t mod_buffer[kExpectedSizeWords * 4];
+ memcpy(mod_buffer, packet.data(), kExpectedSizeWords * 4);
+ mod_buffer[kExpectedSizeWords * 4 - 1] = 0;
+ const uint8_t padding_flag = 1 << 5;
+ mod_buffer[0] &= ~padding_flag; // Unset padding flag.
+
+ EXPECT_THAT(mod_buffer, IsValidFeedback());
+}
+
+TEST(RtcpPacketTest, TransportFeedbackCorrectlySplitsVectorChunks) {
+ const int kOneBitVectorCapacity = 14;
+ const TimeDelta kLargeTimeDelta = TransportFeedback::kDeltaTick * (1 << 8);
+
+ // Test that a number of small deltas followed by a large delta results in a
+ // correct split into multiple chunks, as needed.
+
+ for (int deltas = 0; deltas <= kOneBitVectorCapacity + 1; ++deltas) {
+ TransportFeedback feedback;
+ feedback.SetBase(0, Timestamp::Zero());
+ for (int i = 0; i < deltas; ++i)
+ feedback.AddReceivedPacket(i, Timestamp::Millis(i));
+ feedback.AddReceivedPacket(deltas,
+ Timestamp::Millis(deltas) + kLargeTimeDelta);
+
+ EXPECT_THAT(feedback.Build(), IsValidFeedback());
+ }
+}
+
+TEST(RtcpPacketTest, TransportFeedbackMoveConstructor) {
+ const int kSamples = 100;
+ const uint16_t kBaseSeqNo = 7531;
+ const Timestamp kBaseTimestamp = Timestamp::Micros(123'456'789);
+ const uint8_t kFeedbackSeqNo = 90;
+
+ TransportFeedback feedback;
+ feedback.SetBase(kBaseSeqNo, kBaseTimestamp);
+ feedback.SetFeedbackSequenceNumber(kFeedbackSeqNo);
+ for (int i = 0; i < kSamples; ++i) {
+ feedback.AddReceivedPacket(
+ kBaseSeqNo + i, kBaseTimestamp + i * TransportFeedback::kDeltaTick);
+ }
+ EXPECT_TRUE(feedback.IsConsistent());
+
+ TransportFeedback feedback_copy(feedback);
+ EXPECT_TRUE(feedback_copy.IsConsistent());
+ EXPECT_TRUE(feedback.IsConsistent());
+ EXPECT_EQ(feedback_copy.Build(), feedback.Build());
+
+ TransportFeedback moved(std::move(feedback));
+ EXPECT_TRUE(moved.IsConsistent());
+ EXPECT_TRUE(feedback.IsConsistent());
+ EXPECT_EQ(moved.Build(), feedback_copy.Build());
+}
+
+TEST(TransportFeedbackTest, ReportsMissingPackets) {
+ const uint16_t kBaseSeqNo = 1000;
+ const Timestamp kBaseTimestamp = Timestamp::Millis(10);
+ const uint8_t kFeedbackSeqNo = 90;
+ TransportFeedback feedback_builder(/*include_timestamps*/ true);
+ feedback_builder.SetBase(kBaseSeqNo, kBaseTimestamp);
+ feedback_builder.SetFeedbackSequenceNumber(kFeedbackSeqNo);
+ feedback_builder.AddReceivedPacket(kBaseSeqNo + 0, kBaseTimestamp);
+ // Packet losses indicated by jump in sequence number.
+ feedback_builder.AddReceivedPacket(kBaseSeqNo + 3,
+ kBaseTimestamp + TimeDelta::Millis(2));
+
+ MockFunction<void(uint16_t, TimeDelta)> handler;
+ InSequence s;
+ EXPECT_CALL(handler, Call(kBaseSeqNo + 0, Ne(TimeDelta::PlusInfinity())));
+ EXPECT_CALL(handler, Call(kBaseSeqNo + 1, TimeDelta::PlusInfinity()));
+ EXPECT_CALL(handler, Call(kBaseSeqNo + 2, TimeDelta::PlusInfinity()));
+ EXPECT_CALL(handler, Call(kBaseSeqNo + 3, Ne(TimeDelta::PlusInfinity())));
+ Parse(feedback_builder.Build()).ForAllPackets(handler.AsStdFunction());
+}
+
+TEST(TransportFeedbackTest, ReportsMissingPacketsWithoutTimestamps) {
+ const uint16_t kBaseSeqNo = 1000;
+ const uint8_t kFeedbackSeqNo = 90;
+ TransportFeedback feedback_builder(/*include_timestamps*/ false);
+ feedback_builder.SetBase(kBaseSeqNo, Timestamp::Millis(10));
+ feedback_builder.SetFeedbackSequenceNumber(kFeedbackSeqNo);
+ feedback_builder.AddReceivedPacket(kBaseSeqNo + 0, Timestamp::Zero());
+ // Packet losses indicated by jump in sequence number.
+ feedback_builder.AddReceivedPacket(kBaseSeqNo + 3, Timestamp::Zero());
+
+ MockFunction<void(uint16_t, TimeDelta)> handler;
+ InSequence s;
+ EXPECT_CALL(handler, Call(kBaseSeqNo + 0, Ne(TimeDelta::PlusInfinity())));
+ EXPECT_CALL(handler, Call(kBaseSeqNo + 1, TimeDelta::PlusInfinity()));
+ EXPECT_CALL(handler, Call(kBaseSeqNo + 2, TimeDelta::PlusInfinity()));
+ EXPECT_CALL(handler, Call(kBaseSeqNo + 3, Ne(TimeDelta::PlusInfinity())));
+ Parse(feedback_builder.Build()).ForAllPackets(handler.AsStdFunction());
+}
+} // namespace
+} // namespace webrtc