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.
4
5#include "content/browser/media/capture/desktop_capture_device.h"
6
7#include <string>
8
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_frame.h"
17#include "third_party/webrtc/modules/desktop_capture/desktop_geometry.h"
18#include "third_party/webrtc/modules/desktop_capture/screen_capturer.h"
19
20using ::testing::_;
21using ::testing::AnyNumber;
22using ::testing::DoAll;
23using ::testing::Expectation;
24using ::testing::InvokeWithoutArgs;
25using ::testing::SaveArg;
26
27namespace content {
28
29namespace {
30
31MATCHER_P2(EqualsCaptureCapability, width, height, "") {
32  return arg.width == width && arg.height == height;
33}
34
35const int kTestFrameWidth1 = 100;
36const int kTestFrameHeight1 = 100;
37const int kTestFrameWidth2 = 200;
38const int kTestFrameHeight2 = 150;
39
40const int kFrameRate = 30;
41
42class MockDeviceClient : public media::VideoCaptureDevice::Client {
43 public:
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,
50                    int length,
51                    const media::VideoCaptureFormat& frame_format,
52                    int rotation,
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));
59};
60
61// DesktopFrame wrapper that flips wrapped frame upside down by inverting
62// stride.
63class InvertedDesktopFrame : public webrtc::DesktopFrame {
64 public:
65  // Takes ownership of |frame|.
66  explicit InvertedDesktopFrame(webrtc::DesktopFrame* frame)
67      : webrtc::DesktopFrame(
68            frame->size(),
69            -frame->stride(),
70            frame->data() + (frame->size().height() - 1) * frame->stride(),
71            frame->shared_memory()),
72        original_frame_(frame) {
73    set_dpi(frame->dpi());
74    set_capture_time_ms(frame->capture_time_ms());
75    mutable_updated_region()->Swap(frame->mutable_updated_region());
76  }
77  virtual ~InvertedDesktopFrame() {}
78
79 private:
80  scoped_ptr<webrtc::DesktopFrame> original_frame_;
81
82  DISALLOW_COPY_AND_ASSIGN(InvertedDesktopFrame);
83};
84
85// TODO(sergeyu): Move this to a separate file where it can be reused.
86class FakeScreenCapturer : public webrtc::ScreenCapturer {
87 public:
88  FakeScreenCapturer()
89      : callback_(NULL),
90        frame_index_(0),
91        generate_inverted_frames_(false) {
92  }
93  virtual ~FakeScreenCapturer() {}
94
95  void set_generate_inverted_frames(bool generate_inverted_frames) {
96    generate_inverted_frames_ = generate_inverted_frames;
97  }
98
99  // VideoFrameCapturer interface.
100  virtual void Start(Callback* callback) OVERRIDE {
101    callback_ = callback;
102  }
103
104  virtual void Capture(const webrtc::DesktopRegion& region) OVERRIDE {
105    webrtc::DesktopSize size;
106    if (frame_index_ % 2 == 0) {
107      size = webrtc::DesktopSize(kTestFrameWidth1, kTestFrameHeight1);
108    } else {
109      size = webrtc::DesktopSize(kTestFrameWidth2, kTestFrameHeight2);
110    }
111    frame_index_++;
112
113    webrtc::DesktopFrame* frame = new webrtc::BasicDesktopFrame(size);
114    if (generate_inverted_frames_)
115      frame = new InvertedDesktopFrame(frame);
116    callback_->OnCaptureCompleted(frame);
117  }
118
119  virtual void SetMouseShapeObserver(
120      MouseShapeObserver* mouse_shape_observer) OVERRIDE {
121  }
122
123  virtual bool GetScreenList(ScreenList* screens) OVERRIDE {
124    return false;
125  }
126
127  virtual bool SelectScreen(webrtc::ScreenId id) OVERRIDE {
128    return false;
129  }
130
131 private:
132  Callback* callback_;
133  int frame_index_;
134  bool generate_inverted_frames_;
135};
136
137}  // namespace
138
139class DesktopCaptureDeviceTest : public testing::Test {
140 public:
141  void CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer> capturer) {
142    capture_device_.reset(
143        new DesktopCaptureDevice(capturer.Pass(), DesktopMediaID::TYPE_SCREEN));
144  }
145
146 protected:
147  scoped_ptr<DesktopCaptureDevice> capture_device_;
148};
149
150// There is currently no screen capturer implementation for ozone. So disable
151// the test that uses a real screen-capturer instead of FakeScreenCapturer.
152// http://crbug.com/260318
153#if defined(USE_OZONE)
154#define MAYBE_Capture DISABLED_Capture
155#else
156#define MAYBE_Capture Capture
157#endif
158TEST_F(DesktopCaptureDeviceTest, MAYBE_Capture) {
159  scoped_ptr<webrtc::DesktopCapturer> capturer(
160      webrtc::ScreenCapturer::Create());
161  CreateScreenCaptureDevice(capturer.Pass());
162
163  media::VideoCaptureFormat format;
164  base::WaitableEvent done_event(false, false);
165  int frame_size;
166
167  scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
168  EXPECT_CALL(*client, OnError(_)).Times(0);
169  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
170      DoAll(SaveArg<1>(&frame_size),
171            SaveArg<2>(&format),
172            InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
173
174  media::VideoCaptureParams capture_params;
175  capture_params.requested_format.frame_size.SetSize(640, 480);
176  capture_params.requested_format.frame_rate = kFrameRate;
177  capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
178  capture_device_->AllocateAndStart(
179      capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
180  EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
181  capture_device_->StopAndDeAllocate();
182
183  EXPECT_GT(format.frame_size.width(), 0);
184  EXPECT_GT(format.frame_size.height(), 0);
185  EXPECT_EQ(kFrameRate, format.frame_rate);
186  EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
187
188  EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size);
189}
190
191// Test that screen capturer behaves correctly if the source frame size changes
192// but the caller cannot cope with variable resolution output.
193TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeConstantResolution) {
194  FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
195
196  CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
197
198  media::VideoCaptureFormat format;
199  base::WaitableEvent done_event(false, false);
200  int frame_size;
201
202  scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
203  EXPECT_CALL(*client, OnError(_)).Times(0);
204  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
205      DoAll(SaveArg<1>(&frame_size),
206            SaveArg<2>(&format),
207            InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
208
209  media::VideoCaptureParams capture_params;
210  capture_params.requested_format.frame_size.SetSize(kTestFrameWidth1,
211                                                     kTestFrameHeight1);
212  capture_params.requested_format.frame_rate = kFrameRate;
213  capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
214
215  capture_device_->AllocateAndStart(
216      capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
217
218  // Capture at least two frames, to ensure that the source frame size has
219  // changed while capturing.
220  EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
221  done_event.Reset();
222  EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
223
224  capture_device_->StopAndDeAllocate();
225
226  EXPECT_EQ(kTestFrameWidth1, format.frame_size.width());
227  EXPECT_EQ(kTestFrameHeight1, format.frame_size.height());
228  EXPECT_EQ(kFrameRate, format.frame_rate);
229  EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
230
231  EXPECT_EQ(format.frame_size.GetArea() * 4, frame_size);
232}
233
234// Test that screen capturer behaves correctly if the source frame size changes
235// and the caller can cope with variable resolution output.
236TEST_F(DesktopCaptureDeviceTest, ScreenResolutionChangeVariableResolution) {
237  FakeScreenCapturer* mock_capturer = new FakeScreenCapturer();
238
239  CreateScreenCaptureDevice(scoped_ptr<webrtc::DesktopCapturer>(mock_capturer));
240
241  media::VideoCaptureFormat format;
242  base::WaitableEvent done_event(false, false);
243
244  scoped_ptr<MockDeviceClient> client(new MockDeviceClient());
245  EXPECT_CALL(*client, OnError(_)).Times(0);
246  EXPECT_CALL(*client, OnIncomingCapturedData(_, _, _, _, _)).WillRepeatedly(
247      DoAll(SaveArg<2>(&format),
248            InvokeWithoutArgs(&done_event, &base::WaitableEvent::Signal)));
249
250  media::VideoCaptureParams capture_params;
251  capture_params.requested_format.frame_size.SetSize(kTestFrameWidth2,
252                                                     kTestFrameHeight2);
253  capture_params.requested_format.frame_rate = kFrameRate;
254  capture_params.requested_format.pixel_format = media::PIXEL_FORMAT_I420;
255
256  capture_device_->AllocateAndStart(
257      capture_params, client.PassAs<media::VideoCaptureDevice::Client>());
258
259  // Capture at least three frames, to ensure that the source frame size has
260  // changed at least twice while capturing.
261  EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
262  done_event.Reset();
263  EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
264  done_event.Reset();
265  EXPECT_TRUE(done_event.TimedWait(TestTimeouts::action_max_timeout()));
266
267  capture_device_->StopAndDeAllocate();
268
269  EXPECT_EQ(kTestFrameWidth1, format.frame_size.width());
270  EXPECT_EQ(kTestFrameHeight1, format.frame_size.height());
271  EXPECT_EQ(kFrameRate, format.frame_rate);
272  EXPECT_EQ(media::PIXEL_FORMAT_ARGB, format.pixel_format);
273}
274
275}  // namespace content
276