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