blob: 1e72a79bbd3baee72695f76be84081d2acf45e31 [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"
Jonas Olsson366a50c2018-09-06 13:41:30 +020018#include "rtc_base/strings/string_builder.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020019#include "system_wrappers/include/clock.h"
asapersson0e9d6d92016-05-23 06:07:55 -070020
21namespace webrtc {
22
23namespace {
asaperssone402a142016-10-06 23:39:15 -070024// Default periodic time interval for processing samples.
25const int64_t kDefaultProcessIntervalMs = 2000;
asaperssonfe647f42016-11-01 00:21:34 -070026const uint32_t kStreamId0 = 0;
asapersson0e9d6d92016-05-23 06:07:55 -070027} // namespace
28
asapersson43cb7162016-11-15 08:20:48 -080029std::string AggregatedStats::ToString() const {
asapersson076c0112016-11-30 05:17:16 -080030 return ToStringWithMultiplier(1);
31}
32
33std::string AggregatedStats::ToStringWithMultiplier(int multiplier) const {
Jonas Olsson366a50c2018-09-06 13:41:30 +020034 rtc::StringBuilder ss;
asapersson43cb7162016-11-15 08:20:48 -080035 ss << "periodic_samples:" << num_samples << ", {";
asapersson076c0112016-11-30 05:17:16 -080036 ss << "min:" << (min * multiplier) << ", ";
37 ss << "avg:" << (average * multiplier) << ", ";
38 ss << "max:" << (max * multiplier) << "}";
Jonas Olsson84df1c72018-09-14 16:59:32 +020039 return ss.Release();
asapersson43cb7162016-11-15 08:20:48 -080040}
41
asapersson0e9d6d92016-05-23 06:07:55 -070042// Class holding periodically computed metrics.
43class AggregatedCounter {
44 public:
asaperssonce2e1362016-09-09 00:13:35 -070045 AggregatedCounter() : last_sample_(0), sum_samples_(0) {}
asapersson0e9d6d92016-05-23 06:07:55 -070046 ~AggregatedCounter() {}
47
48 void Add(int sample) {
asaperssonce2e1362016-09-09 00:13:35 -070049 last_sample_ = sample;
50 sum_samples_ += sample;
asapersson0e9d6d92016-05-23 06:07:55 -070051 ++stats_.num_samples;
52 if (stats_.num_samples == 1) {
53 stats_.min = sample;
54 stats_.max = sample;
55 }
56 stats_.min = std::min(sample, stats_.min);
57 stats_.max = std::max(sample, stats_.max);
58 }
59
60 AggregatedStats ComputeStats() {
61 Compute();
62 return stats_;
63 }
64
asaperssonce2e1362016-09-09 00:13:35 -070065 bool Empty() const { return stats_.num_samples == 0; }
66
67 int last_sample() const { return last_sample_; }
68
asapersson0e9d6d92016-05-23 06:07:55 -070069 private:
70 void Compute() {
71 if (stats_.num_samples == 0)
72 return;
73
asaperssonce2e1362016-09-09 00:13:35 -070074 stats_.average =
75 (sum_samples_ + stats_.num_samples / 2) / stats_.num_samples;
asapersson0e9d6d92016-05-23 06:07:55 -070076 }
asaperssonce2e1362016-09-09 00:13:35 -070077 int last_sample_;
78 int64_t sum_samples_;
asapersson0e9d6d92016-05-23 06:07:55 -070079 AggregatedStats stats_;
80};
81
asaperssonfe647f42016-11-01 00:21:34 -070082// Class holding gathered samples within a process interval.
83class Samples {
84 public:
85 Samples() : total_count_(0) {}
86 ~Samples() {}
87
88 void Add(int sample, uint32_t stream_id) {
89 samples_[stream_id].Add(sample);
90 ++total_count_;
91 }
asapersson93e1e232017-02-06 05:18:35 -080092 void Set(int64_t sample, uint32_t stream_id) {
asaperssonfe647f42016-11-01 00:21:34 -070093 samples_[stream_id].Set(sample);
94 ++total_count_;
95 }
asapersson93e1e232017-02-06 05:18:35 -080096 void SetLast(int64_t sample, uint32_t stream_id) {
97 samples_[stream_id].SetLast(sample);
98 }
99 int64_t GetLast(uint32_t stream_id) { return samples_[stream_id].GetLast(); }
asaperssonfe647f42016-11-01 00:21:34 -0700100
101 int64_t Count() const { return total_count_; }
102 bool Empty() const { return total_count_ == 0; }
103
104 int64_t Sum() const {
105 int64_t sum = 0;
106 for (const auto& it : samples_)
107 sum += it.second.sum_;
108 return sum;
109 }
110
111 int Max() const {
112 int max = std::numeric_limits<int>::min();
113 for (const auto& it : samples_)
114 max = std::max(it.second.max_, max);
115 return max;
116 }
117
118 void Reset() {
119 for (auto& it : samples_)
120 it.second.Reset();
121 total_count_ = 0;
122 }
123
124 int64_t Diff() const {
125 int64_t sum_diff = 0;
126 int count = 0;
127 for (const auto& it : samples_) {
128 if (it.second.count_ > 0) {
129 int64_t diff = it.second.sum_ - it.second.last_sum_;
130 if (diff >= 0) {
131 sum_diff += diff;
132 ++count;
133 }
134 }
135 }
136 return (count > 0) ? sum_diff : -1;
137 }
138
139 private:
140 struct Stats {
141 void Add(int sample) {
142 sum_ += sample;
143 ++count_;
144 max_ = std::max(sample, max_);
145 }
asapersson93e1e232017-02-06 05:18:35 -0800146 void Set(int64_t sample) {
asaperssonfe647f42016-11-01 00:21:34 -0700147 sum_ = sample;
148 ++count_;
149 }
asapersson93e1e232017-02-06 05:18:35 -0800150 void SetLast(int64_t sample) { last_sum_ = sample; }
151 int64_t GetLast() const { return last_sum_; }
asaperssonfe647f42016-11-01 00:21:34 -0700152 void Reset() {
153 if (count_ > 0)
154 last_sum_ = sum_;
155 sum_ = 0;
156 count_ = 0;
157 max_ = std::numeric_limits<int>::min();
158 }
159
160 int max_ = std::numeric_limits<int>::min();
161 int64_t count_ = 0;
162 int64_t sum_ = 0;
163 int64_t last_sum_ = 0;
164 };
165
166 int64_t total_count_;
167 std::map<uint32_t, Stats> samples_; // Gathered samples mapped by stream id.
168};
169
asapersson0e9d6d92016-05-23 06:07:55 -0700170// StatsCounter class.
171StatsCounter::StatsCounter(Clock* clock,
asaperssone402a142016-10-06 23:39:15 -0700172 int64_t process_intervals_ms,
asapersson0e9d6d92016-05-23 06:07:55 -0700173 bool include_empty_intervals,
174 StatsCounterObserver* observer)
asaperssonfe647f42016-11-01 00:21:34 -0700175 : include_empty_intervals_(include_empty_intervals),
asaperssone402a142016-10-06 23:39:15 -0700176 process_intervals_ms_(process_intervals_ms),
asaperssonfe647f42016-11-01 00:21:34 -0700177 aggregated_counter_(new AggregatedCounter()),
178 samples_(new Samples()),
asapersson0e9d6d92016-05-23 06:07:55 -0700179 clock_(clock),
asapersson0e9d6d92016-05-23 06:07:55 -0700180 observer_(observer),
asaperssonce2e1362016-09-09 00:13:35 -0700181 last_process_time_ms_(-1),
asapersson93e1e232017-02-06 05:18:35 -0800182 paused_(false),
183 pause_time_ms_(-1),
184 min_pause_time_ms_(0) {
asaperssone402a142016-10-06 23:39:15 -0700185 RTC_DCHECK_GT(process_intervals_ms_, 0);
186}
asapersson0e9d6d92016-05-23 06:07:55 -0700187
188StatsCounter::~StatsCounter() {}
189
190AggregatedStats StatsCounter::GetStats() {
191 return aggregated_counter_->ComputeStats();
192}
193
asaperssonce2e1362016-09-09 00:13:35 -0700194AggregatedStats StatsCounter::ProcessAndGetStats() {
195 if (HasSample())
196 TryProcess();
197 return aggregated_counter_->ComputeStats();
198}
199
asapersson93e1e232017-02-06 05:18:35 -0800200void StatsCounter::ProcessAndPauseForDuration(int64_t min_pause_time_ms) {
201 ProcessAndPause();
202 min_pause_time_ms_ = min_pause_time_ms;
203}
204
asaperssonce2e1362016-09-09 00:13:35 -0700205void StatsCounter::ProcessAndPause() {
206 if (HasSample())
207 TryProcess();
208 paused_ = true;
asapersson93e1e232017-02-06 05:18:35 -0800209 pause_time_ms_ = clock_->TimeInMilliseconds();
210}
211
212void StatsCounter::ProcessAndStopPause() {
213 if (HasSample())
214 TryProcess();
215 Resume();
asaperssonce2e1362016-09-09 00:13:35 -0700216}
217
asapersson250fd972016-09-08 00:07:21 -0700218bool StatsCounter::HasSample() const {
219 return last_process_time_ms_ != -1;
220}
221
asaperssonce2e1362016-09-09 00:13:35 -0700222bool StatsCounter::TimeToProcess(int* elapsed_intervals) {
asapersson0e9d6d92016-05-23 06:07:55 -0700223 int64_t now = clock_->TimeInMilliseconds();
224 if (last_process_time_ms_ == -1)
225 last_process_time_ms_ = now;
226
227 int64_t diff_ms = now - last_process_time_ms_;
asaperssone402a142016-10-06 23:39:15 -0700228 if (diff_ms < process_intervals_ms_)
asapersson0e9d6d92016-05-23 06:07:55 -0700229 return false;
230
asaperssone402a142016-10-06 23:39:15 -0700231 // Advance number of complete |process_intervals_ms_| that have passed.
232 int64_t num_intervals = diff_ms / process_intervals_ms_;
233 last_process_time_ms_ += num_intervals * process_intervals_ms_;
asapersson0e9d6d92016-05-23 06:07:55 -0700234
asaperssonce2e1362016-09-09 00:13:35 -0700235 *elapsed_intervals = num_intervals;
asapersson0e9d6d92016-05-23 06:07:55 -0700236 return true;
237}
238
asapersson0e9d6d92016-05-23 06:07:55 -0700239void StatsCounter::Add(int sample) {
240 TryProcess();
asaperssonfe647f42016-11-01 00:21:34 -0700241 samples_->Add(sample, kStreamId0);
asapersson93e1e232017-02-06 05:18:35 -0800242 ResumeIfMinTimePassed();
243}
244
245void StatsCounter::Set(int64_t sample, uint32_t stream_id) {
246 if (paused_ && sample == samples_->GetLast(stream_id)) {
247 // Do not add same sample while paused (will reset pause).
248 return;
249 }
250 TryProcess();
251 samples_->Set(sample, stream_id);
252 ResumeIfMinTimePassed();
253}
254
255void StatsCounter::SetLast(int64_t sample, uint32_t stream_id) {
256 RTC_DCHECK(!HasSample()) << "Should be set before first sample is added.";
257 samples_->SetLast(sample, stream_id);
asaperssonce2e1362016-09-09 00:13:35 -0700258}
259
260// Reports periodically computed metric.
261void StatsCounter::ReportMetricToAggregatedCounter(
262 int value,
263 int num_values_to_add) const {
264 for (int i = 0; i < num_values_to_add; ++i) {
265 aggregated_counter_->Add(value);
266 if (observer_)
267 observer_->OnMetricUpdated(value);
268 }
asapersson0e9d6d92016-05-23 06:07:55 -0700269}
270
271void StatsCounter::TryProcess() {
asaperssonce2e1362016-09-09 00:13:35 -0700272 int elapsed_intervals;
273 if (!TimeToProcess(&elapsed_intervals))
asapersson0e9d6d92016-05-23 06:07:55 -0700274 return;
275
asaperssonce2e1362016-09-09 00:13:35 -0700276 // Get and report periodically computed metric.
asapersson0e9d6d92016-05-23 06:07:55 -0700277 int metric;
asaperssonce2e1362016-09-09 00:13:35 -0700278 if (GetMetric(&metric))
279 ReportMetricToAggregatedCounter(metric, 1);
280
281 // Report value for elapsed intervals without samples.
282 if (IncludeEmptyIntervals()) {
283 // If there are no samples, all elapsed intervals are empty (otherwise one
284 // interval contains sample(s), discard this interval).
285 int empty_intervals =
asaperssonfe647f42016-11-01 00:21:34 -0700286 samples_->Empty() ? elapsed_intervals : (elapsed_intervals - 1);
asaperssonce2e1362016-09-09 00:13:35 -0700287 ReportMetricToAggregatedCounter(GetValueForEmptyInterval(),
288 empty_intervals);
asapersson0e9d6d92016-05-23 06:07:55 -0700289 }
asaperssonce2e1362016-09-09 00:13:35 -0700290
291 // Reset samples for elapsed interval.
asaperssonfe647f42016-11-01 00:21:34 -0700292 samples_->Reset();
asapersson0e9d6d92016-05-23 06:07:55 -0700293}
294
asaperssonce2e1362016-09-09 00:13:35 -0700295bool StatsCounter::IncludeEmptyIntervals() const {
296 return include_empty_intervals_ && !paused_ && !aggregated_counter_->Empty();
297}
asapersson93e1e232017-02-06 05:18:35 -0800298void StatsCounter::ResumeIfMinTimePassed() {
299 if (paused_ &&
300 (clock_->TimeInMilliseconds() - pause_time_ms_) >= min_pause_time_ms_) {
301 Resume();
302 }
303}
304
305void StatsCounter::Resume() {
306 paused_ = false;
307 min_pause_time_ms_ = 0;
308}
asaperssonce2e1362016-09-09 00:13:35 -0700309
asapersson0e9d6d92016-05-23 06:07:55 -0700310// StatsCounter sub-classes.
asaperssonce2e1362016-09-09 00:13:35 -0700311AvgCounter::AvgCounter(Clock* clock,
312 StatsCounterObserver* observer,
313 bool include_empty_intervals)
asaperssone402a142016-10-06 23:39:15 -0700314 : StatsCounter(clock,
315 kDefaultProcessIntervalMs,
316 include_empty_intervals,
317 observer) {}
asapersson0e9d6d92016-05-23 06:07:55 -0700318
319void AvgCounter::Add(int sample) {
320 StatsCounter::Add(sample);
321}
322
323bool AvgCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700324 int64_t count = samples_->Count();
325 if (count == 0)
asapersson0e9d6d92016-05-23 06:07:55 -0700326 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700327
328 *metric = (samples_->Sum() + count / 2) / count;
asapersson0e9d6d92016-05-23 06:07:55 -0700329 return true;
330}
331
asaperssonce2e1362016-09-09 00:13:35 -0700332int AvgCounter::GetValueForEmptyInterval() const {
333 return aggregated_counter_->last_sample();
334}
335
asaperssone402a142016-10-06 23:39:15 -0700336MaxCounter::MaxCounter(Clock* clock,
337 StatsCounterObserver* observer,
338 int64_t process_intervals_ms)
asapersson0e9d6d92016-05-23 06:07:55 -0700339 : StatsCounter(clock,
asaperssone402a142016-10-06 23:39:15 -0700340 process_intervals_ms,
asapersson0e9d6d92016-05-23 06:07:55 -0700341 false, // |include_empty_intervals|
342 observer) {}
343
344void MaxCounter::Add(int sample) {
345 StatsCounter::Add(sample);
346}
347
348bool MaxCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700349 if (samples_->Empty())
asapersson0e9d6d92016-05-23 06:07:55 -0700350 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700351
352 *metric = samples_->Max();
asapersson0e9d6d92016-05-23 06:07:55 -0700353 return true;
354}
355
asaperssonce2e1362016-09-09 00:13:35 -0700356int MaxCounter::GetValueForEmptyInterval() const {
357 RTC_NOTREACHED();
358 return 0;
359}
360
asapersson0e9d6d92016-05-23 06:07:55 -0700361PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer)
362 : StatsCounter(clock,
asaperssone402a142016-10-06 23:39:15 -0700363 kDefaultProcessIntervalMs,
asapersson0e9d6d92016-05-23 06:07:55 -0700364 false, // |include_empty_intervals|
365 observer) {}
366
367void PercentCounter::Add(bool sample) {
368 StatsCounter::Add(sample ? 1 : 0);
369}
370
371bool PercentCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700372 int64_t count = samples_->Count();
373 if (count == 0)
asapersson0e9d6d92016-05-23 06:07:55 -0700374 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700375
376 *metric = (samples_->Sum() * 100 + count / 2) / count;
asapersson0e9d6d92016-05-23 06:07:55 -0700377 return true;
378}
379
asaperssonce2e1362016-09-09 00:13:35 -0700380int PercentCounter::GetValueForEmptyInterval() const {
381 RTC_NOTREACHED();
382 return 0;
383}
384
asapersson0e9d6d92016-05-23 06:07:55 -0700385PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer)
386 : StatsCounter(clock,
asaperssone402a142016-10-06 23:39:15 -0700387 kDefaultProcessIntervalMs,
asapersson0e9d6d92016-05-23 06:07:55 -0700388 false, // |include_empty_intervals|
389 observer) {}
390
391void PermilleCounter::Add(bool sample) {
392 StatsCounter::Add(sample ? 1 : 0);
393}
394
395bool PermilleCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700396 int64_t count = samples_->Count();
397 if (count == 0)
asapersson0e9d6d92016-05-23 06:07:55 -0700398 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700399
400 *metric = (samples_->Sum() * 1000 + count / 2) / count;
asapersson0e9d6d92016-05-23 06:07:55 -0700401 return true;
402}
403
asaperssonce2e1362016-09-09 00:13:35 -0700404int PermilleCounter::GetValueForEmptyInterval() const {
405 RTC_NOTREACHED();
406 return 0;
407}
408
asapersson5093b382016-08-15 01:20:30 -0700409RateCounter::RateCounter(Clock* clock,
410 StatsCounterObserver* observer,
411 bool include_empty_intervals)
asaperssone402a142016-10-06 23:39:15 -0700412 : StatsCounter(clock,
413 kDefaultProcessIntervalMs,
414 include_empty_intervals,
415 observer) {}
asapersson0e9d6d92016-05-23 06:07:55 -0700416
417void RateCounter::Add(int sample) {
418 StatsCounter::Add(sample);
419}
420
421bool RateCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700422 if (samples_->Empty())
asapersson0e9d6d92016-05-23 06:07:55 -0700423 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700424
425 *metric = (samples_->Sum() * 1000 + process_intervals_ms_ / 2) /
426 process_intervals_ms_;
asapersson0e9d6d92016-05-23 06:07:55 -0700427 return true;
428}
429
asaperssonce2e1362016-09-09 00:13:35 -0700430int RateCounter::GetValueForEmptyInterval() const {
431 return 0;
432}
433
asapersson5093b382016-08-15 01:20:30 -0700434RateAccCounter::RateAccCounter(Clock* clock,
435 StatsCounterObserver* observer,
436 bool include_empty_intervals)
asaperssone402a142016-10-06 23:39:15 -0700437 : StatsCounter(clock,
438 kDefaultProcessIntervalMs,
439 include_empty_intervals,
440 observer) {}
asapersson0e9d6d92016-05-23 06:07:55 -0700441
asapersson93e1e232017-02-06 05:18:35 -0800442void RateAccCounter::Set(int64_t sample, uint32_t stream_id) {
asaperssonfe647f42016-11-01 00:21:34 -0700443 StatsCounter::Set(sample, stream_id);
asapersson0e9d6d92016-05-23 06:07:55 -0700444}
445
asapersson93e1e232017-02-06 05:18:35 -0800446void RateAccCounter::SetLast(int64_t sample, uint32_t stream_id) {
447 StatsCounter::SetLast(sample, stream_id);
448}
449
asapersson0e9d6d92016-05-23 06:07:55 -0700450bool RateAccCounter::GetMetric(int* metric) const {
asaperssonfe647f42016-11-01 00:21:34 -0700451 int64_t diff = samples_->Diff();
452 if (diff < 0 || (!include_empty_intervals_ && diff == 0))
asapersson0e9d6d92016-05-23 06:07:55 -0700453 return false;
asaperssonfe647f42016-11-01 00:21:34 -0700454
455 *metric = (diff * 1000 + process_intervals_ms_ / 2) / process_intervals_ms_;
asapersson0e9d6d92016-05-23 06:07:55 -0700456 return true;
457}
458
asaperssonce2e1362016-09-09 00:13:35 -0700459int RateAccCounter::GetValueForEmptyInterval() const {
460 return 0;
461}
462
asapersson0e9d6d92016-05-23 06:07:55 -0700463} // namespace webrtc