Re-subimission of https://codereview.chromium.org/1041213003/
[chromium-blink-merge.git] / content / browser / media / capture / desktop_capture_device_unittest.cc
blob48be8f11785cbec5721abc64ab6ad4b1855eed52
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 <string>
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"
21 using ::testing::_;
22 using ::testing::AnyNumber;
23 using ::testing::DoAll;
24 using ::testing::Expectation;
25 using ::testing::InvokeWithoutArgs;
26 using ::testing::SaveArg;
28 namespace content {
30 namespace {
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
51 // frame test.
52 const uint8_t kFakePixelValueFirst = 2;
54 class MockDeviceClient : public media::VideoCaptureDevice::Client {
55 public:
56 MOCK_METHOD5(OnIncomingCapturedData,
57 void(const uint8* data,
58 int length,
59 const media::VideoCaptureFormat& frame_format,
60 int rotation,
61 const base::TimeTicks& timestamp));
62 MOCK_METHOD9(OnIncomingCapturedYuvData,
63 void (const uint8* y_data,
64 const uint8* u_data,
65 const uint8* v_data,
66 size_t y_stride,
67 size_t u_stride,
68 size_t v_stride,
69 const media::VideoCaptureFormat& frame_format,
70 int clockwise_rotation,
71 const base::TimeTicks& timestamp));
72 MOCK_METHOD2(ReserveOutputBuffer,
73 scoped_refptr<Buffer>(media::VideoFrame::Format format,
74 const gfx::Size& dimensions));
75 MOCK_METHOD3(OnIncomingCapturedVideoFrame,
76 void(const scoped_refptr<Buffer>& buffer,
77 const scoped_refptr<media::VideoFrame>& frame,
78 const base::TimeTicks& timestamp));
79 MOCK_METHOD1(OnError, void(const std::string& reason));
82 // Creates a DesktopFrame that has the first pixel bytes set to
83 // kFakePixelValueFirst, and the rest of the bytes set to kFakePixelValue, for
84 // UnpackedFrame and InvertedFrame verification.
85 webrtc::BasicDesktopFrame* CreateBasicFrame(const webrtc::DesktopSize& size) {
86 webrtc::BasicDesktopFrame* frame = new webrtc::BasicDesktopFrame(size);;
87 DCHECK_EQ(frame->size().width() * webrtc::DesktopFrame::kBytesPerPixel,
88 frame->stride());
89 memset(frame->data(),
90 kFakePixelValue,
91 frame->stride() * frame->size().height());
92 memset(frame->data(),
93 kFakePixelValueFirst,
94 webrtc::DesktopFrame::kBytesPerPixel);
95 return frame;
98 // DesktopFrame wrapper that flips wrapped frame upside down by inverting
99 // stride.
100 class InvertedDesktopFrame : public webrtc::DesktopFrame {
101 public:
102 // Takes ownership of |frame|.
103 explicit InvertedDesktopFrame(webrtc::DesktopFrame* frame)
104 : webrtc::DesktopFrame(
105 frame->size(),
106 -frame->stride(),
107 frame->data() + (frame->size().height() - 1) * frame->stride(),
108 frame->shared_memory()),
109 original_frame_(frame) {
110 set_dpi(frame->dpi());
111 set_capture_time_ms(frame->capture_time_ms());
112 mutable_updated_region()->Swap(frame->mutable_updated_region());
114 ~InvertedDesktopFrame() override {}
116 private:
117 scoped_ptr<webrtc::DesktopFrame> original_frame_;
119 DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
122 // DesktopFrame wrapper that copies the input frame and doubles the stride.
123 class UnpackedDesktopFrame : public webrtc::DesktopFrame {
124 public:
125 // Takes ownership of |frame|.
126 explicit UnpackedDesktopFrame(webrtc::DesktopFrame* frame)
127 : webrtc::DesktopFrame(
128 frame->size(),
129 frame->stride() * 2,
130 new uint8_t[frame->stride() * 2 * frame->size().height()],
131 NULL) {
132 memset(data(), kFramePaddingValue, stride() * size().height());
133 CopyPixelsFrom(*frame,
134 webrtc::DesktopVector(),
135 webrtc::DesktopRect::MakeSize(size()));
136 delete frame;
138 ~UnpackedDesktopFrame() override {
139 delete[] data_;
142 DISALLOW_COPY_AND_ASSIGN(UnpackedDesktopFrame);
145 // TODO(sergeyu): Move this to a separate file where it can be reused.
146 class FakeScreenCapturer : public webrtc::ScreenCapturer {
147 public:
148 FakeScreenCapturer()
149 : callback_(NULL),
150 frame_index_(0),
151 generate_inverted_frames_(false),
152 generate_cropped_frames_(false) {
154 ~FakeScreenCapturer() override {}
156 void set_generate_inverted_frames(bool generate_inverted_frames) {
157 generate_inverted_frames_ = generate_inverted_frames;
160 void set_generate_cropped_frames(bool generate_cropped_frames) {
161 generate_cropped_frames_ = generate_cropped_frames;
164 // VideoFrameCapturer interface.
165 void Start(Callback* callback) override { callback_ = callback; }
167 void Capture(const webrtc::DesktopRegion& region) override {
168 webrtc::DesktopSize size;
169 if (frame_index_ % 2 == 0) {
170 size = webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1);
171 } else {
172 size = webrtc::DesktopSize(kTestFrameWidth2, kTestFrameHeight2);
174 frame_index_++;
176 webrtc::DesktopFrame* frame = CreateBasicFrame(size);;
178 if (generate_inverted_frames_) {
179 frame = new InvertedDesktopFrame(frame);
180 } else if (generate_cropped_frames_) {
181 frame = new UnpackedDesktopFrame(frame);
183 callback_->OnCaptureCompleted(frame);
186 bool GetScreenList(ScreenList* screens) override { return false; }
188 bool SelectScreen(webrtc::ScreenId id) override { return false; }
190 private:
191 Callback* callback_;
192 int frame_index_;
193 bool generate_inverted_frames_;
194 bool generate_cropped_frames_;
197 } // namespace
199 class DesktopCaptureDeviceTest : public testing::Test {
200 public:
201 void CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer> capturer) {
202 capture_device_.reset(
203 new DesktopCaptureDevice(capturer.Pass(), DesktopMediaID::TYPE_SCREEN));
206 void CopyFrame(const uint8_t* frame, int size,
207 const media::VideoCaptureFormat&, int, base::TimeTicks) {
208 ASSERT_TRUE(output_frame_.get() != NULL);
209 ASSERT_EQ(output_frame_->stride() * output_frame_->size().height(), size);
210 memcpy(output_frame_->data(), frame, size);
213 protected:
214 scoped_ptr<DesktopCaptureDevice> capture_device_;
215 scoped_ptr<webrtc::DesktopFrame> output_frame_;
218 // There is currently no screen capturer implementation for ozone. So disable
219 // the test that uses a real screen-capturer instead of FakeScreenCapturer.
220 // http://crbug.com/260318
221 #if defined(USE_OZONE)
222 #define MAYBE_Capture DISABLED_Capture
223 #else
224 #define MAYBE_Capture Capture
225 #endif
226 TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) {
227 scoped_ptr<webrtc::DesktopCapturer> capturer(
228 webrtc::ScreenCapturer::Create(
229 webrtc::DesktopCaptureOptions::CreateDefault()));
230 CreateScreenCaptureDevice(capturer.Pass());
232 media::VideoCaptureFormat format;
233 base::WaitableEvent done_event(false, false);
234 int frame_size;
236 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
237 EXPECT_CALL(*client, OnError(_)).Times(0);
238 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
239 DoAll(SaveArg<1>(&frame_size),
240 SaveArg<2>(&format),
241 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
243 media::VideoCaptureParams capture_params;
244 capture_params.requested_format.frame_size.SetSize(640, 480);
245 capture_params.requested_format.frame_rate = kFrameRate;
246 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
247 capture_device_->AllocateAndStart(capture_params, client.Pass());
248 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
249 capture_device_->StopAndDeAllocate();
251 EXPECT_GT(format.frame_size.width(), 0);
252 EXPECT_GT(format.frame_size.height(), 0);
253 EXPECT_EQ(kFrameRate, format.frame_rate);
254 EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
256 EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size);
259 // Test that screen capturer behaves correctly if the source frame size changes
260 // but the caller cannot cope with variable resolution output.
261 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) {
262 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
264 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
266 media::VideoCaptureFormat format;
267 base::WaitableEvent done_event(false, false);
268 int frame_size;
270 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
271 EXPECT_CALL(*client, OnError(_)).Times(0);
272 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
273 DoAll(SaveArg<1>(&frame_size),
274 SaveArg<2>(&format),
275 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
277 media::VideoCaptureParams capture_params;
278 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
279 kTestFrameHeight1);
280 capture_params.requested_format.frame_rate = kFrameRate;
281 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
283 capture_device_->AllocateAndStart(capture_params, client.Pass());
285 // Capture at least two frames, to ensure that the source frame size has
286 // changed while capturing.
287 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
288 done_event.Reset();
289 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
291 capture_device_->StopAndDeAllocate();
293 EXPECT_EQ(kTestFrameWidth1, format.frame_size.width());
294 EXPECT_EQ(kTestFrameHeight1, format.frame_size.height());
295 EXPECT_EQ(kFrameRate, format.frame_rate);
296 EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
298 EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size);
301 // Test that screen capturer behaves correctly if the source frame size changes
302 // and the caller can cope with variable resolution output.
303 TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) {
304 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
306 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
308 media::VideoCaptureFormat format;
309 base::WaitableEvent done_event(false, false);
311 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
312 EXPECT_CALL(*client, OnError(_)).Times(0);
313 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
314 DoAll(SaveArg<2>(&format),
315 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
317 media::VideoCaptureParams capture_params;
318 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth2,
319 kTestFrameHeight2);
320 capture_params.requested_format.frame_rate = kFrameRate;
321 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
323 capture_device_->AllocateAndStart(
324 capture_params, client.Pass());
326 // Capture at least three frames, to ensure that the source frame size has
327 // changed at least twice while capturing.
328 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
329 done_event.Reset();
330 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
331 done_event.Reset();
332 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
334 capture_device_->StopAndDeAllocate();
336 EXPECT_EQ(kTestFrameWidth1, format.frame_size.width());
337 EXPECT_EQ(kTestFrameHeight1, format.frame_size.height());
338 EXPECT_EQ(kFrameRate, format.frame_rate);
339 EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
342 // This test verifies that an unpacked frame is converted to a packed frame.
343 TEST_F(DesktopCaptureDeviceTest, UnpackedFrame) {
344 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
345 mock_capturer->set_generate_cropped_frames(true);
346 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
348 media::VideoCaptureFormat format;
349 base::WaitableEvent done_event(false, false);
351 int frame_size = 0;
352 output_frame_.reset(new webrtc::BasicDesktopFrame(
353 webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1)));
355 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
356 EXPECT_CALL(*client, OnError(_)).Times(0);
357 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
358 DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame),
359 SaveArg<1>(&frame_size),
360 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
362 media::VideoCaptureParams capture_params;
363 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
364 kTestFrameHeight1);
365 capture_params.requested_format.frame_rate = kFrameRate;
366 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
368 capture_device_->AllocateAndStart(capture_params, client.Pass());
370 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
371 done_event.Reset();
372 capture_device_->StopAndDeAllocate();
374 // Verifies that |output_frame_| has the same data as a packed frame of the
375 // same size.
376 scoped_ptr<webrtc::BasicDesktopFrame> expected_frame(CreateBasicFrame(
377 webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1)));
378 EXPECT_EQ(output_frame_->stride() * output_frame_->size().height(),
379 frame_size);
380 EXPECT_EQ(
381 0, memcmp(output_frame_->data(), expected_frame->data(), frame_size));
384 // The test verifies that a bottom-to-top frame is converted to top-to-bottom.
385 TEST_F(DesktopCaptureDeviceTest, InvertedFrame) {
386 FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
387 mock_capturer->set_generate_inverted_frames(true);
388 CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
390 media::VideoCaptureFormat format;
391 base::WaitableEvent done_event(false, false);
393 int frame_size = 0;
394 output_frame_.reset(new webrtc::BasicDesktopFrame(
395 webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1)));
397 scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
398 EXPECT_CALL(*client, OnError(_)).Times(0);
399 EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
400 DoAll(Invoke(this, &DesktopCaptureDeviceTest::CopyFrame),
401 SaveArg<1>(&frame_size),
402 InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
404 media::VideoCaptureParams capture_params;
405 capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
406 kTestFrameHeight1);
407 capture_params.requested_format.frame_rate = kFrameRate;
408 capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
410 capture_device_->AllocateAndStart(capture_params, client.Pass());
412 EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
413 done_event.Reset();
414 capture_device_->StopAndDeAllocate();
416 // Verifies that |output_frame_| has the same pixel values as the inverted
417 // frame.
418 scoped_ptr<webrtc::DesktopFrame> inverted_frame(
419 new InvertedDesktopFrame(CreateBasicFrame(
420 webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1))));
421 EXPECT_EQ(output_frame_->stride() * output_frame_->size().height(),
422 frame_size);
423 for (int i = 0; i < output_frame_->size().height(); ++i) {
424 EXPECT_EQ(0,
425 memcmp(inverted_frame->data() + i * inverted_frame->stride(),
426 output_frame_->data() + i * output_frame_->stride(),
427 output_frame_->stride()));
431 } // namespace content