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 #include <limits> | |
15 #include <map> | |
14 | 16 |
15 #include "webrtc/system_wrappers/include/clock.h" | 17 #include "webrtc/system_wrappers/include/clock.h" |
16 | 18 |
17 namespace webrtc { | 19 namespace webrtc { |
18 | 20 |
19 namespace { | 21 namespace { |
20 // Periodic time interval for processing samples. | 22 // Periodic time interval for processing samples. |
21 const int64_t kProcessIntervalMs = 2000; | 23 const int64_t kProcessIntervalMs = 2000; |
24 | |
25 const uint32_t kSsrc0 = 0; | |
26 | |
22 } // namespace | 27 } // namespace |
23 | 28 |
24 // Class holding periodically computed metrics. | 29 // Class holding periodically computed metrics. |
25 class AggregatedCounter { | 30 class AggregatedCounter { |
26 public: | 31 public: |
27 AggregatedCounter() : sum_(0) {} | 32 AggregatedCounter() : sum_(0) {} |
28 ~AggregatedCounter() {} | 33 ~AggregatedCounter() {} |
29 | 34 |
30 void Add(int sample) { | 35 void Add(int sample) { |
31 sum_ += sample; | 36 sum_ += sample; |
(...skipping 15 matching lines...) Expand all Loading... | |
47 void Compute() { | 52 void Compute() { |
48 if (stats_.num_samples == 0) | 53 if (stats_.num_samples == 0) |
49 return; | 54 return; |
50 | 55 |
51 stats_.average = (sum_ + stats_.num_samples / 2) / stats_.num_samples; | 56 stats_.average = (sum_ + stats_.num_samples / 2) / stats_.num_samples; |
52 } | 57 } |
53 int64_t sum_; | 58 int64_t sum_; |
54 AggregatedStats stats_; | 59 AggregatedStats stats_; |
55 }; | 60 }; |
56 | 61 |
62 // Class holding gathered samples within a process interval. | |
63 class Samples { | |
64 public: | |
65 Samples() {} | |
66 ~Samples() {} | |
67 | |
68 void Add(int sample, uint32_t ssrc) { samples_[ssrc].Add(sample); } | |
69 void Set(int sample, uint32_t ssrc) { samples_[ssrc].Set(sample); } | |
70 | |
71 int64_t Num() const { | |
stefan-webrtc
2016/09/08 07:13:34
Should we rename Num and num_ Count() and count_?
åsapersson
2016/09/19 12:45:40
Done.
| |
72 int64_t num = 0; | |
73 for (const auto& it : samples_) | |
74 num += it.second.num_; | |
75 return num; | |
76 } | |
77 | |
78 int64_t Sum() const { | |
79 int64_t sum = 0; | |
80 for (const auto& it : samples_) | |
81 sum += it.second.sum_; | |
82 return sum; | |
83 } | |
84 | |
85 int Max() const { | |
86 int max = std::numeric_limits<int>::min(); | |
87 for (const auto& it : samples_) | |
88 max = std::max(it.second.max_, max); | |
89 return max; | |
90 } | |
91 | |
92 void Reset() { | |
93 for (auto& it : samples_) | |
94 it.second.Reset(); | |
95 } | |
96 | |
97 int64_t Diff() const { | |
98 int64_t sum_diff = 0; | |
99 int num = 0; | |
100 for (const auto& it : samples_) { | |
101 if (it.second.num_ > 0) { | |
102 int64_t diff = it.second.sum_ - it.second.last_sum_; | |
103 if (diff >= 0) { | |
104 sum_diff += diff; | |
105 ++num; | |
106 } | |
107 } | |
108 } | |
109 return (num > 0) ? sum_diff : -1; | |
110 } | |
111 | |
112 private: | |
113 struct Stats { | |
114 void Add(int sample) { | |
115 sum_ += sample; | |
116 ++num_; | |
117 max_ = std::max(sample, max_); | |
118 } | |
119 void Set(int sample) { | |
120 sum_ = sample; | |
121 ++num_; | |
122 } | |
123 void Reset() { | |
124 if (num_ > 0) | |
125 last_sum_ = sum_; | |
126 sum_ = 0; | |
127 num_ = 0; | |
128 max_ = std::numeric_limits<int>::min(); | |
129 } | |
130 | |
131 int max_ = std::numeric_limits<int>::min(); | |
132 int64_t num_ = 0; | |
133 int64_t sum_ = 0; | |
134 int64_t last_sum_ = 0; | |
135 }; | |
136 | |
137 std::map<uint32_t, Stats> samples_; // Gathered samples mapped by SSRC. | |
138 }; | |
139 | |
57 // StatsCounter class. | 140 // StatsCounter class. |
58 StatsCounter::StatsCounter(Clock* clock, | 141 StatsCounter::StatsCounter(Clock* clock, |
59 bool include_empty_intervals, | 142 bool include_empty_intervals, |
60 StatsCounterObserver* observer) | 143 StatsCounterObserver* observer) |
61 : max_(0), | 144 : samples_(new Samples()), |
62 sum_(0), | 145 include_empty_intervals_(include_empty_intervals), |
63 num_samples_(0), | |
64 last_sum_(0), | |
65 clock_(clock), | 146 clock_(clock), |
66 include_empty_intervals_(include_empty_intervals), | |
67 observer_(observer), | 147 observer_(observer), |
68 aggregated_counter_(new AggregatedCounter()), | 148 aggregated_counter_(new AggregatedCounter()), |
69 last_process_time_ms_(-1) {} | 149 last_process_time_ms_(-1) {} |
70 | 150 |
71 StatsCounter::~StatsCounter() {} | 151 StatsCounter::~StatsCounter() {} |
72 | 152 |
73 AggregatedStats StatsCounter::GetStats() { | 153 AggregatedStats StatsCounter::GetStats() { |
74 return aggregated_counter_->ComputeStats(); | 154 return aggregated_counter_->ComputeStats(); |
75 } | 155 } |
76 | 156 |
(...skipping 14 matching lines...) Expand all Loading... | |
91 if (include_empty_intervals_) { | 171 if (include_empty_intervals_) { |
92 for (int64_t i = 0; i < num_intervals - 1; ++i) { | 172 for (int64_t i = 0; i < num_intervals - 1; ++i) { |
93 aggregated_counter_->Add(0); | 173 aggregated_counter_->Add(0); |
94 if (observer_) | 174 if (observer_) |
95 observer_->OnMetricUpdated(0); | 175 observer_->OnMetricUpdated(0); |
96 } | 176 } |
97 } | 177 } |
98 return true; | 178 return true; |
99 } | 179 } |
100 | 180 |
101 void StatsCounter::Set(int sample) { | 181 void StatsCounter::Set(int sample, uint32_t ssrc) { |
102 TryProcess(); | 182 TryProcess(); |
103 ++num_samples_; | 183 samples_->Set(sample, ssrc); |
104 sum_ = sample; | |
105 } | 184 } |
106 | 185 |
107 void StatsCounter::Add(int sample) { | 186 void StatsCounter::Add(int sample) { |
stefan-webrtc
2016/09/08 07:15:13
Do you think it would be better to just expose the
åsapersson
2016/09/19 12:45:40
It is only one subclass that have use for setting
| |
108 TryProcess(); | 187 TryProcess(); |
109 ++num_samples_; | 188 samples_->Add(sample, kSsrc0); |
110 sum_ += sample; | |
111 | |
112 if (num_samples_ == 1) | |
113 max_ = sample; | |
114 max_ = std::max(sample, max_); | |
115 } | 189 } |
116 | 190 |
117 void StatsCounter::TryProcess() { | 191 void StatsCounter::TryProcess() { |
118 if (!TimeToProcess()) | 192 if (!TimeToProcess()) |
119 return; | 193 return; |
120 | 194 |
121 int metric; | 195 int metric; |
122 if (GetMetric(&metric)) { | 196 if (GetMetric(&metric)) { |
123 aggregated_counter_->Add(metric); | 197 aggregated_counter_->Add(metric); |
124 if (observer_) | 198 if (observer_) |
125 observer_->OnMetricUpdated(metric); | 199 observer_->OnMetricUpdated(metric); |
126 } | 200 } |
127 last_sum_ = sum_; | 201 |
128 sum_ = 0; | 202 samples_->Reset(); |
129 max_ = 0; | |
130 num_samples_ = 0; | |
131 } | 203 } |
132 | 204 |
133 // StatsCounter sub-classes. | 205 // StatsCounter sub-classes. |
134 AvgCounter::AvgCounter(Clock* clock, StatsCounterObserver* observer) | 206 AvgCounter::AvgCounter(Clock* clock, StatsCounterObserver* observer) |
135 : StatsCounter(clock, | 207 : StatsCounter(clock, |
136 false, // |include_empty_intervals| | 208 false, // |include_empty_intervals| |
137 observer) {} | 209 observer) {} |
138 | 210 |
139 void AvgCounter::Add(int sample) { | 211 void AvgCounter::Add(int sample) { |
140 StatsCounter::Add(sample); | 212 StatsCounter::Add(sample); |
141 } | 213 } |
142 | 214 |
143 bool AvgCounter::GetMetric(int* metric) const { | 215 bool AvgCounter::GetMetric(int* metric) const { |
144 if (num_samples_ == 0) | 216 int64_t num = samples_->Num(); |
217 if (num == 0) | |
145 return false; | 218 return false; |
stefan-webrtc
2016/09/08 07:13:34
This is a fairly expensive way of checking for no
åsapersson
2016/09/19 12:45:40
The expected size will be one for all sub-classes
| |
146 *metric = (sum_ + num_samples_ / 2) / num_samples_; | 219 |
220 *metric = (samples_->Sum() + num / 2) / num; | |
147 return true; | 221 return true; |
148 } | 222 } |
149 | 223 |
150 MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer) | 224 MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer) |
151 : StatsCounter(clock, | 225 : StatsCounter(clock, |
152 false, // |include_empty_intervals| | 226 false, // |include_empty_intervals| |
153 observer) {} | 227 observer) {} |
154 | 228 |
155 void MaxCounter::Add(int sample) { | 229 void MaxCounter::Add(int sample) { |
156 StatsCounter::Add(sample); | 230 StatsCounter::Add(sample); |
157 } | 231 } |
158 | 232 |
159 bool MaxCounter::GetMetric(int* metric) const { | 233 bool MaxCounter::GetMetric(int* metric) const { |
160 if (num_samples_ == 0) | 234 int64_t num = samples_->Num(); |
235 if (num == 0) | |
161 return false; | 236 return false; |
162 *metric = max_; | 237 |
238 *metric = samples_->Max(); | |
163 return true; | 239 return true; |
164 } | 240 } |
165 | 241 |
166 PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer) | 242 PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer) |
167 : StatsCounter(clock, | 243 : StatsCounter(clock, |
168 false, // |include_empty_intervals| | 244 false, // |include_empty_intervals| |
169 observer) {} | 245 observer) {} |
170 | 246 |
171 void PercentCounter::Add(bool sample) { | 247 void PercentCounter::Add(bool sample) { |
172 StatsCounter::Add(sample ? 1 : 0); | 248 StatsCounter::Add(sample ? 1 : 0); |
173 } | 249 } |
174 | 250 |
175 bool PercentCounter::GetMetric(int* metric) const { | 251 bool PercentCounter::GetMetric(int* metric) const { |
176 if (num_samples_ == 0) | 252 int64_t num = samples_->Num(); |
253 if (num == 0) | |
177 return false; | 254 return false; |
178 *metric = (sum_ * 100 + num_samples_ / 2) / num_samples_; | 255 |
256 *metric = (samples_->Sum() * 100 + num / 2) / num; | |
179 return true; | 257 return true; |
180 } | 258 } |
181 | 259 |
182 PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer) | 260 PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer) |
183 : StatsCounter(clock, | 261 : StatsCounter(clock, |
184 false, // |include_empty_intervals| | 262 false, // |include_empty_intervals| |
185 observer) {} | 263 observer) {} |
186 | 264 |
187 void PermilleCounter::Add(bool sample) { | 265 void PermilleCounter::Add(bool sample) { |
188 StatsCounter::Add(sample ? 1 : 0); | 266 StatsCounter::Add(sample ? 1 : 0); |
189 } | 267 } |
190 | 268 |
191 bool PermilleCounter::GetMetric(int* metric) const { | 269 bool PermilleCounter::GetMetric(int* metric) const { |
192 if (num_samples_ == 0) | 270 int64_t num = samples_->Num(); |
271 if (num == 0) | |
193 return false; | 272 return false; |
194 *metric = (sum_ * 1000 + num_samples_ / 2) / num_samples_; | 273 |
274 *metric = (samples_->Sum() * 1000 + num / 2) / num; | |
195 return true; | 275 return true; |
196 } | 276 } |
197 | 277 |
198 RateCounter::RateCounter(Clock* clock, | 278 RateCounter::RateCounter(Clock* clock, |
199 StatsCounterObserver* observer, | 279 StatsCounterObserver* observer, |
200 bool include_empty_intervals) | 280 bool include_empty_intervals) |
201 : StatsCounter(clock, include_empty_intervals, observer) {} | 281 : StatsCounter(clock, include_empty_intervals, observer) {} |
202 | 282 |
203 void RateCounter::Add(int sample) { | 283 void RateCounter::Add(int sample) { |
204 StatsCounter::Add(sample); | 284 StatsCounter::Add(sample); |
205 } | 285 } |
206 | 286 |
207 bool RateCounter::GetMetric(int* metric) const { | 287 bool RateCounter::GetMetric(int* metric) const { |
208 if (num_samples_ == 0) | 288 if (samples_->Num() == 0) |
209 return false; | 289 return false; |
210 *metric = (sum_ * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | 290 |
291 *metric = | |
292 (samples_->Sum() * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | |
211 return true; | 293 return true; |
212 } | 294 } |
213 | 295 |
214 RateAccCounter::RateAccCounter(Clock* clock, | 296 RateAccCounter::RateAccCounter(Clock* clock, |
215 StatsCounterObserver* observer, | 297 StatsCounterObserver* observer, |
216 bool include_empty_intervals) | 298 bool include_empty_intervals) |
217 : StatsCounter(clock, include_empty_intervals, observer) {} | 299 : StatsCounter(clock, include_empty_intervals, observer) {} |
218 | 300 |
219 void RateAccCounter::Set(int sample) { | 301 void RateAccCounter::Set(int sample, uint32_t ssrc) { |
220 StatsCounter::Set(sample); | 302 StatsCounter::Set(sample, ssrc); |
221 } | 303 } |
222 | 304 |
223 bool RateAccCounter::GetMetric(int* metric) const { | 305 bool RateAccCounter::GetMetric(int* metric) const { |
224 if (num_samples_ == 0 || last_sum_ > sum_) | 306 if (samples_->Num() == 0) |
225 return false; | 307 return false; |
226 *metric = | 308 |
227 ((sum_ - last_sum_) * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | 309 int64_t diff = samples_->Diff(); |
310 if (diff < 0 || (!include_empty_intervals_ && diff == 0)) | |
311 return false; | |
312 | |
313 *metric = (diff * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs; | |
228 return true; | 314 return true; |
229 } | 315 } |
230 | 316 |
231 } // namespace webrtc | 317 } // namespace webrtc |
OLD | NEW |