blob: bcbf9124a252239cc9f3e7705b91f1307ba6e95c [file] [log] [blame]
asapersson0e9d6d92016-05-23 06:07:55 -07001/*
2 * Copyright (c) 2016 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
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "video/stats_counter.h"
asapersson0e9d6d92016-05-23 06:07:55 -070012
13#include <algorithm>
asaperssonfe647f42016-11-01 00:21:34 -070014#include <limits>
15#include <map>
asapersson0e9d6d92016-05-23 06:07:55 -070016
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020017#include "rtc_base/checks.h"
18#include "system_wrappers/include/clock.h"
asapersson0e9d6d92016-05-23 06:07:55 -070019
20namespace webrtc {
21
22namespace {
asaperssone402a142016-10-06 23:39:15 -070023// Default periodic time interval for processing samples.
24const int64_t kDefaultProcessIntervalMs = 2000;
asaperssonfe647f42016-11-01 00:21:34 -070025const uint32_t kStreamId0 = 0;
asapersson0e9d6d92016-05-23 06:07:55 -070026} // namespace
27
asapersson43cb7162016-11-15 08:20:48 -080028std::string AggregatedStats::ToString() const {
asapersson076c0112016-11-30 05:17:16 -080029 return ToStringWithMultiplier(1);
30}
31
32std::string AggregatedStats::ToStringWithMultiplier(int multiplier) const {
asapersson43cb7162016-11-15 08:20:48 -080033 std::stringstream ss;
34 ss << "periodic_samples:" << num_samples << ", {";
asapersson076c0112016-11-30 05:17:16 -080035 ss << "min:" << (min * multiplier) << ", ";
36 ss << "avg:" << (average * multiplier) << ", ";
37 ss << "max:" << (max * multiplier) << "}";
asapersson43cb7162016-11-15 08:20:48 -080038 return ss.str();
39}
40
asapersson0e9d6d92016-05-23 06:07:55 -070041// Class holding periodically computed metrics.
42class AggregatedCounter {
43 public:
asaperssonce2e1362016-09-09 00:13:35 -070044 AggregatedCounter() : last_sample_(0), sum_samples_(0) {}
asapersson0e9d6d92016-05-23 06:07:55 -070045 ~AggregatedCounter() {}
46
47 void Add(int sample) {
asaperssonce2e1362016-09-09 00:13:35 -070048 last_sample_ = sample;
49 sum_samples_ += sample;
asapersson0e9d6d92016-05-23 06:07:55 -070050 ++stats_.num_samples;
51 if (stats_.num_samples == 1) {
52 stats_.min = sample;
53 stats_.max = sample;
54 }
55 stats_.min = std::min(sample, stats_.min);
56 stats_.max = std::max(sample, stats_.max);
57 }
58
59 AggregatedStats ComputeStats() {
60 Compute();
61 return stats_;
62 }
63
asaperssonce2e1362016-09-09 00:13:35 -070064 bool Empty() const { return stats_.num_samples == 0; }
65
66 int last_sample() const { return last_sample_; }
67
asapersson0e9d6d92016-05-23 06:07:55 -070068 private:
69 void Compute() {
70 if (stats_.num_samples == 0)
71 return;
72
asaperssonce2e1362016-09-09 00:13:35 -070073 stats_.average =
74 (sum_samples_ + stats_.num_samples / 2) / stats_.num_samples;
asapersson0e9d6d92016-05-23 06:07:55 -070075 }
asaperssonce2e1362016-09-09 00:13:35 -070076 int last_sample_;
77 int64_t sum_samples_;
asapersson0e9d6d92016-05-23 06:07:55 -070078 AggregatedStats stats_;
79};
80
asaperssonfe647f42016-11-01 00:21:34 -070081// Class holding gathered samples within a process interval.
82class Samples {
83 public:
84 Samples() : total_count_(0) {}
85 ~Samples() {}
86
87 void Add(int sample, uint32_t stream_id) {
88 samples_[stream_id].Add(sample);
89 ++total_count_;
90 }
asapersson93e1e232017-02-06 05:18:35 -080091 void Set(int64_t sample, uint32_t stream_id) {
asaperssonfe647f42016-11-01 00:21:34 -070092 samples_[stream_id].Set(sample);
93 ++total_count_;
94 }
asapersson93e1e232017-02-06 05:18:35 -080095 void SetLast(int64_t sample, uint32_t stream_id) {
96 samples_[stream_id].SetLast(sample);
97 }
98 int64_t GetLast(uint32_t stream_id) { return samples_[stream_id].GetLast(); }
asaperssonfe647f42016-11-01 00:21:34 -070099
100 int64_t Count() const { return total_count_; }
101 bool Empty() const { return total_count_ == 0; }
102
103 int64_t Sum() const {
104 int64_t sum = 0;
105 for (const auto& it : samples_)
106 sum += it.second.sum_;
107 return sum;
108 }
109
110 int Max() const {
111 int max = std::numeric_limits<int>::min();
112 for (const auto& it : samples_)
113 max = std::max(it.second.max_, max);
114 return max;
115 }
116
117 void Reset() {
118 for (auto& it : samples_)
119 it.second.Reset();
120 total_count_ = 0;
121 }
122
123 int64_t Diff() const {
124 int64_t sum_diff = 0;
125 int count = 0;
126 for (const auto& it : samples_) {
127 if (it.second.count_ > 0) {
128 int64_t diff = it.second.sum_ - it.second.last_sum_;
129 if (diff >= 0) {
130 sum_diff += diff;
131 ++count;
132 }
133 }
134 }
135 return (count > 0) ? sum_diff : -1;
136 }
137
138 private:
139 struct Stats {
140 void Add(int sample) {
141 sum_ += sample;
142 ++count_;
143 max_ = std::max(sample, max_);
144 }
asapersson93e1e232017-02-06 05:18:35 -0800145 void Set(int64_t sample) {
asaperssonfe647f42016-11-01 00:21:34 -0700146 sum_ = sample;
147 ++count_;
148 }
asapersson93e1e232017-02-06 05:18:35 -0800149 void SetLast(int64_t sample) { last_sum_ = sample; }
150 int64_t GetLast() const { return last_sum_; }
asaperssonfe647f42016-11-01 00:21:34 -0700151 void Reset() {
152 if (count_ > 0)
153 last_sum_ = sum_;
154 sum_ = 0;
155 count_ = 0;
156 max_ = std::numeric_limits<int>::min();
157 }
158
159 int max_ = std::numeric_limits<int>::min();
160 int64_t count_ = 0;
161 int64_t sum_ = 0;
162 int64_t last_sum_ = 0;
163 };
164
165 int64_t total_count_;
166 std::map<uint32_t, Stats> samples_; // Gathered samples mapped by stream id.
167};
168
asapersson0e9d6d92016-05-23 06:07:55 -0700169// StatsCounter class.
170StatsCounter::StatsCounter(Clock* clock,
asaperssone402a142016-10-06 23:39:15 -0700171 int64_t process_intervals_ms,
asapersson0e9d6d92016-05-23 06:07:55 -0700172 bool include_empty_intervals,
173 StatsCounterObserver* observer)
asaperssonfe647f42016-11-01 00:21:34 -0700174 : include_empty_intervals_(include_empty_intervals),
asaperssone402a142016-10-06 23:39:15 -0700175 process_intervals_ms_(process_intervals_ms),
asaperssonfe647f42016-11-01 00:21:34 -0700176 aggregated_counter_(new AggregatedCounter()),
177 samples_(new Samples()),
asapersson0e9d6d92016-05-23 06:07:55 -0700178 clock_(clock),
asapersson0e9d6d92016-05-23 06:07:55 -0700179 observer_(observer),
asaperssonce2e1362016-09-09 00:13:35 -0700180 last_process_time_ms_(-1),
asapersson93e1e232017-02-06 05:18:35 -0800181 paused_(false),
182 pause_time_ms_(-1),
183 min_pause_time_ms_(0) {
asaperssone402a142016-10-06 23:39:15 -0700184 RTC_DCHECK_GT(process_intervals_ms_, 0);
185}
asapersson0e9d6d92016-05-23 06:07:55 -0700186
187StatsCounter::~StatsCounter() {}
188
189AggregatedStats StatsCounter::GetStats() {
190 return aggregated_counter_->ComputeStats();
191}
192
asaperssonce2e1362016-09-09 00:13:35 -0700193AggregatedStats StatsCounter::ProcessAndGetStats() {
194 if (HasSample())
195 TryProcess();
196 return aggregated_counter_->ComputeStats();
197}
198
asapersson93e1e232017-02-06 05:18:35 -0800199void StatsCounter::ProcessAndPauseForDuration(int64_t min_pause_time_ms) {
200 ProcessAndPause();
201 min_pause_time_ms_ = min_pause_time_ms;
202}
203
asaperssonce2e1362016-09-09 00:13:35 -0700204void StatsCounter::ProcessAndPause() {
205 if (HasSample())
206 TryProcess();
207 paused_ = true;
asapersson93e1e232017-02-06 05:18:35 -0800208 pause_time_ms_ = clock_->TimeInMilliseconds();
209}
210
211void StatsCounter::ProcessAndStopPause() {
212 if (HasSample())
213 TryProcess();
214 Resume();
asaperssonce2e1362016-09-09 00:13:35 -0700215}
216
asapersson250fd972016-09-08 00:07:21 -0700217bool StatsCounter::HasSample() const {
218 return last_process_time_ms_ != -1;
219}
220
asaperssonce2e1362016-09-09 00:13:35 -0700221bool StatsCounter::TimeToProcess(int* elapsed_intervals) {
asapersson0e9d6d92016-05-23 06:07:55 -0700222 int64_t now = clock_->TimeInMilliseconds();
223 if (last_process_time_ms_ == -1)
224 last_process_time_ms_ = now;
225
226 int64_t diff_ms = now - last_process_time_ms_;
asaperssone402a142016-10-06 23:39:15 -0700227 if (diff_ms < process_intervals_ms_)
asapersson0e9d6d92016-05-23 06:07:55 -0700228 return false;
229
asaperssone402a142016-10-06 23:39:15 -0700230 // Advance number of complete |process_intervals_ms_| that have passed.
231 int64_t num_intervals = diff_ms / process_intervals_ms_;
232 last_process_time_ms_ += num_intervals * process_intervals_ms_;
asapersson0e9d6d92016-05-23 06:07:55 -0700233
asaperssonce2e1362016-09-09 00:13:35 -0700234 *elapsed_intervals = num_intervals;
asapersson0e9d6d92016-05-23 06:07:55 -0700235 return true;
236}
237
asapersson0e9d6d92016-05-23 06:07:55 -0700238void StatsCounter::Add(int sample) {
239 TryProcess();
asaperssonfe647f42016-11-01 00:21:34 -0700240 samples_->Add(sample, kStreamId0);
asapersson93e1e232017-02-06 05:18:35 -0800241 ResumeIfMinTimePassed();
242}
243
244void StatsCounter::Set(int64_t sample, uint32_t stream_id) {
245 if (paused_ && sample == samples_->GetLast(stream_id)) {
246 // Do not add same sample while paused (will reset pause).
247 return;
248 }
249 TryProcess();
250 samples_->Set(sample, stream_id);
251 ResumeIfMinTimePassed();
252}
253
254void StatsCounter::SetLast(int64_t sample, uint32_t stream_id) {
255 RTC_DCHECK(!HasSample()) << "Should be set before first sample is added.";
256 samples_->SetLast(sample, stream_id);
asaperssonce2e1362016-09-09 00:13:35 -0700257}
258
259// Reports periodically computed metric.
260void StatsCounter::ReportMetricToAggregatedCounter(
261 int value,
262 int num_values_to_add) const {
263 for (int i = 0; i < num_values_to_add; ++i) {
264 aggregated_counter_->Add(value);
265 if (observer_)
266 observer_->OnMetricUpdated(value);
267 }
asapersson0e9d6d92016-05-23 06:07:55 -0700268}
269
270void StatsCounter::TryProcess() {
asaperssonce2e1362016-09-09 00:13:35 -0700271 int elapsed_intervals;
272 if (!TimeToProcess(&elapsed_intervals))
asapersson0e9d6d92016-05-23 06:07:55 -0700273 return;
274
asaperssonce2e1362016-09-09 00:13:35 -0700275 // Get and report periodically computed metric.
asapersson0e9d6d92016-05-23 06:07:55 -0700276 int metric;
asaperssonce2e1362016-09-09 00:13:35 -0700277 if (GetMetric(&metric))
278 ReportMetricToAggregatedCounter(metric, 1);
279
280 // Report value for elapsed intervals without samples.
281 if (IncludeEmptyIntervals()) {
282 // If there are no samples, all elapsed intervals are empty (otherwise one
283 // interval contains sample(s), discard this interval).
284 int empty_intervals =
asaperssonfe647f42016-11-01 00:21:34 -0700285 samples_->Empty() ? elapsed_intervals : (elapsed_intervals - 1);
asaperssonce2e1362016-09-09 00:13:35 -0700286 ReportMetricToAggregatedCounter(GetValueForEmptyInterval(),
287 empty_intervals);
asapersson0e9d6d92016-05-23 06:07:55 -0700288 }
asaperssonce2e1362016-09-09 00:13:35 -0700289
290 // Reset samples for elapsed interval.
asaperssonfe647f42016-11-01 00:21:34 -0700291 samples_->Reset();
asapersson0e9d6d92016-05-23 06:07:55 -0700292}
293
asaperssonce2e1362016-09-09 00:13:35 -0700294bool StatsCounter::IncludeEmptyIntervals() const {
295 return include_empty_intervals_ && !paused_ && !aggregated_counter_->Empty();
296}
asapersson93e1e232017-02-06 05:18:35 -0800297void StatsCounter::ResumeIfMinTimePassed() {
298 if (paused_ &&
299 (clock_->TimeInMilliseconds() - pause_time_ms_) >= min_pause_time_ms_) {
300 Resume();
301 }
302}
303
304void StatsCounter::Resume() {
305 paused_ = false;
306 min_pause_time_ms_ = 0;
307}
asaperssonce2e1362016-09-09 00:13:35 -0700308
asapersson0e9d6d92016-05-23 06:07:55 -0700309// StatsCounter sub-classes.
asaperssonce2e1362016-09-09 00:13:35 -0700310AvgCounter::AvgCounter(Clock* clock,
311 StatsCounterObserver* observer,
312 bool include_empty_intervals)
asaperssone402a142016-10-06 23:39:15 -0700313 : StatsCounter(clock,
314 kDefaultProcessIntervalMs,
315 include_empty_intervals,
316 observer) {}
asapersson0e9d6d92016-05-23 06:07:55 -0700317
318void AvgCounter::Add(int sample) {
319 StatsCounter::Add(sample);
320}
321
322bool AvgCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700323 int64_t count = samples_->Count();
324 if (count == 0)
asapersson0e9d6d92016-05-23 06:07:55 -0700325 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700326
327 *metric = (samples_->Sum() + count / 2) / count;
asapersson0e9d6d92016-05-23 06:07:55 -0700328 return true;
329}
330
asaperssonce2e1362016-09-09 00:13:35 -0700331int AvgCounter::GetValueForEmptyInterval() const {
332 return aggregated_counter_->last_sample();
333}
334
asaperssone402a142016-10-06 23:39:15 -0700335MaxCounter::MaxCounter(Clock* clock,
336 StatsCounterObserver* observer,
337 int64_t process_intervals_ms)
asapersson0e9d6d92016-05-23 06:07:55 -0700338 : StatsCounter(clock,
asaperssone402a142016-10-06 23:39:15 -0700339 process_intervals_ms,
asapersson0e9d6d92016-05-23 06:07:55 -0700340 false, // |include_empty_intervals|
341 observer) {}
342
343void MaxCounter::Add(int sample) {
344 StatsCounter::Add(sample);
345}
346
347bool MaxCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700348 if (samples_->Empty())
asapersson0e9d6d92016-05-23 06:07:55 -0700349 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700350
351 *metric = samples_->Max();
asapersson0e9d6d92016-05-23 06:07:55 -0700352 return true;
353}
354
asaperssonce2e1362016-09-09 00:13:35 -0700355int MaxCounter::GetValueForEmptyInterval() const {
356 RTC_NOTREACHED();
357 return 0;
358}
359
asapersson0e9d6d92016-05-23 06:07:55 -0700360PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer)
361 : StatsCounter(clock,
asaperssone402a142016-10-06 23:39:15 -0700362 kDefaultProcessIntervalMs,
asapersson0e9d6d92016-05-23 06:07:55 -0700363 false, // |include_empty_intervals|
364 observer) {}
365
366void PercentCounter::Add(bool sample) {
367 StatsCounter::Add(sample ? 1 : 0);
368}
369
370bool PercentCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700371 int64_t count = samples_->Count();
372 if (count == 0)
asapersson0e9d6d92016-05-23 06:07:55 -0700373 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700374
375 *metric = (samples_->Sum() * 100 + count / 2) / count;
asapersson0e9d6d92016-05-23 06:07:55 -0700376 return true;
377}
378
asaperssonce2e1362016-09-09 00:13:35 -0700379int PercentCounter::GetValueForEmptyInterval() const {
380 RTC_NOTREACHED();
381 return 0;
382}
383
asapersson0e9d6d92016-05-23 06:07:55 -0700384PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer)
385 : StatsCounter(clock,
asaperssone402a142016-10-06 23:39:15 -0700386 kDefaultProcessIntervalMs,
asapersson0e9d6d92016-05-23 06:07:55 -0700387 false, // |include_empty_intervals|
388 observer) {}
389
390void PermilleCounter::Add(bool sample) {
391 StatsCounter::Add(sample ? 1 : 0);
392}
393
394bool PermilleCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700395 int64_t count = samples_->Count();
396 if (count == 0)
asapersson0e9d6d92016-05-23 06:07:55 -0700397 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700398
399 *metric = (samples_->Sum() * 1000 + count / 2) / count;
asapersson0e9d6d92016-05-23 06:07:55 -0700400 return true;
401}
402
asaperssonce2e1362016-09-09 00:13:35 -0700403int PermilleCounter::GetValueForEmptyInterval() const {
404 RTC_NOTREACHED();
405 return 0;
406}
407
asapersson5093b382016-08-15 01:20:30 -0700408RateCounter::RateCounter(Clock* clock,
409 StatsCounterObserver* observer,
410 bool include_empty_intervals)
asaperssone402a142016-10-06 23:39:15 -0700411 : StatsCounter(clock,
412 kDefaultProcessIntervalMs,
413 include_empty_intervals,
414 observer) {}
asapersson0e9d6d92016-05-23 06:07:55 -0700415
416void RateCounter::Add(int sample) {
417 StatsCounter::Add(sample);
418}
419
420bool RateCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700421 if (samples_->Empty())
asapersson0e9d6d92016-05-23 06:07:55 -0700422 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700423
424 *metric = (samples_->Sum() * 1000 + process_intervals_ms_ / 2) /
425 process_intervals_ms_;
asapersson0e9d6d92016-05-23 06:07:55 -0700426 return true;
427}
428
asaperssonce2e1362016-09-09 00:13:35 -0700429int RateCounter::GetValueForEmptyInterval() const {
430 return 0;
431}
432
asapersson5093b382016-08-15 01:20:30 -0700433RateAccCounter::RateAccCounter(Clock* clock,
434 StatsCounterObserver* observer,
435 bool include_empty_intervals)
asaperssone402a142016-10-06 23:39:15 -0700436 : StatsCounter(clock,
437 kDefaultProcessIntervalMs,
438 include_empty_intervals,
439 observer) {}
asapersson0e9d6d92016-05-23 06:07:55 -0700440
asapersson93e1e232017-02-06 05:18:35 -0800441void RateAccCounter::Set(int64_t sample, uint32_t stream_id) {
asaperssonfe647f42016-11-01 00:21:34 -0700442 StatsCounter::Set(sample, stream_id);
asapersson0e9d6d92016-05-23 06:07:55 -0700443}
444
asapersson93e1e232017-02-06 05:18:35 -0800445void RateAccCounter::SetLast(int64_t sample, uint32_t stream_id) {
446 StatsCounter::SetLast(sample, stream_id);
447}
448
asapersson0e9d6d92016-05-23 06:07:55 -0700449bool RateAccCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700450 int64_t diff = samples_->Diff();
451 if (diff < 0 || (!include_empty_intervals_ && diff == 0))
asapersson0e9d6d92016-05-23 06:07:55 -0700452 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700453
454 *metric = (diff * 1000 + process_intervals_ms_ / 2) / process_intervals_ms_;
asapersson0e9d6d92016-05-23 06:07:55 -0700455 return true;
456}
457
asaperssonce2e1362016-09-09 00:13:35 -0700458int RateAccCounter::GetValueForEmptyInterval() const {
459 return 0;
460}
461
asapersson0e9d6d92016-05-23 06:07:55 -0700462} // namespace webrtc