/* * Copyright (c) 2013 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 "testing/gmock/include/gmock/gmock.h" #include "testing/gtest/include/gtest/gtest.h" #include "webrtc/system_wrappers/interface/clock.h" #include "webrtc/system_wrappers/interface/scoped_ptr.h" #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() {} virtual ~MockCpuOveruseObserver() {} MOCK_METHOD0(OveruseDetected, void()); MOCK_METHOD0(NormalUsage, void()); }; class OveruseFrameDetectorTest : public ::testing::Test { protected: virtual void SetUp() { clock_.reset(new SimulatedClock(1234)); observer_.reset(new MockCpuOveruseObserver()); overuse_detector_.reset(new OveruseFrameDetector(clock_.get())); 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) { 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); } } scoped_ptr clock_; scoped_ptr observer_; scoped_ptr overuse_detector_; }; 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(); } 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(); } 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(); 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); EXPECT_CALL(*(observer_.get()), NormalUsage()).Times(0); overuse_detector_->Process(); } 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