Home | History | Annotate | Download | only in capture
      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 
     60 using ::testing::_;
     61 using ::testing::SaveArg;
     62 
     63 namespace media {
     64 
     65 class 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 
    100 class 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 
    115 class 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 
    204 TEST_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 
    241 TEST_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 
    268 TEST_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 
    292 TEST_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 
    316 TEST_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 
    360 TEST_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.
    387 TEST_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 
    414 TEST_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