OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2016 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 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 | 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 | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/video/stats_counter.h" | 11 #include "webrtc/video/stats_counter.h" |
12 | 12 |
13 #include <algorithm> | 13 #include <algorithm> |
14 | 14 |
15 #include "webrtc/base/checks.h" | |
15 #include "webrtc/system_wrappers/include/clock.h" | 16 #include "webrtc/system_wrappers/include/clock.h" |
16 | 17 |
17 namespace webrtc { | 18 namespace webrtc { |
18 | 19 |
19 namespace { | 20 namespace { |
20 // Periodic time interval for processing samples. | 21 // Periodic time interval for processing samples. |
21 const int64_t kProcessIntervalMs = 2000; | 22 const int64_t kProcessIntervalMs = 2000; |
22 } // namespace | 23 } // namespace |
23 | 24 |
24 // Class holding periodically computed metrics. | 25 // Class holding periodically computed metrics. |
25 class AggregatedCounter { | 26 class AggregatedCounter { |
26 public: | 27 public: |
27 AggregatedCounter() : sum_(0) {} | 28 AggregatedCounter() : last_sample_(0), sum_samples_(0) {} |
28 ~AggregatedCounter() {} | 29 ~AggregatedCounter() {} |
29 | 30 |
30 void Add(int sample) { | 31 void Add(int sample) { |
31 sum_ += sample; | 32 last_sample_ = sample; |
33 sum_samples_ += sample; | |
32 ++stats_.num_samples; | 34 ++stats_.num_samples; |
33 if (stats_.num_samples == 1) { | 35 if (stats_.num_samples == 1) { |
34 stats_.min = sample; | 36 stats_.min = sample; |
35 stats_.max = sample; | 37 stats_.max = sample; |
36 } | 38 } |
37 stats_.min = std::min(sample, stats_.min); | 39 stats_.min = std::min(sample, stats_.min); |
38 stats_.max = std::max(sample, stats_.max); | 40 stats_.max = std::max(sample, stats_.max); |
39 } | 41 } |
40 | 42 |
41 AggregatedStats ComputeStats() { | 43 AggregatedStats ComputeStats() { |
42 Compute(); | 44 Compute(); |
43 return stats_; | 45 return stats_; |
44 } | 46 } |
45 | 47 |
48 bool HasSamples() const { return stats_.num_samples > 0; } | |
stefan-webrtc
2016/09/08 07:28:14
Maybe call this Empty() instead to align with inte
åsapersson
2016/09/08 13:15:02
Done.
| |
49 | |
50 int last_sample() const { return last_sample_; } | |
51 | |
46 private: | 52 private: |
47 void Compute() { | 53 void Compute() { |
48 if (stats_.num_samples == 0) | 54 if (stats_.num_samples == 0) |
49 return; | 55 return; |
50 | 56 |
51 stats_.average = (sum_ + stats_.num_samples / 2) / stats_.num_samples; | 57 stats_.average = |
58 (sum_samples_ + stats_.num_samples / 2) / stats_.num_samples; | |
52 } | 59 } |
53 int64_t sum_; | 60 int last_sample_; |
61 int64_t sum_samples_; | |
54 AggregatedStats stats_; | 62 AggregatedStats stats_; |
55 }; | 63 }; |
56 | 64 |
57 // StatsCounter class. | 65 // StatsCounter class. |
58 StatsCounter::StatsCounter(Clock* clock, | 66 StatsCounter::StatsCounter(Clock* clock, |
59 bool include_empty_intervals, | 67 bool include_empty_intervals, |
60 StatsCounterObserver* observer) | 68 StatsCounterObserver* observer) |
61 : max_(0), | 69 : max_(0), |
62 sum_(0), | 70 sum_(0), |
63 num_samples_(0), | 71 num_samples_(0), |
64 last_sum_(0), | 72 last_sum_(0), |
73 aggregated_counter_(new AggregatedCounter()), | |
65 clock_(clock), | 74 clock_(clock), |
66 include_empty_intervals_(include_empty_intervals), | 75 include_empty_intervals_(include_empty_intervals), |
67 observer_(observer), | 76 observer_(observer), |
68 aggregated_counter_(new AggregatedCounter()), | 77 last_process_time_ms_(-1), |
69 last_process_time_ms_(-1) {} | 78 paused_(false) {} |
70 | 79 |
71 StatsCounter::~StatsCounter() {} | 80 StatsCounter::~StatsCounter() {} |
72 | 81 |
73 AggregatedStats StatsCounter::GetStats() { | 82 AggregatedStats StatsCounter::GetStats() { |
74 return aggregated_counter_->ComputeStats(); | 83 return aggregated_counter_->ComputeStats(); |
75 } | 84 } |
76 | 85 |
77 bool StatsCounter::TimeToProcess() { | 86 AggregatedStats StatsCounter::ProcessAndGetStats() { |
87 if (last_process_time_ms_ != -1) | |
88 TryProcess(); // At least one sample is added. | |
89 return aggregated_counter_->ComputeStats(); | |
90 } | |
91 | |
92 void StatsCounter::ProcessAndPause() { | |
93 if (last_process_time_ms_ != -1) | |
94 TryProcess(); // At least one sample is added. | |
95 paused_ = true; | |
96 } | |
97 | |
98 bool StatsCounter::TimeToProcess(int* elapsed_intervals) { | |
78 int64_t now = clock_->TimeInMilliseconds(); | 99 int64_t now = clock_->TimeInMilliseconds(); |
79 if (last_process_time_ms_ == -1) | 100 if (last_process_time_ms_ == -1) |
80 last_process_time_ms_ = now; | 101 last_process_time_ms_ = now; |
81 | 102 |
82 int64_t diff_ms = now - last_process_time_ms_; | 103 int64_t diff_ms = now - last_process_time_ms_; |
83 if (diff_ms < kProcessIntervalMs) | 104 if (diff_ms < kProcessIntervalMs) |
84 return false; | 105 return false; |
85 | 106 |
86 // Advance number of complete kProcessIntervalMs that have passed. | 107 // Advance number of complete kProcessIntervalMs that have passed. |
87 int64_t num_intervals = diff_ms / kProcessIntervalMs; | 108 int64_t num_intervals = diff_ms / kProcessIntervalMs; |
88 last_process_time_ms_ += num_intervals * kProcessIntervalMs; | 109 last_process_time_ms_ += num_intervals * kProcessIntervalMs; |
89 | 110 |
90 // Add zero for intervals without samples. | 111 *elapsed_intervals = num_intervals; |
91 if (include_empty_intervals_) { | |
92 for (int64_t i = 0; i < num_intervals - 1; ++i) { | |
93 aggregated_counter_->Add(0); | |
94 if (observer_) | |
95 observer_->OnMetricUpdated(0); | |
96 } | |
97 } | |
98 return true; | 112 return true; |
99 } | 113 } |
100 | 114 |
101 void StatsCounter::Set(int sample) { | 115 void StatsCounter::Set(int sample) { |
102 TryProcess(); | 116 TryProcess(); |
103 ++num_samples_; | 117 ++num_samples_; |
104 sum_ = sample; | 118 sum_ = sample; |
119 paused_ = false; | |
105 } | 120 } |
106 | 121 |
107 void StatsCounter::Add(int sample) { | 122 void StatsCounter::Add(int sample) { |
108 TryProcess(); | 123 TryProcess(); |
109 ++num_samples_; | 124 ++num_samples_; |
110 sum_ += sample; | 125 sum_ += sample; |
111 | 126 |
112 if (num_samples_ == 1) | 127 if (num_samples_ == 1) |
113 max_ = sample; | 128 max_ = sample; |
114 max_ = std::max(sample, max_); | 129 max_ = std::max(sample, max_); |
130 paused_ = false; | |
131 } | |
132 | |
133 // Reports periodically computed metric. | |
134 void StatsCounter::ReportMetricToAggregatedCounter( | |
135 int value, | |
136 int num_values_to_add) const { | |
137 for (int i = 0; i < num_values_to_add; ++i) { | |
138 aggregated_counter_->Add(value); | |
139 if (observer_) | |
140 observer_->OnMetricUpdated(value); | |
141 } | |
115 } | 142 } |
116 | 143 |
117 void StatsCounter::TryProcess() { | 144 void StatsCounter::TryProcess() { |
118 if (!TimeToProcess()) | 145 int elapsed_intervals; |
146 if (!TimeToProcess(&elapsed_intervals)) | |
119 return; | 147 return; |
120 | 148 |
149 // Get and report periodically computed metric. | |
121 int metric; | 150 int metric; |
122 if (GetMetric(&metric)) { | 151 if (GetMetric(&metric)) |
123 aggregated_counter_->Add(metric); | 152 ReportMetricToAggregatedCounter(metric, 1); |
124 if (observer_) | 153 |
125 observer_->OnMetricUpdated(metric); | 154 // Report value for elapsed intervals without samples. |
155 if (IncludeEmptyIntervals()) { | |
156 int empty_intervals = | |
157 (num_samples_ == 0) ? elapsed_intervals : (elapsed_intervals - 1); | |
stefan-webrtc
2016/09/08 07:28:14
I don't follow this, maybe add a comment on why we
åsapersson
2016/09/08 13:15:02
Done.
| |
158 ReportMetricToAggregatedCounter(GetValueForEmptyInterval(), | |
159 empty_intervals); | |
126 } | 160 } |
127 last_sum_ = sum_; | 161 |
162 // Reset samples for elapsed interval. | |
163 if (num_samples_ > 0) | |
164 last_sum_ = sum_; | |
128 sum_ = 0; | 165 sum_ = 0; |
129 max_ = 0; | 166 max_ = 0; |
130 num_samples_ = 0; | 167 num_samples_ = 0; |
131 } | 168 } |
132 | 169 |
170 bool StatsCounter::IncludeEmptyIntervals() const { | |
171 return include_empty_intervals_ && !paused_ && | |
172 aggregated_counter_->HasSamples(); | |
173 } | |
174 | |
133 // StatsCounter sub-classes. | 175 // StatsCounter sub-classes. |
134 AvgCounter::AvgCounter(Clock* clock, StatsCounterObserver* observer) | 176 AvgCounter::AvgCounter(Clock* clock, |
135 : StatsCounter(clock, | 177 StatsCounterObserver* observer, |
136 false, // |include_empty_intervals| | 178 bool include_empty_intervals) |
137 observer) {} | 179 : StatsCounter(clock, include_empty_intervals, observer) {} |
138 | 180 |
139 void AvgCounter::Add(int sample) { | 181 void AvgCounter::Add(int sample) { |
140 StatsCounter::Add(sample); | 182 StatsCounter::Add(sample); |
141 } | 183 } |
142 | 184 |
143 bool AvgCounter::GetMetric(int* metric) const { | 185 bool AvgCounter::GetMetric(int* metric) const { |
144 if (num_samples_ == 0) | 186 if (num_samples_ == 0) |
145 return false; | 187 return false; |
146 *metric = (sum_ + num_samples_ / 2) / num_samples_; | 188 *metric = (sum_ + num_samples_ / 2) / num_samples_; |
147 return true; | 189 return true; |
148 } | 190 } |
149 | 191 |
192 int AvgCounter::GetValueForEmptyInterval() const { | |
193 return aggregated_counter_->last_sample(); | |
194 } | |
195 | |
150 MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer) | 196 MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer) |
151 : StatsCounter(clock, | 197 : StatsCounter(clock, |
152 false, // |include_empty_intervals| | 198 false, // |include_empty_intervals| |
153 observer) {} | 199 observer) {} |
154 | 200 |
155 void MaxCounter::Add(int sample) { | 201 void MaxCounter::Add(int sample) { |
156 StatsCounter::Add(sample); | 202 StatsCounter::Add(sample); |
157 } | 203 } |
158 | 204 |
159 bool MaxCounter::GetMetric(int* metric) const { | 205 bool MaxCounter::GetMetric(int* metric) const { |
160 if (num_samples_ == 0) | 206 if (num_samples_ == 0) |
161 return false; | 207 return false; |
162 *metric = max_; | 208 *metric = max_; |
163 return true; | 209 return true; |
164 } | 210 } |
165 | 211 |
212 int MaxCounter::GetValueForEmptyInterval() const { | |
213 RTC_NOTREACHED(); | |
214 return 0; | |
215 } | |
216 | |
166 PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer) | 217 PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer) |
167 : StatsCounter(clock, | 218 : StatsCounter(clock, |
168 false, // |include_empty_intervals| | 219 false, // |include_empty_intervals| |
169 observer) {} | 220 observer) {} |
170 | 221 |
171 void PercentCounter::Add(bool sample) { | 222 void PercentCounter::Add(bool sample) { |
172 StatsCounter::Add(sample ? 1 : 0); | 223 StatsCounter::Add(sample ? 1 : 0); |
173 } | 224 } |
174 | 225 |
175 bool PercentCounter::GetMetric(int* metric) const { | 226 bool PercentCounter::GetMetric(int* metric) const { |
176 if (num_samples_ == 0) | 227 if (num_samples_ == 0) |
177 return false; | 228 return false; |
178 *metric = (sum_ * 100 + num_samples_ / 2) / num_samples_; | 229 *metric = (sum_ * 100 + num_samples_ / 2) / num_samples_; |
179 return true; | 230 return true; |
180 } | 231 } |
181 | 232 |
233 int PercentCounter::GetValueForEmptyInterval() const { | |
234 RTC_NOTREACHED(); | |
235 return 0; | |
236 } | |
237 | |
182 PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer) | 238 PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer) |
183 : StatsCounter(clock, | 239 : StatsCounter(clock, |
184 false, // |include_empty_intervals| | 240 false, // |include_empty_intervals| |
185 observer) {} | 241 observer) {} |
186 | 242 |
187 void PermilleCounter::Add(bool sample) { | 243 void PermilleCounter::Add(bool sample) { |
188 StatsCounter::Add(sample ? 1 : 0); | 244 StatsCounter::Add(sample ? 1 : 0); |
189 } | 245 } |
190 | 246 |
191 bool PermilleCounter::GetMetric(int* metric) const { | 247 bool PermilleCounter::GetMetric(int* metric) const { |
192 if (num_samples_ == 0) | 248 if (num_samples_ == 0) |
193 return false; | 249 return false; |
194 *metric = (sum_ * 1000 + num_samples_ / 2) / num_samples_; | 250 *metric = (sum_ * 1000 + num_samples_ / 2) / num_samples_; |
195 return true; | 251 return true; |
196 } | 252 } |
197 | 253 |
254 int PermilleCounter::GetValueForEmptyInterval() const { | |
255 RTC_NOTREACHED(); | |
256 return 0; | |
257 } | |
258 | |
198 RateCounter::RateCounter(Clock* clock, | 259 RateCounter::RateCounter(Clock* clock, |
199 StatsCounterObserver* observer, | 260 StatsCounterObserver* observer, |
200 bool include_empty_intervals) | 261 bool include_empty_intervals) |
201 : StatsCounter(clock, include_empty_intervals, observer) {} | 262 : StatsCounter(clock, include_empty_intervals, observer) {} |
202 | 263 |
203 void RateCounter::Add(int sample) { | 264 void RateCounter::Add(int sample) { |
204 StatsCounter::Add(sample); | 265 StatsCounter::Add(sample); |
205 } | 266 } |
206 | 267 |
207 bool RateCounter::GetMetric(int* metric) const { | 268 bool RateCounter::GetMetric(int* metric) const { |
208 if (num_samples_ == 0) | 269 if (num_samples_ == 0) |
209 return false; | 270 return false; |
210 *metric = (sum_ * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | 271 *metric = (sum_ * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; |
211 return true; | 272 return true; |
212 } | 273 } |
213 | 274 |
275 int RateCounter::GetValueForEmptyInterval() const { | |
276 return 0; | |
277 } | |
278 | |
214 RateAccCounter::RateAccCounter(Clock* clock, | 279 RateAccCounter::RateAccCounter(Clock* clock, |
215 StatsCounterObserver* observer, | 280 StatsCounterObserver* observer, |
216 bool include_empty_intervals) | 281 bool include_empty_intervals) |
217 : StatsCounter(clock, include_empty_intervals, observer) {} | 282 : StatsCounter(clock, include_empty_intervals, observer) {} |
218 | 283 |
219 void RateAccCounter::Set(int sample) { | 284 void RateAccCounter::Set(int sample) { |
220 StatsCounter::Set(sample); | 285 StatsCounter::Set(sample); |
221 } | 286 } |
222 | 287 |
223 bool RateAccCounter::GetMetric(int* metric) const { | 288 bool RateAccCounter::GetMetric(int* metric) const { |
224 if (num_samples_ == 0 || last_sum_ > sum_) | 289 if (num_samples_ == 0 || last_sum_ > sum_) |
225 return false; | 290 return false; |
226 *metric = | 291 *metric = |
227 ((sum_ - last_sum_) * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | 292 ((sum_ - last_sum_) * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; |
228 return true; | 293 return true; |
229 } | 294 } |
230 | 295 |
296 int RateAccCounter::GetValueForEmptyInterval() const { | |
297 return 0; | |
298 } | |
299 | |
231 } // namespace webrtc | 300 } // namespace webrtc |
OLD | NEW |