Overuse detection based on capture-input jitter.
This is believed to be more reliable in real-world cases. The camera seems to fall behind sooner than the encoder starts taking too long time encoding, so this is believed to be an earlier trigger. BUG=2325 R=asapersson@webrtc.org, mflodman@webrtc.org Review URL: https://webrtc-codereview.appspot.com/2140004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@4648 4adac7df-926f-26a2-2b94-8c16560cd09d
This commit is contained in:
parent
0b960cf7a1
commit
a957570d62
@ -11,48 +11,79 @@
|
||||
#include "webrtc/video_engine/overuse_frame_detector.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "webrtc/system_wrappers/interface/clock.h"
|
||||
#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
|
||||
#include "webrtc/system_wrappers/interface/trace.h"
|
||||
#include "webrtc/video_engine/include/vie_base.h"
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
Statistics::Statistics() { Reset(); }
|
||||
|
||||
void Statistics::Reset() {
|
||||
sum_ = sum_squared_ = 0.0;
|
||||
count_ = 0;
|
||||
}
|
||||
|
||||
void Statistics::AddSample(double sample) {
|
||||
sum_ += sample;
|
||||
sum_squared_ += sample * sample;
|
||||
++count_;
|
||||
}
|
||||
|
||||
double Statistics::Mean() const {
|
||||
if (count_ == 0)
|
||||
return 0.0;
|
||||
return sum_ / count_;
|
||||
}
|
||||
|
||||
double Statistics::Variance() const {
|
||||
if (count_ == 0)
|
||||
return 0.0;
|
||||
return sum_squared_ / count_ - Mean() * Mean();
|
||||
}
|
||||
|
||||
double Statistics::StdDev() const { return sqrt(Variance()); }
|
||||
|
||||
uint64_t Statistics::Samples() const { return count_; }
|
||||
|
||||
// TODO(mflodman) Test different values for all of these to trigger correctly,
|
||||
// avoid fluctuations etc.
|
||||
|
||||
namespace {
|
||||
// Interval for 'Process' to be called.
|
||||
const int64_t kProcessIntervalMs = 2000;
|
||||
const int64_t kProcessIntervalMs = 5000;
|
||||
|
||||
// Duration capture and encode samples are valid.
|
||||
const int kOveruseHistoryMs = 5000;
|
||||
// Limits on standard deviation for under/overuse.
|
||||
const double kOveruseStdDevMs = 15.0;
|
||||
const double kNormalUseStdDevMs = 10.0;
|
||||
|
||||
// The minimum history to trigger an overuse or underuse.
|
||||
const int64_t kMinValidHistoryMs = kOveruseHistoryMs / 2;
|
||||
// Rampdown checks.
|
||||
const int kConsecutiveChecksAboveThreshold = 2;
|
||||
|
||||
// Encode / capture ratio to decide an overuse.
|
||||
const float kMinEncodeRatio = 29 / 30.0f;
|
||||
// Delay between consecutive rampups. (Used for quick recovery.)
|
||||
const int kQuickRampUpDelayMs = 10 * 1000;
|
||||
// Delay between rampup attempts. Initially uses standard, scales up to max.
|
||||
const int kStandardRampUpDelayMs = 30 * 1000;
|
||||
const int kMaxRampUpDelayMs = 120 * 1000;
|
||||
// Expontential back-off factor, to prevent annoying up-down behaviour.
|
||||
const double kRampUpBackoffFactor = 2.0;
|
||||
|
||||
// Minimum time between two callbacks.
|
||||
const int kMinCallbackDeltaMs = 30000;
|
||||
|
||||
// Safety margin between encode time for different resolutions to decide if we
|
||||
// can trigger an underuse callback.
|
||||
// TODO(mflodman): This should be improved, e.g. test time per pixel?
|
||||
const float kIncreaseThreshold = 1.5f;
|
||||
// Minimum samples required to perform a check.
|
||||
const size_t kMinFrameSampleCount = 30;
|
||||
} // namespace
|
||||
|
||||
OveruseFrameDetector::OveruseFrameDetector(Clock* clock)
|
||||
: crit_(CriticalSectionWrapper::CreateCriticalSection()),
|
||||
observer_(NULL),
|
||||
clock_(clock),
|
||||
last_process_time_(clock->TimeInMilliseconds()),
|
||||
last_callback_time_(clock->TimeInMilliseconds()),
|
||||
underuse_encode_timing_enabled_(false),
|
||||
num_pixels_(0),
|
||||
max_num_pixels_(0) {
|
||||
}
|
||||
next_process_time_(clock_->TimeInMilliseconds()),
|
||||
last_capture_time_(0),
|
||||
last_overuse_time_(0),
|
||||
checks_above_threshold_(0),
|
||||
last_rampup_time_(0),
|
||||
in_quick_rampup_(false),
|
||||
current_rampup_delay_ms_(kStandardRampUpDelayMs) {}
|
||||
|
||||
OveruseFrameDetector::~OveruseFrameDetector() {
|
||||
}
|
||||
@ -62,146 +93,99 @@ void OveruseFrameDetector::SetObserver(CpuOveruseObserver* observer) {
|
||||
observer_ = observer;
|
||||
}
|
||||
|
||||
void OveruseFrameDetector::set_underuse_encode_timing_enabled(bool enable) {
|
||||
CriticalSectionScoped cs(crit_.get());
|
||||
underuse_encode_timing_enabled_ = enable;
|
||||
}
|
||||
|
||||
void OveruseFrameDetector::FrameCaptured() {
|
||||
CriticalSectionScoped cs(crit_.get());
|
||||
capture_times_.push_back(clock_->TimeInMilliseconds());
|
||||
}
|
||||
|
||||
void OveruseFrameDetector::FrameEncoded(int64_t encode_time, size_t width,
|
||||
size_t height) {
|
||||
assert(encode_time >= 0);
|
||||
CriticalSectionScoped cs(crit_.get());
|
||||
// The frame is disregarded in case of a reset, to startup in a fresh state.
|
||||
if (MaybeResetResolution(width, height))
|
||||
return;
|
||||
|
||||
encode_times_.push_back(std::make_pair(clock_->TimeInMilliseconds(),
|
||||
encode_time));
|
||||
int64_t time = clock_->TimeInMilliseconds();
|
||||
if (last_capture_time_ != 0) {
|
||||
capture_deltas_.AddSample(time - last_capture_time_);
|
||||
}
|
||||
last_capture_time_ = time;
|
||||
}
|
||||
|
||||
int32_t OveruseFrameDetector::TimeUntilNextProcess() {
|
||||
CriticalSectionScoped cs(crit_.get());
|
||||
return last_process_time_ + kProcessIntervalMs - clock_->TimeInMilliseconds();
|
||||
return next_process_time_ - clock_->TimeInMilliseconds();
|
||||
}
|
||||
|
||||
int32_t OveruseFrameDetector::Process() {
|
||||
CriticalSectionScoped cs(crit_.get());
|
||||
|
||||
int64_t now = clock_->TimeInMilliseconds();
|
||||
if (now < last_process_time_ + kProcessIntervalMs)
|
||||
|
||||
// Used to protect against Process() being called too often.
|
||||
if (now < next_process_time_)
|
||||
return 0;
|
||||
|
||||
last_process_time_ = now;
|
||||
RemoveOldSamples();
|
||||
next_process_time_ = now + kProcessIntervalMs;
|
||||
|
||||
// Don't trigger an overuse unless we've encoded at least one frame.
|
||||
if (!observer_ || encode_times_.empty() || capture_times_.empty())
|
||||
// Don't trigger overuse unless we've seen any frames
|
||||
if (capture_deltas_.Samples() < kMinFrameSampleCount)
|
||||
return 0;
|
||||
|
||||
// At least half the maximum history should be filled before we trigger an
|
||||
// overuse.
|
||||
// TODO(mflodman) Shall the time difference between the first and the last
|
||||
// sample be checked instead?
|
||||
if (encode_times_.front().first > now - kMinValidHistoryMs) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
if (IsOverusing()) {
|
||||
// Overuse detected.
|
||||
// Remember the average encode time for this overuse, as a help to trigger
|
||||
// normal usage.
|
||||
encode_overuse_times_[num_pixels_] = CalculateAverageEncodeTime();
|
||||
RemoveAllSamples();
|
||||
observer_->OveruseDetected();
|
||||
last_callback_time_ = now;
|
||||
// If the last thing we did was going up, and now have to back down, we need
|
||||
// to check if this peak was short. If so we should back off to avoid going
|
||||
// back and forth between this load, the system doesn't seem to handle it.
|
||||
bool check_for_backoff = last_rampup_time_ > last_overuse_time_;
|
||||
if (check_for_backoff) {
|
||||
if (now - last_rampup_time_ < kStandardRampUpDelayMs) {
|
||||
// Going up was not ok for very long, back off.
|
||||
current_rampup_delay_ms_ *= kRampUpBackoffFactor;
|
||||
if (current_rampup_delay_ms_ > kMaxRampUpDelayMs)
|
||||
current_rampup_delay_ms_ = kMaxRampUpDelayMs;
|
||||
} else {
|
||||
// Not currently backing off, reset rampup delay.
|
||||
current_rampup_delay_ms_ = kStandardRampUpDelayMs;
|
||||
}
|
||||
}
|
||||
|
||||
last_overuse_time_ = now;
|
||||
in_quick_rampup_ = false;
|
||||
checks_above_threshold_ = 0;
|
||||
|
||||
if (observer_ != NULL)
|
||||
observer_->OveruseDetected();
|
||||
} else if (IsUnderusing(now)) {
|
||||
RemoveAllSamples();
|
||||
observer_->NormalUsage();
|
||||
last_callback_time_ = now;
|
||||
last_rampup_time_ = now;
|
||||
in_quick_rampup_ = true;
|
||||
|
||||
if (observer_ != NULL)
|
||||
observer_->NormalUsage();
|
||||
}
|
||||
|
||||
capture_deltas_.Reset();
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OveruseFrameDetector::RemoveOldSamples() {
|
||||
int64_t time_now = clock_->TimeInMilliseconds();
|
||||
while (!capture_times_.empty() &&
|
||||
capture_times_.front() < time_now - kOveruseHistoryMs) {
|
||||
capture_times_.pop_front();
|
||||
}
|
||||
while (!encode_times_.empty() &&
|
||||
encode_times_.front().first < time_now - kOveruseHistoryMs) {
|
||||
encode_times_.pop_front();
|
||||
}
|
||||
}
|
||||
|
||||
void OveruseFrameDetector::RemoveAllSamples() {
|
||||
capture_times_.clear();
|
||||
encode_times_.clear();
|
||||
}
|
||||
|
||||
int64_t OveruseFrameDetector::CalculateAverageEncodeTime() const {
|
||||
if (encode_times_.empty())
|
||||
return 0;
|
||||
|
||||
int64_t total_encode_time = 0;
|
||||
for (std::list<EncodeTime>::const_iterator it = encode_times_.begin();
|
||||
it != encode_times_.end(); ++it) {
|
||||
total_encode_time += it->second;
|
||||
}
|
||||
return total_encode_time / encode_times_.size();
|
||||
}
|
||||
|
||||
bool OveruseFrameDetector::MaybeResetResolution(size_t width, size_t height) {
|
||||
int num_pixels = width * height;
|
||||
if (num_pixels == num_pixels_)
|
||||
return false;
|
||||
|
||||
RemoveAllSamples();
|
||||
num_pixels_ = num_pixels;
|
||||
if (num_pixels > max_num_pixels_)
|
||||
max_num_pixels_ = num_pixels;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
bool OveruseFrameDetector::IsOverusing() {
|
||||
if (encode_times_.empty())
|
||||
return false;
|
||||
WEBRTC_TRACE(
|
||||
webrtc::kTraceInfo,
|
||||
webrtc::kTraceVideo,
|
||||
-1,
|
||||
"Capture input stats: avg: %.2fms, std_dev: %.2fms (rampup delay: "
|
||||
"%dms, overuse: >=%.2fms, "
|
||||
"underuse: <%.2fms)",
|
||||
capture_deltas_.Mean(),
|
||||
capture_deltas_.StdDev(),
|
||||
in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_,
|
||||
kOveruseStdDevMs,
|
||||
kNormalUseStdDevMs);
|
||||
|
||||
float encode_ratio = encode_times_.size() /
|
||||
static_cast<float>(capture_times_.size());
|
||||
return encode_ratio < kMinEncodeRatio;
|
||||
if (capture_deltas_.StdDev() >= kOveruseStdDevMs) {
|
||||
++checks_above_threshold_;
|
||||
} else {
|
||||
checks_above_threshold_ = 0;
|
||||
}
|
||||
|
||||
return checks_above_threshold_ >= kConsecutiveChecksAboveThreshold;
|
||||
}
|
||||
|
||||
bool OveruseFrameDetector::IsUnderusing(int64_t time_now) {
|
||||
if (time_now < last_callback_time_ + kMinCallbackDeltaMs ||
|
||||
num_pixels_ >= max_num_pixels_) {
|
||||
int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
|
||||
if (time_now < last_rampup_time_ + delay)
|
||||
return false;
|
||||
}
|
||||
bool underusing = true;
|
||||
if (underuse_encode_timing_enabled_) {
|
||||
int prev_overuse_encode_time = 0;
|
||||
for (std::map<int, int64_t>::reverse_iterator rit =
|
||||
encode_overuse_times_.rbegin();
|
||||
rit != encode_overuse_times_.rend() && rit->first > num_pixels_;
|
||||
++rit) {
|
||||
prev_overuse_encode_time = rit->second;
|
||||
}
|
||||
// TODO(mflodman): This might happen now if the resolution is decreased
|
||||
// by the user before an overuse has been triggered.
|
||||
assert(prev_overuse_encode_time > 0);
|
||||
|
||||
// TODO(mflodman) Use some other way to guess if an increased resolution
|
||||
// might work or not, e.g. encode time per pixel?
|
||||
if (CalculateAverageEncodeTime() * kIncreaseThreshold >
|
||||
prev_overuse_encode_time) {
|
||||
underusing = false;
|
||||
}
|
||||
}
|
||||
return underusing;
|
||||
return capture_deltas_.StdDev() < kNormalUseStdDevMs;
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
||||
@ -11,10 +11,6 @@
|
||||
#ifndef WEBRTC_VIDEO_ENGINE_OVERUSE_FRAME_DETECTOR_H_
|
||||
#define WEBRTC_VIDEO_ENGINE_OVERUSE_FRAME_DETECTOR_H_
|
||||
|
||||
#include <list>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
|
||||
#include "webrtc/modules/interface/module.h"
|
||||
#include "webrtc/system_wrappers/interface/constructor_magic.h"
|
||||
#include "webrtc/system_wrappers/interface/scoped_ptr.h"
|
||||
@ -25,8 +21,26 @@ class Clock;
|
||||
class CriticalSectionWrapper;
|
||||
class CpuOveruseObserver;
|
||||
|
||||
// Use to detect system overuse based on the number of captured frames vs the
|
||||
// number of encoded frames.
|
||||
// TODO(pbos): Move this somewhere appropriate
|
||||
class Statistics {
|
||||
public:
|
||||
Statistics();
|
||||
|
||||
void AddSample(double sample);
|
||||
void Reset();
|
||||
|
||||
double Mean() const;
|
||||
double Variance() const;
|
||||
double StdDev() const;
|
||||
uint64_t Samples() const;
|
||||
|
||||
private:
|
||||
double sum_;
|
||||
double sum_squared_;
|
||||
uint64_t count_;
|
||||
};
|
||||
|
||||
// Use to detect system overuse based on jitter in incoming frames.
|
||||
class OveruseFrameDetector : public Module {
|
||||
public:
|
||||
explicit OveruseFrameDetector(Clock* clock);
|
||||
@ -36,33 +50,14 @@ class OveruseFrameDetector : public Module {
|
||||
// 'observer' to NULL to disable callbacks.
|
||||
void SetObserver(CpuOveruseObserver* observer);
|
||||
|
||||
// TODO(mflodman): Move to another API?
|
||||
// Enables usage of encode time to trigger normal usage after an overuse,
|
||||
// default false.
|
||||
void set_underuse_encode_timing_enabled(bool enable);
|
||||
|
||||
// Called for each captured frame.
|
||||
void FrameCaptured();
|
||||
|
||||
// Called for every encoded frame.
|
||||
void FrameEncoded(int64_t encode_time, size_t width, size_t height);
|
||||
|
||||
// Implements Module.
|
||||
virtual int32_t TimeUntilNextProcess();
|
||||
virtual int32_t Process();
|
||||
virtual int32_t TimeUntilNextProcess() OVERRIDE;
|
||||
virtual int32_t Process() OVERRIDE;
|
||||
|
||||
private:
|
||||
// All private functions are assumed to be critical section protected.
|
||||
// Clear samples older than the overuse history.
|
||||
void RemoveOldSamples();
|
||||
// Clears the entire history, including samples still affecting the
|
||||
// calculations.
|
||||
void RemoveAllSamples();
|
||||
int64_t CalculateAverageEncodeTime() const;
|
||||
// Returns true and resets calculations and history if a new resolution is
|
||||
// discovered, false otherwise.
|
||||
bool MaybeResetResolution(size_t width, size_t height);
|
||||
|
||||
bool IsOverusing();
|
||||
bool IsUnderusing(int64_t time_now);
|
||||
|
||||
@ -73,24 +68,17 @@ class OveruseFrameDetector : public Module {
|
||||
CpuOveruseObserver* observer_;
|
||||
|
||||
Clock* clock_;
|
||||
int64_t last_process_time_;
|
||||
int64_t last_callback_time_;
|
||||
int64_t next_process_time_;
|
||||
|
||||
// Sorted list of times captured frames were delivered, oldest frame first.
|
||||
std::list<int64_t> capture_times_;
|
||||
// <Encode report time, time spent encoding the frame>.
|
||||
typedef std::pair<int64_t, int64_t> EncodeTime;
|
||||
// Sorted list with oldest frame first.
|
||||
std::list<EncodeTime> encode_times_;
|
||||
Statistics capture_deltas_;
|
||||
int64_t last_capture_time_;
|
||||
|
||||
// True if encode time should be considered to trigger an underuse.
|
||||
bool underuse_encode_timing_enabled_;
|
||||
// Number of pixels in the currently encoded resolution.
|
||||
int num_pixels_;
|
||||
// Maximum resolution encoded.
|
||||
int max_num_pixels_;
|
||||
// <number of pixels, average encode time triggering an overuse>.
|
||||
std::map<int, int64_t> encode_overuse_times_;
|
||||
int64_t last_overuse_time_;
|
||||
int checks_above_threshold_;
|
||||
|
||||
int64_t last_rampup_time_;
|
||||
bool in_quick_rampup_;
|
||||
int current_rampup_delay_ms_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(OveruseFrameDetector);
|
||||
};
|
||||
|
||||
@ -16,17 +16,8 @@
|
||||
#include "webrtc/video_engine/include/vie_base.h"
|
||||
#include "webrtc/video_engine/overuse_frame_detector.h"
|
||||
|
||||
using ::testing::_;
|
||||
using ::testing::AnyNumber;
|
||||
using ::testing::Return;
|
||||
|
||||
namespace webrtc {
|
||||
|
||||
const int kProcessIntervalMs = 2000;
|
||||
const int kOveruseHistoryMs = 5000;
|
||||
const int kMinCallbackDeltaMs = 30000;
|
||||
const int64_t kMinValidHistoryMs = kOveruseHistoryMs / 2;
|
||||
|
||||
class MockCpuOveruseObserver : public CpuOveruseObserver {
|
||||
public:
|
||||
MockCpuOveruseObserver() {}
|
||||
@ -45,29 +36,34 @@ class OveruseFrameDetectorTest : public ::testing::Test {
|
||||
overuse_detector_->SetObserver(observer_.get());
|
||||
}
|
||||
|
||||
void CaptureAndEncodeFrames(int num_frames, int64_t frame_interval_ms,
|
||||
int encode_time_ms, size_t width, size_t height) {
|
||||
for (int frame = 0; frame < num_frames; ++frame) {
|
||||
void InsertFramesWithInterval(size_t num_frames, int interval_ms) {
|
||||
while (num_frames-- > 0) {
|
||||
clock_->AdvanceTimeMilliseconds(interval_ms);
|
||||
overuse_detector_->FrameCaptured();
|
||||
overuse_detector_->FrameEncoded(encode_time_ms, width, height);
|
||||
clock_->AdvanceTimeMilliseconds(frame_interval_ms);
|
||||
}
|
||||
}
|
||||
|
||||
void CaptureAndEncodeWithOveruse(int overuse_time_ms,
|
||||
int64_t frame_interval_ms,
|
||||
int64_t encode_time_ms, size_t width,
|
||||
size_t height) {
|
||||
// 'encodes_before_dropping' is derived from 'kMinEncodeRatio' in
|
||||
// 'overuse_frame_detector.h'.
|
||||
const int encodes_before_dropping = 14;
|
||||
for (int time_ms = 0; time_ms < overuse_time_ms;
|
||||
time_ms += frame_interval_ms * (1 + encodes_before_dropping)) {
|
||||
CaptureAndEncodeFrames(encodes_before_dropping, frame_interval_ms,
|
||||
encode_time_ms, width, height);
|
||||
overuse_detector_->FrameCaptured();
|
||||
clock_->AdvanceTimeMilliseconds(frame_interval_ms);
|
||||
}
|
||||
void TriggerOveruse() {
|
||||
int regular_frame_interval_ms = 33;
|
||||
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
|
||||
|
||||
InsertFramesWithInterval(30, regular_frame_interval_ms);
|
||||
InsertFramesWithInterval(30, 1000);
|
||||
overuse_detector_->Process();
|
||||
|
||||
InsertFramesWithInterval(30, regular_frame_interval_ms);
|
||||
InsertFramesWithInterval(30, 1000);
|
||||
overuse_detector_->Process();
|
||||
}
|
||||
|
||||
void TriggerNormalUsage() {
|
||||
int regular_frame_interval_ms = 33;
|
||||
|
||||
EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1));
|
||||
|
||||
InsertFramesWithInterval(300, regular_frame_interval_ms);
|
||||
overuse_detector_->Process();
|
||||
}
|
||||
|
||||
scoped_ptr<SimulatedClock> clock_;
|
||||
@ -76,140 +72,25 @@ class OveruseFrameDetectorTest : public ::testing::Test {
|
||||
};
|
||||
|
||||
TEST_F(OveruseFrameDetectorTest, TriggerOveruse) {
|
||||
EXPECT_EQ(overuse_detector_->TimeUntilNextProcess(), kProcessIntervalMs);
|
||||
|
||||
// Enough history to trigger an overuse, but life is good so far.
|
||||
int frame_interval_ms = 33;
|
||||
int num_frames = kMinValidHistoryMs / frame_interval_ms + 1;
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 2, 2, 2);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Trigger an overuse.
|
||||
CaptureAndEncodeWithOveruse(kOveruseHistoryMs, frame_interval_ms, 2, 2, 2);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
TriggerOveruse();
|
||||
}
|
||||
|
||||
TEST_F(OveruseFrameDetectorTest, OveruseAndRecover) {
|
||||
overuse_detector_->set_underuse_encode_timing_enabled(true);
|
||||
// Start with triggering an overuse.
|
||||
// A new resolution will trigger a reset, so add one frame to get going.
|
||||
int frame_interval_ms = 33;
|
||||
CaptureAndEncodeWithOveruse(kMinValidHistoryMs, frame_interval_ms, 2, 2, 2);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Make everything good again, but don't advance time long enough to trigger
|
||||
// an underuse.
|
||||
int num_frames = kOveruseHistoryMs / frame_interval_ms;
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 1, 1, 1);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Advance time long enough to trigger an increase callback.
|
||||
num_frames = (kMinCallbackDeltaMs - kOveruseHistoryMs + 1) /
|
||||
(frame_interval_ms - 0.5f);
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 1, 1, 1);
|
||||
EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
TriggerOveruse();
|
||||
TriggerNormalUsage();
|
||||
}
|
||||
|
||||
TEST_F(OveruseFrameDetectorTest, DoubleOveruseAndRecover) {
|
||||
overuse_detector_->set_underuse_encode_timing_enabled(true);
|
||||
// Start with triggering an overuse.
|
||||
int frame_interval_ms = 33;
|
||||
CaptureAndEncodeWithOveruse(kMinValidHistoryMs, frame_interval_ms, 16, 4, 4);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
TriggerOveruse();
|
||||
TriggerOveruse();
|
||||
TriggerNormalUsage();
|
||||
}
|
||||
|
||||
CaptureAndEncodeWithOveruse(kOveruseHistoryMs, frame_interval_ms, 4, 2, 2);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Let life be good again and wait for an underuse callback.
|
||||
int num_frames = kMinCallbackDeltaMs / (frame_interval_ms - 0.5f);
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 1, 1, 1);
|
||||
EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// And one more.
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 4, 2, 2);
|
||||
EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// But no more since we're at the max resolution.
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 4, 4, 4);
|
||||
TEST_F(OveruseFrameDetectorTest, ConstantOveruseGivesNoNormalUsage) {
|
||||
EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0);
|
||||
overuse_detector_->Process();
|
||||
|
||||
for(size_t i = 0; i < 64; ++i)
|
||||
TriggerOveruse();
|
||||
}
|
||||
|
||||
TEST_F(OveruseFrameDetectorTest, OveruseAndNoRecovery) {
|
||||
overuse_detector_->set_underuse_encode_timing_enabled(true);
|
||||
// Start with triggering an overuse.
|
||||
int frame_interval_ms = 33;
|
||||
CaptureAndEncodeWithOveruse(kMinValidHistoryMs, frame_interval_ms, 4, 2, 2);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Everything is fine, but we haven't waited long enough to trigger an
|
||||
// increase callback.
|
||||
CaptureAndEncodeFrames(30, 33, 3, 1, 1);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Advance time enough to trigger an increase callback, but encode time
|
||||
// shouldn't have decreased enough to try an increase.
|
||||
int num_frames = kMinCallbackDeltaMs / (frame_interval_ms - 0.5f);
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 3, 1, 1);
|
||||
EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0);
|
||||
overuse_detector_->Process();
|
||||
}
|
||||
|
||||
TEST_F(OveruseFrameDetectorTest, NoEncodeTimeForUnderuse) {
|
||||
overuse_detector_->set_underuse_encode_timing_enabled(false);
|
||||
// Start with triggering an overuse.
|
||||
int frame_interval_ms = 33;
|
||||
CaptureAndEncodeWithOveruse(kMinValidHistoryMs, frame_interval_ms, 4, 2, 2);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Everything is fine, but we haven't waited long enough to trigger an
|
||||
// increase callback.
|
||||
int num_frames = 1000 / (frame_interval_ms - 0.5f);
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 3, 1, 1);
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Advance time enough to allow underuse, but keep encode time too high to
|
||||
// trigger an underuse if accounted for, see 'OveruseAndNoRecovery' test case.
|
||||
num_frames = kMinCallbackDeltaMs / (frame_interval_ms - 0.5f);
|
||||
CaptureAndEncodeFrames(num_frames, frame_interval_ms, 3, 1, 1);
|
||||
EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
}
|
||||
|
||||
TEST_F(OveruseFrameDetectorTest, ResolutionChange) {
|
||||
overuse_detector_->set_underuse_encode_timing_enabled(true);
|
||||
int frame_interval_ms = 33;
|
||||
CaptureAndEncodeWithOveruse(kMinValidHistoryMs / 2, frame_interval_ms, 3, 1,
|
||||
1);
|
||||
|
||||
// Keep overusing, but with a new resolution.
|
||||
CaptureAndEncodeWithOveruse(kMinValidHistoryMs - frame_interval_ms,
|
||||
frame_interval_ms, 4, 2, 2);
|
||||
|
||||
// Enough samples and time to trigger an overuse, but resolution reset should
|
||||
// prevent this.
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(0);
|
||||
overuse_detector_->Process();
|
||||
|
||||
// Fill the history.
|
||||
CaptureAndEncodeFrames(2, kOveruseHistoryMs / 2, 3, 1, 1);
|
||||
|
||||
// Capture a frame without finish encoding to trigger an overuse.
|
||||
overuse_detector_->FrameCaptured();
|
||||
EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1);
|
||||
overuse_detector_->Process();
|
||||
}
|
||||
} // namespace webrtc
|
||||
|
||||
@ -518,15 +518,7 @@ bool ViECapturer::ViECaptureProcess() {
|
||||
captured_frame_.ResetSize();
|
||||
capture_cs_->Leave();
|
||||
|
||||
int64_t encode_start_time =
|
||||
Clock::GetRealTimeClock()->TimeInMilliseconds();
|
||||
DeliverI420Frame(&deliver_frame_);
|
||||
|
||||
// The frame has been encoded, update the overuse detector with the
|
||||
// duration.
|
||||
overuse_detector_->FrameEncoded(
|
||||
Clock::GetRealTimeClock()->TimeInMilliseconds() - encode_start_time,
|
||||
deliver_frame_.width(), deliver_frame_.height());
|
||||
}
|
||||
deliver_cs_->Leave();
|
||||
if (current_brightness_level_ != reported_brightness_level_) {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user