From 233c4ba4fd294822fd878847feee596c2922d4c9 Mon Sep 17 00:00:00 2001 From: philipel Date: Mon, 1 Aug 2016 08:49:04 -0700 Subject: [PATCH] New ProbingCalculator class. The ProbingCalculator class calculates and validates the results from probing attempts. BUG=webrtc:5859 Review-Url: https://codereview.webrtc.org/2121183002 Cr-Commit-Position: refs/heads/master@{#13589} --- webrtc/modules/congestion_controller/BUILD.gn | 2 + .../congestion_controller.gypi | 4 +- .../probe_bitrate_controller_unittest.cc | 127 ++++++++++++++++++ .../probe_bitrate_estimator.cc | 109 +++++++++++++++ .../probe_bitrate_estimator.h | 56 ++++++++ webrtc/modules/modules.gyp | 1 + 6 files changed, 298 insertions(+), 1 deletion(-) create mode 100644 webrtc/modules/congestion_controller/probe_bitrate_controller_unittest.cc create mode 100644 webrtc/modules/congestion_controller/probe_bitrate_estimator.cc create mode 100644 webrtc/modules/congestion_controller/probe_bitrate_estimator.h diff --git a/webrtc/modules/congestion_controller/BUILD.gn b/webrtc/modules/congestion_controller/BUILD.gn index 2af1754e45..add1a4d974 100644 --- a/webrtc/modules/congestion_controller/BUILD.gn +++ b/webrtc/modules/congestion_controller/BUILD.gn @@ -14,6 +14,8 @@ source_set("congestion_controller") { "delay_based_bwe.cc", "delay_based_bwe.h", "include/congestion_controller.h", + "probe_bitrate_estimator.cc", + "probe_bitrate_estimator.h", ] configs += [ "../..:common_config" ] diff --git a/webrtc/modules/congestion_controller/congestion_controller.gypi b/webrtc/modules/congestion_controller/congestion_controller.gypi index 5b23ae8d6a..c7fdd04455 100644 --- a/webrtc/modules/congestion_controller/congestion_controller.gypi +++ b/webrtc/modules/congestion_controller/congestion_controller.gypi @@ -18,9 +18,11 @@ ], 'sources': [ 'congestion_controller.cc', - 'include/congestion_controller.h', 'delay_based_bwe.cc', 'delay_based_bwe.h', + 'include/congestion_controller.h', + 'probe_bitrate_estimator.cc', + 'probe_bitrate_estimator.h', ], # TODO(jschuh): Bug 1348: fix size_t to int truncations. 'msvs_disabled_warnings': [ 4267, ], diff --git a/webrtc/modules/congestion_controller/probe_bitrate_controller_unittest.cc b/webrtc/modules/congestion_controller/probe_bitrate_controller_unittest.cc new file mode 100644 index 0000000000..40beb0f2f3 --- /dev/null +++ b/webrtc/modules/congestion_controller/probe_bitrate_controller_unittest.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 "webrtc/modules/congestion_controller/probe_bitrate_estimator.h" + +#include +#include + +#include "testing/gmock/include/gmock/gmock.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "webrtc/modules/remote_bitrate_estimator/aimd_rate_control.h" + +namespace webrtc { + +class TestProbeBitrateEstimator : public ::testing::Test { + public: + TestProbeBitrateEstimator() : probe_bitrate_estimator_() {} + + void AddPacketFeedback(int probe_cluster_id, + size_t size, + int64_t send_time_ms, + int64_t arrival_time_ms) { + PacketInfo info(arrival_time_ms, send_time_ms, 0, size, probe_cluster_id); + ProbingResult res = probe_bitrate_estimator_.PacketFeedback(info); + if (res.bps != ProbingResult::kNoEstimate) + results_.emplace_back(res.bps, res.timestamp); + } + + void CheckResult(size_t index, int bps, int max_diff, int64_t timestamp) { + ASSERT_GT(results_.size(), index); + EXPECT_NEAR(results_[index].first, bps, max_diff); + EXPECT_EQ(results_[index].second, timestamp); + } + + protected: + std::vector> results_; + ProbeBitrateEstimator probe_bitrate_estimator_; +}; + +TEST_F(TestProbeBitrateEstimator, OneCluster) { + AddPacketFeedback(0, 1000, 0, 10); + AddPacketFeedback(0, 1000, 10, 20); + AddPacketFeedback(0, 1000, 20, 30); + AddPacketFeedback(0, 1000, 40, 50); + + CheckResult(0, 100000, 10, 50); +} + +TEST_F(TestProbeBitrateEstimator, FastReceive) { + AddPacketFeedback(0, 1000, 0, 15); + AddPacketFeedback(0, 1000, 10, 30); + AddPacketFeedback(0, 1000, 20, 40); + AddPacketFeedback(0, 1000, 40, 50); + + CheckResult(0, 100000, 10, 50); +} + +TEST_F(TestProbeBitrateEstimator, TooFastReceive) { + AddPacketFeedback(0, 1000, 0, 19); + AddPacketFeedback(0, 1000, 10, 30); + AddPacketFeedback(0, 1000, 20, 40); + AddPacketFeedback(0, 1000, 40, 50); + + EXPECT_TRUE(results_.empty()); +} + +TEST_F(TestProbeBitrateEstimator, SlowReceive) { + AddPacketFeedback(0, 1000, 0, 10); + AddPacketFeedback(0, 1000, 10, 40); + AddPacketFeedback(0, 1000, 20, 70); + AddPacketFeedback(0, 1000, 40, 110); + + CheckResult(0, 40000, 10, 110); +} + +TEST_F(TestProbeBitrateEstimator, BurstReceive) { + AddPacketFeedback(0, 1000, 0, 50); + AddPacketFeedback(0, 1000, 10, 50); + AddPacketFeedback(0, 1000, 20, 50); + AddPacketFeedback(0, 1000, 40, 50); + + EXPECT_TRUE(results_.empty()); +} + +TEST_F(TestProbeBitrateEstimator, MultipleClusters) { + AddPacketFeedback(0, 1000, 0, 10); + AddPacketFeedback(0, 1000, 10, 20); + AddPacketFeedback(0, 1000, 20, 30); + AddPacketFeedback(0, 1000, 40, 60); + AddPacketFeedback(0, 1000, 50, 60); + + CheckResult(0, 80000, 10, 60); + CheckResult(1, 100000, 10, 60); + + AddPacketFeedback(1, 1000, 60, 70); + AddPacketFeedback(1, 1000, 65, 77); + AddPacketFeedback(1, 1000, 70, 84); + AddPacketFeedback(1, 1000, 75, 90); + + CheckResult(2, 200000, 10, 90); +} + +TEST_F(TestProbeBitrateEstimator, OldProbe) { + AddPacketFeedback(0, 1000, 0, 10); + AddPacketFeedback(0, 1000, 10, 20); + AddPacketFeedback(0, 1000, 20, 30); + + AddPacketFeedback(1, 1000, 60, 70); + AddPacketFeedback(1, 1000, 65, 77); + AddPacketFeedback(1, 1000, 70, 84); + AddPacketFeedback(1, 1000, 75, 90); + + CheckResult(0, 200000, 10, 90); + + AddPacketFeedback(0, 1000, 40, 60); + + EXPECT_EQ(1ul, results_.size()); +} + +} // namespace webrtc diff --git a/webrtc/modules/congestion_controller/probe_bitrate_estimator.cc b/webrtc/modules/congestion_controller/probe_bitrate_estimator.cc new file mode 100644 index 0000000000..4a63625291 --- /dev/null +++ b/webrtc/modules/congestion_controller/probe_bitrate_estimator.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 "webrtc/modules/congestion_controller/probe_bitrate_estimator.h" + +#include + +#include "webrtc/base/logging.h" + +namespace { +// Max number of saved clusters. +constexpr size_t kMaxNumSavedClusters = 5; + +// The minumum number of probes we need for a valid cluster. +constexpr int kMinNumProbesValidCluster = 4; + +// The maximum (receive rate)/(send rate) ratio for a valid estimate. +constexpr float kValidRatio = 1.2f; +} + +namespace webrtc { + +ProbingResult::ProbingResult() : bps(kNoEstimate), timestamp(0) {} + +ProbingResult::ProbingResult(int bps, int64_t timestamp) + : bps(bps), timestamp(timestamp) {} + +ProbeBitrateEstimator::ProbeBitrateEstimator() : last_valid_cluster_id_(0) {} + +ProbingResult ProbeBitrateEstimator::PacketFeedback( + const PacketInfo& packet_info) { + // If this is not a probing packet or if this probing packet + // belongs to an old cluster, do nothing. + if (packet_info.probe_cluster_id == PacketInfo::kNotAProbe || + packet_info.probe_cluster_id < last_valid_cluster_id_) { + return ProbingResult(); + } + + AggregatedCluster* cluster = &clusters_[packet_info.probe_cluster_id]; + cluster->first_send_ms = + std::min(cluster->first_send_ms, packet_info.send_time_ms); + cluster->last_send_ms = + std::max(cluster->last_send_ms, packet_info.send_time_ms); + cluster->first_receive_ms = + std::min(cluster->first_receive_ms, packet_info.arrival_time_ms); + cluster->last_receive_ms = + std::max(cluster->last_receive_ms, packet_info.arrival_time_ms); + cluster->size += packet_info.payload_size; + cluster->num_probes += 1; + + // Clean up old clusters. + while (clusters_.size() > kMaxNumSavedClusters) + clusters_.erase(clusters_.begin()); + + if (cluster->num_probes < kMinNumProbesValidCluster) + return ProbingResult(); + + int send_interval_ms = cluster->last_send_ms - cluster->first_send_ms; + int receive_interval_ms = + cluster->last_receive_ms - cluster->first_receive_ms; + + if (send_interval_ms == 0 || receive_interval_ms == 0) { + LOG(LS_INFO) << "Probing unsuccessful, invalid send/receive interval" + << " [cluster id: " << packet_info.probe_cluster_id + << "] [send interval: " << send_interval_ms << " ms]" + << " [receive interval: " << receive_interval_ms << " ms]"; + + return ProbingResult(); + } + + float send_bps = static_cast(cluster->size) / send_interval_ms * 1000; + float receive_bps = + static_cast(cluster->size) / receive_interval_ms * 1000; + float ratio = receive_bps / send_bps; + if (ratio > kValidRatio) { + LOG(LS_INFO) << "Probing unsuccessful, receive/send ratio too high" + << " [cluster id: " << packet_info.probe_cluster_id + << "] [send: " << cluster->size << " bytes / " + << send_interval_ms << " ms = " << send_bps / 1000 << " kb/s]" + << " [receive: " << cluster->size << " bytes / " + << receive_interval_ms << " ms = " << receive_bps / 1000 + << " kb/s]" + << " [ratio: " << receive_bps / 1000 << " / " + << send_bps / 1000 << " = " << ratio << " > kValidRatio (" + << kValidRatio << ")]"; + + return ProbingResult(); + } + // We have a valid estimate. + int result_bps = std::min(send_bps, receive_bps); + last_valid_cluster_id_ = packet_info.probe_cluster_id; + LOG(LS_INFO) << "Probing successful" + << " [cluster id: " << packet_info.probe_cluster_id + << "] [send: " << cluster->size << " bytes / " + << send_interval_ms << " ms = " << send_bps / 1000 << " kb/s]" + << " [receive: " << cluster->size << " bytes / " + << receive_interval_ms << " ms = " << receive_bps / 1000 + << " kb/s]"; + + return ProbingResult(result_bps, packet_info.arrival_time_ms); +} +} // namespace webrtc diff --git a/webrtc/modules/congestion_controller/probe_bitrate_estimator.h b/webrtc/modules/congestion_controller/probe_bitrate_estimator.h new file mode 100644 index 0000000000..2aea59c09c --- /dev/null +++ b/webrtc/modules/congestion_controller/probe_bitrate_estimator.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 WEBRTC_MODULES_CONGESTION_CONTROLLER_PROBE_BITRATE_ESTIMATOR_H_ +#define WEBRTC_MODULES_CONGESTION_CONTROLLER_PROBE_BITRATE_ESTIMATOR_H_ + +#include +#include + +#include "webrtc/modules/rtp_rtcp/include/rtp_rtcp_defines.h" + +namespace webrtc { + +struct ProbingResult { + static constexpr int kNoEstimate = -1; + + ProbingResult(); + ProbingResult(int bps, int64_t timestamp); + + int bps; + int64_t timestamp; +}; + +class ProbeBitrateEstimator { + public: + ProbeBitrateEstimator(); + + // Should be called for every packet we receive feedback about. If the + // packet was used for probing it will validate/calculate the resulting + // bitrate and return the result. + ProbingResult PacketFeedback(const PacketInfo& packet_info); + + private: + struct AggregatedCluster { + int num_probes = 0; + int64_t first_send_ms = std::numeric_limits::max(); + int64_t last_send_ms = 0; + int64_t first_receive_ms = std::numeric_limits::max(); + int64_t last_receive_ms = 0; + size_t size = 0; + }; + + std::map clusters_; + int last_valid_cluster_id_; +}; + +} // namespace webrtc + +#endif // WEBRTC_MODULES_CONGESTION_CONTROLLER_PROBE_BITRATE_ESTIMATOR_H_ diff --git a/webrtc/modules/modules.gyp b/webrtc/modules/modules.gyp index 832813f18b..f3b986f12b 100644 --- a/webrtc/modules/modules.gyp +++ b/webrtc/modules/modules.gyp @@ -277,6 +277,7 @@ 'congestion_controller/delay_based_bwe_unittest.cc', 'congestion_controller/delay_based_bwe_unittest_helper.cc', 'congestion_controller/delay_based_bwe_unittest_helper.h', + 'congestion_controller/probe_bitrate_controller_unittest.cc', 'media_file/media_file_unittest.cc', 'module_common_types_unittest.cc', 'pacing/bitrate_prober_unittest.cc',