OLD | NEW |
| (Empty) |
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. | |
4 | |
5 #include "remoting/host/video_scheduler.h" | |
6 | |
7 #include "base/bind.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_object
s.h" | |
26 | |
27 using ::remoting::protocol::MockClientStub; | |
28 using ::remoting::protocol::MockVideoStub; | |
29 | |
30 using ::testing::_; | |
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; | |
41 | |
42 namespace remoting { | |
43 | |
44 namespace { | |
45 | |
46 ACTION(FinishEncode) { | |
47 scoped_ptr<VideoPacket> packet(new VideoPacket()); | |
48 return packet.release(); | |
49 } | |
50 | |
51 ACTION(FinishSend) { | |
52 arg1.Run(); | |
53 } | |
54 | |
55 } // namespace | |
56 | |
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; | |
63 | |
64 class MockVideoEncoder : public VideoEncoder { | |
65 public: | |
66 MockVideoEncoder() {} | |
67 virtual ~MockVideoEncoder() {} | |
68 | |
69 scoped_ptr<VideoPacket> Encode(const webrtc::DesktopFrame& frame) { | |
70 return make_scoped_ptr(EncodePtr(frame)); | |
71 } | |
72 MOCK_METHOD1(EncodePtr, VideoPacket*(const webrtc::DesktopFrame& frame)); | |
73 | |
74 private: | |
75 DISALLOW_COPY_AND_ASSIGN(MockVideoEncoder); | |
76 }; | |
77 | |
78 class ThreadCheckVideoEncoder : public VideoEncoderVerbatim { | |
79 public: | |
80 ThreadCheckVideoEncoder( | |
81 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | |
82 : task_runner_(task_runner) { | |
83 } | |
84 ~ThreadCheckVideoEncoder() override { | |
85 EXPECT_TRUE(task_runner_->BelongsToCurrentThread()); | |
86 } | |
87 | |
88 private: | |
89 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
90 | |
91 DISALLOW_COPY_AND_ASSIGN(ThreadCheckVideoEncoder); | |
92 }; | |
93 | |
94 class ThreadCheckDesktopCapturer : public FakeDesktopCapturer { | |
95 public: | |
96 ThreadCheckDesktopCapturer( | |
97 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | |
98 : task_runner_(task_runner) { | |
99 } | |
100 ~ThreadCheckDesktopCapturer() override { | |
101 EXPECT_TRUE(task_runner_->BelongsToCurrentThread()); | |
102 } | |
103 | |
104 private: | |
105 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
106 | |
107 DISALLOW_COPY_AND_ASSIGN(ThreadCheckDesktopCapturer); | |
108 }; | |
109 | |
110 class ThreadCheckMouseCursorMonitor : public FakeMouseCursorMonitor { | |
111 public: | |
112 ThreadCheckMouseCursorMonitor( | |
113 scoped_refptr<base::SingleThreadTaskRunner> task_runner) | |
114 : task_runner_(task_runner) { | |
115 } | |
116 ~ThreadCheckMouseCursorMonitor() override { | |
117 EXPECT_TRUE(task_runner_->BelongsToCurrentThread()); | |
118 } | |
119 | |
120 private: | |
121 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; | |
122 | |
123 DISALLOW_COPY_AND_ASSIGN(ThreadCheckMouseCursorMonitor); | |
124 }; | |
125 | |
126 class VideoSchedulerTest : public testing::Test { | |
127 public: | |
128 VideoSchedulerTest(); | |
129 | |
130 void SetUp() override; | |
131 void TearDown() override; | |
132 | |
133 void StartVideoScheduler( | |
134 scoped_ptr<webrtc::DesktopCapturer> capturer, | |
135 scoped_ptr<VideoEncoder> encoder, | |
136 scoped_ptr<webrtc::MouseCursorMonitor> mouse_monitor); | |
137 void StopVideoScheduler(); | |
138 | |
139 // webrtc::DesktopCapturer mocks. | |
140 void OnCapturerStart(webrtc::DesktopCapturer::Callback* callback); | |
141 void OnCaptureFrame(const webrtc::DesktopRegion& region); | |
142 | |
143 // webrtc::MouseCursorMonitor mocks. | |
144 void OnMouseCursorMonitorInit( | |
145 webrtc::MouseCursorMonitor::Callback* callback, | |
146 webrtc::MouseCursorMonitor::Mode mode); | |
147 void OnCaptureMouse(); | |
148 void SetCursorShape(const protocol::CursorShapeInfo& cursor_shape); | |
149 | |
150 protected: | |
151 base::MessageLoop message_loop_; | |
152 base::RunLoop run_loop_; | |
153 scoped_refptr<AutoThreadTaskRunner> capture_task_runner_; | |
154 scoped_refptr<AutoThreadTaskRunner> encode_task_runner_; | |
155 scoped_refptr<AutoThreadTaskRunner> main_task_runner_; | |
156 scoped_refptr<VideoScheduler> scheduler_; | |
157 | |
158 MockClientStub client_stub_; | |
159 MockVideoStub video_stub_; | |
160 | |
161 // Points to the callback passed to webrtc::DesktopCapturer::Start(). | |
162 webrtc::DesktopCapturer::Callback* capturer_callback_; | |
163 | |
164 // Points to the callback passed to webrtc::MouseCursor::Init(). | |
165 webrtc::MouseCursorMonitor::Callback* mouse_monitor_callback_; | |
166 | |
167 private: | |
168 DISALLOW_COPY_AND_ASSIGN(VideoSchedulerTest); | |
169 }; | |
170 | |
171 VideoSchedulerTest::VideoSchedulerTest() | |
172 : capturer_callback_(nullptr), | |
173 mouse_monitor_callback_(nullptr) { | |
174 } | |
175 | |
176 void VideoSchedulerTest::SetUp() { | |
177 main_task_runner_ = new AutoThreadTaskRunner( | |
178 message_loop_.message_loop_proxy(), run_loop_.QuitClosure()); | |
179 capture_task_runner_ = main_task_runner_; | |
180 encode_task_runner_ = main_task_runner_; | |
181 } | |
182 | |
183 void VideoSchedulerTest::TearDown() { | |
184 // Release the task runners, so that the test can quit. | |
185 capture_task_runner_ = nullptr; | |
186 encode_task_runner_ = nullptr; | |
187 main_task_runner_ = nullptr; | |
188 | |
189 // Run the MessageLoop until everything has torn down. | |
190 run_loop_.Run(); | |
191 } | |
192 | |
193 void VideoSchedulerTest::StartVideoScheduler( | |
194 scoped_ptr<webrtc::DesktopCapturer> capturer, | |
195 scoped_ptr<VideoEncoder> encoder, | |
196 scoped_ptr<webrtc::MouseCursorMonitor> mouse_monitor) { | |
197 scheduler_ = new VideoScheduler( | |
198 capture_task_runner_, | |
199 encode_task_runner_, | |
200 main_task_runner_, | |
201 capturer.Pass(), | |
202 mouse_monitor.Pass(), | |
203 encoder.Pass(), | |
204 &client_stub_, | |
205 &video_stub_); | |
206 scheduler_->Start(); | |
207 } | |
208 | |
209 void VideoSchedulerTest::StopVideoScheduler() { | |
210 scheduler_->Stop(); | |
211 scheduler_ = nullptr; | |
212 } | |
213 | |
214 void VideoSchedulerTest::OnCapturerStart( | |
215 webrtc::DesktopCapturer::Callback* callback) { | |
216 EXPECT_FALSE(capturer_callback_); | |
217 EXPECT_TRUE(callback); | |
218 | |
219 capturer_callback_ = callback; | |
220 } | |
221 | |
222 void VideoSchedulerTest::OnCaptureFrame(const webrtc::DesktopRegion& region) { | |
223 scoped_ptr<webrtc::DesktopFrame> frame( | |
224 new webrtc::BasicDesktopFrame(webrtc::DesktopSize(kWidth, kHeight))); | |
225 frame->mutable_updated_region()->SetRect( | |
226 webrtc::DesktopRect::MakeXYWH(0, 0, 10, 10)); | |
227 capturer_callback_->OnCaptureCompleted(frame.release()); | |
228 } | |
229 | |
230 void VideoSchedulerTest::OnCaptureMouse() { | |
231 EXPECT_TRUE(mouse_monitor_callback_); | |
232 | |
233 scoped_ptr<webrtc::MouseCursor> mouse_cursor( | |
234 new webrtc::MouseCursor( | |
235 new webrtc::BasicDesktopFrame( | |
236 webrtc::DesktopSize(kCursorWidth, kCursorHeight)), | |
237 webrtc::DesktopVector(kHotspotX, kHotspotY))); | |
238 | |
239 mouse_monitor_callback_->OnMouseCursor(mouse_cursor.release()); | |
240 } | |
241 | |
242 void VideoSchedulerTest::OnMouseCursorMonitorInit( | |
243 webrtc::MouseCursorMonitor::Callback* callback, | |
244 webrtc::MouseCursorMonitor::Mode mode) { | |
245 EXPECT_FALSE(mouse_monitor_callback_); | |
246 EXPECT_TRUE(callback); | |
247 | |
248 mouse_monitor_callback_ = callback; | |
249 } | |
250 | |
251 void VideoSchedulerTest::SetCursorShape( | |
252 const protocol::CursorShapeInfo& cursor_shape) { | |
253 EXPECT_TRUE(cursor_shape.has_width()); | |
254 EXPECT_EQ(kCursorWidth, cursor_shape.width()); | |
255 EXPECT_TRUE(cursor_shape.has_height()); | |
256 EXPECT_EQ(kCursorHeight, cursor_shape.height()); | |
257 EXPECT_TRUE(cursor_shape.has_hotspot_x()); | |
258 EXPECT_EQ(kHotspotX, cursor_shape.hotspot_x()); | |
259 EXPECT_TRUE(cursor_shape.has_hotspot_y()); | |
260 EXPECT_EQ(kHotspotY, cursor_shape.hotspot_y()); | |
261 EXPECT_TRUE(cursor_shape.has_data()); | |
262 EXPECT_EQ(kCursorWidth * kCursorHeight * webrtc::DesktopFrame::kBytesPerPixel, | |
263 static_cast<int>(cursor_shape.data().size())); | |
264 } | |
265 | |
266 // This test mocks capturer, encoder and network layer to simulate one capture | |
267 // cycle. When the first encoded packet is submitted to the network | |
268 // VideoScheduler is instructed to come to a complete stop. We expect the stop | |
269 // sequence to be executed successfully. | |
270 TEST_F(VideoSchedulerTest, StartAndStop) { | |
271 scoped_ptr<webrtc::MockScreenCapturer> capturer( | |
272 new webrtc::MockScreenCapturer()); | |
273 scoped_ptr<MockMouseCursorMonitor> cursor_monitor( | |
274 new MockMouseCursorMonitor()); | |
275 | |
276 { | |
277 InSequence s; | |
278 | |
279 EXPECT_CALL(*cursor_monitor, Init(_, _)) | |
280 .WillOnce( | |
281 Invoke(this, &VideoSchedulerTest::OnMouseCursorMonitorInit)); | |
282 | |
283 EXPECT_CALL(*cursor_monitor, Capture()) | |
284 .WillRepeatedly(Invoke(this, &VideoSchedulerTest::OnCaptureMouse)); | |
285 } | |
286 | |
287 Expectation capturer_start = | |
288 EXPECT_CALL(*capturer, Start(_)) | |
289 .WillOnce(Invoke(this, &VideoSchedulerTest::OnCapturerStart)); | |
290 | |
291 // First the capturer is called. | |
292 Expectation capturer_capture = EXPECT_CALL(*capturer, Capture(_)) | |
293 .After(capturer_start) | |
294 .WillRepeatedly(Invoke(this, &VideoSchedulerTest::OnCaptureFrame)); | |
295 | |
296 scoped_ptr<MockVideoEncoder> encoder(new MockVideoEncoder()); | |
297 | |
298 // Expect the encoder be called. | |
299 EXPECT_CALL(*encoder, EncodePtr(_)) | |
300 .WillRepeatedly(FinishEncode()); | |
301 | |
302 // By default delete the arguments when ProcessVideoPacket is received. | |
303 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) | |
304 .WillRepeatedly(FinishSend()); | |
305 | |
306 // When the first ProcessVideoPacket is received we stop the VideoScheduler. | |
307 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) | |
308 .WillOnce(DoAll( | |
309 FinishSend(), | |
310 InvokeWithoutArgs(this, &VideoSchedulerTest::StopVideoScheduler))) | |
311 .RetiresOnSaturation(); | |
312 | |
313 EXPECT_CALL(client_stub_, SetCursorShape(_)) | |
314 .WillOnce(Invoke(this, &VideoSchedulerTest::SetCursorShape)); | |
315 | |
316 // Start video frame capture. | |
317 scoped_ptr<webrtc::MouseCursorMonitor> mouse_cursor_monitor( | |
318 new FakeMouseCursorMonitor()); | |
319 StartVideoScheduler(capturer.Pass(), encoder.Pass(), cursor_monitor.Pass()); | |
320 | |
321 // Run until there are no more pending tasks from the VideoScheduler. | |
322 // Otherwise, a lingering frame capture might attempt to trigger a capturer | |
323 // expectation action and crash. | |
324 base::RunLoop().RunUntilIdle(); | |
325 } | |
326 | |
327 // Verify that the capturer, encoder and mouse monitor are torn down on the | |
328 // correct threads. | |
329 TEST_F(VideoSchedulerTest, DeleteOnThreads) { | |
330 capture_task_runner_ = AutoThread::Create("capture", main_task_runner_); | |
331 encode_task_runner_ = AutoThread::Create("encode", main_task_runner_); | |
332 | |
333 scoped_ptr<webrtc::DesktopCapturer> capturer( | |
334 new ThreadCheckDesktopCapturer(capture_task_runner_)); | |
335 scoped_ptr<VideoEncoder> encoder( | |
336 new ThreadCheckVideoEncoder(encode_task_runner_)); | |
337 scoped_ptr<webrtc::MouseCursorMonitor> mouse_cursor_monitor( | |
338 new ThreadCheckMouseCursorMonitor(capture_task_runner_)); | |
339 | |
340 // Start and stop the scheduler, so it will tear down the screen capturer, | |
341 // video encoder and mouse monitor. | |
342 StartVideoScheduler(capturer.Pass(), encoder.Pass(), | |
343 mouse_cursor_monitor.Pass()); | |
344 StopVideoScheduler(); | |
345 } | |
346 | |
347 } // namespace remoting | |
OLD | NEW |