1 // Copyright (c) 2013 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 "content/browser/media/capture/desktop_capture_device.h" 6 7 #include "base/basictypes.h" 8 #include "base/sequenced_task_runner.h" 9 #include "base/synchronization/waitable_event.h" 10 #include "base/test/test_timeouts.h" 11 #include "base/threading/sequenced_worker_pool.h" 12 #include "base/threading/thread.h" 13 #include "base/time/time.h" 14 #include "testing/gmock/include/gmock/gmock.h" 15 #include "testing/gtest/include/gtest/gtest.h" 16 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h" 17 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h" 18 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h" 19 20 using ::testing::_; 21 using ::testing::AnyNumber; 22 using ::testing::DoAll; 23 using ::testing::Expectation; 24 using ::testing::InvokeWithoutArgs; 25 using ::testing::SaveArg; 26 27 namespace content { 28 29 namespace { 30 31 MATCHER_P2(EqualsCaptureCapability, width, height, "") { 32 return arg.width == width && arg.height == height; 33 } 34 35 const int kTestFrameWidth1 = 100; 36 const int kTestFrameHeight1 = 100; 37 const int kTestFrameWidth2 = 200; 38 const int kTestFrameHeight2 = 150; 39 40 const int kFrameRate = 30; 41 42 class MockDeviceClient : public media::VideoCaptureDevice::Client { 43 public: 44 MOCK_METHOD2(ReserveOutputBuffer, 45 scoped_refptr<Buffer>(media::VideoFrame::Format format, 46 const gfx::Size& dimensions)); 47 MOCK_METHOD1(OnError, void(const std::string& reason)); 48 MOCK_METHOD5(OnIncomingCapturedData, 49 void(const uint8* data, 50 int length, 51 const media::VideoCaptureFormat& frame_format, 52 int rotation, 53 base::TimeTicks timestamp)); 54 MOCK_METHOD4(OnIncomingCapturedVideoFrame, 55 void(const scoped_refptr<Buffer>& buffer, 56 const media::VideoCaptureFormat& buffer_format, 57 const scoped_refptr<media::VideoFrame>& frame, 58 base::TimeTicks timestamp)); 59 }; 60 61 // DesktopFrame wrapper that flips wrapped frame upside down by inverting 62 // stride. 63 class InvertedDesktopFrame : public webrtc::DesktopFrame { 64 public: 65 // Takes ownership of |frame|. 66 InvertedDesktopFrame(webrtc::DesktopFrame* frame) 67 : webrtc::DesktopFrame( 68 frame->size(), -frame->stride(), 69 frame->data() + (frame->size().height() - 1) * frame->stride(), 70 frame->shared_memory()), 71 original_frame_(frame) { 72 set_dpi(frame->dpi()); 73 set_capture_time_ms(frame->capture_time_ms()); 74 mutable_updated_region()->Swap(frame->mutable_updated_region()); 75 } 76 virtual ~InvertedDesktopFrame() {} 77 78 private: 79 scoped_ptr<webrtc::DesktopFrame> original_frame_; 80 81 DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame); 82 }; 83 84 // TODO(sergeyu): Move this to a separate file where it can be reused. 85 class FakeScreenCapturer : public webrtc::ScreenCapturer { 86 public: 87 FakeScreenCapturer() 88 : callback_(NULL), 89 frame_index_(0), 90 generate_inverted_frames_(false) { 91 } 92 virtual ~FakeScreenCapturer() {} 93 94 void set_generate_inverted_frames(bool generate_inverted_frames) { 95 generate_inverted_frames_ = generate_inverted_frames; 96 } 97 98 // VideoFrameCapturer interface. 99 virtual void Start(Callback* callback) OVERRIDE { 100 callback_ = callback; 101 } 102 103 virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE { 104 webrtc::DesktopSize size; 105 if (frame_index_ % 2 == 0) { 106 size = webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1); 107 } else { 108 size = webrtc::DesktopSize(kTestFrameWidth2, kTestFrameHeight2); 109 } 110 frame_index_++; 111 112 webrtc::DesktopFrame* frame = new webrtc::BasicDesktopFrame(size); 113 if (generate_inverted_frames_) 114 frame = new InvertedDesktopFrame(frame); 115 callback_->OnCaptureCompleted(frame); 116 } 117 118 virtual void SetMouseShapeObserver( 119 MouseShapeObserver* mouse_shape_observer) OVERRIDE { 120 } 121 122 virtual bool GetScreenList(ScreenList* screens) OVERRIDE { 123 return false; 124 } 125 126 virtual bool SelectScreen(webrtc::ScreenId id) OVERRIDE { 127 return false; 128 } 129 130 private: 131 Callback* callback_; 132 int frame_index_; 133 bool generate_inverted_frames_; 134 }; 135 136 } // namespace 137 138 class DesktopCaptureDeviceTest : public testing::Test { 139 public: 140 virtual void SetUp() OVERRIDE { 141 worker_pool_ = new base::SequencedWorkerPool(3, "TestCaptureThread"); 142 } 143 144 void CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer> capturer) { 145 capture_device_.reset(new DesktopCaptureDevice( 146 worker_pool_->GetSequencedTaskRunner(worker_pool_->GetSequenceToken()), 147 thread_.Pass(), 148 capturer.Pass(), 149 DesktopMediaID::TYPE_SCREEN)); 150 } 151 152 protected: 153 scoped_refptr<base::SequencedWorkerPool> worker_pool_; 154 scoped_ptr<base::Thread> thread_; 155 scoped_ptr<DesktopCaptureDevice> capture_device_; 156 }; 157 158 // There is currently no screen capturer implementation for ozone. So disable 159 // the test that uses a real screen-capturer instead of FakeScreenCapturer. 160 // http://crbug.com/260318 161 #if defined(USE_OZONE) 162 #define MAYBE_Capture DISABLED_Capture 163 #else 164 #define MAYBE_Capture Capture 165 #endif 166 TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) { 167 scoped_ptr<webrtc::DesktopCapturer> capturer( 168 webrtc::ScreenCapturer::Create()); 169 CreateScreenCaptureDevice(capturer.Pass()); 170 171 media::VideoCaptureFormat format; 172 base::WaitableEvent done_event(false, false); 173 int frame_size; 174 175 scoped_ptr<MockDeviceClient> client(new MockDeviceClient()); 176 EXPECT_CALL(*client, OnError(_)).Times(0); 177 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly( 178 DoAll(SaveArg<1>(&frame_size), 179 SaveArg<2>(&format), 180 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); 181 182 media::VideoCaptureParams capture_params; 183 capture_params.requested_format.frame_size.SetSize(640, 480); 184 capture_params.requested_format.frame_rate = kFrameRate; 185 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420; 186 capture_params.allow_resolution_change = false; 187 capture_device_->AllocateAndStart( 188 capture_params, client.PassAs<media::VideoCaptureDevice::Client>()); 189 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); 190 capture_device_->StopAndDeAllocate(); 191 192 EXPECT_GT(format.frame_size.width(), 0); 193 EXPECT_GT(format.frame_size.height(), 0); 194 EXPECT_EQ(kFrameRate, format.frame_rate); 195 EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format); 196 197 EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size); 198 worker_pool_->FlushForTesting(); 199 } 200 201 // Test that screen capturer behaves correctly if the source frame size changes 202 // but the caller cannot cope with variable resolution output. 203 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) { 204 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer(); 205 206 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer)); 207 208 media::VideoCaptureFormat format; 209 base::WaitableEvent done_event(false, false); 210 int frame_size; 211 212 scoped_ptr<MockDeviceClient> client(new MockDeviceClient()); 213 EXPECT_CALL(*client, OnError(_)).Times(0); 214 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly( 215 DoAll(SaveArg<1>(&frame_size), 216 SaveArg<2>(&format), 217 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); 218 219 media::VideoCaptureParams capture_params; 220 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1, 221 kTestFrameHeight1); 222 capture_params.requested_format.frame_rate = kFrameRate; 223 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420; 224 capture_params.allow_resolution_change = false; 225 226 capture_device_->AllocateAndStart( 227 capture_params, client.PassAs<media::VideoCaptureDevice::Client>()); 228 229 // Capture at least two frames, to ensure that the source frame size has 230 // changed while capturing. 231 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); 232 done_event.Reset(); 233 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); 234 235 capture_device_->StopAndDeAllocate(); 236 237 EXPECT_EQ(kTestFrameWidth1, format.frame_size.width()); 238 EXPECT_EQ(kTestFrameHeight1, format.frame_size.height()); 239 EXPECT_EQ(kFrameRate, format.frame_rate); 240 EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format); 241 242 EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size); 243 worker_pool_->FlushForTesting(); 244 } 245 246 // Test that screen capturer behaves correctly if the source frame size changes 247 // and the caller can cope with variable resolution output. 248 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) { 249 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer(); 250 251 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer)); 252 253 media::VideoCaptureFormat format; 254 base::WaitableEvent done_event(false, false); 255 256 scoped_ptr<MockDeviceClient> client(new MockDeviceClient()); 257 EXPECT_CALL(*client, OnError(_)).Times(0); 258 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly( 259 DoAll(SaveArg<2>(&format), 260 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal))); 261 262 media::VideoCaptureParams capture_params; 263 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth2, 264 kTestFrameHeight2); 265 capture_params.requested_format.frame_rate = kFrameRate; 266 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420; 267 capture_params.allow_resolution_change = false; 268 269 capture_device_->AllocateAndStart( 270 capture_params, client.PassAs<media::VideoCaptureDevice::Client>()); 271 272 // Capture at least three frames, to ensure that the source frame size has 273 // changed at least twice while capturing. 274 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); 275 done_event.Reset(); 276 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); 277 done_event.Reset(); 278 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout())); 279 280 capture_device_->StopAndDeAllocate(); 281 282 EXPECT_EQ(kTestFrameWidth1, format.frame_size.width()); 283 EXPECT_EQ(kTestFrameHeight1, format.frame_size.height()); 284 EXPECT_EQ(kFrameRate, format.frame_rate); 285 EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format); 286 worker_pool_->FlushForTesting(); 287 } 288 289 } // namespace content 290