1 /* 2 * Copyright 2015 The WebRTC Project Authors. All rights reserved. 3 * 4 * Use of this source code is governed by a BSD-style license 5 * that can be found in the LICENSE file in the root of the source 6 * tree. An additional intellectual property rights grant can be found 7 * in the file PATENTS. All contributing project authors may 8 * be found in the AUTHORS file in the root of the source tree. 9 */ 10 11 #include "webrtc/base/ratetracker.h" 12 13 #include <stddef.h> 14 15 #include <algorithm> 16 17 #include "webrtc/base/checks.h" 18 #include "webrtc/base/timeutils.h" 19 20 namespace rtc { 21 22 RateTracker::RateTracker(uint32_t bucket_milliseconds, size_t bucket_count) 23 : bucket_milliseconds_(bucket_milliseconds), 24 bucket_count_(bucket_count), 25 sample_buckets_(new size_t[bucket_count + 1]), 26 total_sample_count_(0u), 27 bucket_start_time_milliseconds_(~0u) { 28 RTC_CHECK(bucket_milliseconds > 0u); 29 RTC_CHECK(bucket_count > 0u); 30 } 31 32 RateTracker::~RateTracker() { 33 delete[] sample_buckets_; 34 } 35 36 double RateTracker::ComputeRateForInterval( 37 uint32_t interval_milliseconds) const { 38 if (bucket_start_time_milliseconds_ == ~0u) { 39 return 0.0; 40 } 41 uint32_t current_time = Time(); 42 // Calculate which buckets to sum up given the current time. If the time 43 // has passed to a new bucket then we have to skip some of the oldest buckets. 44 uint32_t available_interval_milliseconds = std::min<uint32_t>( 45 interval_milliseconds, 46 bucket_milliseconds_ * static_cast<uint32_t>(bucket_count_)); 47 // number of old buckets (i.e. after the current bucket in the ring buffer) 48 // that are expired given our current time interval. 49 size_t buckets_to_skip; 50 // Number of milliseconds of the first bucket that are not a portion of the 51 // current interval. 52 uint32_t milliseconds_to_skip; 53 if (current_time > 54 initialization_time_milliseconds_ + available_interval_milliseconds) { 55 uint32_t time_to_skip = 56 current_time - bucket_start_time_milliseconds_ + 57 static_cast<uint32_t>(bucket_count_) * bucket_milliseconds_ - 58 available_interval_milliseconds; 59 buckets_to_skip = time_to_skip / bucket_milliseconds_; 60 milliseconds_to_skip = time_to_skip % bucket_milliseconds_; 61 } else { 62 buckets_to_skip = bucket_count_ - current_bucket_; 63 milliseconds_to_skip = 0u; 64 available_interval_milliseconds = 65 TimeDiff(current_time, initialization_time_milliseconds_); 66 } 67 // If we're skipping all buckets that means that there have been no samples 68 // within the sampling interval so report 0. 69 if (buckets_to_skip > bucket_count_ || 70 available_interval_milliseconds == 0u) { 71 return 0.0; 72 } 73 size_t start_bucket = NextBucketIndex(current_bucket_ + buckets_to_skip); 74 // Only count a portion of the first bucket according to how much of the 75 // first bucket is within the current interval. 76 size_t total_samples = ((sample_buckets_[start_bucket] * 77 (bucket_milliseconds_ - milliseconds_to_skip)) + 78 (bucket_milliseconds_ >> 1)) / 79 bucket_milliseconds_; 80 // All other buckets in the interval are counted in their entirety. 81 for (size_t i = NextBucketIndex(start_bucket); 82 i != NextBucketIndex(current_bucket_); 83 i = NextBucketIndex(i)) { 84 total_samples += sample_buckets_[i]; 85 } 86 // Convert to samples per second. 87 return static_cast<double>(total_samples * 1000u) / 88 static_cast<double>(available_interval_milliseconds); 89 } 90 91 double RateTracker::ComputeTotalRate() const { 92 if (bucket_start_time_milliseconds_ == ~0u) { 93 return 0.0; 94 } 95 uint32_t current_time = Time(); 96 if (TimeIsLaterOrEqual(current_time, initialization_time_milliseconds_)) { 97 return 0.0; 98 } 99 return static_cast<double>(total_sample_count_ * 1000u) / 100 static_cast<double>( 101 TimeDiff(current_time, initialization_time_milliseconds_)); 102 } 103 104 size_t RateTracker::TotalSampleCount() const { 105 return total_sample_count_; 106 } 107 108 void RateTracker::AddSamples(size_t sample_count) { 109 EnsureInitialized(); 110 uint32_t current_time = Time(); 111 // Advance the current bucket as needed for the current time, and reset 112 // bucket counts as we advance. 113 for (size_t i = 0u; i <= bucket_count_ && 114 current_time >= bucket_start_time_milliseconds_ + bucket_milliseconds_; 115 ++i) { 116 bucket_start_time_milliseconds_ += bucket_milliseconds_; 117 current_bucket_ = NextBucketIndex(current_bucket_); 118 sample_buckets_[current_bucket_] = 0u; 119 } 120 // Ensure that bucket_start_time_milliseconds_ is updated appropriately if 121 // the entire buffer of samples has been expired. 122 bucket_start_time_milliseconds_ += bucket_milliseconds_ * 123 ((current_time - bucket_start_time_milliseconds_) / bucket_milliseconds_); 124 // Add all samples in the bucket that includes the current time. 125 sample_buckets_[current_bucket_] += sample_count; 126 total_sample_count_ += sample_count; 127 } 128 129 uint32_t RateTracker::Time() const { 130 return rtc::Time(); 131 } 132 133 void RateTracker::EnsureInitialized() { 134 if (bucket_start_time_milliseconds_ == ~0u) { 135 initialization_time_milliseconds_ = Time(); 136 bucket_start_time_milliseconds_ = initialization_time_milliseconds_; 137 current_bucket_ = 0u; 138 // We only need to initialize the first bucket because we reset buckets when 139 // current_bucket_ increments. 140 sample_buckets_[current_bucket_] = 0u; 141 } 142 } 143 144 size_t RateTracker::NextBucketIndex(size_t bucket_index) const { 145 return (bucket_index + 1u) % (bucket_count_ + 1u); 146 } 147 148 } // namespace rtc 149