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"
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_capture_options.h"
17 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
18 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
19 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
22 using ::testing::AnyNumber
;
23 using ::testing::DoAll
;
24 using ::testing::Expectation
;
25 using ::testing::InvokeWithoutArgs
;
26 using ::testing::SaveArg
;
32 MATCHER_P2(EqualsCaptureCapability
, width
, height
, "") {
33 return arg
.width
== width
&& arg
.height
== height
;
36 const int kTestFrameWidth1
= 100;
37 const int kTestFrameHeight1
= 100;
38 const int kTestFrameWidth2
= 200;
39 const int kTestFrameHeight2
= 150;
41 const int kFrameRate
= 30;
43 // The value of the padding bytes in unpacked frames.
44 const uint8_t kFramePaddingValue
= 0;
46 // Use a special value for frame pixels to tell pixel bytes apart from the
47 // padding bytes in the unpacked frame test.
48 const uint8_t kFakePixelValue
= 1;
50 // Use a special value for the first pixel to verify the result in the inverted
52 const uint8_t kFakePixelValueFirst
= 2;
54 class MockDeviceClient
: public media::VideoCaptureDevice::Client
{
56 MOCK_METHOD5(OnIncomingCapturedData
,
57 void(const uint8
* data
,
59 const media::VideoCaptureFormat
& frame_format
,
61 const base::TimeTicks
& timestamp
));
62 MOCK_METHOD9(OnIncomingCapturedYuvData
,
63 void (const uint8
* y_data
,
69 const media::VideoCaptureFormat
& frame_format
,
70 int clockwise_rotation
,
71 const base::TimeTicks
& timestamp
));
72 MOCK_METHOD0(DoReserveOutputBuffer
, void(void));
73 MOCK_METHOD0(DoOnIncomingCapturedBuffer
, void(void));
74 MOCK_METHOD0(DoOnIncomingCapturedVideoFrame
, void(void));
75 MOCK_METHOD1(OnError
, void(const std::string
& reason
));
77 // Trampoline methods to workaround GMOCK problems with scoped_ptr<>.
78 scoped_ptr
<Buffer
> ReserveOutputBuffer(media::VideoPixelFormat format
,
79 const gfx::Size
& dimensions
) override
{
80 DoReserveOutputBuffer();
81 return scoped_ptr
<Buffer
>();
83 void OnIncomingCapturedBuffer(scoped_ptr
<Buffer
> buffer
,
84 const media::VideoCaptureFormat
& frame_format
,
85 const base::TimeTicks
& timestamp
) override
{
86 DoOnIncomingCapturedBuffer();
88 void OnIncomingCapturedVideoFrame(
89 scoped_ptr
<Buffer
> buffer
,
90 const scoped_refptr
<media::VideoFrame
>& frame
,
91 const base::TimeTicks
& timestamp
) override
{
92 DoOnIncomingCapturedVideoFrame();
96 // Creates a DesktopFrame that has the first pixel bytes set to
97 // kFakePixelValueFirst, and the rest of the bytes set to kFakePixelValue, for
98 // UnpackedFrame and InvertedFrame verification.
99 webrtc::BasicDesktopFrame
* CreateBasicFrame(const webrtc::DesktopSize
& size
) {
100 webrtc::BasicDesktopFrame
* frame
= new webrtc::BasicDesktopFrame(size
);;
101 DCHECK_EQ(frame
->size().width() * webrtc::DesktopFrame::kBytesPerPixel
,
103 memset(frame
->data(),
105 frame
->stride() * frame
->size().height());
106 memset(frame
->data(),
107 kFakePixelValueFirst
,
108 webrtc::DesktopFrame::kBytesPerPixel
);
112 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
114 class InvertedDesktopFrame
: public webrtc::DesktopFrame
{
116 // Takes ownership of |frame|.
117 explicit InvertedDesktopFrame(webrtc::DesktopFrame
* frame
)
118 : webrtc::DesktopFrame(
121 frame
->data() + (frame
->size().height() - 1) * frame
->stride(),
122 frame
->shared_memory()),
123 original_frame_(frame
) {
124 set_dpi(frame
->dpi());
125 set_capture_time_ms(frame
->capture_time_ms());
126 mutable_updated_region()->Swap(frame
->mutable_updated_region());
128 ~InvertedDesktopFrame() override
{}
131 scoped_ptr
<webrtc::DesktopFrame
> original_frame_
;
133 DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame
);
136 // DesktopFrame wrapper that copies the input frame and doubles the stride.
137 class UnpackedDesktopFrame
: public webrtc::DesktopFrame
{
139 // Takes ownership of |frame|.
140 explicit UnpackedDesktopFrame(webrtc::DesktopFrame
* frame
)
141 : webrtc::DesktopFrame(
144 new uint8_t[frame
->stride() * 2 * frame
->size().height()],
146 memset(data(), kFramePaddingValue
, stride() * size().height());
147 CopyPixelsFrom(*frame
,
148 webrtc::DesktopVector(),
149 webrtc::DesktopRect::MakeSize(size()));
152 ~UnpackedDesktopFrame() override
{
156 DISALLOW_COPY_AND_ASSIGN(UnpackedDesktopFrame
);
159 // TODO(sergeyu): Move this to a separate file where it can be reused.
160 class FakeScreenCapturer
: public webrtc::ScreenCapturer
{
165 generate_inverted_frames_(false),
166 generate_cropped_frames_(false) {
168 ~FakeScreenCapturer() override
{}
170 void set_generate_inverted_frames(bool generate_inverted_frames
) {
171 generate_inverted_frames_
= generate_inverted_frames
;
174 void set_generate_cropped_frames(bool generate_cropped_frames
) {
175 generate_cropped_frames_
= generate_cropped_frames
;
178 // VideoFrameCapturer interface.
179 void Start(Callback
* callback
) override
{ callback_
= callback
; }
181 void Capture(const webrtc::DesktopRegion
& region
) override
{
182 webrtc::DesktopSize size
;
183 if (frame_index_
% 2 == 0) {
184 size
= webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
);
186 size
= webrtc::DesktopSize(kTestFrameWidth2
, kTestFrameHeight2
);
190 webrtc::DesktopFrame
* frame
= CreateBasicFrame(size
);;
192 if (generate_inverted_frames_
) {
193 frame
= new InvertedDesktopFrame(frame
);
194 } else if (generate_cropped_frames_
) {
195 frame
= new UnpackedDesktopFrame(frame
);
197 callback_
->OnCaptureCompleted(frame
);
200 bool GetScreenList(ScreenList
* screens
) override
{ return false; }
202 bool SelectScreen(webrtc::ScreenId id
) override
{ return false; }
207 bool generate_inverted_frames_
;
208 bool generate_cropped_frames_
;
213 class DesktopCaptureDeviceTest
: public testing::Test
{
215 void CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
> capturer
) {
216 capture_device_
.reset(
217 new DesktopCaptureDevice(capturer
.Pass(), DesktopMediaID::TYPE_SCREEN
));
220 void CopyFrame(const uint8_t* frame
, int size
,
221 const media::VideoCaptureFormat
&, int, base::TimeTicks
) {
222 ASSERT_TRUE(output_frame_
.get() != NULL
);
223 ASSERT_EQ(output_frame_
->stride() * output_frame_
->size().height(), size
);
224 memcpy(output_frame_
->data(), frame
, size
);
228 scoped_ptr
<DesktopCaptureDevice
> capture_device_
;
229 scoped_ptr
<webrtc::DesktopFrame
> output_frame_
;
232 // There is currently no screen capturer implementation for ozone. So disable
233 // the test that uses a real screen-capturer instead of FakeScreenCapturer.
234 // http://crbug.com/260318
235 #if defined(USE_OZONE)
236 #define MAYBE_Capture DISABLED_Capture
238 #define MAYBE_Capture Capture
240 TEST_F(DesktopCaptureDeviceTest
, MAYBE_Capture
) {
241 scoped_ptr
<webrtc::DesktopCapturer
> capturer(
242 webrtc::ScreenCapturer::Create(
243 webrtc::DesktopCaptureOptions::CreateDefault()));
244 CreateScreenCaptureDevice(capturer
.Pass());
246 media::VideoCaptureFormat format
;
247 base::WaitableEvent
done_event(false, false);
250 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
251 EXPECT_CALL(*client
, OnError(_
)).Times(0);
252 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
253 DoAll(SaveArg
<1>(&frame_size
),
255 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
257 media::VideoCaptureParams capture_params
;
258 capture_params
.requested_format
.frame_size
.SetSize(640, 480);
259 capture_params
.requested_format
.frame_rate
= kFrameRate
;
260 capture_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
261 capture_device_
->AllocateAndStart(capture_params
, client
.Pass());
262 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
263 capture_device_
->StopAndDeAllocate();
265 EXPECT_GT(format
.frame_size
.width(), 0);
266 EXPECT_GT(format
.frame_size
.height(), 0);
267 EXPECT_EQ(kFrameRate
, format
.frame_rate
);
268 EXPECT_EQ(media::PIXEL_FORMAT_ARGB
, format
.pixel_format
);
270 EXPECT_EQ(format
.frame_size
.GetArea() * 4, frame_size
);
273 // Test that screen capturer behaves correctly if the source frame size changes
274 // but the caller cannot cope with variable resolution output.
275 TEST_F(DesktopCaptureDeviceTest
, ScreenResolutionChangeConstantResolution
) {
276 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
278 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
280 media::VideoCaptureFormat format
;
281 base::WaitableEvent
done_event(false, false);
284 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
285 EXPECT_CALL(*client
, OnError(_
)).Times(0);
286 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
287 DoAll(SaveArg
<1>(&frame_size
),
289 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
291 media::VideoCaptureParams capture_params
;
292 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth1
,
294 capture_params
.requested_format
.frame_rate
= kFrameRate
;
295 capture_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
297 capture_device_
->AllocateAndStart(capture_params
, client
.Pass());
299 // Capture at least two frames, to ensure that the source frame size has
300 // changed while capturing.
301 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
303 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
305 capture_device_
->StopAndDeAllocate();
307 EXPECT_EQ(kTestFrameWidth1
, format
.frame_size
.width());
308 EXPECT_EQ(kTestFrameHeight1
, format
.frame_size
.height());
309 EXPECT_EQ(kFrameRate
, format
.frame_rate
);
310 EXPECT_EQ(media::PIXEL_FORMAT_ARGB
, format
.pixel_format
);
312 EXPECT_EQ(format
.frame_size
.GetArea() * 4, frame_size
);
315 // Test that screen capturer behaves correctly if the source frame size changes
316 // and the caller can cope with variable resolution output.
317 TEST_F(DesktopCaptureDeviceTest
, ScreenResolutionChangeVariableResolution
) {
318 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
320 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
322 media::VideoCaptureFormat format
;
323 base::WaitableEvent
done_event(false, false);
325 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
326 EXPECT_CALL(*client
, OnError(_
)).Times(0);
327 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
328 DoAll(SaveArg
<2>(&format
),
329 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
331 media::VideoCaptureParams capture_params
;
332 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth2
,
334 capture_params
.requested_format
.frame_rate
= kFrameRate
;
335 capture_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
337 capture_device_
->AllocateAndStart(
338 capture_params
, client
.Pass());
340 // Capture at least three frames, to ensure that the source frame size has
341 // changed at least twice while capturing.
342 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
344 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
346 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
348 capture_device_
->StopAndDeAllocate();
350 EXPECT_EQ(kTestFrameWidth1
, format
.frame_size
.width());
351 EXPECT_EQ(kTestFrameHeight1
, format
.frame_size
.height());
352 EXPECT_EQ(kFrameRate
, format
.frame_rate
);
353 EXPECT_EQ(media::PIXEL_FORMAT_ARGB
, format
.pixel_format
);
356 // This test verifies that an unpacked frame is converted to a packed frame.
357 TEST_F(DesktopCaptureDeviceTest
, UnpackedFrame
) {
358 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
359 mock_capturer
->set_generate_cropped_frames(true);
360 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
362 media::VideoCaptureFormat format
;
363 base::WaitableEvent
done_event(false, false);
366 output_frame_
.reset(new webrtc::BasicDesktopFrame(
367 webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
)));
369 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
370 EXPECT_CALL(*client
, OnError(_
)).Times(0);
371 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
372 DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame
),
373 SaveArg
<1>(&frame_size
),
374 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
376 media::VideoCaptureParams capture_params
;
377 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth1
,
379 capture_params
.requested_format
.frame_rate
= kFrameRate
;
380 capture_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
382 capture_device_
->AllocateAndStart(capture_params
, client
.Pass());
384 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
386 capture_device_
->StopAndDeAllocate();
388 // Verifies that |output_frame_| has the same data as a packed frame of the
390 scoped_ptr
<webrtc::BasicDesktopFrame
> expected_frame(CreateBasicFrame(
391 webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
)));
392 EXPECT_EQ(output_frame_
->stride() * output_frame_
->size().height(),
395 0, memcmp(output_frame_
->data(), expected_frame
->data(), frame_size
));
398 // The test verifies that a bottom-to-top frame is converted to top-to-bottom.
399 TEST_F(DesktopCaptureDeviceTest
, InvertedFrame
) {
400 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
401 mock_capturer
->set_generate_inverted_frames(true);
402 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
404 media::VideoCaptureFormat format
;
405 base::WaitableEvent
done_event(false, false);
408 output_frame_
.reset(new webrtc::BasicDesktopFrame(
409 webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
)));
411 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
412 EXPECT_CALL(*client
, OnError(_
)).Times(0);
413 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
414 DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame
),
415 SaveArg
<1>(&frame_size
),
416 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
418 media::VideoCaptureParams capture_params
;
419 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth1
,
421 capture_params
.requested_format
.frame_rate
= kFrameRate
;
422 capture_params
.requested_format
.pixel_format
= media::PIXEL_FORMAT_I420
;
424 capture_device_
->AllocateAndStart(capture_params
, client
.Pass());
426 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
428 capture_device_
->StopAndDeAllocate();
430 // Verifies that |output_frame_| has the same pixel values as the inverted
432 scoped_ptr
<webrtc::DesktopFrame
> inverted_frame(
433 new InvertedDesktopFrame(CreateBasicFrame(
434 webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
))));
435 EXPECT_EQ(output_frame_
->stride() * output_frame_
->size().height(),
437 for (int i
= 0; i
< output_frame_
->size().height(); ++i
) {
439 memcmp(inverted_frame
->data() + i
* inverted_frame
->stride(),
440 output_frame_
->data() + i
* output_frame_
->stride(),
441 output_frame_
->stride()));
445 } // namespace content