diff --git a/webrtc/modules/congestion_controller/trendline_estimator.cc b/webrtc/modules/congestion_controller/trendline_estimator.cc index a086f6aaa1..ad05a64e1e 100644 --- a/webrtc/modules/congestion_controller/trendline_estimator.cc +++ b/webrtc/modules/congestion_controller/trendline_estimator.cc @@ -13,12 +13,14 @@ #include #include "webrtc/base/checks.h" +#include "webrtc/base/optional.h" #include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h" namespace webrtc { namespace { -double LinearFitSlope(const std::list> points) { +rtc::Optional LinearFitSlope( + const std::list> points) { RTC_DCHECK(points.size() >= 2); // Compute the "center of mass". double sum_x = 0; @@ -36,7 +38,9 @@ double LinearFitSlope(const std::list> points) { numerator += (point.first - x_avg) * (point.second - y_avg); denominator += (point.first - x_avg) * (point.first - x_avg); } - return numerator / denominator; + if (denominator == 0) + return rtc::Optional(); + return rtc::Optional(numerator / denominator); } } // namespace @@ -49,6 +53,7 @@ TrendlineEstimator::TrendlineEstimator(size_t window_size, smoothing_coef_(smoothing_coef), threshold_gain_(threshold_gain), num_of_deltas_(0), + first_arrival_time_ms(-1), accumulated_delay_(0), smoothed_delay_(0), delay_hist_(), @@ -58,30 +63,35 @@ TrendlineEstimator::~TrendlineEstimator() {} void TrendlineEstimator::Update(double recv_delta_ms, double send_delta_ms, - double now_ms) { + int64_t arrival_time_ms) { const double delta_ms = recv_delta_ms - send_delta_ms; ++num_of_deltas_; - if (num_of_deltas_ > kDeltaCounterMax) { + if (num_of_deltas_ > kDeltaCounterMax) num_of_deltas_ = kDeltaCounterMax; - } + if (first_arrival_time_ms == -1) + first_arrival_time_ms = arrival_time_ms; // Exponential backoff filter. accumulated_delay_ += delta_ms; - BWE_TEST_LOGGING_PLOT(1, "accumulated_delay_ms", now_ms, accumulated_delay_); + BWE_TEST_LOGGING_PLOT(1, "accumulated_delay_ms", arrival_time_ms, + accumulated_delay_); smoothed_delay_ = smoothing_coef_ * smoothed_delay_ + (1 - smoothing_coef_) * accumulated_delay_; - BWE_TEST_LOGGING_PLOT(1, "smoothed_delay_ms", now_ms, smoothed_delay_); + BWE_TEST_LOGGING_PLOT(1, "smoothed_delay_ms", arrival_time_ms, + smoothed_delay_); // Simple linear regression. - delay_hist_.push_back(std::make_pair(now_ms, smoothed_delay_)); - if (delay_hist_.size() > window_size_) { + delay_hist_.push_back(std::make_pair( + static_cast(arrival_time_ms - first_arrival_time_ms), + smoothed_delay_)); + if (delay_hist_.size() > window_size_) delay_hist_.pop_front(); - } if (delay_hist_.size() == window_size_) { - trendline_ = LinearFitSlope(delay_hist_); + // Only update trendline_ if it is possible to fit a line to the data. + trendline_ = LinearFitSlope(delay_hist_).value_or(trendline_); } - BWE_TEST_LOGGING_PLOT(1, "trendline_slope", now_ms, trendline_); + BWE_TEST_LOGGING_PLOT(1, "trendline_slope", arrival_time_ms, trendline_); } } // namespace webrtc diff --git a/webrtc/modules/congestion_controller/trendline_estimator.h b/webrtc/modules/congestion_controller/trendline_estimator.h index 0bf9bf27c7..7ca9b7d072 100644 --- a/webrtc/modules/congestion_controller/trendline_estimator.h +++ b/webrtc/modules/congestion_controller/trendline_estimator.h @@ -10,11 +10,13 @@ #ifndef WEBRTC_MODULES_CONGESTION_CONTROLLER_TRENDLINE_ESTIMATOR_H_ #define WEBRTC_MODULES_CONGESTION_CONTROLLER_TRENDLINE_ESTIMATOR_H_ +#include +#include + #include #include #include "webrtc/base/constructormagic.h" -#include "webrtc/common_types.h" namespace webrtc { @@ -33,7 +35,9 @@ class TrendlineEstimator { // Update the estimator with a new sample. The deltas should represent deltas // between timestamp groups as defined by the InterArrival class. - void Update(double recv_delta_ms, double send_delta_ms, double now_ms); + void Update(double recv_delta_ms, + double send_delta_ms, + int64_t arrival_time_ms); // Returns the estimated trend k multiplied by some gain. // 0 < k < 1 -> the delay increases, queues are filling up @@ -51,6 +55,8 @@ class TrendlineEstimator { const double threshold_gain_; // Used by the existing threshold. unsigned int num_of_deltas_; + // Keep the arrival times small by using the change from the first packet. + int64_t first_arrival_time_ms; // Exponential backoff filtering. double accumulated_delay_; double smoothed_delay_; diff --git a/webrtc/modules/congestion_controller/trendline_estimator_unittest.cc b/webrtc/modules/congestion_controller/trendline_estimator_unittest.cc index 51778e6cf3..cf2d021af0 100644 --- a/webrtc/modules/congestion_controller/trendline_estimator_unittest.cc +++ b/webrtc/modules/congestion_controller/trendline_estimator_unittest.cc @@ -15,100 +15,59 @@ namespace webrtc { namespace { -constexpr size_t kWindowSize = 15; +constexpr size_t kWindowSize = 20; constexpr double kSmoothing = 0.0; constexpr double kGain = 1; constexpr int64_t kAvgTimeBetweenPackets = 10; +constexpr size_t kPacketCount = 2 * kWindowSize + 1; + +void TestEstimator(double slope, double jitter_stddev, double tolerance) { + TrendlineEstimator estimator(kWindowSize, kSmoothing, kGain); + Random random(0x1234567); + int64_t send_times[kPacketCount]; + int64_t recv_times[kPacketCount]; + int64_t send_start_time = random.Rand(1000000); + int64_t recv_start_time = random.Rand(1000000); + for (size_t i = 0; i < kPacketCount; ++i) { + send_times[i] = send_start_time + i * kAvgTimeBetweenPackets; + double latency = i * kAvgTimeBetweenPackets / (1 - slope); + double jitter = random.Gaussian(0, jitter_stddev); + recv_times[i] = recv_start_time + latency + jitter; + } + for (size_t i = 1; i < kPacketCount; ++i) { + double recv_delta = recv_times[i] - recv_times[i - 1]; + double send_delta = send_times[i] - send_times[i - 1]; + estimator.Update(recv_delta, send_delta, recv_times[i]); + if (i < kWindowSize) + EXPECT_NEAR(estimator.trendline_slope(), 0, 0.001); + else + EXPECT_NEAR(estimator.trendline_slope(), slope, tolerance); + } +} } // namespace TEST(TrendlineEstimator, PerfectLineSlopeOneHalf) { - TrendlineEstimator estimator(kWindowSize, kSmoothing, kGain); - Random rand(0x1234567); - double now_ms = rand.Rand() * 10000; - for (size_t i = 1; i < 2 * kWindowSize; i++) { - double send_delta = rand.Rand() * 2 * kAvgTimeBetweenPackets; - double recv_delta = 2 * send_delta; - now_ms += recv_delta; - estimator.Update(recv_delta, send_delta, now_ms); - if (i < kWindowSize) - EXPECT_NEAR(estimator.trendline_slope(), 0, 0.001); - else - EXPECT_NEAR(estimator.trendline_slope(), 0.5, 0.001); - } + TestEstimator(0.5, 0, 0.001); } TEST(TrendlineEstimator, PerfectLineSlopeMinusOne) { - TrendlineEstimator estimator(kWindowSize, kSmoothing, kGain); - Random rand(0x1234567); - double now_ms = rand.Rand() * 10000; - for (size_t i = 1; i < 2 * kWindowSize; i++) { - double send_delta = rand.Rand() * 2 * kAvgTimeBetweenPackets; - double recv_delta = 0.5 * send_delta; - now_ms += recv_delta; - estimator.Update(recv_delta, send_delta, now_ms); - if (i < kWindowSize) - EXPECT_NEAR(estimator.trendline_slope(), 0, 0.001); - else - EXPECT_NEAR(estimator.trendline_slope(), -1, 0.001); - } + TestEstimator(-1, 0, 0.001); } TEST(TrendlineEstimator, PerfectLineSlopeZero) { - TrendlineEstimator estimator(kWindowSize, kSmoothing, kGain); - Random rand(0x1234567); - double now_ms = rand.Rand() * 10000; - for (size_t i = 1; i < 2 * kWindowSize; i++) { - double send_delta = rand.Rand() * 2 * kAvgTimeBetweenPackets; - double recv_delta = send_delta; - now_ms += recv_delta; - estimator.Update(recv_delta, send_delta, now_ms); - EXPECT_NEAR(estimator.trendline_slope(), 0, 0.001); - } + TestEstimator(0, 0, 0.001); } TEST(TrendlineEstimator, JitteryLineSlopeOneHalf) { - TrendlineEstimator estimator(kWindowSize, kSmoothing, kGain); - Random rand(0x1234567); - double now_ms = rand.Rand() * 10000; - for (size_t i = 1; i < 2 * kWindowSize; i++) { - double send_delta = rand.Rand() * 2 * kAvgTimeBetweenPackets; - double recv_delta = 2 * send_delta + rand.Gaussian(0, send_delta / 3); - now_ms += recv_delta; - estimator.Update(recv_delta, send_delta, now_ms); - if (i < kWindowSize) - EXPECT_NEAR(estimator.trendline_slope(), 0, 0.001); - else - EXPECT_NEAR(estimator.trendline_slope(), 0.5, 0.1); - } + TestEstimator(0.5, kAvgTimeBetweenPackets / 3.0, 0.01); } TEST(TrendlineEstimator, JitteryLineSlopeMinusOne) { - TrendlineEstimator estimator(kWindowSize, kSmoothing, kGain); - Random rand(0x1234567); - double now_ms = rand.Rand() * 10000; - for (size_t i = 1; i < 2 * kWindowSize; i++) { - double send_delta = rand.Rand() * 2 * kAvgTimeBetweenPackets; - double recv_delta = 0.5 * send_delta + rand.Gaussian(0, send_delta / 25); - now_ms += recv_delta; - estimator.Update(recv_delta, send_delta, now_ms); - if (i < kWindowSize) - EXPECT_NEAR(estimator.trendline_slope(), 0, 0.001); - else - EXPECT_NEAR(estimator.trendline_slope(), -1, 0.1); - } + TestEstimator(-1, kAvgTimeBetweenPackets / 3.0, 0.075); } TEST(TrendlineEstimator, JitteryLineSlopeZero) { - TrendlineEstimator estimator(kWindowSize, kSmoothing, kGain); - Random rand(0x1234567); - double now_ms = rand.Rand() * 10000; - for (size_t i = 1; i < 2 * kWindowSize; i++) { - double send_delta = rand.Rand() * 2 * kAvgTimeBetweenPackets; - double recv_delta = send_delta + rand.Gaussian(0, send_delta / 8); - now_ms += recv_delta; - estimator.Update(recv_delta, send_delta, now_ms); - EXPECT_NEAR(estimator.trendline_slope(), 0, 0.1); - } + TestEstimator(0, kAvgTimeBetweenPackets / 3.0, 0.02); } } // namespace webrtc