blob: 0a66015cee6db31ed858cc3a81a574b28aae0409 [file] [log] [blame]
/*
* Copyright (c) 2016 The WebRTC project authors. All Rights Reserved.
*
* Use of this source code is governed by a BSD-style license
* that can be found in the LICENSE file in the root of the source
* tree. An additional intellectual property rights grant can be found
* in the file PATENTS. All contributing project authors may
* be found in the AUTHORS file in the root of the source tree.
*/
#include "webrtc/video/stats_counter.h"
#include <algorithm>
#include "webrtc/base/checks.h"
#include "webrtc/system_wrappers/include/clock.h"
namespace webrtc {
namespace {
// Periodic time interval for processing samples.
const int64_t kProcessIntervalMs = 2000;
} // namespace
// Class holding periodically computed metrics.
class AggregatedCounter {
public:
AggregatedCounter() : last_sample_(0), sum_samples_(0) {}
~AggregatedCounter() {}
void Add(int sample) {
last_sample_ = sample;
sum_samples_ += sample;
++stats_.num_samples;
if (stats_.num_samples == 1) {
stats_.min = sample;
stats_.max = sample;
}
stats_.min = std::min(sample, stats_.min);
stats_.max = std::max(sample, stats_.max);
}
AggregatedStats ComputeStats() {
Compute();
return stats_;
}
bool Empty() const { return stats_.num_samples == 0; }
int last_sample() const { return last_sample_; }
private:
void Compute() {
if (stats_.num_samples == 0)
return;
stats_.average =
(sum_samples_ + stats_.num_samples / 2) / stats_.num_samples;
}
int last_sample_;
int64_t sum_samples_;
AggregatedStats stats_;
};
// StatsCounter class.
StatsCounter::StatsCounter(Clock* clock,
bool include_empty_intervals,
StatsCounterObserver* observer)
: max_(0),
sum_(0),
num_samples_(0),
last_sum_(0),
aggregated_counter_(new AggregatedCounter()),
clock_(clock),
include_empty_intervals_(include_empty_intervals),
observer_(observer),
last_process_time_ms_(-1),
paused_(false) {}
StatsCounter::~StatsCounter() {}
AggregatedStats StatsCounter::GetStats() {
return aggregated_counter_->ComputeStats();
}
AggregatedStats StatsCounter::ProcessAndGetStats() {
if (HasSample())
TryProcess();
return aggregated_counter_->ComputeStats();
}
void StatsCounter::ProcessAndPause() {
if (HasSample())
TryProcess();
paused_ = true;
}
bool StatsCounter::HasSample() const {
return last_process_time_ms_ != -1;
}
bool StatsCounter::TimeToProcess(int* elapsed_intervals) {
int64_t now = clock_->TimeInMilliseconds();
if (last_process_time_ms_ == -1)
last_process_time_ms_ = now;
int64_t diff_ms = now - last_process_time_ms_;
if (diff_ms < kProcessIntervalMs)
return false;
// Advance number of complete kProcessIntervalMs that have passed.
int64_t num_intervals = diff_ms / kProcessIntervalMs;
last_process_time_ms_ += num_intervals * kProcessIntervalMs;
*elapsed_intervals = num_intervals;
return true;
}
void StatsCounter::Set(int sample) {
TryProcess();
++num_samples_;
sum_ = sample;
paused_ = false;
}
void StatsCounter::Add(int sample) {
TryProcess();
++num_samples_;
sum_ += sample;
if (num_samples_ == 1)
max_ = sample;
max_ = std::max(sample, max_);
paused_ = false;
}
// Reports periodically computed metric.
void StatsCounter::ReportMetricToAggregatedCounter(
int value,
int num_values_to_add) const {
for (int i = 0; i < num_values_to_add; ++i) {
aggregated_counter_->Add(value);
if (observer_)
observer_->OnMetricUpdated(value);
}
}
void StatsCounter::TryProcess() {
int elapsed_intervals;
if (!TimeToProcess(&elapsed_intervals))
return;
// Get and report periodically computed metric.
int metric;
if (GetMetric(&metric))
ReportMetricToAggregatedCounter(metric, 1);
// Report value for elapsed intervals without samples.
if (IncludeEmptyIntervals()) {
// If there are no samples, all elapsed intervals are empty (otherwise one
// interval contains sample(s), discard this interval).
int empty_intervals =
(num_samples_ == 0) ? elapsed_intervals : (elapsed_intervals - 1);
ReportMetricToAggregatedCounter(GetValueForEmptyInterval(),
empty_intervals);
}
// Reset samples for elapsed interval.
if (num_samples_ > 0)
last_sum_ = sum_;
sum_ = 0;
max_ = 0;
num_samples_ = 0;
}
bool StatsCounter::IncludeEmptyIntervals() const {
return include_empty_intervals_ && !paused_ && !aggregated_counter_->Empty();
}
// StatsCounter sub-classes.
AvgCounter::AvgCounter(Clock* clock,
StatsCounterObserver* observer,
bool include_empty_intervals)
: StatsCounter(clock, include_empty_intervals, observer) {}
void AvgCounter::Add(int sample) {
StatsCounter::Add(sample);
}
bool AvgCounter::GetMetric(int* metric) const {
if (num_samples_ == 0)
return false;
*metric = (sum_ + num_samples_ / 2) / num_samples_;
return true;
}
int AvgCounter::GetValueForEmptyInterval() const {
return aggregated_counter_->last_sample();
}
MaxCounter::MaxCounter(Clock* clock, StatsCounterObserver* observer)
: StatsCounter(clock,
false, // |include_empty_intervals|
observer) {}
void MaxCounter::Add(int sample) {
StatsCounter::Add(sample);
}
bool MaxCounter::GetMetric(int* metric) const {
if (num_samples_ == 0)
return false;
*metric = max_;
return true;
}
int MaxCounter::GetValueForEmptyInterval() const {
RTC_NOTREACHED();
return 0;
}
PercentCounter::PercentCounter(Clock* clock, StatsCounterObserver* observer)
: StatsCounter(clock,
false, // |include_empty_intervals|
observer) {}
void PercentCounter::Add(bool sample) {
StatsCounter::Add(sample ? 1 : 0);
}
bool PercentCounter::GetMetric(int* metric) const {
if (num_samples_ == 0)
return false;
*metric = (sum_ * 100 + num_samples_ / 2) / num_samples_;
return true;
}
int PercentCounter::GetValueForEmptyInterval() const {
RTC_NOTREACHED();
return 0;
}
PermilleCounter::PermilleCounter(Clock* clock, StatsCounterObserver* observer)
: StatsCounter(clock,
false, // |include_empty_intervals|
observer) {}
void PermilleCounter::Add(bool sample) {
StatsCounter::Add(sample ? 1 : 0);
}
bool PermilleCounter::GetMetric(int* metric) const {
if (num_samples_ == 0)
return false;
*metric = (sum_ * 1000 + num_samples_ / 2) / num_samples_;
return true;
}
int PermilleCounter::GetValueForEmptyInterval() const {
RTC_NOTREACHED();
return 0;
}
RateCounter::RateCounter(Clock* clock,
StatsCounterObserver* observer,
bool include_empty_intervals)
: StatsCounter(clock, include_empty_intervals, observer) {}
void RateCounter::Add(int sample) {
StatsCounter::Add(sample);
}
bool RateCounter::GetMetric(int* metric) const {
if (num_samples_ == 0)
return false;
*metric = (sum_ * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs;
return true;
}
int RateCounter::GetValueForEmptyInterval() const {
return 0;
}
RateAccCounter::RateAccCounter(Clock* clock,
StatsCounterObserver* observer,
bool include_empty_intervals)
: StatsCounter(clock, include_empty_intervals, observer) {}
void RateAccCounter::Set(int sample) {
StatsCounter::Set(sample);
}
bool RateAccCounter::GetMetric(int* metric) const {
if (num_samples_ == 0 || last_sum_ > sum_)
return false;
*metric =
((sum_ - last_sum_) * 1000 + kProcessIntervalMs / 2) / kProcessIntervalMs;
return true;
}
int RateAccCounter::GetValueForEmptyInterval() const {
return 0;
}
} // namespace webrtc