diff --git a/webrtc/video_engine/include/vie_base.h b/webrtc/video_engine/include/vie_base.h index 64c53ec90..a08cbacb4 100644 --- a/webrtc/video_engine/include/vie_base.h +++ b/webrtc/video_engine/include/vie_base.h @@ -66,6 +66,9 @@ struct 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), @@ -75,6 +78,10 @@ struct CpuOveruseOptions { bool enable_capture_jitter_method; float low_capture_jitter_threshold_ms; // Threshold for triggering underuse. float high_capture_jitter_threshold_ms; // Threshold for triggering overuse. + // Method based on encode time of frames. + 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. // General settings. int frame_timeout_interval_ms; // The maximum allowed interval between two // frames before resetting estimations. @@ -90,6 +97,11 @@ struct CpuOveruseOptions { low_capture_jitter_threshold_ms == o.low_capture_jitter_threshold_ms && high_capture_jitter_threshold_ms == o.high_capture_jitter_threshold_ms && + enable_encode_usage_method == o.enable_encode_usage_method && + low_encode_usage_threshold_percent == + o.low_encode_usage_threshold_percent && + high_encode_usage_threshold_percent == + o.high_encode_usage_threshold_percent && frame_timeout_interval_ms == o.frame_timeout_interval_ms && min_frame_samples == o.min_frame_samples && min_process_count == o.min_process_count && diff --git a/webrtc/video_engine/overuse_frame_detector.cc b/webrtc/video_engine/overuse_frame_detector.cc index bc283e27c..bfecbb5bc 100644 --- a/webrtc/video_engine/overuse_frame_detector.cc +++ b/webrtc/video_engine/overuse_frame_detector.cc @@ -140,13 +140,27 @@ class OveruseFrameDetector::EncodeUsage { EncodeUsage() : kWeightFactorFrameDiff(0.998f), kWeightFactorEncodeTime(0.995f), + kInitialSampleDiffMs(50.0f), + kMaxSampleDiffMs(66.0f), + count_(0), filtered_encode_time_ms_(new VCMExpFilter(kWeightFactorEncodeTime)), filtered_frame_diff_ms_(new VCMExpFilter(kWeightFactorFrameDiff)) { - filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs); - filtered_frame_diff_ms_->Apply(1.0f, kSampleDiffMs); + Reset(); } ~EncodeUsage() {} + void SetOptions(const CpuOveruseOptions& options) { + options_ = options; + } + + void Reset() { + count_ = 0; + filtered_frame_diff_ms_->Reset(kWeightFactorFrameDiff); + filtered_frame_diff_ms_->Apply(1.0f, kInitialSampleDiffMs); + filtered_encode_time_ms_->Reset(kWeightFactorEncodeTime); + filtered_encode_time_ms_->Apply(1.0f, InitialEncodeTimeMs()); + } + void AddSample(float sample_ms) { float exp = sample_ms / kSampleDiffMs; exp = std::min(exp, kMaxExp); @@ -154,21 +168,40 @@ class OveruseFrameDetector::EncodeUsage { } void AddEncodeSample(float encode_time_ms, int64_t diff_last_sample_ms) { + ++count_; float exp = diff_last_sample_ms / kSampleDiffMs; exp = std::min(exp, kMaxExp); filtered_encode_time_ms_->Apply(exp, encode_time_ms); } int UsageInPercent() const { + if (count_ < static_cast(options_.min_frame_samples)) { + return static_cast(InitialUsageInPercent() + 0.5f); + } float frame_diff_ms = std::max(filtered_frame_diff_ms_->Value(), 1.0f); + frame_diff_ms = std::min(frame_diff_ms, kMaxSampleDiffMs); float encode_usage_percent = 100.0f * filtered_encode_time_ms_->Value() / frame_diff_ms; return static_cast(encode_usage_percent + 0.5); } + float InitialUsageInPercent() const { + // Start in between the underuse and overuse threshold. + return (options_.low_encode_usage_threshold_percent + + options_.high_encode_usage_threshold_percent) / 2.0f; + } + + float InitialEncodeTimeMs() const { + return InitialUsageInPercent() * kInitialSampleDiffMs / 100; + } + private: const float kWeightFactorFrameDiff; const float kWeightFactorEncodeTime; + const float kInitialSampleDiffMs; + const float kMaxSampleDiffMs; + uint64_t count_; + CpuOveruseOptions options_; scoped_ptr filtered_encode_time_ms_; scoped_ptr filtered_frame_diff_ms_; }; @@ -265,6 +298,7 @@ void OveruseFrameDetector::SetOptions(const CpuOveruseOptions& options) { } options_ = options; capture_deltas_.SetOptions(options); + encode_usage_->SetOptions(options); ResetAll(num_pixels_); } @@ -315,6 +349,7 @@ bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { void OveruseFrameDetector::ResetAll(int num_pixels) { num_pixels_ = num_pixels; capture_deltas_.Reset(); + encode_usage_->Reset(); capture_queue_delay_->ClearFrames(); last_capture_time_ = 0; num_process_times_ = 0; @@ -420,8 +455,15 @@ int32_t OveruseFrameDetector::Process() { } bool OveruseFrameDetector::IsOverusing() { - bool overusing = options_.enable_capture_jitter_method && - (capture_deltas_.StdDev() >= options_.high_capture_jitter_threshold_ms); + bool overusing = false; + if (options_.enable_capture_jitter_method) { + overusing = capture_deltas_.StdDev() >= + options_.high_capture_jitter_threshold_ms; + } else if (options_.enable_encode_usage_method) { + overusing = encode_usage_->UsageInPercent() >= + options_.high_encode_usage_threshold_percent; + } + if (overusing) { ++checks_above_threshold_; } else { @@ -435,8 +477,14 @@ bool OveruseFrameDetector::IsUnderusing(int64_t time_now) { if (time_now < last_rampup_time_ + delay) return false; - bool underusing = options_.enable_capture_jitter_method && - (capture_deltas_.StdDev() < options_.low_capture_jitter_threshold_ms); + bool underusing = false; + if (options_.enable_capture_jitter_method) { + underusing = capture_deltas_.StdDev() < + options_.low_capture_jitter_threshold_ms; + } else if (options_.enable_encode_usage_method) { + underusing = encode_usage_->UsageInPercent() < + options_.low_encode_usage_threshold_percent; + } return underusing; } } // namespace webrtc diff --git a/webrtc/video_engine/overuse_frame_detector_unittest.cc b/webrtc/video_engine/overuse_frame_detector_unittest.cc index 5d9cedb5f..a760fbfbc 100644 --- a/webrtc/video_engine/overuse_frame_detector_unittest.cc +++ b/webrtc/video_engine/overuse_frame_detector_unittest.cc @@ -66,6 +66,11 @@ class OveruseFrameDetectorTest : public ::testing::Test { options_.high_capture_jitter_threshold_ms) / 2.0f) + 0.5; } + int InitialEncodeUsage() { + return ((options_.low_encode_usage_threshold_percent + + options_.high_encode_usage_threshold_percent) / 2.0f) + 0.5; + } + void InsertFramesWithInterval( size_t num_frames, int interval_ms, int width, int height) { while (num_frames-- > 0) { @@ -74,6 +79,16 @@ class OveruseFrameDetectorTest : public ::testing::Test { } } + void InsertAndEncodeFramesWithInterval( + int num_frames, int interval_ms, int width, int height, int encode_ms) { + while (num_frames-- > 0) { + overuse_detector_->FrameCaptured(width, height); + clock_->AdvanceTimeMilliseconds(encode_ms); + overuse_detector_->FrameEncoded(encode_ms); + clock_->AdvanceTimeMilliseconds(interval_ms - encode_ms); + } + } + void TriggerOveruse(int num_times) { for (int i = 0; i < num_times; ++i) { InsertFramesWithInterval(200, kFrameInterval33ms, kWidth, kHeight); @@ -87,6 +102,22 @@ class OveruseFrameDetectorTest : public ::testing::Test { overuse_detector_->Process(); } + void TriggerOveruseWithEncodeUsage(int num_times) { + const int kEncodeTimeMs = 32; + for (int i = 0; i < num_times; ++i) { + InsertAndEncodeFramesWithInterval( + 1000, kFrameInterval33ms, kWidth, kHeight, kEncodeTimeMs); + overuse_detector_->Process(); + } + } + + void TriggerNormalUsageWithEncodeUsage() { + const int kEncodeTimeMs = 5; + InsertAndEncodeFramesWithInterval( + 1000, kFrameInterval33ms, kWidth, kHeight, kEncodeTimeMs); + overuse_detector_->Process(); + } + CpuOveruseOptions options_; scoped_ptr clock_; scoped_ptr observer_; @@ -266,13 +297,42 @@ TEST_F(OveruseFrameDetectorTest, EncodedFrame) { EXPECT_EQ(2, overuse_detector_->AvgEncodeTimeMs()); } +TEST_F(OveruseFrameDetectorTest, InitialEncodeUsage) { + EXPECT_EQ(InitialEncodeUsage(), overuse_detector_->EncodeUsagePercent()); +} + TEST_F(OveruseFrameDetectorTest, EncodedUsage) { - for (int i = 0; i < 30; i++) { - overuse_detector_->FrameCaptured(kWidth, kHeight); - clock_->AdvanceTimeMilliseconds(5); - overuse_detector_->FrameEncoded(5); - clock_->AdvanceTimeMilliseconds(33-5); - } + const int kEncodeTimeMs = 5; + InsertAndEncodeFramesWithInterval( + 1000, kFrameInterval33ms, kWidth, kHeight, kEncodeTimeMs); EXPECT_EQ(15, overuse_detector_->EncodeUsagePercent()); } + +TEST_F(OveruseFrameDetectorTest, EncodeUsageResetAfterChangingThreshold) { + EXPECT_EQ(InitialEncodeUsage(), overuse_detector_->EncodeUsagePercent()); + options_.high_encode_usage_threshold_percent = 100; + overuse_detector_->SetOptions(options_); + EXPECT_EQ(InitialEncodeUsage(), overuse_detector_->EncodeUsagePercent()); + options_.low_encode_usage_threshold_percent = 20; + overuse_detector_->SetOptions(options_); + EXPECT_EQ(InitialEncodeUsage(), overuse_detector_->EncodeUsagePercent()); +} + +TEST_F(OveruseFrameDetectorTest, TriggerOveruseWithEncodeUsage) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = true; + overuse_detector_->SetOptions(options_); + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + TriggerOveruseWithEncodeUsage(options_.high_threshold_consecutive_count); +} + +TEST_F(OveruseFrameDetectorTest, OveruseAndRecoverWithEncodeUsage) { + options_.enable_capture_jitter_method = false; + options_.enable_encode_usage_method = true; + overuse_detector_->SetOptions(options_); + EXPECT_CALL(*(observer_.get()), OveruseDetected()).Times(1); + TriggerOveruseWithEncodeUsage(options_.high_threshold_consecutive_count); + EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(testing::AtLeast(1)); + TriggerNormalUsageWithEncodeUsage(); +} } // namespace webrtc