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"
10 #include "base/basictypes.h"
11 #include "base/synchronization/waitable_event.h"
12 #include "base/test/test_timeouts.h"
13 #include "base/time/time.h"
14 #include "content/public/test/test_browser_thread_bundle.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
17 #include "third_party/webrtc/modules/desktop_capture/desktop_capture_options.h"
18 #include "third_party/webrtc/modules/desktop_capture/desktop_frame.h"
19 #include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
20 #include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
23 using ::testing::AnyNumber
;
24 using ::testing::DoAll
;
25 using ::testing::Expectation
;
26 using ::testing::Invoke
;
27 using ::testing::InvokeWithoutArgs
;
28 using ::testing::SaveArg
;
29 using ::testing::WithArg
;
35 MATCHER_P2(EqualsCaptureCapability
, width
, height
, "") {
36 return arg
.width
== width
&& arg
.height
== height
;
39 const int kTestFrameWidth1
= 500;
40 const int kTestFrameHeight1
= 500;
41 const int kTestFrameWidth2
= 400;
42 const int kTestFrameHeight2
= 300;
44 const int kFrameRate
= 30;
46 // The value of the padding bytes in unpacked frames.
47 const uint8_t kFramePaddingValue
= 0;
49 // Use a special value for frame pixels to tell pixel bytes apart from the
50 // padding bytes in the unpacked frame test.
51 const uint8_t kFakePixelValue
= 1;
53 // Use a special value for the first pixel to verify the result in the inverted
55 const uint8_t kFakePixelValueFirst
= 2;
57 class MockDeviceClient
: public media::VideoCaptureDevice::Client
{
59 MOCK_METHOD5(OnIncomingCapturedData
,
60 void(const uint8
* data
,
62 const media::VideoCaptureFormat
& frame_format
,
64 const base::TimeTicks
& timestamp
));
65 MOCK_METHOD9(OnIncomingCapturedYuvData
,
66 void (const uint8
* y_data
,
72 const media::VideoCaptureFormat
& frame_format
,
73 int clockwise_rotation
,
74 const base::TimeTicks
& timestamp
));
75 MOCK_METHOD0(DoReserveOutputBuffer
, void(void));
76 MOCK_METHOD0(DoOnIncomingCapturedBuffer
, void(void));
77 MOCK_METHOD0(DoOnIncomingCapturedVideoFrame
, void(void));
78 MOCK_METHOD1(OnError
, void(const std::string
& reason
));
80 // Trampoline methods to workaround GMOCK problems with scoped_ptr<>.
81 scoped_ptr
<Buffer
> ReserveOutputBuffer(
82 const gfx::Size
& dimensions
,
83 media::VideoCapturePixelFormat format
,
84 media::VideoPixelStorage storage
) override
{
85 EXPECT_TRUE(format
== media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
&&
86 storage
== media::PIXEL_STORAGE_CPU
);
87 DoReserveOutputBuffer();
88 return scoped_ptr
<Buffer
>();
90 void OnIncomingCapturedBuffer(scoped_ptr
<Buffer
> buffer
,
91 const media::VideoCaptureFormat
& frame_format
,
92 const base::TimeTicks
& timestamp
) override
{
93 DoOnIncomingCapturedBuffer();
95 void OnIncomingCapturedVideoFrame(
96 scoped_ptr
<Buffer
> buffer
,
97 const scoped_refptr
<media::VideoFrame
>& frame
,
98 const base::TimeTicks
& timestamp
) override
{
99 DoOnIncomingCapturedVideoFrame();
102 double GetBufferPoolUtilization() const override
{ return 0.0; }
105 // Creates a DesktopFrame that has the first pixel bytes set to
106 // kFakePixelValueFirst, and the rest of the bytes set to kFakePixelValue, for
107 // UnpackedFrame and InvertedFrame verification.
108 webrtc::BasicDesktopFrame
* CreateBasicFrame(const webrtc::DesktopSize
& size
) {
109 webrtc::BasicDesktopFrame
* frame
= new webrtc::BasicDesktopFrame(size
);;
110 DCHECK_EQ(frame
->size().width() * webrtc::DesktopFrame::kBytesPerPixel
,
112 memset(frame
->data(),
114 frame
->stride() * frame
->size().height());
115 memset(frame
->data(),
116 kFakePixelValueFirst
,
117 webrtc::DesktopFrame::kBytesPerPixel
);
121 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
123 class InvertedDesktopFrame
: public webrtc::DesktopFrame
{
125 // Takes ownership of |frame|.
126 explicit InvertedDesktopFrame(webrtc::DesktopFrame
* frame
)
127 : webrtc::DesktopFrame(
130 frame
->data() + (frame
->size().height() - 1) * frame
->stride(),
131 frame
->shared_memory()),
132 original_frame_(frame
) {
133 set_dpi(frame
->dpi());
134 set_capture_time_ms(frame
->capture_time_ms());
135 mutable_updated_region()->Swap(frame
->mutable_updated_region());
137 ~InvertedDesktopFrame() override
{}
140 scoped_ptr
<webrtc::DesktopFrame
> original_frame_
;
142 DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame
);
145 // DesktopFrame wrapper that copies the input frame and doubles the stride.
146 class UnpackedDesktopFrame
: public webrtc::DesktopFrame
{
148 // Takes ownership of |frame|.
149 explicit UnpackedDesktopFrame(webrtc::DesktopFrame
* frame
)
150 : webrtc::DesktopFrame(
153 new uint8_t[frame
->stride() * 2 * frame
->size().height()],
155 memset(data(), kFramePaddingValue
, stride() * size().height());
156 CopyPixelsFrom(*frame
,
157 webrtc::DesktopVector(),
158 webrtc::DesktopRect::MakeSize(size()));
161 ~UnpackedDesktopFrame() override
{
165 DISALLOW_COPY_AND_ASSIGN(UnpackedDesktopFrame
);
168 // TODO(sergeyu): Move this to a separate file where it can be reused.
169 class FakeScreenCapturer
: public webrtc::ScreenCapturer
{
174 generate_inverted_frames_(false),
175 generate_cropped_frames_(false) {
177 ~FakeScreenCapturer() override
{}
179 void set_generate_inverted_frames(bool generate_inverted_frames
) {
180 generate_inverted_frames_
= generate_inverted_frames
;
183 void set_generate_cropped_frames(bool generate_cropped_frames
) {
184 generate_cropped_frames_
= generate_cropped_frames
;
187 // VideoFrameCapturer interface.
188 void Start(Callback
* callback
) override
{ callback_
= callback
; }
190 void Capture(const webrtc::DesktopRegion
& region
) override
{
191 webrtc::DesktopSize size
;
192 if (frame_index_
% 2 == 0) {
193 size
= webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
);
195 size
= webrtc::DesktopSize(kTestFrameWidth2
, kTestFrameHeight2
);
199 webrtc::DesktopFrame
* frame
= CreateBasicFrame(size
);;
201 if (generate_inverted_frames_
) {
202 frame
= new InvertedDesktopFrame(frame
);
203 } else if (generate_cropped_frames_
) {
204 frame
= new UnpackedDesktopFrame(frame
);
206 callback_
->OnCaptureCompleted(frame
);
209 bool GetScreenList(ScreenList
* screens
) override
{ return false; }
211 bool SelectScreen(webrtc::ScreenId id
) override
{ return false; }
216 bool generate_inverted_frames_
;
217 bool generate_cropped_frames_
;
220 // Helper used to check that only two specific frame sizes are delivered to the
221 // OnIncomingCapturedData() callback.
222 class FormatChecker
{
224 FormatChecker(const gfx::Size
& size_for_even_frames
,
225 const gfx::Size
& size_for_odd_frames
)
226 : size_for_even_frames_(size_for_even_frames
),
227 size_for_odd_frames_(size_for_odd_frames
),
230 void ExpectAcceptableSize(const media::VideoCaptureFormat
& format
) {
231 if (frame_count_
% 2 == 0)
232 EXPECT_EQ(size_for_even_frames_
, format
.frame_size
);
234 EXPECT_EQ(size_for_odd_frames_
, format
.frame_size
);
236 EXPECT_EQ(kFrameRate
, format
.frame_rate
);
237 EXPECT_EQ(media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB
, format
.pixel_format
);
241 const gfx::Size size_for_even_frames_
;
242 const gfx::Size size_for_odd_frames_
;
248 class DesktopCaptureDeviceTest
: public testing::Test
{
250 void CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
> capturer
) {
251 capture_device_
.reset(
252 new DesktopCaptureDevice(capturer
.Pass(), DesktopMediaID::TYPE_SCREEN
));
255 void CopyFrame(const uint8_t* frame
, int size
,
256 const media::VideoCaptureFormat
&, int, base::TimeTicks
) {
257 ASSERT_TRUE(output_frame_
.get() != NULL
);
258 ASSERT_EQ(output_frame_
->stride() * output_frame_
->size().height(), size
);
259 memcpy(output_frame_
->data(), frame
, size
);
263 scoped_ptr
<DesktopCaptureDevice
> capture_device_
;
264 scoped_ptr
<webrtc::DesktopFrame
> output_frame_
;
267 // There is currently no screen capturer implementation for ozone. So disable
268 // the test that uses a real screen-capturer instead of FakeScreenCapturer.
269 // http://crbug.com/260318
270 #if defined(USE_OZONE)
271 #define MAYBE_Capture DISABLED_Capture
273 #define MAYBE_Capture Capture
275 TEST_F(DesktopCaptureDeviceTest
, MAYBE_Capture
) {
276 scoped_ptr
<webrtc::DesktopCapturer
> capturer(
277 webrtc::ScreenCapturer::Create(
278 webrtc::DesktopCaptureOptions::CreateDefault()));
279 CreateScreenCaptureDevice(capturer
.Pass());
281 media::VideoCaptureFormat format
;
282 base::WaitableEvent
done_event(false, false);
285 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
286 EXPECT_CALL(*client
, OnError(_
)).Times(0);
287 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
288 DoAll(SaveArg
<1>(&frame_size
),
290 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
292 media::VideoCaptureParams capture_params
;
293 capture_params
.requested_format
.frame_size
.SetSize(640, 480);
294 capture_params
.requested_format
.frame_rate
= kFrameRate
;
295 capture_params
.requested_format
.pixel_format
=
296 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
297 capture_device_
->AllocateAndStart(capture_params
, client
.Pass());
298 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
299 capture_device_
->StopAndDeAllocate();
301 EXPECT_GT(format
.frame_size
.width(), 0);
302 EXPECT_GT(format
.frame_size
.height(), 0);
303 EXPECT_EQ(kFrameRate
, format
.frame_rate
);
304 EXPECT_EQ(media::VIDEO_CAPTURE_PIXEL_FORMAT_ARGB
, format
.pixel_format
);
306 EXPECT_EQ(format
.frame_size
.GetArea() * 4, frame_size
);
309 // Test that screen capturer behaves correctly if the source frame size changes
310 // but the caller cannot cope with variable resolution output.
311 TEST_F(DesktopCaptureDeviceTest
, ScreenResolutionChangeConstantResolution
) {
312 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
314 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
316 FormatChecker
format_checker(gfx::Size(kTestFrameWidth1
, kTestFrameHeight1
),
317 gfx::Size(kTestFrameWidth1
, kTestFrameHeight1
));
318 base::WaitableEvent
done_event(false, false);
320 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
321 EXPECT_CALL(*client
, OnError(_
)).Times(0);
322 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
323 DoAll(WithArg
<2>(Invoke(&format_checker
,
324 &FormatChecker::ExpectAcceptableSize
)),
325 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
327 media::VideoCaptureParams capture_params
;
328 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth1
,
330 capture_params
.requested_format
.frame_rate
= kFrameRate
;
331 capture_params
.requested_format
.pixel_format
=
332 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
333 capture_params
.resolution_change_policy
=
334 media::RESOLUTION_POLICY_FIXED_RESOLUTION
;
336 capture_device_
->AllocateAndStart(capture_params
, client
.Pass());
338 // Capture at least two frames, to ensure that the source frame size has
339 // changed to two different sizes while capturing. The mock for
340 // OnIncomingCapturedData() will use FormatChecker to examine the format of
341 // each frame being delivered.
342 for (int i
= 0; i
< 2; ++i
) {
343 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
347 capture_device_
->StopAndDeAllocate();
350 // Test that screen capturer behaves correctly if the source frame size changes,
351 // where the video frames sent the the client vary in resolution but maintain
352 // the same aspect ratio.
353 TEST_F(DesktopCaptureDeviceTest
, ScreenResolutionChangeFixedAspectRatio
) {
354 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
356 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
358 FormatChecker
format_checker(gfx::Size(888, 500), gfx::Size(532, 300));
359 base::WaitableEvent
done_event(false, false);
361 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
362 EXPECT_CALL(*client
, OnError(_
)).Times(0);
363 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
364 DoAll(WithArg
<2>(Invoke(&format_checker
,
365 &FormatChecker::ExpectAcceptableSize
)),
366 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
368 media::VideoCaptureParams capture_params
;
369 const gfx::Size
high_def_16_by_9(1920, 1080);
370 ASSERT_GE(high_def_16_by_9
.width(),
371 std::max(kTestFrameWidth1
, kTestFrameWidth2
));
372 ASSERT_GE(high_def_16_by_9
.height(),
373 std::max(kTestFrameHeight1
, kTestFrameHeight2
));
374 capture_params
.requested_format
.frame_size
= high_def_16_by_9
;
375 capture_params
.requested_format
.frame_rate
= kFrameRate
;
376 capture_params
.requested_format
.pixel_format
=
377 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
378 capture_params
.resolution_change_policy
=
379 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO
;
381 capture_device_
->AllocateAndStart(
382 capture_params
, client
.Pass());
384 // Capture at least three frames, to ensure that the source frame size has
385 // changed to two different sizes while capturing. The mock for
386 // OnIncomingCapturedData() will use FormatChecker to examine the format of
387 // each frame being delivered.
388 for (int i
= 0; i
< 3; ++i
) {
389 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
393 capture_device_
->StopAndDeAllocate();
396 // Test that screen capturer behaves correctly if the source frame size changes
397 // and the caller can cope with variable resolution output.
398 TEST_F(DesktopCaptureDeviceTest
, ScreenResolutionChangeVariableResolution
) {
399 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
401 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
403 FormatChecker
format_checker(gfx::Size(kTestFrameWidth1
, kTestFrameHeight1
),
404 gfx::Size(kTestFrameWidth2
, kTestFrameHeight2
));
405 base::WaitableEvent
done_event(false, false);
407 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
408 EXPECT_CALL(*client
, OnError(_
)).Times(0);
409 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
410 DoAll(WithArg
<2>(Invoke(&format_checker
,
411 &FormatChecker::ExpectAcceptableSize
)),
412 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
414 media::VideoCaptureParams capture_params
;
415 const gfx::Size
high_def_16_by_9(1920, 1080);
416 ASSERT_GE(high_def_16_by_9
.width(),
417 std::max(kTestFrameWidth1
, kTestFrameWidth2
));
418 ASSERT_GE(high_def_16_by_9
.height(),
419 std::max(kTestFrameHeight1
, kTestFrameHeight2
));
420 capture_params
.requested_format
.frame_size
= high_def_16_by_9
;
421 capture_params
.requested_format
.frame_rate
= kFrameRate
;
422 capture_params
.requested_format
.pixel_format
=
423 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
424 capture_params
.resolution_change_policy
=
425 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT
;
427 capture_device_
->AllocateAndStart(
428 capture_params
, client
.Pass());
430 // Capture at least three frames, to ensure that the source frame size has
431 // changed to two different sizes while capturing. The mock for
432 // OnIncomingCapturedData() will use FormatChecker to examine the format of
433 // each frame being delivered.
434 for (int i
= 0; i
< 3; ++i
) {
435 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
439 capture_device_
->StopAndDeAllocate();
442 // This test verifies that an unpacked frame is converted to a packed frame.
443 TEST_F(DesktopCaptureDeviceTest
, UnpackedFrame
) {
444 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
445 mock_capturer
->set_generate_cropped_frames(true);
446 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
448 media::VideoCaptureFormat format
;
449 base::WaitableEvent
done_event(false, false);
452 output_frame_
.reset(new webrtc::BasicDesktopFrame(
453 webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
)));
455 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
456 EXPECT_CALL(*client
, OnError(_
)).Times(0);
457 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
458 DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame
),
459 SaveArg
<1>(&frame_size
),
460 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
462 media::VideoCaptureParams capture_params
;
463 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth1
,
465 capture_params
.requested_format
.frame_rate
= kFrameRate
;
466 capture_params
.requested_format
.pixel_format
=
467 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
469 capture_device_
->AllocateAndStart(capture_params
, client
.Pass());
471 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
473 capture_device_
->StopAndDeAllocate();
475 // Verifies that |output_frame_| has the same data as a packed frame of the
477 scoped_ptr
<webrtc::BasicDesktopFrame
> expected_frame(CreateBasicFrame(
478 webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
)));
479 EXPECT_EQ(output_frame_
->stride() * output_frame_
->size().height(),
482 0, memcmp(output_frame_
->data(), expected_frame
->data(), frame_size
));
485 // The test verifies that a bottom-to-top frame is converted to top-to-bottom.
486 TEST_F(DesktopCaptureDeviceTest
, InvertedFrame
) {
487 FakeScreenCapturer
* mock_capturer
= new FakeScreenCapturer();
488 mock_capturer
->set_generate_inverted_frames(true);
489 CreateScreenCaptureDevice(scoped_ptr
<webrtc::DesktopCapturer
>(mock_capturer
));
491 media::VideoCaptureFormat format
;
492 base::WaitableEvent
done_event(false, false);
495 output_frame_
.reset(new webrtc::BasicDesktopFrame(
496 webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
)));
498 scoped_ptr
<MockDeviceClient
> client(new MockDeviceClient());
499 EXPECT_CALL(*client
, OnError(_
)).Times(0);
500 EXPECT_CALL(*client
, OnIncomingCapturedData(_
, _
, _
, _
, _
)).WillRepeatedly(
501 DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame
),
502 SaveArg
<1>(&frame_size
),
503 InvokeWithoutArgs(&done_event
, &base::WaitableEvent::Signal
)));
505 media::VideoCaptureParams capture_params
;
506 capture_params
.requested_format
.frame_size
.SetSize(kTestFrameWidth1
,
508 capture_params
.requested_format
.frame_rate
= kFrameRate
;
509 capture_params
.requested_format
.pixel_format
=
510 media::VIDEO_CAPTURE_PIXEL_FORMAT_I420
;
512 capture_device_
->AllocateAndStart(capture_params
, client
.Pass());
514 EXPECT_TRUE(done_event
.TimedWait(TestTimeouts::action_max_timeout()));
516 capture_device_
->StopAndDeAllocate();
518 // Verifies that |output_frame_| has the same pixel values as the inverted
520 scoped_ptr
<webrtc::DesktopFrame
> inverted_frame(
521 new InvertedDesktopFrame(CreateBasicFrame(
522 webrtc::DesktopSize(kTestFrameWidth1
, kTestFrameHeight1
))));
523 EXPECT_EQ(output_frame_
->stride() * output_frame_
->size().height(),
525 for (int i
= 0; i
< output_frame_
->size().height(); ++i
) {
527 memcmp(inverted_frame
->data() + i
* inverted_frame
->stride(),
528 output_frame_
->data() + i
* output_frame_
->stride(),
529 output_frame_
->stride()));
533 } // namespace content