1// Copyright (c) 2012 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 "base/bind.h"
6#include "base/bind_helpers.h"
7#include "base/memory/ref_counted.h"
8#include "base/memory/scoped_ptr.h"
9#include "base/message_loop/message_loop_proxy.h"
10#include "base/run_loop.h"
11#include "base/test/test_timeouts.h"
12#include "base/threading/thread.h"
13#include "media/video/capture/video_capture_device.h"
14#include "media/video/capture/video_capture_device_factory.h"
15#include "media/video/capture/video_capture_types.h"
16#include "testing/gmock/include/gmock/gmock.h"
17#include "testing/gtest/include/gtest/gtest.h"
18
19#if defined(OS_WIN)
20#include "base/win/scoped_com_initializer.h"
21#include "media/video/capture/win/video_capture_device_factory_win.h"
22#endif
23
24#if defined(OS_MACOSX)
25#include "media/video/capture/mac/video_capture_device_factory_mac.h"
26#endif
27
28#if defined(OS_ANDROID)
29#include "base/android/jni_android.h"
30#include "media/video/capture/android/video_capture_device_android.h"
31#endif
32
33#if defined(OS_MACOSX)
34// Mac/QTKit will always give you the size you ask for and this case will fail.
35#define MAYBE_AllocateBadSize DISABLED_AllocateBadSize
36// We will always get YUYV from the Mac QTKit/AVFoundation implementations.
37#define MAYBE_CaptureMjpeg DISABLED_CaptureMjpeg
38#elif defined(OS_WIN)
39#define MAYBE_AllocateBadSize AllocateBadSize
40// Windows currently uses DirectShow to convert from MJPEG and a raw format is
41// always delivered.
42#define MAYBE_CaptureMjpeg DISABLED_CaptureMjpeg
43#elif defined(OS_ANDROID)
44// TODO(wjia): enable those tests on Android.
45// On Android, native camera (JAVA) delivers frames on UI thread which is the
46// main thread for tests. This results in no frame received by
47// VideoCaptureAndroid.
48#define CaptureVGA DISABLED_CaptureVGA
49#define Capture720p DISABLED_Capture720p
50#define MAYBE_AllocateBadSize DISABLED_AllocateBadSize
51#define ReAllocateCamera DISABLED_ReAllocateCamera
52#define DeAllocateCameraWhileRunning DISABLED_DeAllocateCameraWhileRunning
53#define DeAllocateCameraWhileRunning DISABLED_DeAllocateCameraWhileRunning
54#define MAYBE_CaptureMjpeg DISABLED_CaptureMjpeg
55#else
56#define MAYBE_AllocateBadSize AllocateBadSize
57#define MAYBE_CaptureMjpeg CaptureMjpeg
58#endif
59
60using ::testing::_;
61using ::testing::SaveArg;
62
63namespace media {
64
65class MockClient : public media::VideoCaptureDevice::Client {
66 public:
67  MOCK_METHOD2(ReserveOutputBuffer,
68               scoped_refptr<Buffer>(media::VideoFrame::Format format,
69                                     const gfx::Size& dimensions));
70  MOCK_METHOD0(OnErr, void());
71
72  explicit MockClient(base::Callback<void(const VideoCaptureFormat&)> frame_cb)
73      : main_thread_(base::MessageLoopProxy::current()), frame_cb_(frame_cb) {}
74
75  virtual void OnError(const std::string& error_message) OVERRIDE {
76    OnErr();
77  }
78
79  virtual void OnIncomingCapturedData(const uint8* data,
80                                      int length,
81                                      const VideoCaptureFormat& format,
82                                      int rotation,
83                                      base::TimeTicks timestamp) OVERRIDE {
84    main_thread_->PostTask(FROM_HERE, base::Bind(frame_cb_, format));
85  }
86
87  virtual void OnIncomingCapturedVideoFrame(
88      const scoped_refptr<Buffer>& buffer,
89      const media::VideoCaptureFormat& buffer_format,
90      const scoped_refptr<media::VideoFrame>& frame,
91      base::TimeTicks timestamp) OVERRIDE {
92    NOTREACHED();
93  }
94
95 private:
96  scoped_refptr<base::SingleThreadTaskRunner> main_thread_;
97  base::Callback<void(const VideoCaptureFormat&)> frame_cb_;
98};
99
100class DeviceEnumerationListener :
101    public base::RefCounted<DeviceEnumerationListener>{
102 public:
103  MOCK_METHOD1(OnEnumeratedDevicesCallbackPtr,
104               void(media::VideoCaptureDevice::Names* names));
105  // GMock doesn't support move-only arguments, so we use this forward method.
106  void OnEnumeratedDevicesCallback(
107      scoped_ptr<media::VideoCaptureDevice::Names> names) {
108    OnEnumeratedDevicesCallbackPtr(names.release());
109  }
110 private:
111  friend class base::RefCounted<DeviceEnumerationListener>;
112  virtual ~DeviceEnumerationListener() {}
113};
114
115class VideoCaptureDeviceTest : public testing::Test {
116 protected:
117  typedef media::VideoCaptureDevice::Client Client;
118
119  VideoCaptureDeviceTest()
120      : loop_(new base::MessageLoop()),
121        client_(
122            new MockClient(base::Bind(&VideoCaptureDeviceTest::OnFrameCaptured,
123                                      base::Unretained(this)))),
124        video_capture_device_factory_(VideoCaptureDeviceFactory::CreateFactory(
125            base::MessageLoopProxy::current())) {
126    device_enumeration_listener_ = new DeviceEnumerationListener();
127  }
128
129  virtual void SetUp() {
130#if defined(OS_ANDROID)
131    media::VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(
132        base::android::AttachCurrentThread());
133#endif
134  }
135
136  void ResetWithNewClient() {
137    client_.reset(new MockClient(base::Bind(
138        &VideoCaptureDeviceTest::OnFrameCaptured, base::Unretained(this))));
139  }
140
141  void OnFrameCaptured(const VideoCaptureFormat& format) {
142    last_format_ = format;
143    run_loop_->QuitClosure().Run();
144  }
145
146  void WaitForCapturedFrame() {
147    run_loop_.reset(new base::RunLoop());
148    run_loop_->Run();
149  }
150
151  scoped_ptr<media::VideoCaptureDevice::Names> EnumerateDevices() {
152    media::VideoCaptureDevice::Names* names;
153    EXPECT_CALL(*device_enumeration_listener_.get(),
154                OnEnumeratedDevicesCallbackPtr(_)).WillOnce(SaveArg<0>(&names));
155
156    video_capture_device_factory_->EnumerateDeviceNames(
157        base::Bind(&DeviceEnumerationListener::OnEnumeratedDevicesCallback,
158                   device_enumeration_listener_));
159    base::MessageLoop::current()->RunUntilIdle();
160    return scoped_ptr<media::VideoCaptureDevice::Names>(names);
161  }
162
163  const VideoCaptureFormat& last_format() const { return last_format_; }
164
165  scoped_ptr<VideoCaptureDevice::Name> GetFirstDeviceNameSupportingPixelFormat(
166      const VideoPixelFormat& pixel_format) {
167    names_ = EnumerateDevices();
168    if (!names_->size()) {
169      DVLOG(1) << "No camera available.";
170      return scoped_ptr<VideoCaptureDevice::Name>();
171    }
172    VideoCaptureDevice::Names::iterator names_iterator;
173    for (names_iterator = names_->begin(); names_iterator != names_->end();
174         ++names_iterator) {
175      VideoCaptureFormats supported_formats;
176      video_capture_device_factory_->GetDeviceSupportedFormats(
177          *names_iterator,
178          &supported_formats);
179      VideoCaptureFormats::iterator formats_iterator;
180      for (formats_iterator = supported_formats.begin();
181           formats_iterator != supported_formats.end(); ++formats_iterator) {
182        if (formats_iterator->pixel_format == pixel_format) {
183          return scoped_ptr<VideoCaptureDevice::Name>(
184              new VideoCaptureDevice::Name(*names_iterator));
185        }
186      }
187    }
188    DVLOG(1) << "No camera can capture the format: " << pixel_format;
189    return scoped_ptr<VideoCaptureDevice::Name>();
190  }
191
192#if defined(OS_WIN)
193  base::win::ScopedCOMInitializer initialize_com_;
194#endif
195  scoped_ptr<VideoCaptureDevice::Names> names_;
196  scoped_ptr<base::MessageLoop> loop_;
197  scoped_ptr<base::RunLoop> run_loop_;
198  scoped_ptr<MockClient> client_;
199  scoped_refptr<DeviceEnumerationListener> device_enumeration_listener_;
200  VideoCaptureFormat last_format_;
201  scoped_ptr<VideoCaptureDeviceFactory> video_capture_device_factory_;
202};
203
204TEST_F(VideoCaptureDeviceTest, OpenInvalidDevice) {
205#if defined(OS_WIN)
206  VideoCaptureDevice::Name::CaptureApiType api_type =
207      VideoCaptureDeviceFactoryWin::PlatformSupportsMediaFoundation()
208          ? VideoCaptureDevice::Name::MEDIA_FOUNDATION
209          : VideoCaptureDevice::Name::DIRECT_SHOW;
210  VideoCaptureDevice::Name device_name("jibberish", "jibberish", api_type);
211#elif defined(OS_MACOSX)
212  VideoCaptureDevice::Name device_name("jibberish", "jibberish",
213      VideoCaptureDeviceFactoryMac::PlatformSupportsAVFoundation()
214          ? VideoCaptureDevice::Name::AVFOUNDATION
215          : VideoCaptureDevice::Name::QTKIT);
216#else
217  VideoCaptureDevice::Name device_name("jibberish", "jibberish");
218#endif
219  scoped_ptr<VideoCaptureDevice> device =
220      video_capture_device_factory_->Create(device_name);
221#if !defined(OS_MACOSX)
222  EXPECT_TRUE(device == NULL);
223#else
224  if (VideoCaptureDeviceFactoryMac::PlatformSupportsAVFoundation()) {
225    EXPECT_TRUE(device == NULL);
226  } else {
227    // The presence of the actual device is only checked on AllocateAndStart()
228    // and not on creation for QTKit API in Mac OS X platform.
229    EXPECT_CALL(*client_, OnErr()).Times(1);
230
231    VideoCaptureParams capture_params;
232    capture_params.requested_format.frame_size.SetSize(640, 480);
233    capture_params.requested_format.frame_rate = 30;
234    capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420;
235    device->AllocateAndStart(capture_params, client_.PassAs<Client>());
236    device->StopAndDeAllocate();
237  }
238#endif
239}
240
241TEST_F(VideoCaptureDeviceTest, CaptureVGA) {
242  names_ = EnumerateDevices();
243  if (!names_->size()) {
244    DVLOG(1) << "No camera available. Exiting test.";
245    return;
246  }
247
248  scoped_ptr<VideoCaptureDevice> device(
249      video_capture_device_factory_->Create(names_->front()));
250  ASSERT_TRUE(device);
251  DVLOG(1) << names_->front().id();
252
253  EXPECT_CALL(*client_, OnErr())
254      .Times(0);
255
256  VideoCaptureParams capture_params;
257  capture_params.requested_format.frame_size.SetSize(640, 480);
258  capture_params.requested_format.frame_rate = 30;
259  capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420;
260  device->AllocateAndStart(capture_params, client_.PassAs<Client>());
261  // Get captured video frames.
262  WaitForCapturedFrame();
263  EXPECT_EQ(last_format().frame_size.width(), 640);
264  EXPECT_EQ(last_format().frame_size.height(), 480);
265  device->StopAndDeAllocate();
266}
267
268TEST_F(VideoCaptureDeviceTest, Capture720p) {
269  names_ = EnumerateDevices();
270  if (!names_->size()) {
271    DVLOG(1) << "No camera available. Exiting test.";
272    return;
273  }
274
275  scoped_ptr<VideoCaptureDevice> device(
276      video_capture_device_factory_->Create(names_->front()));
277  ASSERT_TRUE(device);
278
279  EXPECT_CALL(*client_, OnErr())
280      .Times(0);
281
282  VideoCaptureParams capture_params;
283  capture_params.requested_format.frame_size.SetSize(1280, 720);
284  capture_params.requested_format.frame_rate = 30;
285  capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420;
286  device->AllocateAndStart(capture_params, client_.PassAs<Client>());
287  // Get captured video frames.
288  WaitForCapturedFrame();
289  device->StopAndDeAllocate();
290}
291
292TEST_F(VideoCaptureDeviceTest, MAYBE_AllocateBadSize) {
293  names_ = EnumerateDevices();
294  if (!names_->size()) {
295    DVLOG(1) << "No camera available. Exiting test.";
296    return;
297  }
298  scoped_ptr<VideoCaptureDevice> device(
299      video_capture_device_factory_->Create(names_->front()));
300  ASSERT_TRUE(device);
301
302  EXPECT_CALL(*client_, OnErr())
303      .Times(0);
304
305  VideoCaptureParams capture_params;
306  capture_params.requested_format.frame_size.SetSize(637, 472);
307  capture_params.requested_format.frame_rate = 35;
308  capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420;
309  device->AllocateAndStart(capture_params, client_.PassAs<Client>());
310  WaitForCapturedFrame();
311  device->StopAndDeAllocate();
312  EXPECT_EQ(last_format().frame_size.width(), 640);
313  EXPECT_EQ(last_format().frame_size.height(), 480);
314}
315
316TEST_F(VideoCaptureDeviceTest, ReAllocateCamera) {
317  names_ = EnumerateDevices();
318  if (!names_->size()) {
319    DVLOG(1) << "No camera available. Exiting test.";
320    return;
321  }
322
323  // First, do a number of very fast device start/stops.
324  for (int i = 0; i <= 5; i++) {
325    ResetWithNewClient();
326    scoped_ptr<VideoCaptureDevice> device(
327        video_capture_device_factory_->Create(names_->front()));
328    gfx::Size resolution;
329    if (i % 2) {
330      resolution = gfx::Size(640, 480);
331    } else {
332      resolution = gfx::Size(1280, 1024);
333    }
334    VideoCaptureParams capture_params;
335    capture_params.requested_format.frame_size = resolution;
336    capture_params.requested_format.frame_rate = 30;
337    capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420;
338    device->AllocateAndStart(capture_params, client_.PassAs<Client>());
339    device->StopAndDeAllocate();
340  }
341
342  // Finally, do a device start and wait for it to finish.
343  VideoCaptureParams capture_params;
344  capture_params.requested_format.frame_size.SetSize(320, 240);
345  capture_params.requested_format.frame_rate = 30;
346  capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420;
347
348  ResetWithNewClient();
349  scoped_ptr<VideoCaptureDevice> device(
350      video_capture_device_factory_->Create(names_->front()));
351
352  device->AllocateAndStart(capture_params, client_.PassAs<Client>());
353  WaitForCapturedFrame();
354  device->StopAndDeAllocate();
355  device.reset();
356  EXPECT_EQ(last_format().frame_size.width(), 320);
357  EXPECT_EQ(last_format().frame_size.height(), 240);
358}
359
360TEST_F(VideoCaptureDeviceTest, DeAllocateCameraWhileRunning) {
361  names_ = EnumerateDevices();
362  if (!names_->size()) {
363    DVLOG(1) << "No camera available. Exiting test.";
364    return;
365  }
366  scoped_ptr<VideoCaptureDevice> device(
367      video_capture_device_factory_->Create(names_->front()));
368  ASSERT_TRUE(device);
369
370  EXPECT_CALL(*client_, OnErr())
371      .Times(0);
372
373  VideoCaptureParams capture_params;
374  capture_params.requested_format.frame_size.SetSize(640, 480);
375  capture_params.requested_format.frame_rate = 30;
376  capture_params.requested_format.pixel_format = PIXEL_FORMAT_I420;
377  device->AllocateAndStart(capture_params, client_.PassAs<Client>());
378  // Get captured video frames.
379  WaitForCapturedFrame();
380  EXPECT_EQ(last_format().frame_size.width(), 640);
381  EXPECT_EQ(last_format().frame_size.height(), 480);
382  EXPECT_EQ(last_format().frame_rate, 30);
383  device->StopAndDeAllocate();
384}
385
386// Start the camera in 720p to capture MJPEG instead of a raw format.
387TEST_F(VideoCaptureDeviceTest, MAYBE_CaptureMjpeg) {
388  scoped_ptr<VideoCaptureDevice::Name> name =
389      GetFirstDeviceNameSupportingPixelFormat(PIXEL_FORMAT_MJPEG);
390  if (!name) {
391    DVLOG(1) << "No camera supports MJPEG format. Exiting test.";
392    return;
393  }
394  scoped_ptr<VideoCaptureDevice> device(
395      video_capture_device_factory_->Create(*name));
396  ASSERT_TRUE(device);
397
398  EXPECT_CALL(*client_, OnErr())
399      .Times(0);
400
401  VideoCaptureParams capture_params;
402  capture_params.requested_format.frame_size.SetSize(1280, 720);
403  capture_params.requested_format.frame_rate = 30;
404  capture_params.requested_format.pixel_format = PIXEL_FORMAT_MJPEG;
405  device->AllocateAndStart(capture_params, client_.PassAs<Client>());
406  // Get captured video frames.
407  WaitForCapturedFrame();
408  // Verify we get MJPEG from the device. Not all devices can capture 1280x720
409  // @ 30 fps, so we don't care about the exact resolution we get.
410  EXPECT_EQ(last_format().pixel_format, PIXEL_FORMAT_MJPEG);
411  device->StopAndDeAllocate();
412}
413
414TEST_F(VideoCaptureDeviceTest, GetDeviceSupportedFormats) {
415  // Use PIXEL_FORMAT_MAX to iterate all device names for testing
416  // GetDeviceSupportedFormats().
417  scoped_ptr<VideoCaptureDevice::Name> name =
418      GetFirstDeviceNameSupportingPixelFormat(PIXEL_FORMAT_MAX);
419  // Verify no camera returned for PIXEL_FORMAT_MAX. Nothing else to test here
420  // since we cannot forecast the hardware capabilities.
421  ASSERT_FALSE(name);
422}
423
424};  // namespace media
425