Add additional metric (relative standard deviation of encode time) for overuse detection.
This code is currently only for testing. BUG=1577 R=stefan@webrtc.org Review URL: https://webrtc-codereview.appspot.com/19619004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@6381 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
0238682984
commit
734a532723
@ -63,16 +63,18 @@ const float kNormalUseStdDevMs = 20.0f;
|
||||
|
||||
struct CpuOveruseOptions {
|
||||
CpuOveruseOptions()
|
||||
: enable_capture_jitter_method(true),
|
||||
low_capture_jitter_threshold_ms(kNormalUseStdDevMs),
|
||||
high_capture_jitter_threshold_ms(kOveruseStdDevMs),
|
||||
enable_encode_usage_method(false),
|
||||
low_encode_usage_threshold_percent(60),
|
||||
high_encode_usage_threshold_percent(90),
|
||||
frame_timeout_interval_ms(1500),
|
||||
min_frame_samples(120),
|
||||
min_process_count(3),
|
||||
high_threshold_consecutive_count(2) {}
|
||||
: enable_capture_jitter_method(true),
|
||||
low_capture_jitter_threshold_ms(kNormalUseStdDevMs),
|
||||
high_capture_jitter_threshold_ms(kOveruseStdDevMs),
|
||||
enable_encode_usage_method(false),
|
||||
low_encode_usage_threshold_percent(60),
|
||||
high_encode_usage_threshold_percent(90),
|
||||
low_encode_time_rsd_threshold(0),
|
||||
high_encode_time_rsd_threshold(0),
|
||||
frame_timeout_interval_ms(1500),
|
||||
min_frame_samples(120),
|
||||
min_process_count(3),
|
||||
high_threshold_consecutive_count(2) {}
|
||||
|
||||
// Method based on inter-arrival jitter of captured frames.
|
||||
bool enable_capture_jitter_method;
|
||||
@ -82,6 +84,8 @@ struct CpuOveruseOptions {
|
||||
bool enable_encode_usage_method;
|
||||
int low_encode_usage_threshold_percent; // Threshold for triggering underuse.
|
||||
int high_encode_usage_threshold_percent; // Threshold for triggering overuse.
|
||||
int low_encode_time_rsd_threshold; // Threshold for triggering underuse.
|
||||
int high_encode_time_rsd_threshold; // Threshold for triggering overuse.
|
||||
// General settings.
|
||||
int frame_timeout_interval_ms; // The maximum allowed interval between two
|
||||
// frames before resetting estimations.
|
||||
@ -102,6 +106,8 @@ struct CpuOveruseOptions {
|
||||
o.low_encode_usage_threshold_percent &&
|
||||
high_encode_usage_threshold_percent ==
|
||||
o.high_encode_usage_threshold_percent &&
|
||||
low_encode_time_rsd_threshold == o.low_encode_time_rsd_threshold &&
|
||||
high_encode_time_rsd_threshold == o.high_encode_time_rsd_threshold &&
|
||||
frame_timeout_interval_ms == o.frame_timeout_interval_ms &&
|
||||
min_frame_samples == o.min_frame_samples &&
|
||||
min_process_count == o.min_process_count &&
|
||||
@ -111,16 +117,18 @@ struct CpuOveruseOptions {
|
||||
|
||||
struct CpuOveruseMetrics {
|
||||
CpuOveruseMetrics()
|
||||
: capture_jitter_ms(-1),
|
||||
avg_encode_time_ms(-1),
|
||||
encode_usage_percent(-1),
|
||||
capture_queue_delay_ms_per_s(-1) {}
|
||||
: capture_jitter_ms(-1),
|
||||
avg_encode_time_ms(-1),
|
||||
encode_usage_percent(-1),
|
||||
encode_rsd(-1),
|
||||
capture_queue_delay_ms_per_s(-1) {}
|
||||
|
||||
int capture_jitter_ms; // The current estimated jitter in ms based on
|
||||
// incoming captured frames.
|
||||
int avg_encode_time_ms; // The average encode time in ms.
|
||||
int encode_usage_percent; // The average encode time divided by the average
|
||||
// time difference between incoming captured frames.
|
||||
int encode_rsd; // The relative std dev of encode time of frames.
|
||||
int capture_queue_delay_ms_per_s; // The current time delay between an
|
||||
// incoming captured frame until the frame
|
||||
// is being processed. The delay is
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
#include <algorithm>
|
||||
#include <list>
|
||||
#include <map>
|
||||
|
||||
#include "webrtc/modules/video_coding/utility/include/exp_filter.h"
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
@ -206,6 +207,111 @@ class OveruseFrameDetector::EncodeUsage {
|
||||
scoped_ptr<VCMExpFilter> filtered_frame_diff_ms_;
|
||||
};
|
||||
|
||||
// Class for calculating the relative standard deviation of encode times.
|
||||
class OveruseFrameDetector::EncodeTimeRsd {
|
||||
public:
|
||||
EncodeTimeRsd(Clock* clock)
|
||||
: kWeightFactor(0.6f),
|
||||
count_(0),
|
||||
filtered_rsd_(new VCMExpFilter(kWeightFactor)),
|
||||
hist_samples_(0),
|
||||
hist_sum_(0.0f),
|
||||
last_process_time_ms_(clock->TimeInMilliseconds()) {
|
||||
Reset();
|
||||
}
|
||||
~EncodeTimeRsd() {}
|
||||
|
||||
void SetOptions(const CpuOveruseOptions& options) {
|
||||
options_ = options;
|
||||
}
|
||||
|
||||
void Reset() {
|
||||
count_ = 0;
|
||||
filtered_rsd_->Reset(kWeightFactor);
|
||||
filtered_rsd_->Apply(1.0f, InitialValue());
|
||||
hist_.clear();
|
||||
hist_samples_ = 0;
|
||||
hist_sum_ = 0.0f;
|
||||
}
|
||||
|
||||
void AddEncodeSample(float encode_time_ms) {
|
||||
int bin = static_cast<int>(encode_time_ms + 0.5f);
|
||||
if (bin <= 0) {
|
||||
// The frame was probably not encoded, skip possible dropped frame.
|
||||
return;
|
||||
}
|
||||
++count_;
|
||||
++hist_[bin];
|
||||
++hist_samples_;
|
||||
hist_sum_ += bin;
|
||||
}
|
||||
|
||||
void Process(int64_t now) {
|
||||
if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) {
|
||||
// Have not received min number of frames since last reset.
|
||||
return;
|
||||
}
|
||||
const int kMinHistSamples = 20;
|
||||
if (hist_samples_ < kMinHistSamples) {
|
||||
return;
|
||||
}
|
||||
const int64_t kMinDiffSinceLastProcessMs = 1000;
|
||||
int64_t diff_last_process_ms = now - last_process_time_ms_;
|
||||
if (now - last_process_time_ms_ <= kMinDiffSinceLastProcessMs) {
|
||||
return;
|
||||
}
|
||||
last_process_time_ms_ = now;
|
||||
|
||||
// Calculate variance (using samples above the mean).
|
||||
// Checks for a larger encode time of some frames while there is a small
|
||||
// increase in the average time.
|
||||
int mean = hist_sum_ / hist_samples_;
|
||||
float variance = 0.0f;
|
||||
int total_count = 0;
|
||||
for (std::map<int,int>::iterator it = hist_.begin();
|
||||
it != hist_.end(); ++it) {
|
||||
int time = it->first;
|
||||
int count = it->second;
|
||||
if (time > mean) {
|
||||
total_count += count;
|
||||
for (int i = 0; i < count; ++i) {
|
||||
variance += ((time - mean) * (time - mean));
|
||||
}
|
||||
}
|
||||
}
|
||||
variance /= std::max(total_count, 1);
|
||||
float cov = sqrt(variance) / mean;
|
||||
|
||||
hist_.clear();
|
||||
hist_samples_ = 0;
|
||||
hist_sum_ = 0.0f;
|
||||
|
||||
float exp = static_cast<float>(diff_last_process_ms) / kProcessIntervalMs;
|
||||
exp = std::min(exp, kMaxExp);
|
||||
filtered_rsd_->Apply(exp, 100.0f * cov);
|
||||
}
|
||||
|
||||
int Value() const {
|
||||
return static_cast<int>(filtered_rsd_->Value() + 0.5);
|
||||
}
|
||||
|
||||
float InitialValue() const {
|
||||
// Start in between the underuse and overuse threshold.
|
||||
return (options_.low_encode_time_rsd_threshold +
|
||||
options_.high_encode_time_rsd_threshold) / 2.0f;
|
||||
}
|
||||
|
||||
private:
|
||||
const float kWeightFactor;
|
||||
uint32_t count_; // Number of encode samples since last reset.
|
||||
CpuOveruseOptions options_;
|
||||
scoped_ptr<VCMExpFilter> filtered_rsd_;
|
||||
int hist_samples_;
|
||||
float hist_sum_;
|
||||
std::map<int,int> hist_; // Histogram of encode time of frames.
|
||||
int64_t last_process_time_ms_;
|
||||
};
|
||||
|
||||
// Class for calculating the capture queue delay change.
|
||||
class OveruseFrameDetector::CaptureQueueDelay {
|
||||
public:
|
||||
@ -278,6 +384,7 @@ OveruseFrameDetector::OveruseFrameDetector(Clock* clock)
|
||||
num_pixels_(0),
|
||||
last_encode_sample_ms_(0),
|
||||
encode_time_(new EncodeTimeAvg()),
|
||||
encode_rsd_(new EncodeTimeRsd(clock)),
|
||||
encode_usage_(new EncodeUsage()),
|
||||
capture_queue_delay_(new CaptureQueueDelay()) {
|
||||
}
|
||||
@ -299,6 +406,7 @@ void OveruseFrameDetector::SetOptions(const CpuOveruseOptions& options) {
|
||||
options_ = options;
|
||||
capture_deltas_.SetOptions(options);
|
||||
encode_usage_->SetOptions(options);
|
||||
encode_rsd_->SetOptions(options);
|
||||
ResetAll(num_pixels_);
|
||||
}
|
||||
|
||||
@ -312,6 +420,7 @@ void OveruseFrameDetector::GetCpuOveruseMetrics(
|
||||
CriticalSectionScoped cs(crit_.get());
|
||||
metrics->capture_jitter_ms = static_cast<int>(capture_deltas_.StdDev() + 0.5);
|
||||
metrics->avg_encode_time_ms = encode_time_->filtered_encode_time_ms();
|
||||
metrics->encode_rsd = encode_rsd_->Value();
|
||||
metrics->encode_usage_percent = encode_usage_->UsageInPercent();
|
||||
metrics->capture_queue_delay_ms_per_s =
|
||||
capture_queue_delay_->filtered_delay_ms_per_s();
|
||||
@ -340,6 +449,7 @@ void OveruseFrameDetector::ResetAll(int num_pixels) {
|
||||
num_pixels_ = num_pixels;
|
||||
capture_deltas_.Reset();
|
||||
encode_usage_->Reset();
|
||||
encode_rsd_->Reset();
|
||||
capture_queue_delay_->ClearFrames();
|
||||
last_capture_time_ = 0;
|
||||
num_process_times_ = 0;
|
||||
@ -374,6 +484,7 @@ void OveruseFrameDetector::FrameEncoded(int encode_time_ms) {
|
||||
int64_t diff_ms = time - last_encode_sample_ms_;
|
||||
encode_time_->AddEncodeSample(encode_time_ms, diff_ms);
|
||||
encode_usage_->AddEncodeSample(encode_time_ms, diff_ms);
|
||||
encode_rsd_->AddEncodeSample(encode_time_ms);
|
||||
}
|
||||
last_encode_sample_ms_ = time;
|
||||
}
|
||||
@ -391,6 +502,7 @@ int32_t OveruseFrameDetector::Process() {
|
||||
next_process_time_ = now + kProcessIntervalMs;
|
||||
++num_process_times_;
|
||||
|
||||
encode_rsd_->Process(now);
|
||||
capture_queue_delay_->CalculateDelayChange(diff_ms);
|
||||
|
||||
if (num_process_times_ <= options_.min_process_count) {
|
||||
@ -430,11 +542,11 @@ int32_t OveruseFrameDetector::Process() {
|
||||
|
||||
int rampup_delay =
|
||||
in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
|
||||
LOG(LS_VERBOSE) << "Capture input stats: avg: " << capture_deltas_.Mean()
|
||||
<< " std_dev " << capture_deltas_.StdDev()
|
||||
<< " rampup delay " << rampup_delay
|
||||
<< " overuse >= " << options_.high_capture_jitter_threshold_ms
|
||||
<< " underuse < " << options_.low_capture_jitter_threshold_ms;
|
||||
LOG(LS_INFO) << " Frame stats: capture avg: " << capture_deltas_.Mean()
|
||||
<< " capture stddev " << capture_deltas_.StdDev()
|
||||
<< " encode usage " << encode_usage_->UsageInPercent()
|
||||
<< " encode rsd " << encode_rsd_->Value()
|
||||
<< " rampup delay " << rampup_delay;
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -95,6 +95,7 @@ class OveruseFrameDetector : public Module {
|
||||
|
||||
private:
|
||||
class EncodeTimeAvg;
|
||||
class EncodeTimeRsd;
|
||||
class EncodeUsage;
|
||||
class CaptureQueueDelay;
|
||||
|
||||
@ -133,6 +134,7 @@ class OveruseFrameDetector : public Module {
|
||||
|
||||
int64_t last_encode_sample_ms_;
|
||||
scoped_ptr<EncodeTimeAvg> encode_time_;
|
||||
scoped_ptr<EncodeTimeRsd> encode_rsd_;
|
||||
scoped_ptr<EncodeUsage> encode_usage_;
|
||||
|
||||
scoped_ptr<CaptureQueueDelay> capture_queue_delay_;
|
||||
|
Loading…
x
Reference in New Issue
Block a user