Line data Source code
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 : static const int64_t kTimeUnset = -1;
23 :
24 0 : RateTracker::RateTracker(int64_t bucket_milliseconds, size_t bucket_count)
25 : : bucket_milliseconds_(bucket_milliseconds),
26 : bucket_count_(bucket_count),
27 0 : sample_buckets_(new size_t[bucket_count + 1]),
28 : total_sample_count_(0u),
29 0 : bucket_start_time_milliseconds_(kTimeUnset) {
30 0 : RTC_CHECK(bucket_milliseconds > 0);
31 0 : RTC_CHECK(bucket_count > 0);
32 0 : }
33 :
34 0 : RateTracker::~RateTracker() {
35 0 : delete[] sample_buckets_;
36 0 : }
37 :
38 0 : double RateTracker::ComputeRateForInterval(
39 : int64_t interval_milliseconds) const {
40 0 : if (bucket_start_time_milliseconds_ == kTimeUnset) {
41 0 : return 0.0;
42 : }
43 0 : int64_t current_time = Time();
44 : // Calculate which buckets to sum up given the current time. If the time
45 : // has passed to a new bucket then we have to skip some of the oldest buckets.
46 : int64_t available_interval_milliseconds =
47 : std::min(interval_milliseconds,
48 0 : bucket_milliseconds_ * static_cast<int64_t>(bucket_count_));
49 : // number of old buckets (i.e. after the current bucket in the ring buffer)
50 : // that are expired given our current time interval.
51 : size_t buckets_to_skip;
52 : // Number of milliseconds of the first bucket that are not a portion of the
53 : // current interval.
54 : int64_t milliseconds_to_skip;
55 0 : if (current_time >
56 0 : initialization_time_milliseconds_ + available_interval_milliseconds) {
57 : int64_t time_to_skip =
58 0 : current_time - bucket_start_time_milliseconds_ +
59 0 : static_cast<int64_t>(bucket_count_) * bucket_milliseconds_ -
60 0 : available_interval_milliseconds;
61 0 : buckets_to_skip = time_to_skip / bucket_milliseconds_;
62 0 : milliseconds_to_skip = time_to_skip % bucket_milliseconds_;
63 : } else {
64 0 : buckets_to_skip = bucket_count_ - current_bucket_;
65 0 : milliseconds_to_skip = 0;
66 : available_interval_milliseconds =
67 0 : TimeDiff(current_time, initialization_time_milliseconds_);
68 : // Let one bucket interval pass after initialization before reporting.
69 0 : if (available_interval_milliseconds < bucket_milliseconds_) {
70 0 : return 0.0;
71 : }
72 : }
73 : // If we're skipping all buckets that means that there have been no samples
74 : // within the sampling interval so report 0.
75 0 : if (buckets_to_skip > bucket_count_ || available_interval_milliseconds == 0) {
76 0 : return 0.0;
77 : }
78 0 : size_t start_bucket = NextBucketIndex(current_bucket_ + buckets_to_skip);
79 : // Only count a portion of the first bucket according to how much of the
80 : // first bucket is within the current interval.
81 0 : size_t total_samples = ((sample_buckets_[start_bucket] *
82 0 : (bucket_milliseconds_ - milliseconds_to_skip)) +
83 0 : (bucket_milliseconds_ >> 1)) /
84 0 : bucket_milliseconds_;
85 : // All other buckets in the interval are counted in their entirety.
86 0 : for (size_t i = NextBucketIndex(start_bucket);
87 0 : i != NextBucketIndex(current_bucket_);
88 : i = NextBucketIndex(i)) {
89 0 : total_samples += sample_buckets_[i];
90 : }
91 : // Convert to samples per second.
92 0 : return static_cast<double>(total_samples * 1000) /
93 0 : static_cast<double>(available_interval_milliseconds);
94 : }
95 :
96 0 : double RateTracker::ComputeTotalRate() const {
97 0 : if (bucket_start_time_milliseconds_ == kTimeUnset) {
98 0 : return 0.0;
99 : }
100 0 : int64_t current_time = Time();
101 0 : if (current_time <= initialization_time_milliseconds_) {
102 0 : return 0.0;
103 : }
104 0 : return static_cast<double>(total_sample_count_ * 1000) /
105 0 : static_cast<double>(
106 0 : TimeDiff(current_time, initialization_time_milliseconds_));
107 : }
108 :
109 0 : size_t RateTracker::TotalSampleCount() const {
110 0 : return total_sample_count_;
111 : }
112 :
113 0 : void RateTracker::AddSamples(size_t sample_count) {
114 0 : EnsureInitialized();
115 0 : int64_t current_time = Time();
116 : // Advance the current bucket as needed for the current time, and reset
117 : // bucket counts as we advance.
118 0 : for (size_t i = 0;
119 0 : i <= bucket_count_ &&
120 0 : current_time >= bucket_start_time_milliseconds_ + bucket_milliseconds_;
121 : ++i) {
122 0 : bucket_start_time_milliseconds_ += bucket_milliseconds_;
123 0 : current_bucket_ = NextBucketIndex(current_bucket_);
124 0 : sample_buckets_[current_bucket_] = 0;
125 : }
126 : // Ensure that bucket_start_time_milliseconds_ is updated appropriately if
127 : // the entire buffer of samples has been expired.
128 0 : bucket_start_time_milliseconds_ += bucket_milliseconds_ *
129 0 : ((current_time - bucket_start_time_milliseconds_) / bucket_milliseconds_);
130 : // Add all samples in the bucket that includes the current time.
131 0 : sample_buckets_[current_bucket_] += sample_count;
132 0 : total_sample_count_ += sample_count;
133 0 : }
134 :
135 0 : int64_t RateTracker::Time() const {
136 0 : return rtc::TimeMillis();
137 : }
138 :
139 0 : void RateTracker::EnsureInitialized() {
140 0 : if (bucket_start_time_milliseconds_ == kTimeUnset) {
141 0 : initialization_time_milliseconds_ = Time();
142 0 : bucket_start_time_milliseconds_ = initialization_time_milliseconds_;
143 0 : current_bucket_ = 0;
144 : // We only need to initialize the first bucket because we reset buckets when
145 : // current_bucket_ increments.
146 0 : sample_buckets_[current_bucket_] = 0;
147 : }
148 0 : }
149 :
150 0 : size_t RateTracker::NextBucketIndex(size_t bucket_index) const {
151 0 : return (bucket_index + 1u) % (bucket_count_ + 1u);
152 : }
153 :
154 : } // namespace rtc
|