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_objects.h" 26 27using ::remoting::protocol::MockClientStub; 28using ::remoting::protocol::MockVideoStub; 29 30using ::testing::_; 31using ::testing::AtLeast; 32using ::testing::AnyNumber; 33using ::testing::DeleteArg; 34using ::testing::DoAll; 35using ::testing::Expectation; 36using ::testing::InSequence; 37using ::testing::InvokeWithoutArgs; 38using ::testing::Return; 39using ::testing::ReturnRef; 40using ::testing::SaveArg; 41 42namespace remoting { 43 44namespace { 45 46ACTION(FinishEncode) { 47 scoped_ptr<VideoPacket> packet(new VideoPacket()); 48 return packet.release(); 49} 50 51ACTION(FinishSend) { 52 arg1.Run(); 53} 54 55} // namespace 56 57static const int kWidth = 640; 58static const int kHeight = 480; 59static const int kCursorWidth = 64; 60static const int kCursorHeight = 32; 61static const int kHotspotX = 11; 62static const int kHotspotY = 12; 63 64class MockVideoEncoder : public VideoEncoder { 65 public: 66 MockVideoEncoder() {} 67 virtual ~MockVideoEncoder() {} 68 69 scoped_ptr<VideoPacket> Encode( 70 const webrtc::DesktopFrame& frame) { 71 return scoped_ptr<VideoPacket>(EncodePtr(frame)); 72 } 73 MOCK_METHOD1(EncodePtr, VideoPacket*(const webrtc::DesktopFrame& frame)); 74 75 private: 76 DISALLOW_COPY_AND_ASSIGN(MockVideoEncoder); 77}; 78 79class ThreadCheckVideoEncoder : public VideoEncoderVerbatim { 80 public: 81 ThreadCheckVideoEncoder( 82 scoped_refptr<base::SingleThreadTaskRunner> task_runner) 83 : task_runner_(task_runner) { 84 } 85 virtual ~ThreadCheckVideoEncoder() { 86 EXPECT_TRUE(task_runner_->BelongsToCurrentThread()); 87 } 88 89 private: 90 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; 91 92 DISALLOW_COPY_AND_ASSIGN(ThreadCheckVideoEncoder); 93}; 94 95class ThreadCheckDesktopCapturer : public FakeDesktopCapturer { 96 public: 97 ThreadCheckDesktopCapturer( 98 scoped_refptr<base::SingleThreadTaskRunner> task_runner) 99 : task_runner_(task_runner) { 100 } 101 virtual ~ThreadCheckDesktopCapturer() { 102 EXPECT_TRUE(task_runner_->BelongsToCurrentThread()); 103 } 104 105 private: 106 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; 107 108 DISALLOW_COPY_AND_ASSIGN(ThreadCheckDesktopCapturer); 109}; 110 111class ThreadCheckMouseCursorMonitor : public FakeMouseCursorMonitor { 112 public: 113 ThreadCheckMouseCursorMonitor( 114 scoped_refptr<base::SingleThreadTaskRunner> task_runner) 115 : task_runner_(task_runner) { 116 } 117 virtual ~ThreadCheckMouseCursorMonitor() { 118 EXPECT_TRUE(task_runner_->BelongsToCurrentThread()); 119 } 120 121 private: 122 scoped_refptr<base::SingleThreadTaskRunner> task_runner_; 123 124 DISALLOW_COPY_AND_ASSIGN(ThreadCheckMouseCursorMonitor); 125}; 126 127class VideoSchedulerTest : public testing::Test { 128 public: 129 VideoSchedulerTest(); 130 131 virtual void SetUp() OVERRIDE; 132 virtual void TearDown() OVERRIDE; 133 134 void StartVideoScheduler( 135 scoped_ptr<webrtc::DesktopCapturer> capturer, 136 scoped_ptr<VideoEncoder> encoder, 137 scoped_ptr<webrtc::MouseCursorMonitor> mouse_monitor); 138 void StopVideoScheduler(); 139 140 // webrtc::DesktopCapturer mocks. 141 void OnCapturerStart(webrtc::DesktopCapturer::Callback* callback); 142 void OnCaptureFrame(const webrtc::DesktopRegion& region); 143 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); 150 151 protected: 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_; 158 159 MockClientStub client_stub_; 160 MockVideoStub video_stub_; 161 162 // Points to the callback passed to webrtc::DesktopCapturer::Start(). 163 webrtc::DesktopCapturer::Callback* capturer_callback_; 164 165 // Points to the callback passed to webrtc::MouseCursor::Init(). 166 webrtc::MouseCursorMonitor::Callback* mouse_monitor_callback_; 167 168 private: 169 DISALLOW_COPY_AND_ASSIGN(VideoSchedulerTest); 170}; 171 172VideoSchedulerTest::VideoSchedulerTest() 173 : capturer_callback_(NULL), 174 mouse_monitor_callback_(NULL) { 175} 176 177void 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_; 182} 183 184void 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; 189 190 // Run the MessageLoop until everything has torn down. 191 run_loop_.Run(); 192} 193 194void 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_, 200 encode_task_runner_, 201 main_task_runner_, 202 capturer.Pass(), 203 mouse_monitor.Pass(), 204 encoder.Pass(), 205 &client_stub_, 206 &video_stub_); 207 scheduler_->Start(); 208} 209 210void VideoSchedulerTest::StopVideoScheduler() { 211 scheduler_->Stop(); 212 scheduler_ = NULL; 213} 214 215void VideoSchedulerTest::OnCapturerStart( 216 webrtc::DesktopCapturer::Callback* callback) { 217 EXPECT_FALSE(capturer_callback_); 218 EXPECT_TRUE(callback); 219 220 capturer_callback_ = callback; 221} 222 223void 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()); 229} 230 231void VideoSchedulerTest::OnCaptureMouse() { 232 EXPECT_TRUE(mouse_monitor_callback_); 233 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))); 239 240 mouse_monitor_callback_->OnMouseCursor(mouse_cursor.release()); 241} 242 243void VideoSchedulerTest::OnMouseCursorMonitorInit( 244 webrtc::MouseCursorMonitor::Callback* callback, 245 webrtc::MouseCursorMonitor::Mode mode) { 246 EXPECT_FALSE(mouse_monitor_callback_); 247 EXPECT_TRUE(callback); 248 249 mouse_monitor_callback_ = callback; 250} 251 252void 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())); 265} 266 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. 271TEST_F(VideoSchedulerTest, StartAndStop) { 272 scoped_ptr<webrtc::MockScreenCapturer> capturer( 273 new webrtc::MockScreenCapturer()); 274 scoped_ptr<MockMouseCursorMonitor> cursor_monitor( 275 new MockMouseCursorMonitor()); 276 277 { 278 InSequence s; 279 280 EXPECT_CALL(*cursor_monitor, Init(_, _)) 281 .WillOnce( 282 Invoke(this, &VideoSchedulerTest::OnMouseCursorMonitorInit)); 283 284 EXPECT_CALL(*cursor_monitor, Capture()) 285 .WillRepeatedly(Invoke(this, &VideoSchedulerTest::OnCaptureMouse)); 286 } 287 288 Expectation capturer_start = 289 EXPECT_CALL(*capturer, Start(_)) 290 .WillOnce(Invoke(this, &VideoSchedulerTest::OnCapturerStart)); 291 292 // First the capturer is called. 293 Expectation capturer_capture = EXPECT_CALL(*capturer, Capture(_)) 294 .After(capturer_start) 295 .WillRepeatedly(Invoke(this, &VideoSchedulerTest::OnCaptureFrame)); 296 297 scoped_ptr<MockVideoEncoder> encoder(new MockVideoEncoder()); 298 299 // Expect the encoder be called. 300 EXPECT_CALL(*encoder, EncodePtr(_)) 301 .WillRepeatedly(FinishEncode()); 302 303 // By default delete the arguments when ProcessVideoPacket is received. 304 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) 305 .WillRepeatedly(FinishSend()); 306 307 // When the first ProcessVideoPacket is received we stop the VideoScheduler. 308 EXPECT_CALL(video_stub_, ProcessVideoPacketPtr(_, _)) 309 .WillOnce(DoAll( 310 FinishSend(), 311 InvokeWithoutArgs(this, &VideoSchedulerTest::StopVideoScheduler))) 312 .RetiresOnSaturation(); 313 314 EXPECT_CALL(client_stub_, SetCursorShape(_)) 315 .WillOnce(Invoke(this, &VideoSchedulerTest::SetCursorShape)); 316 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>()); 323 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(); 328} 329 330// Verify that the capturer, encoder and mouse monitor are torn down on the 331// correct threads. 332TEST_F(VideoSchedulerTest, DeleteOnThreads) { 333 capture_task_runner_ = AutoThread::Create("capture", main_task_runner_); 334 encode_task_runner_ = AutoThread::Create("encode", main_task_runner_); 335 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_)); 342 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(); 348} 349 350} // namespace remoting 351