blob: c5398feec650cb568eedfb917d7d07c05d8c7a23 [file] [log] [blame]
/*
* 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<SimulatedClock> clock_;
scoped_ptr<MockCpuOveruseObserver> observer_;
scoped_ptr<OveruseFrameDetector> 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