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 
RateTracker(uint32_t bucket_milliseconds,size_t bucket_count)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 
~RateTracker()32 RateTracker::~RateTracker() {
33   delete[] sample_buckets_;
34 }
35 
ComputeRateForInterval(uint32_t interval_milliseconds) const36 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 
ComputeTotalRate() const91 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 
TotalSampleCount() const104 size_t RateTracker::TotalSampleCount() const {
105   return total_sample_count_;
106 }
107 
AddSamples(size_t sample_count)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 
Time() const129 uint32_t RateTracker::Time() const {
130   return rtc::Time();
131 }
132 
EnsureInitialized()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 
NextBucketIndex(size_t bucket_index) const144 size_t RateTracker::NextBucketIndex(size_t bucket_index) const {
145   return (bucket_index + 1u) % (bucket_count_ + 1u);
146 }
147 
148 }  // namespace rtc
149