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.
5 #include "content/browser/media/capture/desktop_capture_device.h"
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"
21 using ::testing::AnyNumber
;
22 using ::testing::DoAll
;
23 using ::testing::Expectation
;
24 using ::testing::InvokeWithoutArgs
;
25 using ::testing::SaveArg
;
31 MATCHER_P2(EqualsCaptureCapability
, width
, height
, "") {
32 return arg
.width
== width
&& arg
.height
== height
;
35 const int kTestFrameWidth1
= 100;
36 const int kTestFrameHeight1
= 100;
37 const int kTestFrameWidth2
= 200;
38 const int kTestFrameHeight2
= 150;
40 const int kFrameRate
= 30;
42 class MockDeviceClient
: public media::VideoCaptureDevice::Client
{
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
,
51 const media::VideoCaptureFormat
& frame_format
,
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
));
61 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
63 class InvertedDesktopFrame
: public webrtc::DesktopFrame
{
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());
76 virtual ~InvertedDesktopFrame() {}
79 scoped_ptr
<webrtc::DesktopFrame
> original_frame_
;
81 DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame
);
84 // TODO(sergeyu): Move this to a separate file where it can be reused.
85 class FakeScreenCapturer
: public webrtc::ScreenCapturer
{
90 generate_inverted_frames_(false) {
92 virtual ~FakeScreenCapturer() {}
94 void set_generate_inverted_frames(bool generate_inverted_frames
) {
95 generate_inverted_frames_
= generate_inverted_frames
;
98 // VideoFrameCapturer interface.
99 virtual void Start(Callback
* callback
) OVERRIDE
{
100 callback_
= callback
;
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
);
108 size
= webrtc::DesktopSize(kTestFrameWidth2
, kTestFrameHeight2
);
112 webrtc::DesktopFrame
* frame
= new webrtc::BasicDesktopFrame(size
);
113 if (generate_inverted_frames_
)
114 frame
= new InvertedDesktopFrame(frame
);
115 callback_
->OnCaptureCompleted(frame
);
118 virtual void SetMouseShapeObserver(
119 MouseShapeObserver
* mouse_shape_observer
) OVERRIDE
{
122 virtual bool GetScreenList(ScreenList
* screens
) OVERRIDE
{
126 virtual bool SelectScreen(webrtc::ScreenId id
) OVERRIDE
{
133 bool generate_inverted_frames_
;
138 class DesktopCaptureDeviceTest
: public testing::Test
{
140 virtual void SetUp() OVERRIDE
{
141 worker_pool_
= new base::SequencedWorkerPool(3, "TestCaptureThread");
144 void CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
> capturer
) {
145 capture_device_
.reset(new DesktopCaptureDevice(
146 worker_pool_
->GetSequencedTaskRunner(worker_pool_
->GetSequenceToken()),
149 DesktopMediaID::TYPE_SCREEN
));
153 scoped_refptr
<base::SequencedWorkerPool
> worker_pool_
;
154 scoped_ptr
<base::Thread
> thread_
;
155 scoped_ptr
<DesktopCaptureDevice
> capture_device_
;
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
164 #define MAYBE_Capture Capture
166 TEST_F(DesktopCaptureDeviceTest
, MAYBE_Capture
) {
167 scoped_ptr
<webrtc::DesktopCapturer
> capturer(
168 webrtc::ScreenCapturer::Create());
169 CreateScreenCaptureDevice(capturer
.Pass());
171 media::VideoCaptureFormat format
;
172 base::WaitableEvent
done_event(false, false);
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
),
180 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
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();
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
);
197 EXPECT_EQ(format
.frame_size
.GetArea() * 4, frame_size
);
198 worker_pool_
->FlushForTesting();
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();
206 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
208 media::VideoCaptureFormat format
;
209 base::WaitableEvent
done_event(false, false);
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
),
217 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
219 media::VideoCaptureParams capture_params
;
220 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth1
,
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;
226 capture_device_
->AllocateAndStart(
227 capture_params
, client
.PassAs
<media::VideoCaptureDevice::Client
>());
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()));
233 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
235 capture_device_
->StopAndDeAllocate();
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
);
242 EXPECT_EQ(format
.frame_size
.GetArea() * 4, frame_size
);
243 worker_pool_
->FlushForTesting();
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();
251 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
253 media::VideoCaptureFormat format
;
254 base::WaitableEvent
done_event(false, false);
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
)));
262 media::VideoCaptureParams capture_params
;
263 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth2
,
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;
269 capture_device_
->AllocateAndStart(
270 capture_params
, client
.PassAs
<media::VideoCaptureDevice::Client
>());
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()));
276 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
278 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
280 capture_device_
->StopAndDeAllocate();
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();
289 } // namespace content