1 // Copyright (c) 2012 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "remoting/host/video_scheduler.h"
8 #include "base/message_loop/message_loop.h"
9 #include "base/run_loop.h"
10 #include "base/single_thread_task_runner.h"
11 #include "remoting/base/auto_thread.h"
12 #include "remoting/base/auto_thread_task_runner.h"
13 #include "remoting/codec/video_encoder.h"
14 #include "remoting/codec/video_encoder_verbatim.h"
15 #include "remoting/host/fake_desktop_capturer.h"
16 #include "remoting/host/fake_mouse_cursor_monitor.h"
17 #include "remoting/host/host_mock_objects.h"
18 #include "remoting/proto/control.pb.h"
19 #include "remoting/proto/video.pb.h"
20 #include "remoting/protocol/protocol_mock_objects.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
24 #include "third_party/webrtc/modules/desktop_capture/mouse_cursor.h"
25 #include "third_party/webrtc/modules/desktop_capture/screen_capturer_mock_objects.h"
27 using ::remoting::protocol::MockClientStub
;
28 using ::remoting::protocol::MockVideoStub
;
31 using ::testing::AtLeast
;
32 using ::testing::AnyNumber
;
33 using ::testing::DeleteArg
;
34 using ::testing::DoAll
;
35 using ::testing::Expectation
;
36 using ::testing::InSequence
;
37 using ::testing::InvokeWithoutArgs
;
38 using ::testing::Return
;
39 using ::testing::ReturnRef
;
40 using ::testing::SaveArg
;
46 ACTION(FinishEncode
) {
47 scoped_ptr
<VideoPacket
> packet(new VideoPacket());
48 return packet
.release();
57 static const int kWidth
= 640;
58 static const int kHeight
= 480;
59 static const int kCursorWidth
= 64;
60 static const int kCursorHeight
= 32;
61 static const int kHotspotX
= 11;
62 static const int kHotspotY
= 12;
64 class MockVideoEncoder
: public VideoEncoder
{
67 virtual ~MockVideoEncoder() {}
69 scoped_ptr
<VideoPacket
> Encode(
70 const webrtc::DesktopFrame
& frame
) {
71 return scoped_ptr
<VideoPacket
>(EncodePtr(frame
));
73 MOCK_METHOD1(EncodePtr
, VideoPacket
*(const webrtc::DesktopFrame
& frame
));
76 DISALLOW_COPY_AND_ASSIGN(MockVideoEncoder
);
79 class ThreadCheckVideoEncoder
: public VideoEncoderVerbatim
{
81 ThreadCheckVideoEncoder(
82 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
83 : task_runner_(task_runner
) {
85 virtual ~ThreadCheckVideoEncoder() {
86 EXPECT_TRUE(task_runner_
->BelongsToCurrentThread());
90 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
92 DISALLOW_COPY_AND_ASSIGN(ThreadCheckVideoEncoder
);
95 class ThreadCheckDesktopCapturer
: public FakeDesktopCapturer
{
97 ThreadCheckDesktopCapturer(
98 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
99 : task_runner_(task_runner
) {
101 virtual ~ThreadCheckDesktopCapturer() {
102 EXPECT_TRUE(task_runner_
->BelongsToCurrentThread());
106 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
108 DISALLOW_COPY_AND_ASSIGN(ThreadCheckDesktopCapturer
);
111 class ThreadCheckMouseCursorMonitor
: public FakeMouseCursorMonitor
{
113 ThreadCheckMouseCursorMonitor(
114 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner
)
115 : task_runner_(task_runner
) {
117 virtual ~ThreadCheckMouseCursorMonitor() {
118 EXPECT_TRUE(task_runner_
->BelongsToCurrentThread());
122 scoped_refptr
<base::SingleThreadTaskRunner
> task_runner_
;
124 DISALLOW_COPY_AND_ASSIGN(ThreadCheckMouseCursorMonitor
);
127 class VideoSchedulerTest
: public testing::Test
{
129 VideoSchedulerTest();
131 virtual void SetUp() OVERRIDE
;
132 virtual void TearDown() OVERRIDE
;
134 void StartVideoScheduler(
135 scoped_ptr
<webrtc::DesktopCapturer
> capturer
,
136 scoped_ptr
<VideoEncoder
> encoder
,
137 scoped_ptr
<webrtc::MouseCursorMonitor
> mouse_monitor
);
138 void StopVideoScheduler();
140 // webrtc::DesktopCapturer mocks.
141 void OnCapturerStart(webrtc::DesktopCapturer::Callback
* callback
);
142 void OnCaptureFrame(const webrtc::DesktopRegion
& region
);
144 // webrtc::MouseCursorMonitor mocks.
145 void OnMouseCursorMonitorInit(
146 webrtc::MouseCursorMonitor::Callback
* callback
,
147 webrtc::MouseCursorMonitor::Mode mode
);
148 void OnCaptureMouse();
149 void SetCursorShape(const protocol::CursorShapeInfo
& cursor_shape
);
152 base::MessageLoop message_loop_
;
153 base::RunLoop run_loop_
;
154 scoped_refptr
<AutoThreadTaskRunner
> capture_task_runner_
;
155 scoped_refptr
<AutoThreadTaskRunner
> encode_task_runner_
;
156 scoped_refptr
<AutoThreadTaskRunner
> main_task_runner_
;
157 scoped_refptr
<VideoScheduler
> scheduler_
;
159 MockClientStub client_stub_
;
160 MockVideoStub video_stub_
;
162 // Points to the callback passed to webrtc::DesktopCapturer::Start().
163 webrtc::DesktopCapturer::Callback
* capturer_callback_
;
165 // Points to the callback passed to webrtc::MouseCursor::Init().
166 webrtc::MouseCursorMonitor::Callback
* mouse_monitor_callback_
;
169 DISALLOW_COPY_AND_ASSIGN(VideoSchedulerTest
);
172 VideoSchedulerTest::VideoSchedulerTest()
173 : capturer_callback_(NULL
),
174 mouse_monitor_callback_(NULL
) {
177 void VideoSchedulerTest::SetUp() {
178 main_task_runner_
= new AutoThreadTaskRunner(
179 message_loop_
.message_loop_proxy(), run_loop_
.QuitClosure());
180 capture_task_runner_
= main_task_runner_
;
181 encode_task_runner_
= main_task_runner_
;
184 void VideoSchedulerTest::TearDown() {
185 // Release the task runners, so that the test can quit.
186 capture_task_runner_
= NULL
;
187 encode_task_runner_
= NULL
;
188 main_task_runner_
= NULL
;
190 // Run the MessageLoop until everything has torn down.
194 void VideoSchedulerTest::StartVideoScheduler(
195 scoped_ptr
<webrtc::DesktopCapturer
> capturer
,
196 scoped_ptr
<VideoEncoder
> encoder
,
197 scoped_ptr
<webrtc::MouseCursorMonitor
> mouse_monitor
) {
198 scheduler_
= new VideoScheduler(
199 capture_task_runner_
,
203 mouse_monitor
.Pass(),
210 void VideoSchedulerTest::StopVideoScheduler() {
215 void VideoSchedulerTest::OnCapturerStart(
216 webrtc::DesktopCapturer::Callback
* callback
) {
217 EXPECT_FALSE(capturer_callback_
);
218 EXPECT_TRUE(callback
);
220 capturer_callback_
= callback
;
223 void VideoSchedulerTest::OnCaptureFrame(const webrtc::DesktopRegion
& region
) {
224 scoped_ptr
<webrtc::DesktopFrame
> frame(
225 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth
, kHeight
)));
226 frame
->mutable_updated_region()->SetRect(
227 webrtc::DesktopRect::MakeXYWH(0, 0, 10, 10));
228 capturer_callback_
->OnCaptureCompleted(frame
.release());
231 void VideoSchedulerTest::OnCaptureMouse() {
232 EXPECT_TRUE(mouse_monitor_callback_
);
234 scoped_ptr
<webrtc::MouseCursor
> mouse_cursor(
235 new webrtc::MouseCursor(
236 new webrtc::BasicDesktopFrame(
237 webrtc::DesktopSize(kCursorWidth
, kCursorHeight
)),
238 webrtc::DesktopVector(kHotspotX
, kHotspotY
)));
240 mouse_monitor_callback_
->OnMouseCursor(mouse_cursor
.release());
243 void VideoSchedulerTest::OnMouseCursorMonitorInit(
244 webrtc::MouseCursorMonitor::Callback
* callback
,
245 webrtc::MouseCursorMonitor::Mode mode
) {
246 EXPECT_FALSE(mouse_monitor_callback_
);
247 EXPECT_TRUE(callback
);
249 mouse_monitor_callback_
= callback
;
252 void VideoSchedulerTest::SetCursorShape(
253 const protocol::CursorShapeInfo
& cursor_shape
) {
254 EXPECT_TRUE(cursor_shape
.has_width());
255 EXPECT_EQ(kCursorWidth
, cursor_shape
.width());
256 EXPECT_TRUE(cursor_shape
.has_height());
257 EXPECT_EQ(kCursorHeight
, cursor_shape
.height());
258 EXPECT_TRUE(cursor_shape
.has_hotspot_x());
259 EXPECT_EQ(kHotspotX
, cursor_shape
.hotspot_x());
260 EXPECT_TRUE(cursor_shape
.has_hotspot_y());
261 EXPECT_EQ(kHotspotY
, cursor_shape
.hotspot_y());
262 EXPECT_TRUE(cursor_shape
.has_data());
263 EXPECT_EQ(kCursorWidth
* kCursorHeight
* webrtc::DesktopFrame::kBytesPerPixel
,
264 static_cast<int>(cursor_shape
.data().size()));
267 // This test mocks capturer, encoder and network layer to simulate one capture
268 // cycle. When the first encoded packet is submitted to the network
269 // VideoScheduler is instructed to come to a complete stop. We expect the stop
270 // sequence to be executed successfully.
271 TEST_F(VideoSchedulerTest
, StartAndStop
) {
272 scoped_ptr
<webrtc::MockScreenCapturer
> capturer(
273 new webrtc::MockScreenCapturer());
274 scoped_ptr
<MockMouseCursorMonitor
> cursor_monitor(
275 new MockMouseCursorMonitor());
280 EXPECT_CALL(*cursor_monitor
, Init(_
, _
))
282 Invoke(this, &VideoSchedulerTest::OnMouseCursorMonitorInit
));
284 EXPECT_CALL(*cursor_monitor
, Capture())
285 .WillRepeatedly(Invoke(this, &VideoSchedulerTest::OnCaptureMouse
));
288 Expectation capturer_start
=
289 EXPECT_CALL(*capturer
, Start(_
))
290 .WillOnce(Invoke(this, &VideoSchedulerTest::OnCapturerStart
));
292 // First the capturer is called.
293 Expectation capturer_capture
= EXPECT_CALL(*capturer
, Capture(_
))
294 .After(capturer_start
)
295 .WillRepeatedly(Invoke(this, &VideoSchedulerTest::OnCaptureFrame
));
297 scoped_ptr
<MockVideoEncoder
> encoder(new MockVideoEncoder());
299 // Expect the encoder be called.
300 EXPECT_CALL(*encoder
, EncodePtr(_
))
301 .WillRepeatedly(FinishEncode());
303 // By default delete the arguments when ProcessVideoPacket is received.
304 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
305 .WillRepeatedly(FinishSend());
307 // When the first ProcessVideoPacket is received we stop the VideoScheduler.
308 EXPECT_CALL(video_stub_
, ProcessVideoPacketPtr(_
, _
))
311 InvokeWithoutArgs(this, &VideoSchedulerTest::StopVideoScheduler
)))
312 .RetiresOnSaturation();
314 EXPECT_CALL(client_stub_
, SetCursorShape(_
))
315 .WillOnce(Invoke(this, &VideoSchedulerTest::SetCursorShape
));
317 // Start video frame capture.
318 scoped_ptr
<webrtc::MouseCursorMonitor
> mouse_cursor_monitor(
319 new FakeMouseCursorMonitor());
320 StartVideoScheduler(capturer
.PassAs
<webrtc::DesktopCapturer
>(),
321 encoder
.PassAs
<VideoEncoder
>(),
322 cursor_monitor
.PassAs
<webrtc::MouseCursorMonitor
>());
324 // Run until there are no more pending tasks from the VideoScheduler.
325 // Otherwise, a lingering frame capture might attempt to trigger a capturer
326 // expectation action and crash.
327 base::RunLoop().RunUntilIdle();
330 // Verify that the capturer, encoder and mouse monitor are torn down on the
332 TEST_F(VideoSchedulerTest
, DeleteOnThreads
) {
333 capture_task_runner_
= AutoThread::Create("capture", main_task_runner_
);
334 encode_task_runner_
= AutoThread::Create("encode", main_task_runner_
);
336 scoped_ptr
<webrtc::DesktopCapturer
> capturer(
337 new ThreadCheckDesktopCapturer(capture_task_runner_
));
338 scoped_ptr
<VideoEncoder
> encoder(
339 new ThreadCheckVideoEncoder(encode_task_runner_
));
340 scoped_ptr
<webrtc::MouseCursorMonitor
> mouse_cursor_monitor(
341 new ThreadCheckMouseCursorMonitor(capture_task_runner_
));
343 // Start and stop the scheduler, so it will tear down the screen capturer,
344 // video encoder and mouse monitor.
345 StartVideoScheduler(capturer
.Pass(), encoder
.Pass(),
346 mouse_cursor_monitor
.Pass());
347 StopVideoScheduler();
350 } // namespace remoting