Adds remote estimate RTCP packet.

This adds the RemoteEstimate rtcp packet and wires it up to GoogCC where
it's used to improve congestion controller behavior.

The functionality is negotiated using SDP.

It's added with a field trial that allow disabling the functionality in
case there's any issues.

Bug: webrtc:10742
Change-Id: I1ea8e4216a27cd2b00505c99b42d1e38726256c8
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/146602
Reviewed-by: Stefan Holmer <stefan@webrtc.org>
Reviewed-by: Erik Språng <sprang@webrtc.org>
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#28654}
diff --git a/call/rtp_transport_controller_send.cc b/call/rtp_transport_controller_send.cc
index 842afd7..a1a42e1 100644
--- a/call/rtp_transport_controller_send.cc
+++ b/call/rtp_transport_controller_send.cc
@@ -157,6 +157,11 @@
   return &packet_router_;
 }
 
+NetworkStateEstimateObserver*
+RtpTransportControllerSend::network_state_estimate_observer() {
+  return this;
+}
+
 TransportFeedbackObserver*
 RtpTransportControllerSend::transport_feedback_observer() {
   return this;
@@ -451,6 +456,16 @@
       transport_feedback_adapter_.GetOutstandingData().bytes());
 }
 
+void RtpTransportControllerSend::OnRemoteNetworkEstimate(
+    NetworkStateEstimate estimate) {
+  estimate.update_time = Timestamp::ms(clock_->TimeInMilliseconds());
+  task_queue_.PostTask([this, estimate] {
+    RTC_DCHECK_RUN_ON(&task_queue_);
+    if (controller_)
+      controller_->OnNetworkStateEstimate(estimate);
+  });
+}
+
 void RtpTransportControllerSend::MaybeCreateControllers() {
   RTC_DCHECK(!controller_);
   RTC_DCHECK(!control_handler_);
diff --git a/call/rtp_transport_controller_send.h b/call/rtp_transport_controller_send.h
index 235fc15..e7e1610 100644
--- a/call/rtp_transport_controller_send.h
+++ b/call/rtp_transport_controller_send.h
@@ -43,7 +43,8 @@
 class RtpTransportControllerSend final
     : public RtpTransportControllerSendInterface,
       public RtcpBandwidthObserver,
-      public TransportFeedbackObserver {
+      public TransportFeedbackObserver,
+      public NetworkStateEstimateObserver {
  public:
   RtpTransportControllerSend(
       Clock* clock,
@@ -73,6 +74,7 @@
   rtc::TaskQueue* GetWorkerQueue() override;
   PacketRouter* packet_router() override;
 
+  NetworkStateEstimateObserver* network_state_estimate_observer() override;
   TransportFeedbackObserver* transport_feedback_observer() override;
   RtpPacketPacer* packet_sender() override;
 
@@ -114,6 +116,9 @@
   void OnAddPacket(const RtpPacketSendInfo& packet_info) override;
   void OnTransportFeedback(const rtcp::TransportFeedback& feedback) override;
 
+  // Implements NetworkStateEstimateObserver interface
+  void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) override;
+
  private:
   void MaybeCreateControllers() RTC_RUN_ON(task_queue_);
   void UpdateInitialConstraints(TargetRateConstraints new_contraints)
diff --git a/call/rtp_transport_controller_send_interface.h b/call/rtp_transport_controller_send_interface.h
index 4cfe7c9..0178758 100644
--- a/call/rtp_transport_controller_send_interface.h
+++ b/call/rtp_transport_controller_send_interface.h
@@ -116,6 +116,7 @@
   virtual void DestroyRtpVideoSender(
       RtpVideoSenderInterface* rtp_video_sender) = 0;
 
+  virtual NetworkStateEstimateObserver* network_state_estimate_observer() = 0;
   virtual TransportFeedbackObserver* transport_feedback_observer() = 0;
 
   virtual RtpPacketPacer* packet_sender() = 0;
diff --git a/call/rtp_video_sender.cc b/call/rtp_video_sender.cc
index 529e0ad..70fe821 100644
--- a/call/rtp_video_sender.cc
+++ b/call/rtp_video_sender.cc
@@ -86,6 +86,8 @@
   configuration.rtcp_loss_notification_observer =
       rtcp_loss_notification_observer;
   configuration.bandwidth_callback = bandwidth_callback;
+  configuration.network_state_estimate_observer =
+      transport->network_state_estimate_observer();
   configuration.transport_feedback_callback =
       transport->transport_feedback_observer();
   configuration.rtt_stats = rtt_stats;
diff --git a/call/test/mock_rtp_transport_controller_send.h b/call/test/mock_rtp_transport_controller_send.h
index 2e81396..81db587 100644
--- a/call/test/mock_rtp_transport_controller_send.h
+++ b/call/test/mock_rtp_transport_controller_send.h
@@ -45,6 +45,8 @@
   MOCK_METHOD1(DestroyRtpVideoSender, void(RtpVideoSenderInterface*));
   MOCK_METHOD0(GetWorkerQueue, rtc::TaskQueue*());
   MOCK_METHOD0(packet_router, PacketRouter*());
+  MOCK_METHOD0(network_state_estimate_observer,
+               NetworkStateEstimateObserver*());
   MOCK_METHOD0(transport_feedback_observer, TransportFeedbackObserver*());
   MOCK_METHOD0(packet_sender, RtpPacketPacer*());
   MOCK_METHOD3(SetAllocatedSendBitrateLimits, void(int, int, int));
diff --git a/media/base/media_channel.h b/media/base/media_channel.h
index 8c4e8b8..856dba4 100644
--- a/media/base/media_channel.h
+++ b/media/base/media_channel.h
@@ -719,6 +719,7 @@
 
 struct RtcpParameters {
   bool reduced_size = false;
+  bool remote_estimate = false;
 };
 
 template <class Codec>
diff --git a/modules/rtp_rtcp/BUILD.gn b/modules/rtp_rtcp/BUILD.gn
index 791cdd3..98a512e 100644
--- a/modules/rtp_rtcp/BUILD.gn
+++ b/modules/rtp_rtcp/BUILD.gn
@@ -34,6 +34,7 @@
     "source/rtcp_packet/rapid_resync_request.h",
     "source/rtcp_packet/receiver_report.h",
     "source/rtcp_packet/remb.h",
+    "source/rtcp_packet/remote_estimate.h",
     "source/rtcp_packet/report_block.h",
     "source/rtcp_packet/rrtr.h",
     "source/rtcp_packet/rtpfb.h",
@@ -73,6 +74,7 @@
     "source/rtcp_packet/rapid_resync_request.cc",
     "source/rtcp_packet/receiver_report.cc",
     "source/rtcp_packet/remb.cc",
+    "source/rtcp_packet/remote_estimate.cc",
     "source/rtcp_packet/report_block.cc",
     "source/rtcp_packet/rrtr.cc",
     "source/rtcp_packet/rtpfb.cc",
@@ -411,6 +413,7 @@
       "source/rtcp_packet/rapid_resync_request_unittest.cc",
       "source/rtcp_packet/receiver_report_unittest.cc",
       "source/rtcp_packet/remb_unittest.cc",
+      "source/rtcp_packet/remote_estimate_unittest.cc",
       "source/rtcp_packet/report_block_unittest.cc",
       "source/rtcp_packet/rrtr_unittest.cc",
       "source/rtcp_packet/sdes_unittest.cc",
diff --git a/modules/rtp_rtcp/include/rtp_rtcp.h b/modules/rtp_rtcp/include/rtp_rtcp.h
index 83c4cfc..f4a8c9d 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp.h
@@ -78,6 +78,7 @@
     // stream.
     RtcpBandwidthObserver* bandwidth_callback = nullptr;
 
+    NetworkStateEstimateObserver* network_state_estimate_observer = nullptr;
     TransportFeedbackObserver* transport_feedback_callback = nullptr;
     VideoBitrateAllocationObserver* bitrate_allocation_observer = nullptr;
     RtcpRttStats* rtt_stats = nullptr;
diff --git a/modules/rtp_rtcp/include/rtp_rtcp_defines.h b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
index 2b46764..a779633 100644
--- a/modules/rtp_rtcp/include/rtp_rtcp_defines.h
+++ b/modules/rtp_rtcp/include/rtp_rtcp_defines.h
@@ -24,6 +24,7 @@
 #include "api/rtp_headers.h"
 #include "api/transport/network_types.h"
 #include "modules/include/module_common_types.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
 #include "system_wrappers/include/clock.h"
 
 #define RTCP_CNAME_SIZE 256  // RFC 3550 page 44, including null termination
@@ -291,6 +292,11 @@
   size_t length = 0;
   PacedPacketInfo pacing_info;
 };
+class NetworkStateEstimateObserver {
+ public:
+  virtual void OnRemoteNetworkEstimate(NetworkStateEstimate estimate) = 0;
+  virtual ~NetworkStateEstimateObserver() = default;
+};
 
 class TransportFeedbackObserver {
  public:
@@ -310,6 +316,8 @@
   virtual ~RtcpFeedbackSenderInterface() = default;
   virtual uint32_t SSRC() const = 0;
   virtual bool SendFeedbackPacket(const rtcp::TransportFeedback& feedback) = 0;
+  virtual bool SendNetworkStateEstimatePacket(
+      const rtcp::RemoteEstimate& packet) = 0;
   virtual void SetRemb(int64_t bitrate_bps, std::vector<uint32_t> ssrcs) = 0;
   virtual void UnsetRemb() = 0;
 };
diff --git a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
index 68ded28..1cb488c 100644
--- a/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
+++ b/modules/rtp_rtcp/mocks/mock_rtp_rtcp.h
@@ -155,6 +155,8 @@
   MOCK_METHOD0(GetRtcpStatisticsCallback, RtcpStatisticsCallback*());
   MOCK_METHOD1(SetReportBlockDataObserver, void(ReportBlockDataObserver*));
   MOCK_METHOD1(SendFeedbackPacket, bool(const rtcp::TransportFeedback& packet));
+  MOCK_METHOD1(SendNetworkStateEstimatePacket,
+               bool(const rtcp::RemoteEstimate& packet));
   MOCK_METHOD1(SetTargetSendBitrate, void(uint32_t bitrate_bps));
   MOCK_METHOD4(SendLossNotification,
                int32_t(uint16_t last_decoded_seq_num,
diff --git a/modules/rtp_rtcp/source/rtcp_packet/app.h b/modules/rtp_rtcp/source/rtcp_packet/app.h
index a9602a8..ff5f52d 100644
--- a/modules/rtp_rtcp/source/rtcp_packet/app.h
+++ b/modules/rtp_rtcp/source/rtcp_packet/app.h
@@ -48,6 +48,12 @@
               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;
diff --git a/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc
new file mode 100644
index 0000000..81d1a1a
--- /dev/null
+++ b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.cc
@@ -0,0 +1,157 @@
+/*
+ *  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 <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);
+  SetSsrc(0);
+}
+
+bool RemoteEstimate::IsNetworkEstimate(const CommonHeader& packet) {
+  if (packet.fmt() != kSubType)
+    return false;
+  size_t kNameSize = sizeof(uint32_t);
+  if (packet.packet_size() < CommonHeader::kHeaderSizeBytes + kNameSize)
+    return false;
+  if (ByteReader<uint32_t>::ReadBigEndian(&packet.payload()[4]) != kName)
+    return false;
+  return true;
+}
+
+bool RemoteEstimate::Parse(const CommonHeader& packet) {
+  if (!App::Parse(packet))
+    return false;
+  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/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h
new file mode 100644
index 0000000..a63b624
--- /dev/null
+++ b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h
@@ -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.
+ */
+#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();
+
+class RemoteEstimate : public App {
+ public:
+  RemoteEstimate();
+  // 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();
+
+  static bool IsNetworkEstimate(const CommonHeader& packet);
+  bool Parse(const CommonHeader& packet);
+  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/modules/rtp_rtcp/source/rtcp_packet/remote_estimate_unittest.cc b/modules/rtp_rtcp/source/rtcp_packet/remote_estimate_unittest.cc
new file mode 100644
index 0000000..bbeb227
--- /dev/null
+++ b/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::kbps(10);
+  src.link_capacity_upper = DataRate::kbps(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::kbps(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::kbps(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::kbps(300));
+  EXPECT_TRUE(dst.link_capacity_upper.IsMinusInfinity());
+}
+}  // namespace rtcp
+}  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtcp_receiver.cc b/modules/rtp_rtcp/source/rtcp_receiver.cc
index 99b55ef..c73c7ad 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver.cc
+++ b/modules/rtp_rtcp/source/rtcp_receiver.cc
@@ -32,6 +32,7 @@
 #include "modules/rtp_rtcp/source/rtcp_packet/rapid_resync_request.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/receiver_report.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/remb.h"
+#include "modules/rtp_rtcp/source/rtcp_packet/remote_estimate.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/sdes.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/sender_report.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/tmmbn.h"
@@ -79,6 +80,7 @@
   uint32_t receiver_estimated_max_bitrate_bps = 0;
   std::unique_ptr<rtcp::TransportFeedback> transport_feedback;
   absl::optional<VideoBitrateAllocation> target_bitrate_allocation;
+  absl::optional<NetworkStateEstimate> network_state_estimate;
   std::unique_ptr<rtcp::LossNotification> loss_notification;
 };
 
@@ -128,6 +130,7 @@
       rtcp_bandwidth_observer_(config.bandwidth_callback),
       rtcp_intra_frame_observer_(config.intra_frame_callback),
       rtcp_loss_notification_observer_(config.rtcp_loss_notification_observer),
+      network_state_estimate_observer_(config.network_state_estimate_observer),
       transport_feedback_observer_(config.transport_feedback_callback),
       bitrate_allocation_observer_(config.bitrate_allocation_observer),
       report_interval_ms_(config.rtcp_report_interval_ms > 0
@@ -360,6 +363,9 @@
       case rtcp::Bye::kPacketType:
         HandleBye(rtcp_block);
         break;
+      case rtcp::App::kPacketType:
+        HandleApp(rtcp_block, packet_information);
+        break;
       case rtcp::Rtpfb::kPacketType:
         switch (rtcp_block.fmt()) {
           case rtcp::Nack::kFeedbackMessageType:
@@ -690,6 +696,18 @@
   }
 }
 
+void RTCPReceiver::HandleApp(const rtcp::CommonHeader& rtcp_block,
+                             PacketInformation* packet_information) {
+  if (rtcp::RemoteEstimate::IsNetworkEstimate(rtcp_block)) {
+    rtcp::RemoteEstimate estimate;
+    if (estimate.Parse(rtcp_block)) {
+      packet_information->network_state_estimate = estimate.estimate();
+      return;
+    }
+  }
+  ++num_skipped_packets_;
+}
+
 void RTCPReceiver::HandleBye(const CommonHeader& rtcp_block) {
   rtcp::Bye bye;
   if (!bye.Parse(rtcp_block)) {
@@ -1074,6 +1092,12 @@
     }
   }
 
+  if (network_state_estimate_observer_ &&
+      packet_information.network_state_estimate) {
+    network_state_estimate_observer_->OnRemoteNetworkEstimate(
+        *packet_information.network_state_estimate);
+  }
+
   if (bitrate_allocation_observer_ &&
       packet_information.target_bitrate_allocation) {
     bitrate_allocation_observer_->OnBitrateAllocationUpdated(
diff --git a/modules/rtp_rtcp/source/rtcp_receiver.h b/modules/rtp_rtcp/source/rtcp_receiver.h
index f49b748..7d684cb 100644
--- a/modules/rtp_rtcp/source/rtcp_receiver.h
+++ b/modules/rtp_rtcp/source/rtcp_receiver.h
@@ -176,6 +176,10 @@
                   PacketInformation* packet_information)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
 
+  void HandleApp(const rtcp::CommonHeader& rtcp_block,
+                 PacketInformation* packet_information)
+      RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
+
   void HandleBye(const rtcp::CommonHeader& rtcp_block)
       RTC_EXCLUSIVE_LOCKS_REQUIRED(rtcp_receiver_lock_);
 
@@ -215,6 +219,7 @@
   RtcpBandwidthObserver* const rtcp_bandwidth_observer_;
   RtcpIntraFrameObserver* const rtcp_intra_frame_observer_;
   RtcpLossNotificationObserver* const rtcp_loss_notification_observer_;
+  NetworkStateEstimateObserver* const network_state_estimate_observer_;
   TransportFeedbackObserver* const transport_feedback_observer_;
   VideoBitrateAllocationObserver* const bitrate_allocation_observer_;
   const int report_interval_ms_;
diff --git a/modules/rtp_rtcp/source/rtcp_sender.cc b/modules/rtp_rtcp/source/rtcp_sender.cc
index 8c5f821..ffa5e90 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.cc
+++ b/modules/rtp_rtcp/source/rtcp_sender.cc
@@ -997,4 +997,22 @@
   return packet.Build(max_packet_size, callback) && !send_failure;
 }
 
+bool RTCPSender::SendNetworkStateEstimatePacket(
+    const rtcp::RemoteEstimate& packet) {
+  size_t max_packet_size;
+  {
+    rtc::CritScope lock(&critical_section_rtcp_sender_);
+    if (method_ == RtcpMode::kOff)
+      return false;
+    max_packet_size = max_packet_size_;
+  }
+
+  RTC_DCHECK_LE(max_packet_size, IP_PACKET_SIZE);
+  bool send_success = false;
+  auto callback = [&](rtc::ArrayView<const uint8_t> packet) {
+    send_success = transport_->SendRtcp(packet.data(), packet.size());
+  };
+  return packet.Build(max_packet_size, callback) && send_success;
+}
+
 }  // namespace webrtc
diff --git a/modules/rtp_rtcp/source/rtcp_sender.h b/modules/rtp_rtcp/source/rtcp_sender.h
index 628121e..33db97a 100644
--- a/modules/rtp_rtcp/source/rtcp_sender.h
+++ b/modules/rtp_rtcp/source/rtcp_sender.h
@@ -141,6 +141,7 @@
   void SetTargetBitrate(unsigned int target_bitrate);
   void SetVideoBitrateAllocation(const VideoBitrateAllocation& bitrate);
   bool SendFeedbackPacket(const rtcp::TransportFeedback& packet);
+  bool SendNetworkStateEstimatePacket(const rtcp::RemoteEstimate& packet);
 
  private:
   class RtcpContext;
diff --git a/modules/rtp_rtcp/source/rtcp_transceiver.cc b/modules/rtp_rtcp/source/rtcp_transceiver.cc
index a378b36..9fb20d6 100644
--- a/modules/rtp_rtcp/source/rtcp_transceiver.cc
+++ b/modules/rtp_rtcp/source/rtcp_transceiver.cc
@@ -129,6 +129,18 @@
   return true;
 }
 
+bool RtcpTransceiver::SendNetworkStateEstimatePacket(
+    const rtcp::RemoteEstimate& packet) {
+  RTC_CHECK(rtcp_transceiver_);
+  struct Closure {
+    void operator()() { ptr->SendRawPacket(raw_packet); }
+    RtcpTransceiverImpl* ptr;
+    rtc::Buffer raw_packet;
+  };
+  task_queue_->PostTask(Closure{rtcp_transceiver_.get(), packet.Build()});
+  return true;
+}
+
 void RtcpTransceiver::SendNack(uint32_t ssrc,
                                std::vector<uint16_t> sequence_numbers) {
   RTC_CHECK(rtcp_transceiver_);
diff --git a/modules/rtp_rtcp/source/rtcp_transceiver.h b/modules/rtp_rtcp/source/rtcp_transceiver.h
index 16fd5a7..8b70c6d 100644
--- a/modules/rtp_rtcp/source/rtcp_transceiver.h
+++ b/modules/rtp_rtcp/source/rtcp_transceiver.h
@@ -79,6 +79,8 @@
   // Returns ssrc to put as sender ssrc into rtcp::TransportFeedback.
   uint32_t SSRC() const override;
   bool SendFeedbackPacket(const rtcp::TransportFeedback& packet) override;
+  bool SendNetworkStateEstimatePacket(
+      const rtcp::RemoteEstimate& packet) override;
 
   // Reports missing packets, https://tools.ietf.org/html/rfc4585#section-6.2.1
   void SendNack(uint32_t ssrc, std::vector<uint16_t> sequence_numbers);
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
index 1fdb356..9252de3 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.cc
@@ -676,6 +676,11 @@
   return rtcp_sender_.SendFeedbackPacket(packet);
 }
 
+bool ModuleRtpRtcpImpl::SendNetworkStateEstimatePacket(
+    const rtcp::RemoteEstimate& packet) {
+  return rtcp_sender_.SendNetworkStateEstimatePacket(packet);
+}
+
 int32_t ModuleRtpRtcpImpl::SendLossNotification(uint16_t last_decoded_seq_num,
                                                 uint16_t last_received_seq_num,
                                                 bool decodability_flag,
diff --git a/modules/rtp_rtcp/source/rtp_rtcp_impl.h b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
index 8cb01be..1c8ef9c 100644
--- a/modules/rtp_rtcp/source/rtp_rtcp_impl.h
+++ b/modules/rtp_rtcp/source/rtp_rtcp_impl.h
@@ -245,6 +245,8 @@
   void SetReportBlockDataObserver(ReportBlockDataObserver* observer) override;
 
   bool SendFeedbackPacket(const rtcp::TransportFeedback& packet) override;
+  bool SendNetworkStateEstimatePacket(
+      const rtcp::RemoteEstimate& packet) override;
   // (APP) Application specific data.
   int32_t SetRTCPApplicationSpecificData(uint8_t sub_type,
                                          uint32_t name,
diff --git a/pc/channel.cc b/pc/channel.cc
index 5483963..caf8c93 100644
--- a/pc/channel.cc
+++ b/pc/channel.cc
@@ -111,6 +111,7 @@
     params->extensions = extensions;
   }
   params->rtcp.reduced_size = desc->rtcp_reduced_size();
+  params->rtcp.remote_estimate = desc->remote_estimate();
 }
 
 template <class Codec>
diff --git a/pc/media_session.cc b/pc/media_session.cc
index fff2a40..f4f1554 100644
--- a/pc/media_session.cc
+++ b/pc/media_session.cc
@@ -1159,6 +1159,8 @@
     answer->set_rtcp_reduced_size(offer->rtcp_reduced_size());
   }
 
+  answer->set_remote_estimate(offer->remote_estimate());
+
   if (sdes_policy != SEC_DISABLED) {
     CryptoParams crypto;
     if (SelectCrypto(offer, bundle_enabled, session_options.crypto_options,
diff --git a/pc/session_description.h b/pc/session_description.h
index bc84740..229d331 100644
--- a/pc/session_description.h
+++ b/pc/session_description.h
@@ -113,6 +113,11 @@
     rtcp_reduced_size_ = reduced_size;
   }
 
+  virtual bool remote_estimate() const { return remote_estimate_; }
+  virtual void set_remote_estimate(bool remote_estimate) {
+    remote_estimate_ = remote_estimate;
+  }
+
   virtual int bandwidth() const { return bandwidth_; }
   virtual void set_bandwidth(int bandwidth) { bandwidth_ = bandwidth; }
 
@@ -245,6 +250,7 @@
  protected:
   bool rtcp_mux_ = false;
   bool rtcp_reduced_size_ = false;
+  bool remote_estimate_ = false;
   int bandwidth_ = kAutoBandwidth;
   std::string protocol_;
   std::vector<CryptoParams> cryptos_;
diff --git a/pc/webrtc_sdp.cc b/pc/webrtc_sdp.cc
index a022e56..3e0cf42 100644
--- a/pc/webrtc_sdp.cc
+++ b/pc/webrtc_sdp.cc
@@ -185,6 +185,8 @@
 static const char kAttributeXGoogleFlag[] = "x-google-flag";
 static const char kValueConference[] = "conference";
 
+static const char kAttributeRtcpRemoteEstimate[] = "remote-net-estimate";
+
 // Candidate
 static const char kCandidateHost[] = "host";
 static const char kCandidateSrflx[] = "srflx";
@@ -1666,6 +1668,11 @@
     AddLine(os.str(), message);
   }
 
+  if (media_desc->remote_estimate()) {
+    InitAttrLine(kAttributeRtcpRemoteEstimate, &os);
+    AddLine(os.str(), message);
+  }
+
   // RFC 4568
   // a=crypto:<tag> <crypto-suite> <key-params> [<session-params>]
   for (const CryptoParams& crypto_params : media_desc->cryptos()) {
@@ -3238,6 +3245,8 @@
         media_desc->set_rtcp_mux(true);
       } else if (HasAttribute(line, kAttributeRtcpReducedSize)) {
         media_desc->set_rtcp_reduced_size(true);
+      } else if (HasAttribute(line, kAttributeRtcpRemoteEstimate)) {
+        media_desc->set_remote_estimate(true);
       } else if (HasAttribute(line, kAttributeSsrcGroup)) {
         if (!ParseSsrcGroupAttribute(line, &ssrc_groups, error)) {
           return false;