// Copyright 2015 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. #include "remoting/protocol/capture_scheduler.h" #include #include #include "base/macros.h" #include "base/message_loop/message_loop.h" #include "base/test/simple_test_tick_clock.h" #include "base/timer/mock_timer.h" #include "remoting/proto/video.pb.h" #include "testing/gtest/include/gtest/gtest.h" namespace remoting { namespace protocol { static const int kTestInputs[] = { 100, 50, 30, 20, 10, 30, 60, 80 }; static const int kMinumumFrameIntervalMs = 50; class CaptureSchedulerTest : public testing::Test { public: CaptureSchedulerTest() : capture_called_(false) {} void InitScheduler() { scheduler_.reset(new CaptureScheduler( base::Bind(&CaptureSchedulerTest::DoCapture, base::Unretained(this)))); scheduler_->set_minimum_interval( base::TimeDelta::FromMilliseconds(kMinumumFrameIntervalMs)); tick_clock_ = new base::SimpleTestTickClock(); scheduler_->SetTickClockForTest(make_scoped_ptr(tick_clock_)); capture_timer_ = new base::MockTimer(false, false); scheduler_->SetTimerForTest(make_scoped_ptr(capture_timer_)); scheduler_->Start(); } void DoCapture() { capture_called_ = true; } void CheckCaptureCalled() { EXPECT_TRUE(capture_called_); capture_called_ = false; } void SimulateSingleFrameCapture( base::TimeDelta capture_delay, base::TimeDelta encode_delay, base::TimeDelta expected_delay_between_frames) { capture_timer_->Fire(); CheckCaptureCalled(); tick_clock_->Advance(capture_delay); scheduler_->OnCaptureCompleted(); VideoPacket packet; packet.set_encode_time_ms(encode_delay.InMilliseconds()); scheduler_->OnFrameEncoded(&packet); scheduler_->OnFrameSent(); scoped_ptr ack(new VideoAck()); ack->set_frame_id(packet.frame_id()); scheduler_->ProcessVideoAck(std::move(ack)); EXPECT_TRUE(capture_timer_->IsRunning()); EXPECT_EQ(std::max(base::TimeDelta(), expected_delay_between_frames - capture_delay), capture_timer_->GetCurrentDelay()); } protected: base::MessageLoop message_loop_; scoped_ptr scheduler_; // Owned by |scheduler_|. base::SimpleTestTickClock* tick_clock_; base::MockTimer* capture_timer_; bool capture_called_; }; TEST_F(CaptureSchedulerTest, SingleSampleSameTimes) { const int kTestResults[][arraysize(kTestInputs)] = { { 400, 200, 120, 80, 50, 120, 240, 320 }, // One core. { 200, 100, 60, 50, 50, 60, 120, 160 }, // Two cores. { 100, 50, 50, 50, 50, 50, 60, 80 }, // Four cores. { 50, 50, 50, 50, 50, 50, 50, 50 } // Eight cores. }; for (size_t i = 0; i < arraysize(kTestResults); ++i) { for (size_t j = 0; j < arraysize(kTestInputs); ++j) { InitScheduler(); scheduler_->SetNumOfProcessorsForTest(1 << i); SimulateSingleFrameCapture( base::TimeDelta::FromMilliseconds(kTestInputs[j]), base::TimeDelta::FromMilliseconds(kTestInputs[j]), base::TimeDelta::FromMilliseconds(kTestResults[i][j])); } } } TEST_F(CaptureSchedulerTest, SingleSampleDifferentTimes) { const int kTestResults[][arraysize(kTestInputs)] = { { 360, 220, 120, 60, 60, 120, 220, 360 }, // One core. { 180, 110, 60, 50, 50, 60, 110, 180 }, // Two cores. { 90, 55, 50, 50, 50, 50, 55, 90 }, // Four cores. { 50, 50, 50, 50, 50, 50, 50, 50 } // Eight cores. }; for (size_t i = 0; i < arraysize(kTestResults); ++i) { for (size_t j = 0; j < arraysize(kTestInputs); ++j) { InitScheduler(); scheduler_->SetNumOfProcessorsForTest(1 << i); SimulateSingleFrameCapture( base::TimeDelta::FromMilliseconds(kTestInputs[j]), base::TimeDelta::FromMilliseconds( kTestInputs[arraysize(kTestInputs) - 1 - j]), base::TimeDelta::FromMilliseconds(kTestResults[i][j])); } } } TEST_F(CaptureSchedulerTest, RollingAverageDifferentTimes) { const int kTestResults[][arraysize(kTestInputs)] = { { 360, 290, 233, 133, 80, 80, 133, 233 }, // One core. { 180, 145, 116, 66, 50, 50, 66, 116 }, // Two cores. { 90, 72, 58, 50, 50, 50, 50, 58 }, // Four cores. { 50, 50, 50, 50, 50, 50, 50, 50 } // Eight cores. }; for (size_t i = 0; i < arraysize(kTestResults); ++i) { InitScheduler(); scheduler_->SetNumOfProcessorsForTest(1 << i); for (size_t j = 0; j < arraysize(kTestInputs); ++j) { SimulateSingleFrameCapture( base::TimeDelta::FromMilliseconds(kTestInputs[j]), base::TimeDelta::FromMilliseconds( kTestInputs[arraysize(kTestInputs) - 1 - j]), base::TimeDelta::FromMilliseconds(kTestResults[i][j])); } } } // Verify that we never have more than 2 encoding frames. TEST_F(CaptureSchedulerTest, MaximumEncodingFrames) { InitScheduler(); // Process the first frame to let the scheduler know that receiver supports // ACKs. SimulateSingleFrameCapture( base::TimeDelta(), base::TimeDelta(), base::TimeDelta::FromMilliseconds(kMinumumFrameIntervalMs)); capture_timer_->Fire(); CheckCaptureCalled(); scheduler_->OnCaptureCompleted(); capture_timer_->Fire(); CheckCaptureCalled(); scheduler_->OnCaptureCompleted(); EXPECT_FALSE(capture_timer_->IsRunning()); VideoPacket packet; scheduler_->OnFrameEncoded(&packet); EXPECT_TRUE(capture_timer_->IsRunning()); } // Verify that the scheduler doesn't exceed maximum number of pending frames. TEST_F(CaptureSchedulerTest, MaximumPendingFrames) { InitScheduler(); // Process the first frame to let the scheduler know that receiver supports // ACKs. SimulateSingleFrameCapture( base::TimeDelta(), base::TimeDelta(), base::TimeDelta::FromMilliseconds(kMinumumFrameIntervalMs)); // Queue some frames until the sender is blocked. while (capture_timer_->IsRunning()) { capture_timer_->Fire(); CheckCaptureCalled(); scheduler_->OnCaptureCompleted(); VideoPacket packet; scheduler_->OnFrameEncoded(&packet); scheduler_->OnFrameSent(); } // Next frame should be scheduled, once one of the queued frames is // acknowledged. EXPECT_FALSE(capture_timer_->IsRunning()); scheduler_->ProcessVideoAck(make_scoped_ptr(new VideoAck())); EXPECT_TRUE(capture_timer_->IsRunning()); } } // namespace protocol } // namespace remoting