| 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 |