The default process interval (2000 ms) is used for the other counter classes. BUG=webrtc:5283 Review-Url: https://codereview.webrtc.org/2388043003 Cr-Commit-Position: refs/heads/master@{#14560}
324 lines
8.3 KiB
C++
324 lines
8.3 KiB
C++
/*
|
|
* 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 {
|
|
// Default periodic time interval for processing samples.
|
|
const int64_t kDefaultProcessIntervalMs = 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,
|
|
int64_t process_intervals_ms,
|
|
bool include_empty_intervals,
|
|
StatsCounterObserver* observer)
|
|
: max_(0),
|
|
sum_(0),
|
|
num_samples_(0),
|
|
last_sum_(0),
|
|
aggregated_counter_(new AggregatedCounter()),
|
|
process_intervals_ms_(process_intervals_ms),
|
|
clock_(clock),
|
|
include_empty_intervals_(include_empty_intervals),
|
|
observer_(observer),
|
|
last_process_time_ms_(-1),
|
|
paused_(false) {
|
|
RTC_DCHECK_GT(process_intervals_ms_, 0);
|
|
}
|
|
|
|
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 < process_intervals_ms_)
|
|
return false;
|
|
|
|
// Advance number of complete |process_intervals_ms_| that have passed.
|
|
int64_t num_intervals = diff_ms / process_intervals_ms_;
|
|
last_process_time_ms_ += num_intervals * process_intervals_ms_;
|
|
|
|
*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,
|
|
kDefaultProcessIntervalMs,
|
|
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,
|
|
int64_t process_intervals_ms)
|
|
: StatsCounter(clock,
|
|
process_intervals_ms,
|
|
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,
|
|
kDefaultProcessIntervalMs,
|
|
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,
|
|
kDefaultProcessIntervalMs,
|
|
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,
|
|
kDefaultProcessIntervalMs,
|
|
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 + process_intervals_ms_ / 2) / process_intervals_ms_;
|
|
return true;
|
|
}
|
|
|
|
int RateCounter::GetValueForEmptyInterval() const {
|
|
return 0;
|
|
}
|
|
|
|
RateAccCounter::RateAccCounter(Clock* clock,
|
|
StatsCounterObserver* observer,
|
|
bool include_empty_intervals)
|
|
: StatsCounter(clock,
|
|
kDefaultProcessIntervalMs,
|
|
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 + process_intervals_ms_ / 2) /
|
|
process_intervals_ms_;
|
|
return true;
|
|
}
|
|
|
|
int RateAccCounter::GetValueForEmptyInterval() const {
|
|
return 0;
|
|
}
|
|
|
|
} // namespace webrtc
|