diff --git a/webrtc/modules/remote_bitrate_estimator/bitrate_estimator.cc b/webrtc/modules/remote_bitrate_estimator/bitrate_estimator.cc index 316297f03b..8934d43ada 100644 --- a/webrtc/modules/remote_bitrate_estimator/bitrate_estimator.cc +++ b/webrtc/modules/remote_bitrate_estimator/bitrate_estimator.cc @@ -12,52 +12,73 @@ namespace webrtc { -const float kBitrateAverageWindowMs = 500.0f; +const int kBitrateAverageWindowMs = 500; BitRateStats::BitRateStats() - : data_samples_(), - accumulated_bytes_(0) { + : num_buckets_(kBitrateAverageWindowMs + 1), // N ms in (N+1) buckets. + buckets_(new uint32_t[num_buckets_]()), + accumulated_bytes_(0), + oldest_time_(0), + oldest_index_(0), + bps_coefficient_(8.f * 1000.f / (num_buckets_ - 1)) { } BitRateStats::~BitRateStats() { - Init(); } void BitRateStats::Init() { accumulated_bytes_ = 0; - while (data_samples_.size() > 0) { - delete data_samples_.front(); - data_samples_.pop_front(); + oldest_time_ = 0; + oldest_index_ = 0; + for (int i = 0; i < num_buckets_; i++) { + buckets_[i] = 0; } } void BitRateStats::Update(uint32_t packet_size_bytes, int64_t now_ms) { - // Find an empty slot for storing the new sample and at the same time - // accumulate the history. - data_samples_.push_back(new DataTimeSizeTuple(packet_size_bytes, now_ms)); - accumulated_bytes_ += packet_size_bytes; - EraseOld(now_ms); -} - -void BitRateStats::EraseOld(int64_t now_ms) { - while (data_samples_.size() > 0) { - if (now_ms - data_samples_.front()->time_complete_ms > - kBitrateAverageWindowMs) { - // Delete old sample - accumulated_bytes_ -= data_samples_.front()->size_bytes; - delete data_samples_.front(); - data_samples_.pop_front(); - } else { - break; - } + if (now_ms < oldest_time_) { + // Too old data is ignored. + return; } + + EraseOld(now_ms); + + int now_offset = static_cast(now_ms - oldest_time_); + assert(now_offset < num_buckets_); + int index = oldest_index_ + now_offset; + if (index >= num_buckets_) { + index -= num_buckets_; + } + buckets_[index] += packet_size_bytes; + accumulated_bytes_ += packet_size_bytes; } uint32_t BitRateStats::BitRate(int64_t now_ms) { - // Calculate the average bit rate the past BITRATE_AVERAGE_WINDOW ms. - // Removes any old samples from the list. EraseOld(now_ms); - return static_cast(accumulated_bytes_ * 8.0f * 1000.0f / - kBitrateAverageWindowMs + 0.5f); + return static_cast(accumulated_bytes_ * bps_coefficient_ + 0.5f); +} + +void BitRateStats::EraseOld(int64_t now_ms) { + int64_t new_oldest_time = now_ms - num_buckets_ + 1; + if (new_oldest_time <= oldest_time_) { + return; + } + + while (oldest_time_ < new_oldest_time) { + uint32_t num_bytes_in_oldest_bucket = buckets_[oldest_index_]; + assert(accumulated_bytes_ >= num_bytes_in_oldest_bucket); + accumulated_bytes_ -= num_bytes_in_oldest_bucket; + buckets_[oldest_index_] = 0; + if (++oldest_index_ >= num_buckets_) { + oldest_index_ = 0; + } + ++oldest_time_; + if (accumulated_bytes_ == 0) { + // This guarantees we go through all the buckets at most once, even if + // |new_oldest_time| is far greater than |oldest_time_|. + break; + } + } + oldest_time_ = new_oldest_time; } } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/bitrate_estimator.h b/webrtc/modules/remote_bitrate_estimator/bitrate_estimator.h index 3d5d51b0e9..9ee31eb17f 100644 --- a/webrtc/modules/remote_bitrate_estimator/bitrate_estimator.h +++ b/webrtc/modules/remote_bitrate_estimator/bitrate_estimator.h @@ -11,8 +11,7 @@ #ifndef WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_BITRATE_ESTIMATOR_H_ #define WEBRTC_MODULES_REMOTE_BITRATE_ESTIMATOR_BITRATE_ESTIMATOR_H_ -#include - +#include "webrtc/system_wrappers/interface/scoped_ptr.h" #include "webrtc/typedefs.h" namespace webrtc { @@ -27,20 +26,24 @@ class BitRateStats { uint32_t BitRate(int64_t now_ms); private: - struct DataTimeSizeTuple { - DataTimeSizeTuple(uint32_t size_bytes_in, int64_t time_complete_ms_in) - : size_bytes(size_bytes_in), - time_complete_ms(time_complete_ms_in) { - } - - uint32_t size_bytes; - int64_t time_complete_ms; - }; - void EraseOld(int64_t now_ms); - std::list data_samples_; + // Numbers of bytes are kept in buckets (circular buffer), with one bucket + // per millisecond. + const int num_buckets_; + scoped_array buckets_; + + // Total number of bytes recorded in buckets. uint32_t accumulated_bytes_; + + // Oldest time recorded in buckets. + int64_t oldest_time_; + + // Bucket index of oldest bytes recorded in buckets. + int oldest_index_; + + // To convert number of bytes in bits/second. + const float bps_coefficient_; }; } // namespace webrtc diff --git a/webrtc/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc b/webrtc/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc index ca9da99c01..3d49ca4778 100644 --- a/webrtc/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc +++ b/webrtc/modules/remote_bitrate_estimator/bitrate_estimator_unittest.cc @@ -17,7 +17,7 @@ using webrtc::BitRateStats; class BitRateStatsTest : public ::testing::Test { protected: - BitRateStatsTest() {}; + BitRateStatsTest() {} BitRateStats stats_; }; @@ -47,4 +47,51 @@ TEST_F(BitRateStatsTest, TestStrictMode) { // the estimate should be 0. EXPECT_EQ(0u, stats_.BitRate(now_ms)); } + +TEST_F(BitRateStatsTest, IncreasingThenDecreasingBitrate) { + int64_t now_ms = 0; + stats_.Init(); + // Expecting 0 after init. + uint32_t bitrate = stats_.BitRate(now_ms); + EXPECT_EQ(0u, bitrate); + // 1000 bytes per millisecond until plateau is reached. + while (++now_ms < 10000) { + stats_.Update(1000, now_ms); + uint32_t new_bitrate = stats_.BitRate(now_ms); + if (new_bitrate != bitrate) { + // New bitrate must be higher than previous one. + EXPECT_GT(new_bitrate, bitrate); + } else { + // Plateau reached, 8000 kbps expected. + EXPECT_NEAR(8000000u, bitrate, 80000u); + break; + } + bitrate = new_bitrate; + } + // 1000 bytes per millisecond until 10-second mark, 8000 kbps expected. + while (++now_ms < 10000) { + stats_.Update(1000, now_ms); + bitrate = stats_.BitRate(now_ms); + EXPECT_NEAR(8000000u, bitrate, 80000u); + } + // Zero bytes per millisecond until 0 is reached. + while (++now_ms < 20000) { + stats_.Update(0, now_ms); + uint32_t new_bitrate = stats_.BitRate(now_ms); + if (new_bitrate != bitrate) { + // New bitrate must be lower than previous one. + EXPECT_LT(new_bitrate, bitrate); + } else { + // 0 kbps expected. + EXPECT_EQ(0u, bitrate); + break; + } + bitrate = new_bitrate; + } + // Zero bytes per millisecond until 20-second mark, 0 kbps expected. + while (++now_ms < 20000) { + stats_.Update(0, now_ms); + EXPECT_EQ(0u, stats_.BitRate(now_ms)); + } +} } // namespace