Make NetworkStateEstimator injectable in RemoteBitrateEstimator

The NetworkStateEstimator is updated on every incoming RTP packet if available.

A rtcp::RemoteEstimate packet is sent every time a rtcp::TransportFeedback packet is sent.

BUG=webrtc:10742

Change-Id: I4cd8e9d85d35faf76aeefd2e26c2a9fe1a62ca3b
Reviewed-on: https://webrtc-review.googlesource.com/c/src/+/152161
Commit-Queue: Per Kjellander <perkj@webrtc.org>
Reviewed-by: Sebastian Jansson <srte@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#29143}
diff --git a/api/rtp_headers.h b/api/rtp_headers.h
index 4415bd3..44d2dee 100644
--- a/api/rtp_headers.h
+++ b/api/rtp_headers.h
@@ -101,10 +101,19 @@
   Timestamp GetAbsoluteSendTimestamp() const {
     RTC_DCHECK(hasAbsoluteSendTime);
     RTC_DCHECK(absoluteSendTime < (1ul << 24));
-    return Timestamp::us((absoluteSendTime * 1000000L) /
+    return Timestamp::us((absoluteSendTime * 1000000ll) /
                          (1 << kAbsSendTimeFraction));
   }
 
+  TimeDelta GetAbsoluteSendTimeDelta(uint32_t previous_sendtime) const {
+    RTC_DCHECK(hasAbsoluteSendTime);
+    RTC_DCHECK(absoluteSendTime < (1ul << 24));
+    RTC_DCHECK(previous_sendtime < (1ul << 24));
+    int32_t delta =
+        static_cast<int32_t>((absoluteSendTime - previous_sendtime) << 8) >> 8;
+    return TimeDelta::us((delta * 1000000ll) / (1 << kAbsSendTimeFraction));
+  }
+
   bool hasTransmissionTimeOffset;
   int32_t transmissionTimeOffset;
   bool hasAbsoluteSendTime;
diff --git a/api/transport/BUILD.gn b/api/transport/BUILD.gn
index 966cb7d..1b48555 100644
--- a/api/transport/BUILD.gn
+++ b/api/transport/BUILD.gn
@@ -110,3 +110,16 @@
     ]
   }
 }
+
+if (rtc_include_tests) {
+  rtc_source_set("mock_network_control") {
+    testonly = true
+    sources = [
+      "test/mock_network_control.h",
+    ]
+    deps = [
+      ":network_control",
+      "../../test:test_support",
+    ]
+  }
+}
diff --git a/api/transport/network_control.h b/api/transport/network_control.h
index 8efb1fc..8b2958c 100644
--- a/api/transport/network_control.h
+++ b/api/transport/network_control.h
@@ -110,7 +110,11 @@
   // Gets the current best estimate according to the estimator.
   virtual absl::optional<NetworkStateEstimate> GetCurrentEstimate() = 0;
   // Called with per packet feedback regarding receive time.
+  // Used when the NetworkStateEstimator runs in the sending endpoint.
   virtual void OnTransportPacketsFeedback(const TransportPacketsFeedback&) = 0;
+  // Called with per packet feedback regarding receive time.
+  // Used when the NetworkStateEstimator runs in the receiving endpoint.
+  virtual void OnReceivedPacket(const PacketResult&) {}
   // Called when the receiving or sending endpoint changes address.
   virtual void OnRouteChange(const NetworkRouteChange&) = 0;
   virtual ~NetworkStateEstimator() = default;
diff --git a/api/transport/test/mock_network_control.h b/api/transport/test/mock_network_control.h
new file mode 100644
index 0000000..54a416c
--- /dev/null
+++ b/api/transport/test/mock_network_control.h
@@ -0,0 +1,30 @@
+/*
+ *  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 API_TRANSPORT_TEST_MOCK_NETWORK_CONTROL_H_
+#define API_TRANSPORT_TEST_MOCK_NETWORK_CONTROL_H_
+
+#include "api/transport/network_control.h"
+#include "test/gmock.h"
+
+namespace webrtc {
+
+class MockNetworkStateEstimator : public NetworkStateEstimator {
+ public:
+  MOCK_METHOD0(GetCurrentEstimate, absl::optional<NetworkStateEstimate>());
+  MOCK_METHOD1(OnTransportPacketsFeedback,
+               void(const TransportPacketsFeedback&));
+  MOCK_METHOD1(OnReceivedPacket, void(const PacketResult&));
+  MOCK_METHOD1(OnRouteChange, void(const NetworkRouteChange&));
+};
+
+}  // namespace webrtc
+
+#endif  // API_TRANSPORT_TEST_MOCK_NETWORK_CONTROL_H_
diff --git a/modules/congestion_controller/BUILD.gn b/modules/congestion_controller/BUILD.gn
index bb13093..3f2f5d9 100644
--- a/modules/congestion_controller/BUILD.gn
+++ b/modules/congestion_controller/BUILD.gn
@@ -27,6 +27,7 @@
   deps = [
     "..:module_api",
     "../../api/transport:field_trial_based_config",
+    "../../api/transport:network_control",
     "../pacing",
     "../remote_bitrate_estimator",
     "../rtp_rtcp:rtp_rtcp_format",
diff --git a/modules/congestion_controller/include/receive_side_congestion_controller.h b/modules/congestion_controller/include/receive_side_congestion_controller.h
index dd8a0cf..4f13b4d 100644
--- a/modules/congestion_controller/include/receive_side_congestion_controller.h
+++ b/modules/congestion_controller/include/receive_side_congestion_controller.h
@@ -15,6 +15,7 @@
 #include <vector>
 
 #include "api/transport/field_trial_based_config.h"
+#include "api/transport/network_control.h"
 #include "modules/include/module.h"
 #include "modules/remote_bitrate_estimator/remote_estimator_proxy.h"
 #include "rtc_base/constructor_magic.h"
@@ -33,6 +34,10 @@
                                         public Module {
  public:
   ReceiveSideCongestionController(Clock* clock, PacketRouter* packet_router);
+  ReceiveSideCongestionController(
+      Clock* clock,
+      PacketRouter* packet_router,
+      NetworkStateEstimator* network_state_estimator);
 
   ~ReceiveSideCongestionController() override {}
 
diff --git a/modules/congestion_controller/receive_side_congestion_controller.cc b/modules/congestion_controller/receive_side_congestion_controller.cc
index 9f674d1..628981f 100644
--- a/modules/congestion_controller/receive_side_congestion_controller.cc
+++ b/modules/congestion_controller/receive_side_congestion_controller.cc
@@ -121,18 +121,24 @@
 ReceiveSideCongestionController::ReceiveSideCongestionController(
     Clock* clock,
     PacketRouter* packet_router)
+    : ReceiveSideCongestionController(clock, packet_router, nullptr) {}
+
+ReceiveSideCongestionController::ReceiveSideCongestionController(
+    Clock* clock,
+    PacketRouter* packet_router,
+    NetworkStateEstimator* network_state_estimator)
     : remote_bitrate_estimator_(packet_router, clock),
-      remote_estimator_proxy_(clock, packet_router, &field_trial_config_) {}
+      remote_estimator_proxy_(clock,
+                              packet_router,
+                              &field_trial_config_,
+                              network_state_estimator) {}
 
 void ReceiveSideCongestionController::OnReceivedPacket(
     int64_t arrival_time_ms,
     size_t payload_size,
     const RTPHeader& header) {
-  // Send-side BWE.
-  if (header.extension.hasTransportSequenceNumber) {
-    remote_estimator_proxy_.IncomingPacket(arrival_time_ms, payload_size,
-                                           header);
-  } else {
+  remote_estimator_proxy_.IncomingPacket(arrival_time_ms, payload_size, header);
+  if (!header.extension.hasTransportSequenceNumber) {
     // Receive-side BWE.
     remote_bitrate_estimator_.IncomingPacket(arrival_time_ms, payload_size,
                                              header);
diff --git a/modules/pacing/packet_router.cc b/modules/pacing/packet_router.cc
index b0069f0..31b1ef1 100644
--- a/modules/pacing/packet_router.cc
+++ b/modules/pacing/packet_router.cc
@@ -292,6 +292,17 @@
   return false;
 }
 
+void PacketRouter::SendNetworkStateEstimatePacket(
+    rtcp::RemoteEstimate* packet) {
+  rtc::CritScope cs(&modules_crit_);
+  for (auto* rtcp_sender : rtcp_feedback_senders_) {
+    packet->SetSsrc(rtcp_sender->SSRC());
+    if (rtcp_sender->SendNetworkStateEstimatePacket(*packet)) {
+      break;
+    }
+  }
+}
+
 void PacketRouter::AddRembModuleCandidate(
     RtcpFeedbackSenderInterface* candidate_module,
     bool media_sender) {
diff --git a/modules/pacing/packet_router.h b/modules/pacing/packet_router.h
index 07ef4b3..be535fe 100644
--- a/modules/pacing/packet_router.h
+++ b/modules/pacing/packet_router.h
@@ -78,6 +78,8 @@
 
   // Send transport feedback packet to send-side.
   bool SendTransportFeedback(rtcp::TransportFeedback* packet) override;
+  // Send RemoteEstimate packet to send-side.
+  void SendNetworkStateEstimatePacket(rtcp::RemoteEstimate* packet) override;
 
  private:
   RtpRtcp* FindRtpModule(uint32_t ssrc)
diff --git a/modules/remote_bitrate_estimator/BUILD.gn b/modules/remote_bitrate_estimator/BUILD.gn
index d6fe053..cec58a1 100644
--- a/modules/remote_bitrate_estimator/BUILD.gn
+++ b/modules/remote_bitrate_estimator/BUILD.gn
@@ -114,6 +114,7 @@
       "..:module_api_public",
       "../..:webrtc_common",
       "../../api/transport:field_trial_based_config",
+      "../../api/transport:mock_network_control",
       "../../rtc_base",
       "../../rtc_base:checks",
       "../../rtc_base:rtc_base_approved",
diff --git a/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h b/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h
index 1546f30..a6bba70 100644
--- a/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h
+++ b/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h
@@ -43,6 +43,7 @@
  public:
   virtual ~TransportFeedbackSenderInterface() = default;
   virtual bool SendTransportFeedback(rtcp::TransportFeedback* packet) = 0;
+  virtual void SendNetworkStateEstimatePacket(rtcp::RemoteEstimate* packet) = 0;
 };
 
 // TODO(holmer): Remove when all implementations have been updated.
diff --git a/modules/remote_bitrate_estimator/remote_estimator_proxy.cc b/modules/remote_bitrate_estimator/remote_estimator_proxy.cc
index 9d9be06..e9b6100 100644
--- a/modules/remote_bitrate_estimator/remote_estimator_proxy.cc
+++ b/modules/remote_bitrate_estimator/remote_estimator_proxy.cc
@@ -32,15 +32,19 @@
 RemoteEstimatorProxy::RemoteEstimatorProxy(
     Clock* clock,
     TransportFeedbackSenderInterface* feedback_sender,
-    const WebRtcKeyValueConfig* key_value_config)
+    const WebRtcKeyValueConfig* key_value_config,
+    NetworkStateEstimator* network_state_estimator)
     : clock_(clock),
       feedback_sender_(feedback_sender),
       send_config_(key_value_config),
       last_process_time_ms_(-1),
+      network_state_estimator_(network_state_estimator),
       media_ssrc_(0),
       feedback_packet_count_(0),
       send_interval_ms_(send_config_.default_interval->ms()),
-      send_periodic_feedback_(true) {
+      send_periodic_feedback_(true),
+      previous_abs_send_time_(0),
+      abs_send_timestamp_(clock->CurrentTime()) {
   RTC_LOG(LS_INFO)
       << "Maximum interval between transport feedback RTCP messages (ms): "
       << send_config_.max_interval->ms();
@@ -51,60 +55,74 @@
 void RemoteEstimatorProxy::IncomingPacket(int64_t arrival_time_ms,
                                           size_t payload_size,
                                           const RTPHeader& header) {
-  if (!header.extension.hasTransportSequenceNumber) {
-    RTC_LOG(LS_WARNING)
-        << "RemoteEstimatorProxy: Incoming packet "
-           "is missing the transport sequence number extension!";
-    return;
-  }
   if (arrival_time_ms < 0 || arrival_time_ms > kMaxTimeMs) {
     RTC_LOG(LS_WARNING) << "Arrival time out of bounds: " << arrival_time_ms;
     return;
   }
   rtc::CritScope cs(&lock_);
   media_ssrc_ = header.ssrc;
+  int64_t seq = 0;
 
-  int64_t seq = unwrapper_.Unwrap(header.extension.transportSequenceNumber);
+  if (header.extension.hasTransportSequenceNumber) {
+    seq = unwrapper_.Unwrap(header.extension.transportSequenceNumber);
 
-  if (send_periodic_feedback_) {
-    if (periodic_window_start_seq_ &&
-        packet_arrival_times_.lower_bound(*periodic_window_start_seq_) ==
-            packet_arrival_times_.end()) {
-      // Start new feedback packet, cull old packets.
-      for (auto it = packet_arrival_times_.begin();
-           it != packet_arrival_times_.end() && it->first < seq &&
-           arrival_time_ms - it->second >= send_config_.back_window->ms();) {
-        it = packet_arrival_times_.erase(it);
+    if (send_periodic_feedback_) {
+      if (periodic_window_start_seq_ &&
+          packet_arrival_times_.lower_bound(*periodic_window_start_seq_) ==
+              packet_arrival_times_.end()) {
+        // Start new feedback packet, cull old packets.
+        for (auto it = packet_arrival_times_.begin();
+             it != packet_arrival_times_.end() && it->first < seq &&
+             arrival_time_ms - it->second >= send_config_.back_window->ms();) {
+          it = packet_arrival_times_.erase(it);
+        }
+      }
+      if (!periodic_window_start_seq_ || seq < *periodic_window_start_seq_) {
+        periodic_window_start_seq_ = seq;
       }
     }
-    if (!periodic_window_start_seq_ || seq < *periodic_window_start_seq_) {
-      periodic_window_start_seq_ = seq;
+
+    // We are only interested in the first time a packet is received.
+    if (packet_arrival_times_.find(seq) != packet_arrival_times_.end())
+      return;
+
+    packet_arrival_times_[seq] = arrival_time_ms;
+
+    // Limit the range of sequence numbers to send feedback for.
+    auto first_arrival_time_to_keep = packet_arrival_times_.lower_bound(
+        packet_arrival_times_.rbegin()->first - kMaxNumberOfPackets);
+    if (first_arrival_time_to_keep != packet_arrival_times_.begin()) {
+      packet_arrival_times_.erase(packet_arrival_times_.begin(),
+                                  first_arrival_time_to_keep);
+      if (send_periodic_feedback_) {
+        // |packet_arrival_times_| cannot be empty since we just added one
+        // element and the last element is not deleted.
+        RTC_DCHECK(!packet_arrival_times_.empty());
+        periodic_window_start_seq_ = packet_arrival_times_.begin()->first;
+      }
+    }
+
+    if (header.extension.feedback_request) {
+      // Send feedback packet immediately.
+      SendFeedbackOnRequest(seq, header.extension.feedback_request.value());
     }
   }
 
-  // We are only interested in the first time a packet is received.
-  if (packet_arrival_times_.find(seq) != packet_arrival_times_.end())
-    return;
-
-  packet_arrival_times_[seq] = arrival_time_ms;
-
-  // Limit the range of sequence numbers to send feedback for.
-  auto first_arrival_time_to_keep = packet_arrival_times_.lower_bound(
-      packet_arrival_times_.rbegin()->first - kMaxNumberOfPackets);
-  if (first_arrival_time_to_keep != packet_arrival_times_.begin()) {
-    packet_arrival_times_.erase(packet_arrival_times_.begin(),
-                                first_arrival_time_to_keep);
-    if (send_periodic_feedback_) {
-      // |packet_arrival_times_| cannot be empty since we just added one element
-      // and the last element is not deleted.
-      RTC_DCHECK(!packet_arrival_times_.empty());
-      periodic_window_start_seq_ = packet_arrival_times_.begin()->first;
-    }
-  }
-
-  if (header.extension.feedback_request) {
-    // Send feedback packet immediately.
-    SendFeedbackOnRequest(seq, header.extension.feedback_request.value());
+  if (network_state_estimator_ && header.extension.hasAbsoluteSendTime) {
+    PacketResult packet_result;
+    packet_result.receive_time = Timestamp::ms(arrival_time_ms);
+    // Ignore reordering of packets and assume they have approximately the same
+    // send time.
+    abs_send_timestamp_ += std::max(
+        header.extension.GetAbsoluteSendTimeDelta(previous_abs_send_time_),
+        TimeDelta::ms(0));
+    previous_abs_send_time_ = header.extension.absoluteSendTime;
+    packet_result.sent_packet.send_time = abs_send_timestamp_;
+    // TODO(webrtc:10742): Take IP header and transport overhead into account.
+    packet_result.sent_packet.size =
+        DataSize::bytes(header.headerLength + payload_size);
+    packet_result.sent_packet.sequence_number = seq;
+    network_state_estimator_->OnReceivedPacket(packet_result);
   }
 }
 
@@ -169,6 +187,16 @@
   if (!periodic_window_start_seq_)
     return;
 
+  if (network_state_estimator_) {
+    absl::optional<NetworkStateEstimate> state_estimate =
+        network_state_estimator_->GetCurrentEstimate();
+    if (state_estimate) {
+      rtcp::RemoteEstimate rtcp_estimate;
+      rtcp_estimate.SetEstimate(state_estimate.value());
+      feedback_sender_->SendNetworkStateEstimatePacket(&rtcp_estimate);
+    }
+  }
+
   for (auto begin_iterator =
            packet_arrival_times_.lower_bound(*periodic_window_start_seq_);
        begin_iterator != packet_arrival_times_.cend();
diff --git a/modules/remote_bitrate_estimator/remote_estimator_proxy.h b/modules/remote_bitrate_estimator/remote_estimator_proxy.h
index d750cc3..a772b58 100644
--- a/modules/remote_bitrate_estimator/remote_estimator_proxy.h
+++ b/modules/remote_bitrate_estimator/remote_estimator_proxy.h
@@ -14,6 +14,7 @@
 #include <map>
 #include <vector>
 
+#include "api/transport/network_control.h"
 #include "api/transport/webrtc_key_value_config.h"
 #include "modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
 #include "rtc_base/critical_section.h"
@@ -36,7 +37,8 @@
  public:
   RemoteEstimatorProxy(Clock* clock,
                        TransportFeedbackSenderInterface* feedback_sender,
-                       const WebRtcKeyValueConfig* key_value_config);
+                       const WebRtcKeyValueConfig* key_value_config,
+                       NetworkStateEstimator* network_state_estimator);
   ~RemoteEstimatorProxy() override;
 
   void IncomingPacket(int64_t arrival_time_ms,
@@ -90,7 +92,9 @@
   int64_t last_process_time_ms_;
 
   rtc::CriticalSection lock_;
-
+  //  |network_state_estimator_| may be null.
+  NetworkStateEstimator* const network_state_estimator_
+      RTC_PT_GUARDED_BY(&lock_);
   uint32_t media_ssrc_ RTC_GUARDED_BY(&lock_);
   uint8_t feedback_packet_count_ RTC_GUARDED_BY(&lock_);
   SeqNumUnwrapper<uint16_t> unwrapper_ RTC_GUARDED_BY(&lock_);
@@ -99,6 +103,10 @@
   std::map<int64_t, int64_t> packet_arrival_times_ RTC_GUARDED_BY(&lock_);
   int64_t send_interval_ms_ RTC_GUARDED_BY(&lock_);
   bool send_periodic_feedback_ RTC_GUARDED_BY(&lock_);
+
+  // Unwraps absolute send times.
+  uint32_t previous_abs_send_time_ RTC_GUARDED_BY(&lock_);
+  Timestamp abs_send_timestamp_ RTC_GUARDED_BY(&lock_);
 };
 
 }  // namespace webrtc
diff --git a/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc b/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc
index 6962723..30e6ef4 100644
--- a/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc
+++ b/modules/remote_bitrate_estimator/remote_estimator_proxy_unittest.cc
@@ -11,6 +11,7 @@
 #include "modules/remote_bitrate_estimator/remote_estimator_proxy.h"
 
 #include "api/transport/field_trial_based_config.h"
+#include "api/transport/test/mock_network_control.h"
 #include "modules/pacing/packet_router.h"
 #include "modules/rtp_rtcp/source/rtcp_packet/transport_feedback.h"
 #include "system_wrappers/include/clock.h"
@@ -61,27 +62,43 @@
  public:
   MOCK_METHOD1(SendTransportFeedback,
                bool(rtcp::TransportFeedback* feedback_packet));
+  MOCK_METHOD1(SendNetworkStateEstimatePacket,
+               void(rtcp::RemoteEstimate* packet));
 };
 
 class RemoteEstimatorProxyTest : public ::testing::Test {
  public:
   RemoteEstimatorProxyTest()
-      : clock_(0), proxy_(&clock_, &router_, &field_trial_config_) {}
+      : clock_(0),
+        proxy_(&clock_,
+               &router_,
+               &field_trial_config_,
+               &network_state_estimator_) {}
 
  protected:
-  void IncomingPacket(uint16_t seq,
-                      int64_t time_ms,
-                      absl::optional<FeedbackRequest> feedback_request) {
-    RTPHeader header;
-    header.extension.hasTransportSequenceNumber = true;
-    header.extension.transportSequenceNumber = seq;
-    header.extension.feedback_request = feedback_request;
-    header.ssrc = kMediaSsrc;
-    proxy_.IncomingPacket(time_ms, kDefaultPacketSize, header);
+  void IncomingPacket(
+      uint16_t seq,
+      int64_t time_ms,
+      absl::optional<FeedbackRequest> feedback_request = absl::nullopt) {
+    proxy_.IncomingPacket(time_ms, kDefaultPacketSize,
+                          CreateHeader(seq, feedback_request, absl::nullopt));
   }
 
-  void IncomingPacket(uint16_t seq, int64_t time_ms) {
-    IncomingPacket(seq, time_ms, absl::nullopt);
+  RTPHeader CreateHeader(absl::optional<uint16_t> transport_sequence,
+                         absl::optional<FeedbackRequest> feedback_request,
+                         absl::optional<uint32_t> absolute_send_time) {
+    RTPHeader header;
+    if (transport_sequence) {
+      header.extension.hasTransportSequenceNumber = true;
+      header.extension.transportSequenceNumber = transport_sequence.value();
+    }
+    header.extension.feedback_request = feedback_request;
+    if (absolute_send_time) {
+      header.extension.hasAbsoluteSendTime = true;
+      header.extension.absoluteSendTime = absolute_send_time.value();
+    }
+    header.ssrc = kMediaSsrc;
+    return header;
   }
 
   void Process() {
@@ -92,6 +109,7 @@
   FieldTrialBasedConfig field_trial_config_;
   SimulatedClock clock_;
   ::testing::StrictMock<MockTransportFeedbackSender> router_;
+  ::testing::NiceMock<MockNetworkStateEstimator> network_state_estimator_;
   RemoteEstimatorProxy proxy_;
 };
 
@@ -499,5 +517,74 @@
                  kFivePacketsFeedbackRequest);
 }
 
+TEST_F(RemoteEstimatorProxyTest, ReportsIncomingPacketToNetworkStateEstimator) {
+  Timestamp first_send_timestamp = Timestamp::ms(0);
+  EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_))
+      .WillOnce(Invoke([&first_send_timestamp](const PacketResult& packet) {
+        EXPECT_EQ(packet.receive_time, Timestamp::ms(kBaseTimeMs));
+        first_send_timestamp = packet.sent_packet.send_time;
+      }));
+  // Incoming packet with abs sendtime but without transport sequence number.
+  proxy_.IncomingPacket(
+      kBaseTimeMs, kDefaultPacketSize,
+      CreateHeader(absl::nullopt, absl::nullopt,
+                   AbsoluteSendTime::MsTo24Bits(kBaseTimeMs)));
+
+  // Expect packet with older abs send time to be treated as sent at the same
+  // time as the previous packet due to reordering.
+  EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_))
+      .WillOnce(Invoke([&first_send_timestamp](const PacketResult& packet) {
+        EXPECT_EQ(packet.receive_time, Timestamp::ms(kBaseTimeMs));
+        EXPECT_EQ(packet.sent_packet.send_time, first_send_timestamp);
+      }));
+  proxy_.IncomingPacket(
+      kBaseTimeMs, kDefaultPacketSize,
+      CreateHeader(absl::nullopt, absl::nullopt,
+                   AbsoluteSendTime::MsTo24Bits(kBaseTimeMs - 12)));
+}
+
+TEST_F(RemoteEstimatorProxyTest, IncomingPacketHandlesWrapInAbsSendTime) {
+  // abs send time use 24bit precision.
+  const uint32_t kFirstAbsSendTime =
+      AbsoluteSendTime::MsTo24Bits((1 << 24) - 30);
+  // Second abs send time has wrapped.
+  const uint32_t kSecondAbsSendTime = AbsoluteSendTime::MsTo24Bits((1 << 24));
+  const TimeDelta kExpectedAbsSendTimeDelta = TimeDelta::ms(30);
+
+  Timestamp first_send_timestamp = Timestamp::ms(0);
+  EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_))
+      .WillOnce(Invoke([&first_send_timestamp](const PacketResult& packet) {
+        EXPECT_EQ(packet.receive_time, Timestamp::ms(kBaseTimeMs));
+        first_send_timestamp = packet.sent_packet.send_time;
+      }));
+  proxy_.IncomingPacket(
+      kBaseTimeMs, kDefaultPacketSize,
+      CreateHeader(kBaseSeq, absl::nullopt, kFirstAbsSendTime));
+
+  EXPECT_CALL(network_state_estimator_, OnReceivedPacket(_))
+      .WillOnce(Invoke([first_send_timestamp,
+                        kExpectedAbsSendTimeDelta](const PacketResult& packet) {
+        EXPECT_EQ(packet.receive_time, Timestamp::ms(kBaseTimeMs + 123));
+        EXPECT_EQ(packet.sent_packet.send_time.ms(),
+                  (first_send_timestamp + kExpectedAbsSendTimeDelta).ms());
+      }));
+  proxy_.IncomingPacket(
+      kBaseTimeMs + 123, kDefaultPacketSize,
+      CreateHeader(kBaseSeq + 1, absl::nullopt, kSecondAbsSendTime));
+}
+
+TEST_F(RemoteEstimatorProxyTest, SendTransportFeedbackAndNetworkStateUpdate) {
+  proxy_.IncomingPacket(
+      kBaseTimeMs, kDefaultPacketSize,
+      CreateHeader(kBaseSeq, absl::nullopt,
+                   AbsoluteSendTime::MsTo24Bits(kBaseTimeMs - 1)));
+
+  EXPECT_CALL(router_, SendTransportFeedback(_));
+  EXPECT_CALL(network_state_estimator_, GetCurrentEstimate())
+      .WillOnce(Return(NetworkStateEstimate()));
+  EXPECT_CALL(router_, SendNetworkStateEstimatePacket(_));
+  Process();
+}
+
 }  // namespace
 }  // namespace webrtc