Add class which periodically computes statistics.
During a period (e.g. 2 seconds), different metrics can be computed, e.g.: - average of samples - percentage/permille of samples - units per second Each periodic metric can be either: - reported to an observer each period - aggregated during the call (e.g. min, max, average) BUG=webrtc:5283 Review-Url: https://codereview.webrtc.org/1640053003 Cr-Commit-Position: refs/heads/master@{#12847}
This commit is contained in:
@ -30,6 +30,8 @@ source_set("video") {
|
||||
"send_delay_stats.h",
|
||||
"send_statistics_proxy.cc",
|
||||
"send_statistics_proxy.h",
|
||||
"stats_counter.cc",
|
||||
"stats_counter.h",
|
||||
"stream_synchronization.cc",
|
||||
"stream_synchronization.h",
|
||||
"video_capture_input.cc",
|
||||
|
||||
231
webrtc/video/stats_counter.cc
Normal file
231
webrtc/video/stats_counter.cc
Normal file
@ -0,0 +1,231 @@
|
||||
/*
|
||||
* 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/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() : sum_(0) {}
|
||||
~AggregatedCounter() {}
|
||||
|
||||
void Add(int sample) {
|
||||
sum_ += 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_;
|
||||
}
|
||||
|
||||
private:
|
||||
void Compute() {
|
||||
if (stats_.num_samples == 0)
|
||||
return;
|
||||
|
||||
stats_.average = (sum_ + stats_.num_samples / 2) / stats_.num_samples;
|
||||
}
|
||||
int64_t sum_;
|
||||
AggregatedStats stats_;
|
||||
};
|
||||
|
||||
// StatsCounter class.
|
||||
StatsCounter::StatsCounter(Clock* clock,
|
||||
bool include_empty_intervals,
|
||||
StatsCounterObserver* observer)
|
||||
: max_(0),
|
||||
sum_(0),
|
||||
num_samples_(0),
|
||||
last_sum_(0),
|
||||
clock_(clock),
|
||||
include_empty_intervals_(include_empty_intervals),
|
||||
observer_(observer),
|
||||
aggregated_counter_(new AggregatedCounter()),
|
||||
last_process_time_ms_(-1) {}
|
||||
|
||||
StatsCounter::~StatsCounter() {}
|
||||
|
||||
AggregatedStats StatsCounter::GetStats() {
|
||||
return aggregated_counter_->ComputeStats();
|
||||
}
|
||||
|
||||
bool StatsCounter::TimeToProcess() {
|
||||
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;
|
||||
|
||||
// Add zero for intervals without samples.
|
||||
if (include_empty_intervals_) {
|
||||
for (int64_t i = 0; i < num_intervals - 1; ++i) {
|
||||
aggregated_counter_->Add(0);
|
||||
if (observer_)
|
||||
observer_->OnMetricUpdated(0);
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void StatsCounter::Set(int sample) {
|
||||
TryProcess();
|
||||
++num_samples_;
|
||||
sum_ = sample;
|
||||
}
|
||||
|
||||
void StatsCounter::Add(int sample) {
|
||||
TryProcess();
|
||||
++num_samples_;
|
||||
sum_ += sample;
|
||||
|
||||
if (num_samples_ == 1)
|
||||
max_ = sample;
|
||||
max_ = std::max(sample, max_);
|
||||
}
|
||||
|
||||
void StatsCounter::TryProcess() {
|
||||
if (!TimeToProcess())
|
||||
return;
|
||||
|
||||
int metric;
|
||||
if (GetMetric(&metric)) {
|
||||
aggregated_counter_->Add(metric);
|
||||
if (observer_)
|
||||
observer_->OnMetricUpdated(metric);
|
||||
}
|
||||
last_sum_ = sum_;
|
||||
sum_ = 0;
|
||||
max_ = 0;
|
||||
num_samples_ = 0;
|
||||
}
|
||||
|
||||
// StatsCounter sub-classes.
|
||||
AvgCounter::AvgCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
false, // |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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
RateCounter::RateCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
true, // |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;
|
||||
}
|
||||
|
||||
RateAccCounter::RateAccCounter(Clock* clock, StatsCounterObserver* observer)
|
||||
: StatsCounter(clock,
|
||||
true, // |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;
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
227
webrtc/video/stats_counter.h
Normal file
227
webrtc/video/stats_counter.h
Normal file
@ -0,0 +1,227 @@
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
|
||||
#ifndef WEBRTC_VIDEO_STATS_COUNTER_H_
|
||||
#define WEBRTC_VIDEO_STATS_COUNTER_H_
|
||||
|
||||
#include <memory>
|
||||
|
||||
#include "webrtc/base/constructormagic.h"
|
||||
#include "webrtc/typedefs.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
class AggregatedCounter;
|
||||
class Clock;
|
||||
|
||||
// |StatsCounterObserver| is called periodically when a metric is updated.
|
||||
class StatsCounterObserver {
|
||||
public:
|
||||
virtual void OnMetricUpdated(int sample) = 0;
|
||||
|
||||
virtual ~StatsCounterObserver() {}
|
||||
};
|
||||
|
||||
struct AggregatedStats {
|
||||
int64_t num_samples = 0;
|
||||
int min = -1;
|
||||
int max = -1;
|
||||
int average = -1;
|
||||
// TODO(asapersson): Consider adding median/percentiles.
|
||||
};
|
||||
|
||||
// Classes which periodically computes a metric.
|
||||
//
|
||||
// During a period, |kProcessIntervalMs|, different metrics can be computed e.g:
|
||||
// - |AvgCounter|: average of samples
|
||||
// - |PercentCounter|: percentage of samples
|
||||
// - |PermilleCounter|: permille of samples
|
||||
//
|
||||
// Each periodic metric can be either:
|
||||
// - reported to an |observer| each period
|
||||
// - aggregated during the call (e.g. min, max, average)
|
||||
//
|
||||
// periodically computed
|
||||
// GetMetric() GetMetric() => AggregatedStats
|
||||
// ^ ^ (e.g. min/max/avg)
|
||||
// | |
|
||||
// | * * * * | ** * * * * | ...
|
||||
// |<- process interval ->|
|
||||
//
|
||||
// (*) - samples
|
||||
//
|
||||
//
|
||||
// Example usage:
|
||||
//
|
||||
// AvgCounter counter(&clock, nullptr);
|
||||
// counter.Add(5);
|
||||
// counter.Add(1);
|
||||
// counter.Add(6); // process interval passed -> GetMetric() avg:4
|
||||
// counter.Add(7);
|
||||
// counter.Add(3); // process interval passed -> GetMetric() avg:5
|
||||
// counter.Add(10);
|
||||
// counter.Add(20); // process interval passed -> GetMetric() avg:15
|
||||
// AggregatedStats stats = counter.GetStats();
|
||||
// stats: {min:4, max:15, avg:8}
|
||||
//
|
||||
|
||||
// Note: StatsCounter takes ownership of |observer|.
|
||||
|
||||
class StatsCounter {
|
||||
public:
|
||||
virtual ~StatsCounter();
|
||||
|
||||
virtual bool GetMetric(int* metric) const = 0;
|
||||
|
||||
AggregatedStats GetStats();
|
||||
|
||||
protected:
|
||||
StatsCounter(Clock* clock,
|
||||
bool include_empty_intervals,
|
||||
StatsCounterObserver* observer);
|
||||
|
||||
void Add(int sample);
|
||||
void Set(int sample);
|
||||
|
||||
int max_;
|
||||
int64_t sum_;
|
||||
int64_t num_samples_;
|
||||
int64_t last_sum_;
|
||||
|
||||
private:
|
||||
bool TimeToProcess();
|
||||
void TryProcess();
|
||||
|
||||
Clock* const clock_;
|
||||
const bool include_empty_intervals_;
|
||||
const std::unique_ptr<StatsCounterObserver> observer_;
|
||||
const std::unique_ptr<AggregatedCounter> aggregated_counter_;
|
||||
int64_t last_process_time_ms_;
|
||||
};
|
||||
|
||||
// AvgCounter: average of samples
|
||||
//
|
||||
// | * * * | * * | ...
|
||||
// | Add(5) Add(1) Add(6) | Add(5) Add(5) |
|
||||
// GetMetric | (5 + 1 + 6) / 3 | (5 + 5) / 2 |
|
||||
//
|
||||
class AvgCounter : public StatsCounter {
|
||||
public:
|
||||
AvgCounter(Clock* clock, StatsCounterObserver* observer);
|
||||
~AvgCounter() override {}
|
||||
|
||||
void Add(int sample);
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(AvgCounter);
|
||||
};
|
||||
|
||||
// MaxCounter: maximum of samples
|
||||
//
|
||||
// | * * * | * * | ...
|
||||
// | Add(5) Add(1) Add(6) | Add(5) Add(5) |
|
||||
// GetMetric | max: (5, 1, 6) | max: (5, 5) |
|
||||
//
|
||||
class MaxCounter : public StatsCounter {
|
||||
public:
|
||||
MaxCounter(Clock* clock, StatsCounterObserver* observer);
|
||||
~MaxCounter() override {}
|
||||
|
||||
void Add(int sample);
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(MaxCounter);
|
||||
};
|
||||
|
||||
// PercentCounter: percentage of samples
|
||||
//
|
||||
// | * * * | * * | ...
|
||||
// | Add(T) Add(F) Add(T) | Add(F) Add(T) |
|
||||
// GetMetric | 100 * 2 / 3 | 100 * 1 / 2 |
|
||||
//
|
||||
class PercentCounter : public StatsCounter {
|
||||
public:
|
||||
PercentCounter(Clock* clock, StatsCounterObserver* observer);
|
||||
~PercentCounter() override {}
|
||||
|
||||
void Add(bool sample);
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(PercentCounter);
|
||||
};
|
||||
|
||||
// PermilleCounter: permille of samples
|
||||
//
|
||||
// | * * * | * * | ...
|
||||
// | Add(T) Add(F) Add(T) | Add(F) Add(T) |
|
||||
// GetMetric | 1000 * 2 / 3 | 1000 * 1 / 2 |
|
||||
//
|
||||
class PermilleCounter : public StatsCounter {
|
||||
public:
|
||||
PermilleCounter(Clock* clock, StatsCounterObserver* observer);
|
||||
~PermilleCounter() override {}
|
||||
|
||||
void Add(bool sample);
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(PermilleCounter);
|
||||
};
|
||||
|
||||
// RateCounter: units per second
|
||||
//
|
||||
// | * * * | * * | ...
|
||||
// | Add(5) Add(1) Add(6) | Add(5) Add(5) |
|
||||
// |<------ 2 sec ------->| |
|
||||
// GetMetric | (5 + 1 + 6) / 2 | (5 + 5) / 2 |
|
||||
//
|
||||
class RateCounter : public StatsCounter {
|
||||
public:
|
||||
RateCounter(Clock* clock, StatsCounterObserver* observer);
|
||||
~RateCounter() override {}
|
||||
|
||||
void Add(int sample);
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RateCounter);
|
||||
};
|
||||
|
||||
// RateAccCounter: units per second (used for counters)
|
||||
//
|
||||
// | * * * | * * | ...
|
||||
// | Set(5) Set(6) Set(8) | Set(11) Set(13) |
|
||||
// |<------ 2 sec ------->| |
|
||||
// GetMetric | 8 / 2 | (13 - 8) / 2 |
|
||||
//
|
||||
class RateAccCounter : public StatsCounter {
|
||||
public:
|
||||
RateAccCounter(Clock* clock, StatsCounterObserver* observer);
|
||||
~RateAccCounter() override {}
|
||||
|
||||
void Set(int sample);
|
||||
|
||||
private:
|
||||
bool GetMetric(int* metric) const override;
|
||||
|
||||
RTC_DISALLOW_COPY_AND_ASSIGN(RateAccCounter);
|
||||
};
|
||||
|
||||
} // namespace webrtc
|
||||
|
||||
#endif // WEBRTC_VIDEO_STATS_COUNTER_H_
|
||||
320
webrtc/video/stats_counter_unittest.cc
Normal file
320
webrtc/video/stats_counter_unittest.cc
Normal file
@ -0,0 +1,320 @@
|
||||
/*
|
||||
* 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 "testing/gtest/include/gtest/gtest.h"
|
||||
|
||||
#include "webrtc/system_wrappers/include/clock.h"
|
||||
|
||||
namespace webrtc {
|
||||
namespace {
|
||||
const int kProcessIntervalMs = 2000;
|
||||
|
||||
class StatsCounterObserverImpl : public StatsCounterObserver {
|
||||
public:
|
||||
StatsCounterObserverImpl() : num_calls_(0), last_sample_(-1) {}
|
||||
void OnMetricUpdated(int sample) override {
|
||||
++num_calls_;
|
||||
last_sample_ = sample;
|
||||
}
|
||||
int num_calls_;
|
||||
int last_sample_;
|
||||
};
|
||||
} // namespace
|
||||
|
||||
class StatsCounterTest : public ::testing::Test {
|
||||
protected:
|
||||
StatsCounterTest()
|
||||
: clock_(1234) {}
|
||||
|
||||
void AddSampleAndAdvance(int sample, int interval_ms, AvgCounter* counter) {
|
||||
counter->Add(sample);
|
||||
clock_.AdvanceTimeMilliseconds(interval_ms);
|
||||
}
|
||||
|
||||
void SetSampleAndAdvance(int sample,
|
||||
int interval_ms,
|
||||
RateAccCounter* counter) {
|
||||
counter->Set(sample);
|
||||
clock_.AdvanceTimeMilliseconds(interval_ms);
|
||||
}
|
||||
|
||||
void VerifyStatsIsNotSet(const AggregatedStats& stats) {
|
||||
EXPECT_EQ(0, stats.num_samples);
|
||||
EXPECT_EQ(-1, stats.min);
|
||||
EXPECT_EQ(-1, stats.max);
|
||||
EXPECT_EQ(-1, stats.average);
|
||||
}
|
||||
|
||||
SimulatedClock clock_;
|
||||
};
|
||||
|
||||
TEST_F(StatsCounterTest, NoSamples) {
|
||||
AvgCounter counter(&clock_, nullptr);
|
||||
VerifyStatsIsNotSet(counter.GetStats());
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestRegisterObserver) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
const int kSample = 22;
|
||||
AvgCounter counter(&clock_, observer);
|
||||
AddSampleAndAdvance(kSample, kProcessIntervalMs, &counter);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(111);
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, VerifyProcessInterval) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
AvgCounter counter(&clock_, observer);
|
||||
counter.Add(4);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs - 1);
|
||||
// Try trigger process (interval has not passed).
|
||||
counter.Add(8);
|
||||
EXPECT_EQ(0, observer->num_calls_);
|
||||
VerifyStatsIsNotSet(counter.GetStats());
|
||||
// Make process interval pass.
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(111);
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(6, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(1, stats.num_samples);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestMetric_AvgCounter) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
AvgCounter counter(&clock_, observer);
|
||||
counter.Add(4);
|
||||
counter.Add(8);
|
||||
counter.Add(9);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(111);
|
||||
// Average per interval.
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(7, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(1, stats.num_samples);
|
||||
EXPECT_EQ(7, stats.min);
|
||||
EXPECT_EQ(7, stats.max);
|
||||
EXPECT_EQ(7, stats.average);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestMetric_MaxCounter) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
MaxCounter counter(&clock_, observer);
|
||||
counter.Add(4);
|
||||
counter.Add(9);
|
||||
counter.Add(8);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(111);
|
||||
// Average per interval.
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(9, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(1, stats.num_samples);
|
||||
EXPECT_EQ(9, stats.min);
|
||||
EXPECT_EQ(9, stats.max);
|
||||
EXPECT_EQ(9, stats.average);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestMetric_PercentCounter) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
PercentCounter counter(&clock_, observer);
|
||||
counter.Add(true);
|
||||
counter.Add(false);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(false);
|
||||
// Percentage per interval.
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(50, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(1, stats.num_samples);
|
||||
EXPECT_EQ(50, stats.min);
|
||||
EXPECT_EQ(50, stats.max);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestMetric_PermilleCounter) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
PermilleCounter counter(&clock_, observer);
|
||||
counter.Add(true);
|
||||
counter.Add(false);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(false);
|
||||
// Permille per interval.
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(500, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(1, stats.num_samples);
|
||||
EXPECT_EQ(500, stats.min);
|
||||
EXPECT_EQ(500, stats.max);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestMetric_RateCounter) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
RateCounter counter(&clock_, observer);
|
||||
counter.Add(186);
|
||||
counter.Add(350);
|
||||
counter.Add(22);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(111);
|
||||
// Rate per interval, (186 + 350 + 22) / 2 sec = 279 samples/sec
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(279, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(1, stats.num_samples);
|
||||
EXPECT_EQ(279, stats.min);
|
||||
EXPECT_EQ(279, stats.max);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestMetric_RateAccCounter) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
RateAccCounter counter(&clock_, observer);
|
||||
counter.Set(175);
|
||||
counter.Set(188);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Set(192);
|
||||
// Rate per interval: (188 - 0) / 2 sec = 94 samples/sec
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(94, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(1, stats.num_samples);
|
||||
EXPECT_EQ(94, stats.min);
|
||||
EXPECT_EQ(94, stats.max);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestGetStats_MultipleIntervals) {
|
||||
AvgCounter counter(&clock_, nullptr);
|
||||
const int kSample1 = 1;
|
||||
const int kSample2 = 5;
|
||||
const int kSample3 = 8;
|
||||
const int kSample4 = 11;
|
||||
const int kSample5 = 50;
|
||||
AddSampleAndAdvance(kSample1, kProcessIntervalMs, &counter);
|
||||
AddSampleAndAdvance(kSample2, kProcessIntervalMs, &counter);
|
||||
AddSampleAndAdvance(kSample3, kProcessIntervalMs, &counter);
|
||||
AddSampleAndAdvance(kSample4, kProcessIntervalMs, &counter);
|
||||
AddSampleAndAdvance(kSample5, kProcessIntervalMs, &counter);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(111);
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(5, stats.num_samples);
|
||||
EXPECT_EQ(kSample1, stats.min);
|
||||
EXPECT_EQ(kSample5, stats.max);
|
||||
EXPECT_EQ(15, stats.average);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestGetStatsTwice) {
|
||||
const int kSample1 = 4;
|
||||
const int kSample2 = 7;
|
||||
AvgCounter counter(&clock_, nullptr);
|
||||
AddSampleAndAdvance(kSample1, kProcessIntervalMs, &counter);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(kSample2);
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(1, stats.num_samples);
|
||||
EXPECT_EQ(kSample1, stats.min);
|
||||
EXPECT_EQ(kSample1, stats.max);
|
||||
// Trigger process (sample included in next interval).
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs);
|
||||
counter.Add(111);
|
||||
stats = counter.GetStats();
|
||||
EXPECT_EQ(2, stats.num_samples);
|
||||
EXPECT_EQ(kSample1, stats.min);
|
||||
EXPECT_EQ(kSample2, stats.max);
|
||||
EXPECT_EQ(6, stats.average);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestRateAccCounter_NegativeRateIgnored) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
const int kSample1 = 200; // 200 / 2 sec
|
||||
const int kSample2 = 100; // -100 / 2 sec - negative ignored
|
||||
const int kSample3 = 700; // 600 / 2 sec
|
||||
RateAccCounter counter(&clock_, observer);
|
||||
SetSampleAndAdvance(kSample1, kProcessIntervalMs, &counter);
|
||||
SetSampleAndAdvance(kSample2, kProcessIntervalMs, &counter);
|
||||
SetSampleAndAdvance(kSample3, kProcessIntervalMs, &counter);
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(100, observer->last_sample_);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Set(2000);
|
||||
EXPECT_EQ(2, observer->num_calls_);
|
||||
EXPECT_EQ(300, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(2, stats.num_samples);
|
||||
EXPECT_EQ(100, stats.min);
|
||||
EXPECT_EQ(300, stats.max);
|
||||
EXPECT_EQ(200, stats.average);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestAvgCounter_IntervalsWithoutSamplesIgnored) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
AvgCounter counter(&clock_, observer);
|
||||
AddSampleAndAdvance(6, kProcessIntervalMs * 4 - 1, &counter);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(8);
|
||||
// [6:1], two intervals without samples passed.
|
||||
EXPECT_EQ(1, observer->num_calls_);
|
||||
EXPECT_EQ(6, observer->last_sample_);
|
||||
// Make last interval pass.
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.Add(111); // Trigger process (sample included in next interval).
|
||||
// [6:1],[8:1]
|
||||
EXPECT_EQ(2, observer->num_calls_);
|
||||
EXPECT_EQ(8, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(2, stats.num_samples);
|
||||
EXPECT_EQ(6, stats.min);
|
||||
EXPECT_EQ(8, stats.max);
|
||||
}
|
||||
|
||||
TEST_F(StatsCounterTest, TestRateCounter_IntervalsWithoutSamplesIncluded) {
|
||||
StatsCounterObserverImpl* observer = new StatsCounterObserverImpl();
|
||||
const int kSample1 = 50; // 50 / 2 sec
|
||||
const int kSample2 = 20; // 20 / 2 sec
|
||||
RateCounter counter(&clock_, observer);
|
||||
counter.Add(kSample1);
|
||||
clock_.AdvanceTimeMilliseconds(kProcessIntervalMs * 3 - 1);
|
||||
// Trigger process (sample included in next interval).
|
||||
counter.Add(kSample2);
|
||||
// [0:1],[25:1], one interval without samples passed.
|
||||
EXPECT_EQ(2, observer->num_calls_);
|
||||
EXPECT_EQ(25, observer->last_sample_);
|
||||
// Make last interval pass.
|
||||
clock_.AdvanceTimeMilliseconds(1);
|
||||
counter.Add(111); // Trigger process (sample included in next interval).
|
||||
// [0:1],[10:1],[25:1]
|
||||
EXPECT_EQ(3, observer->num_calls_);
|
||||
EXPECT_EQ(10, observer->last_sample_);
|
||||
// Aggregated stats.
|
||||
AggregatedStats stats = counter.GetStats();
|
||||
EXPECT_EQ(3, stats.num_samples);
|
||||
EXPECT_EQ(0, stats.min);
|
||||
EXPECT_EQ(25, stats.max);
|
||||
}
|
||||
|
||||
} // namespace webrtc
|
||||
@ -44,6 +44,8 @@
|
||||
'video/send_delay_stats.h',
|
||||
'video/send_statistics_proxy.cc',
|
||||
'video/send_statistics_proxy.h',
|
||||
'video/stats_counter.cc',
|
||||
'video/stats_counter.h',
|
||||
'video/stream_synchronization.cc',
|
||||
'video/stream_synchronization.h',
|
||||
'video/video_capture_input.cc',
|
||||
|
||||
@ -170,6 +170,7 @@
|
||||
'video/report_block_stats_unittest.cc',
|
||||
'video/send_delay_stats_unittest.cc',
|
||||
'video/send_statistics_proxy_unittest.cc',
|
||||
'video/stats_counter_unittest.cc',
|
||||
'video/stream_synchronization_unittest.cc',
|
||||
'video/video_capture_input_unittest.cc',
|
||||
'video/video_decoder_unittest.cc',
|
||||
|
||||
Reference in New Issue
Block a user