Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / content / browser / media / capture / desktop_capture_device_unittest.cc
blobafaac4456989e953024c1033317541f3c8ab68b2
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 <algorithm>
8 #include <string>
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"
22 using ::testing::_;
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;
31 namespace content {
33 namespace {
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
54 // frame test.
55 const uint8_t kFakePixelValueFirst = 2;
57 class MockDeviceClient : public media::VideoCaptureDevice::Client {
58 public:
59 MOCK_METHOD5(OnIncomingCapturedData,
60 void(const uint8* data,
61 int length,
62 const media::VideoCaptureFormat& frame_format,
63 int rotation,
64 const base::TimeTicks& timestamp));
65 MOCK_METHOD9(OnIncomingCapturedYuvData,
66 void (const uint8* y_data,
67 const uint8* u_data,
68 const uint8* v_data,
69 size_t y_stride,
70 size_t u_stride,
71 size_t v_stride,
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::VideoPixelFormat format,
84 media::VideoPixelStorage storage) override {
85 EXPECT_TRUE(format == media::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,
111 frame->stride());
112 memset(frame->data(),
113 kFakePixelValue,
114 frame->stride() * frame->size().height());
115 memset(frame->data(),
116 kFakePixelValueFirst,
117 webrtc::DesktopFrame::kBytesPerPixel);
118 return frame;
121 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
122 // stride.
123 class InvertedDesktopFrame : public webrtc::DesktopFrame {
124 public:
125 // Takes ownership of |frame|.
126 explicit InvertedDesktopFrame(webrtc::DesktopFrame* frame)
127 : webrtc::DesktopFrame(
128 frame->size(),
129 -frame->stride(),
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 {}
139 private:
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 {
147 public:
148 // Takes ownership of |frame|.
149 explicit UnpackedDesktopFrame(webrtc::DesktopFrame* frame)
150 : webrtc::DesktopFrame(
151 frame->size(),
152 frame->stride() * 2,
153 new uint8_t[frame->stride() * 2 * frame->size().height()],
154 NULL) {
155 memset(data(), kFramePaddingValue, stride() * size().height());
156 CopyPixelsFrom(*frame,
157 webrtc::DesktopVector(),
158 webrtc::DesktopRect::MakeSize(size()));
159 delete frame;
161 ~UnpackedDesktopFrame() override {
162 delete[] data_;
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 {
170 public:
171 FakeScreenCapturer()
172 : callback_(NULL),
173 frame_index_(0),
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);
194 } else {
195 size = webrtc::DesktopSize(kTestFrameWidth2, kTestFrameHeight2);
197 frame_index_++;
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; }
213 private:
214 Callback* callback_;
215 int frame_index_;
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 {
223 public:
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),
228 frame_count_(0) {}
230 void ExpectAcceptableSize(const media::VideoCaptureFormat& format) {
231 if (frame_count_ % 2 == 0)
232 EXPECT_EQ(size_for_even_frames_, format.frame_size);
233 else
234 EXPECT_EQ(size_for_odd_frames_, format.frame_size);
235 ++frame_count_;
236 EXPECT_EQ(kFrameRate, format.frame_rate);
237 EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
240 private:
241 const gfx::Size size_for_even_frames_;
242 const gfx::Size size_for_odd_frames_;
243 int frame_count_;
246 } // namespace
248 class DesktopCaptureDeviceTest : public testing::Test {
249 public:
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);
262 protected:
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
272 #else
273 #define MAYBE_Capture Capture
274 #endif
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);
283 int frame_size;
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),
289 SaveArg<2>(&format),
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 = media::PIXEL_FORMAT_I420;
296 capture_device_->AllocateAndStart(capture_params, client.Pass());
297 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
298 capture_device_->StopAndDeAllocate();
300 EXPECT_GT(format.frame_size.width(), 0);
301 EXPECT_GT(format.frame_size.height(), 0);
302 EXPECT_EQ(kFrameRate, format.frame_rate);
303 EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
305 EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size);
308 // Test that screen capturer behaves correctly if the source frame size changes
309 // but the caller cannot cope with variable resolution output.
310 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) {
311 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
313 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
315 FormatChecker format_checker(gfx::Size(kTestFrameWidth1, kTestFrameHeight1),
316 gfx::Size(kTestFrameWidth1, kTestFrameHeight1));
317 base::WaitableEvent done_event(false, false);
319 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
320 EXPECT_CALL(*client, OnError(_)).Times(0);
321 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
322 DoAll(WithArg<2>(Invoke(&format_checker,
323 &FormatChecker::ExpectAcceptableSize)),
324 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
326 media::VideoCaptureParams capture_params;
327 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
328 kTestFrameHeight1);
329 capture_params.requested_format.frame_rate = kFrameRate;
330 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
331 capture_params.resolution_change_policy =
332 media::RESOLUTION_POLICY_FIXED_RESOLUTION;
334 capture_device_->AllocateAndStart(capture_params, client.Pass());
336 // Capture at least two frames, to ensure that the source frame size has
337 // changed to two different sizes while capturing. The mock for
338 // OnIncomingCapturedData() will use FormatChecker to examine the format of
339 // each frame being delivered.
340 for (int i = 0; i < 2; ++i) {
341 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
342 done_event.Reset();
345 capture_device_->StopAndDeAllocate();
348 // Test that screen capturer behaves correctly if the source frame size changes,
349 // where the video frames sent the the client vary in resolution but maintain
350 // the same aspect ratio.
351 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeFixedAspectRatio) {
352 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
354 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
356 FormatChecker format_checker(gfx::Size(888, 500), gfx::Size(532, 300));
357 base::WaitableEvent done_event(false, false);
359 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
360 EXPECT_CALL(*client, OnError(_)).Times(0);
361 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
362 DoAll(WithArg<2>(Invoke(&format_checker,
363 &FormatChecker::ExpectAcceptableSize)),
364 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
366 media::VideoCaptureParams capture_params;
367 const gfx::Size high_def_16_by_9(1920, 1080);
368 ASSERT_GE(high_def_16_by_9.width(),
369 std::max(kTestFrameWidth1, kTestFrameWidth2));
370 ASSERT_GE(high_def_16_by_9.height(),
371 std::max(kTestFrameHeight1, kTestFrameHeight2));
372 capture_params.requested_format.frame_size = high_def_16_by_9;
373 capture_params.requested_format.frame_rate = kFrameRate;
374 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
375 capture_params.resolution_change_policy =
376 media::RESOLUTION_POLICY_FIXED_ASPECT_RATIO;
378 capture_device_->AllocateAndStart(
379 capture_params, client.Pass());
381 // Capture at least three frames, to ensure that the source frame size has
382 // changed to two different sizes while capturing. The mock for
383 // OnIncomingCapturedData() will use FormatChecker to examine the format of
384 // each frame being delivered.
385 for (int i = 0; i < 3; ++i) {
386 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
387 done_event.Reset();
390 capture_device_->StopAndDeAllocate();
393 // Test that screen capturer behaves correctly if the source frame size changes
394 // and the caller can cope with variable resolution output.
395 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) {
396 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
398 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
400 FormatChecker format_checker(gfx::Size(kTestFrameWidth1, kTestFrameHeight1),
401 gfx::Size(kTestFrameWidth2, kTestFrameHeight2));
402 base::WaitableEvent done_event(false, false);
404 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
405 EXPECT_CALL(*client, OnError(_)).Times(0);
406 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
407 DoAll(WithArg<2>(Invoke(&format_checker,
408 &FormatChecker::ExpectAcceptableSize)),
409 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
411 media::VideoCaptureParams capture_params;
412 const gfx::Size high_def_16_by_9(1920, 1080);
413 ASSERT_GE(high_def_16_by_9.width(),
414 std::max(kTestFrameWidth1, kTestFrameWidth2));
415 ASSERT_GE(high_def_16_by_9.height(),
416 std::max(kTestFrameHeight1, kTestFrameHeight2));
417 capture_params.requested_format.frame_size = high_def_16_by_9;
418 capture_params.requested_format.frame_rate = kFrameRate;
419 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
420 capture_params.resolution_change_policy =
421 media::RESOLUTION_POLICY_ANY_WITHIN_LIMIT;
423 capture_device_->AllocateAndStart(
424 capture_params, client.Pass());
426 // Capture at least three frames, to ensure that the source frame size has
427 // changed to two different sizes while capturing. The mock for
428 // OnIncomingCapturedData() will use FormatChecker to examine the format of
429 // each frame being delivered.
430 for (int i = 0; i < 3; ++i) {
431 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
432 done_event.Reset();
435 capture_device_->StopAndDeAllocate();
438 // This test verifies that an unpacked frame is converted to a packed frame.
439 TEST_F(DesktopCaptureDeviceTest, UnpackedFrame) {
440 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
441 mock_capturer->set_generate_cropped_frames(true);
442 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
444 media::VideoCaptureFormat format;
445 base::WaitableEvent done_event(false, false);
447 int frame_size = 0;
448 output_frame_.reset(new webrtc::BasicDesktopFrame(
449 webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1)));
451 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
452 EXPECT_CALL(*client, OnError(_)).Times(0);
453 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
454 DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame),
455 SaveArg<1>(&frame_size),
456 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
458 media::VideoCaptureParams capture_params;
459 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
460 kTestFrameHeight1);
461 capture_params.requested_format.frame_rate = kFrameRate;
462 capture_params.requested_format.pixel_format =
463 media::PIXEL_FORMAT_I420;
465 capture_device_->AllocateAndStart(capture_params, client.Pass());
467 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
468 done_event.Reset();
469 capture_device_->StopAndDeAllocate();
471 // Verifies that |output_frame_| has the same data as a packed frame of the
472 // same size.
473 scoped_ptr<webrtc::BasicDesktopFrame> expected_frame(CreateBasicFrame(
474 webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1)));
475 EXPECT_EQ(output_frame_->stride() * output_frame_->size().height(),
476 frame_size);
477 EXPECT_EQ(
478 0, memcmp(output_frame_->data(), expected_frame->data(), frame_size));
481 // The test verifies that a bottom-to-top frame is converted to top-to-bottom.
482 TEST_F(DesktopCaptureDeviceTest, InvertedFrame) {
483 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
484 mock_capturer->set_generate_inverted_frames(true);
485 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
487 media::VideoCaptureFormat format;
488 base::WaitableEvent done_event(false, false);
490 int frame_size = 0;
491 output_frame_.reset(new webrtc::BasicDesktopFrame(
492 webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1)));
494 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
495 EXPECT_CALL(*client, OnError(_)).Times(0);
496 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
497 DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame),
498 SaveArg<1>(&frame_size),
499 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
501 media::VideoCaptureParams capture_params;
502 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
503 kTestFrameHeight1);
504 capture_params.requested_format.frame_rate = kFrameRate;
505 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
507 capture_device_->AllocateAndStart(capture_params, client.Pass());
509 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
510 done_event.Reset();
511 capture_device_->StopAndDeAllocate();
513 // Verifies that |output_frame_| has the same pixel values as the inverted
514 // frame.
515 scoped_ptr<webrtc::DesktopFrame> inverted_frame(
516 new InvertedDesktopFrame(CreateBasicFrame(
517 webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1))));
518 EXPECT_EQ(output_frame_->stride() * output_frame_->size().height(),
519 frame_size);
520 for (int i = 0; i < output_frame_->size().height(); ++i) {
521 EXPECT_EQ(0,
522 memcmp(inverted_frame->data() + i * inverted_frame->stride(),
523 output_frame_->data() + i * output_frame_->stride(),
524 output_frame_->stride()));
528 } // namespace content