Adding RttStats class for BBR.

This is part of a series of CLs adding a network controller based on
the BBR congestion control method. The code is based on the QUIC BBR
implementation in Chromium.

Bug: webrtc:8415
Change-Id: Ib952b9c3f1cdd0741330a5ae95aebcf2488f7f1d
Reviewed-on: https://webrtc-review.googlesource.com/63644
Commit-Queue: Sebastian Jansson <srte@webrtc.org>
Reviewed-by: Philip Eliasson <philipel@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#22581}
diff --git a/modules/congestion_controller/BUILD.gn b/modules/congestion_controller/BUILD.gn
index a88f5fb..3028e18 100644
--- a/modules/congestion_controller/BUILD.gn
+++ b/modules/congestion_controller/BUILD.gn
@@ -183,6 +183,7 @@
       "../pacing:pacing",
       "../remote_bitrate_estimator:remote_bitrate_estimator",
       "../rtp_rtcp:rtp_rtcp_format",
+      "bbr:bbr_unittests",
       "goog_cc:goog_cc_unittests",
       "network_control:network_control_unittests",
       "rtp:congestion_controller_unittests",
diff --git a/modules/congestion_controller/bbr/BUILD.gn b/modules/congestion_controller/bbr/BUILD.gn
new file mode 100644
index 0000000..ce39abd
--- /dev/null
+++ b/modules/congestion_controller/bbr/BUILD.gn
@@ -0,0 +1,34 @@
+# Copyright (c) 2018 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.
+
+import("../../../webrtc.gni")
+
+rtc_source_set("rtt_stats") {
+  sources = [
+    "rtt_stats.cc",
+    "rtt_stats.h",
+  ]
+  deps = [
+    "../../../rtc_base:rtc_base_approved",
+    "../network_control",
+  ]
+}
+
+if (rtc_include_tests) {
+  rtc_source_set("bbr_unittests") {
+    testonly = true
+    sources = [
+      "rtt_stats_unittest.cc",
+    ]
+    deps = [
+      ":rtt_stats",
+      "../../../test:test_support",
+      "../network_control",
+    ]
+  }
+}
diff --git a/modules/congestion_controller/bbr/rtt_stats.cc b/modules/congestion_controller/bbr/rtt_stats.cc
new file mode 100644
index 0000000..a7cfea8
--- /dev/null
+++ b/modules/congestion_controller/bbr/rtt_stats.cc
@@ -0,0 +1,96 @@
+/*
+ *  Copyright (c) 2018 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/congestion_controller/bbr/rtt_stats.h"
+
+#include <cstdlib>
+
+#include "modules/congestion_controller/network_control/include/network_units.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace bbr {
+namespace {
+
+// Default initial rtt used before any samples are received.
+const int kInitialRttMs = 100;
+const double kAlpha = 0.125;
+const double kOneMinusAlpha = (1 - kAlpha);
+const double kBeta = 0.25;
+const double kOneMinusBeta = (1 - kBeta);
+const int64_t kNumMicrosPerMilli = 1000;
+}  // namespace
+
+RttStats::RttStats()
+    : latest_rtt_(TimeDelta::Zero()),
+      min_rtt_(TimeDelta::Zero()),
+      smoothed_rtt_(TimeDelta::Zero()),
+      previous_srtt_(TimeDelta::Zero()),
+      mean_deviation_(TimeDelta::Zero()),
+      initial_rtt_us_(kInitialRttMs * kNumMicrosPerMilli) {}
+
+void RttStats::ExpireSmoothedMetrics() {
+  mean_deviation_ =
+      std::max(mean_deviation_, (smoothed_rtt_ - latest_rtt_).Abs());
+  smoothed_rtt_ = std::max(smoothed_rtt_, latest_rtt_);
+}
+
+// Updates the RTT based on a new sample.
+void RttStats::UpdateRtt(TimeDelta send_delta,
+                         TimeDelta ack_delay,
+                         Timestamp now) {
+  if (send_delta.IsInfinite() || send_delta <= TimeDelta::Zero()) {
+    RTC_LOG(LS_WARNING) << "Ignoring measured send_delta, because it's is "
+                        << "either infinite, zero, or negative.  send_delta = "
+                        << send_delta;
+    return;
+  }
+
+  // Update min_rtt_ first. min_rtt_ does not use an rtt_sample corrected for
+  // ack_delay but the raw observed send_delta, since poor clock granularity at
+  // the client may cause a high ack_delay to result in underestimation of the
+  // min_rtt_.
+  if (min_rtt_.IsZero() || min_rtt_ > send_delta) {
+    min_rtt_ = send_delta;
+  }
+
+  // Correct for ack_delay if information received from the peer results in a
+  // positive RTT sample. Otherwise, we use the send_delta as a reasonable
+  // measure for smoothed_rtt.
+  TimeDelta rtt_sample = send_delta;
+  previous_srtt_ = smoothed_rtt_;
+
+  if (rtt_sample > ack_delay) {
+    rtt_sample = rtt_sample - ack_delay;
+  }
+  latest_rtt_ = rtt_sample;
+  // First time call.
+  if (smoothed_rtt_.IsZero()) {
+    smoothed_rtt_ = rtt_sample;
+    mean_deviation_ = rtt_sample / 2;
+  } else {
+    mean_deviation_ = kOneMinusBeta * mean_deviation_ +
+                      kBeta * (smoothed_rtt_ - rtt_sample).Abs();
+    smoothed_rtt_ = kOneMinusAlpha * smoothed_rtt_ + kAlpha * rtt_sample;
+    RTC_LOG(LS_VERBOSE) << " smoothed_rtt(us):" << smoothed_rtt_.us()
+                        << " mean_deviation(us):" << mean_deviation_.us();
+  }
+}
+
+void RttStats::OnConnectionMigration() {
+  latest_rtt_ = TimeDelta::Zero();
+  min_rtt_ = TimeDelta::Zero();
+  smoothed_rtt_ = TimeDelta::Zero();
+  mean_deviation_ = TimeDelta::Zero();
+  initial_rtt_us_ = kInitialRttMs * kNumMicrosPerMilli;
+}
+
+}  // namespace bbr
+}  // namespace webrtc
diff --git a/modules/congestion_controller/bbr/rtt_stats.h b/modules/congestion_controller/bbr/rtt_stats.h
new file mode 100644
index 0000000..26b843d
--- /dev/null
+++ b/modules/congestion_controller/bbr/rtt_stats.h
@@ -0,0 +1,88 @@
+/*
+ *  Copyright (c) 2018 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.
+ */
+// A convenience class to store RTT samples and calculate smoothed RTT.
+// From the Quic BBR implementation in Chromium.
+
+#ifndef MODULES_CONGESTION_CONTROLLER_BBR_RTT_STATS_H_
+#define MODULES_CONGESTION_CONTROLLER_BBR_RTT_STATS_H_
+
+#include <algorithm>
+#include <cstdint>
+
+#include "modules/congestion_controller/network_control/include/network_units.h"
+#include "rtc_base/constructormagic.h"
+#include "rtc_base/logging.h"
+
+namespace webrtc {
+namespace bbr {
+
+class RttStats {
+ public:
+  RttStats();
+
+  // Updates the RTT from an incoming ack which is received |send_delta| after
+  // the packet is sent and the peer reports the ack being delayed |ack_delay|.
+  void UpdateRtt(TimeDelta send_delta, TimeDelta ack_delay, Timestamp now);
+
+  // Causes the smoothed_rtt to be increased to the latest_rtt if the latest_rtt
+  // is larger. The mean deviation is increased to the most recent deviation if
+  // it's larger.
+  void ExpireSmoothedMetrics();
+
+  // Called when connection migrates and RTT measurement needs to be reset.
+  void OnConnectionMigration();
+
+  // Returns the EWMA smoothed RTT for the connection.
+  // May return Zero if no valid updates have occurred.
+  TimeDelta smoothed_rtt() const { return smoothed_rtt_; }
+
+  // Returns the EWMA smoothed RTT prior to the most recent RTT sample.
+  TimeDelta previous_srtt() const { return previous_srtt_; }
+
+  int64_t initial_rtt_us() const { return initial_rtt_us_; }
+
+  // Sets an initial RTT to be used for SmoothedRtt before any RTT updates.
+  void set_initial_rtt_us(int64_t initial_rtt_us) {
+    RTC_DCHECK_GE(initial_rtt_us, 0);
+    if (initial_rtt_us <= 0) {
+      RTC_LOG(LS_ERROR) << "Attempt to set initial rtt to <= 0.";
+      return;
+    }
+    initial_rtt_us_ = initial_rtt_us;
+  }
+
+  // The most recent RTT measurement.
+  // May return Zero if no valid updates have occurred.
+  TimeDelta latest_rtt() const { return latest_rtt_; }
+
+  // Returns the min_rtt for the entire connection.
+  // May return Zero if no valid updates have occurred.
+  TimeDelta min_rtt() const { return min_rtt_; }
+
+  TimeDelta mean_deviation() const { return mean_deviation_; }
+
+ private:
+  TimeDelta latest_rtt_;
+  TimeDelta min_rtt_;
+  TimeDelta smoothed_rtt_;
+  TimeDelta previous_srtt_;
+  // Mean RTT deviation during this session.
+  // Approximation of standard deviation, the error is roughly 1.25 times
+  // larger than the standard deviation, for a normally distributed signal.
+  TimeDelta mean_deviation_;
+  int64_t initial_rtt_us_;
+
+  RTC_DISALLOW_COPY_AND_ASSIGN(RttStats);
+};
+
+}  // namespace bbr
+}  // namespace webrtc
+
+#endif  // MODULES_CONGESTION_CONTROLLER_BBR_RTT_STATS_H_
diff --git a/modules/congestion_controller/bbr/rtt_stats_unittest.cc b/modules/congestion_controller/bbr/rtt_stats_unittest.cc
new file mode 100644
index 0000000..1a07a527
--- /dev/null
+++ b/modules/congestion_controller/bbr/rtt_stats_unittest.cc
@@ -0,0 +1,160 @@
+/*
+ *  Copyright (c) 2018 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/congestion_controller/bbr/rtt_stats.h"
+
+#include <vector>
+
+#include "test/gtest.h"
+
+namespace webrtc {
+namespace bbr {
+namespace test {
+
+class RttStatsTest : public ::testing::Test {
+ protected:
+  RttStats rtt_stats_;
+};
+
+TEST_F(RttStatsTest, DefaultsBeforeUpdate) {
+  EXPECT_LT(0u, rtt_stats_.initial_rtt_us());
+  EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.min_rtt());
+  EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.smoothed_rtt());
+}
+
+TEST_F(RttStatsTest, SmoothedRtt) {
+  // Verify that ack_delay is corrected for in Smoothed RTT.
+  rtt_stats_.UpdateRtt(TimeDelta::ms(300), TimeDelta::ms(100),
+                       Timestamp::ms(0));
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.latest_rtt());
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.smoothed_rtt());
+  // Verify that effective RTT of zero does not change Smoothed RTT.
+  rtt_stats_.UpdateRtt(TimeDelta::ms(200), TimeDelta::ms(200),
+                       Timestamp::ms(0));
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.latest_rtt());
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.smoothed_rtt());
+  // Verify that large erroneous ack_delay does not change Smoothed RTT.
+  rtt_stats_.UpdateRtt(TimeDelta::ms(200), TimeDelta::ms(300),
+                       Timestamp::ms(0));
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.latest_rtt());
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.smoothed_rtt());
+}
+
+// Ensure that the potential rounding artifacts in EWMA calculation do not cause
+// the SRTT to drift too far from the exact value.
+TEST_F(RttStatsTest, SmoothedRttStability) {
+  for (int64_t time = 3; time < 20000; time++) {
+    RttStats stats;
+    for (int64_t i = 0; i < 100; i++) {
+      stats.UpdateRtt(TimeDelta::us(time), TimeDelta::ms(0), Timestamp::ms(0));
+      int64_t time_delta_us = stats.smoothed_rtt().us() - time;
+      ASSERT_LE(std::abs(time_delta_us), 1);
+    }
+  }
+}
+
+TEST_F(RttStatsTest, PreviousSmoothedRtt) {
+  // Verify that ack_delay is corrected for in Smoothed RTT.
+  rtt_stats_.UpdateRtt(TimeDelta::ms(300), TimeDelta::ms(100),
+                       Timestamp::ms(0));
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.latest_rtt());
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.smoothed_rtt());
+  EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.previous_srtt());
+  // Ensure the previous SRTT is 200ms after a 100ms sample.
+  rtt_stats_.UpdateRtt(TimeDelta::ms(100), TimeDelta::Zero(), Timestamp::ms(0));
+  EXPECT_EQ(TimeDelta::ms(100), rtt_stats_.latest_rtt());
+  EXPECT_EQ(TimeDelta::us(187500).us(), rtt_stats_.smoothed_rtt().us());
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.previous_srtt());
+}
+
+TEST_F(RttStatsTest, MinRtt) {
+  rtt_stats_.UpdateRtt(TimeDelta::ms(200), TimeDelta::Zero(), Timestamp::ms(0));
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.min_rtt());
+  rtt_stats_.UpdateRtt(TimeDelta::ms(10), TimeDelta::Zero(),
+                       Timestamp::ms(0) + TimeDelta::ms(10));
+  EXPECT_EQ(TimeDelta::ms(10), rtt_stats_.min_rtt());
+  rtt_stats_.UpdateRtt(TimeDelta::ms(50), TimeDelta::Zero(),
+                       Timestamp::ms(0) + TimeDelta::ms(20));
+  EXPECT_EQ(TimeDelta::ms(10), rtt_stats_.min_rtt());
+  rtt_stats_.UpdateRtt(TimeDelta::ms(50), TimeDelta::Zero(),
+                       Timestamp::ms(0) + TimeDelta::ms(30));
+  EXPECT_EQ(TimeDelta::ms(10), rtt_stats_.min_rtt());
+  rtt_stats_.UpdateRtt(TimeDelta::ms(50), TimeDelta::Zero(),
+                       Timestamp::ms(0) + TimeDelta::ms(40));
+  EXPECT_EQ(TimeDelta::ms(10), rtt_stats_.min_rtt());
+  // Verify that ack_delay does not go into recording of min_rtt_.
+  rtt_stats_.UpdateRtt(TimeDelta::ms(7), TimeDelta::ms(2),
+                       Timestamp::ms(0) + TimeDelta::ms(50));
+  EXPECT_EQ(TimeDelta::ms(7), rtt_stats_.min_rtt());
+}
+
+TEST_F(RttStatsTest, ExpireSmoothedMetrics) {
+  TimeDelta initial_rtt = TimeDelta::ms(10);
+  rtt_stats_.UpdateRtt(initial_rtt, TimeDelta::Zero(), Timestamp::ms(0));
+  EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt());
+  EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt());
+
+  EXPECT_EQ(0.5 * initial_rtt, rtt_stats_.mean_deviation());
+
+  // Update once with a 20ms RTT.
+  TimeDelta doubled_rtt = 2 * initial_rtt;
+  rtt_stats_.UpdateRtt(doubled_rtt, TimeDelta::Zero(), Timestamp::ms(0));
+  EXPECT_EQ(1.125 * initial_rtt, rtt_stats_.smoothed_rtt());
+
+  // Expire the smoothed metrics, increasing smoothed rtt and mean deviation.
+  rtt_stats_.ExpireSmoothedMetrics();
+  EXPECT_EQ(doubled_rtt, rtt_stats_.smoothed_rtt());
+  EXPECT_EQ(0.875 * initial_rtt, rtt_stats_.mean_deviation());
+
+  // Now go back down to 5ms and expire the smoothed metrics, and ensure the
+  // mean deviation increases to 15ms.
+  TimeDelta half_rtt = 0.5 * initial_rtt;
+  rtt_stats_.UpdateRtt(half_rtt, TimeDelta::Zero(), Timestamp::ms(0));
+  EXPECT_GT(doubled_rtt, rtt_stats_.smoothed_rtt());
+  EXPECT_LT(initial_rtt, rtt_stats_.mean_deviation());
+}
+
+TEST_F(RttStatsTest, UpdateRttWithBadSendDeltas) {
+  // Make sure we ignore bad RTTs.
+
+  TimeDelta initial_rtt = TimeDelta::ms(10);
+  rtt_stats_.UpdateRtt(initial_rtt, TimeDelta::Zero(), Timestamp::ms(0));
+  EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt());
+  EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt());
+
+  std::vector<TimeDelta> bad_send_deltas;
+  bad_send_deltas.push_back(TimeDelta::Zero());
+  bad_send_deltas.push_back(TimeDelta::Infinity());
+  bad_send_deltas.push_back(TimeDelta::us(-1000));
+
+  for (TimeDelta bad_send_delta : bad_send_deltas) {
+    rtt_stats_.UpdateRtt(bad_send_delta, TimeDelta::Zero(), Timestamp::ms(0));
+    EXPECT_EQ(initial_rtt, rtt_stats_.min_rtt());
+    EXPECT_EQ(initial_rtt, rtt_stats_.smoothed_rtt());
+  }
+}
+
+TEST_F(RttStatsTest, ResetAfterConnectionMigrations) {
+  rtt_stats_.UpdateRtt(TimeDelta::ms(300), TimeDelta::ms(100),
+                       Timestamp::ms(0));
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.latest_rtt());
+  EXPECT_EQ(TimeDelta::ms(200), rtt_stats_.smoothed_rtt());
+  EXPECT_EQ(TimeDelta::ms(300), rtt_stats_.min_rtt());
+
+  // Reset rtt stats on connection migrations.
+  rtt_stats_.OnConnectionMigration();
+  EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.latest_rtt());
+  EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.smoothed_rtt());
+  EXPECT_EQ(TimeDelta::Zero(), rtt_stats_.min_rtt());
+}
+
+}  // namespace test
+}  // namespace bbr
+}  // namespace webrtc
diff --git a/modules/congestion_controller/network_control/include/network_units.h b/modules/congestion_controller/network_control/include/network_units.h
index 8145c75..1812b0d 100644
--- a/modules/congestion_controller/network_control/include/network_units.h
+++ b/modules/congestion_controller/network_control/include/network_units.h
@@ -91,6 +91,9 @@
   TimeDelta operator*(int32_t scalar) const {
     return TimeDelta::us(us() * scalar);
   }
+  TimeDelta operator/(int64_t scalar) const {
+    return TimeDelta::us(us() / scalar);
+  }
   bool operator==(const TimeDelta& other) const {
     return microseconds_ == other.microseconds_;
   }