• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
MockClient(base::Callback<void (const VideoCaptureFormat &)> frame_cb)72   explicit MockClient(base::Callback<void(const VideoCaptureFormat&)> frame_cb)
73       : main_thread_(base::MessageLoopProxy::current()), frame_cb_(frame_cb) {}
74 
OnError(const std::string & error_message)75   virtual void OnError(const std::string& error_message) OVERRIDE {
76     OnErr();
77   }
78 
OnIncomingCapturedData(const uint8 * data,int length,const VideoCaptureFormat & format,int rotation,base::TimeTicks timestamp)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 
OnIncomingCapturedVideoFrame(const scoped_refptr<Buffer> & buffer,const media::VideoCaptureFormat & buffer_format,const scoped_refptr<media::VideoFrame> & frame,base::TimeTicks timestamp)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.
OnEnumeratedDevicesCallback(scoped_ptr<media::VideoCaptureDevice::Names> names)106   void OnEnumeratedDevicesCallback(
107       scoped_ptr<media::VideoCaptureDevice::Names> names) {
108     OnEnumeratedDevicesCallbackPtr(names.release());
109   }
110  private:
111   friend class base::RefCounted<DeviceEnumerationListener>;
~DeviceEnumerationListener()112   virtual ~DeviceEnumerationListener() {}
113 };
114 
115 class VideoCaptureDeviceTest : public testing::Test {
116  protected:
117   typedef media::VideoCaptureDevice::Client Client;
118 
VideoCaptureDeviceTest()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 
SetUp()129   virtual void SetUp() {
130 #if defined(OS_ANDROID)
131     media::VideoCaptureDeviceAndroid::RegisterVideoCaptureDevice(
132         base::android::AttachCurrentThread());
133 #endif
134   }
135 
ResetWithNewClient()136   void ResetWithNewClient() {
137     client_.reset(new MockClient(base::Bind(
138         &VideoCaptureDeviceTest::OnFrameCaptured, base::Unretained(this))));
139   }
140 
OnFrameCaptured(const VideoCaptureFormat & format)141   void OnFrameCaptured(const VideoCaptureFormat& format) {
142     last_format_ = format;
143     run_loop_->QuitClosure().Run();
144   }
145 
WaitForCapturedFrame()146   void WaitForCapturedFrame() {
147     run_loop_.reset(new base::RunLoop());
148     run_loop_->Run();
149   }
150 
EnumerateDevices()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 
last_format() const163   const VideoCaptureFormat& last_format() const { return last_format_; }
164 
GetFirstDeviceNameSupportingPixelFormat(const VideoPixelFormat & pixel_format)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 
TEST_F(VideoCaptureDeviceTest,OpenInvalidDevice)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 
TEST_F(VideoCaptureDeviceTest,CaptureVGA)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 
TEST_F(VideoCaptureDeviceTest,Capture720p)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 
TEST_F(VideoCaptureDeviceTest,MAYBE_AllocateBadSize)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 
TEST_F(VideoCaptureDeviceTest,ReAllocateCamera)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 
TEST_F(VideoCaptureDeviceTest,DeAllocateCameraWhileRunning)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.
TEST_F(VideoCaptureDeviceTest,MAYBE_CaptureMjpeg)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 
TEST_F(VideoCaptureDeviceTest,GetDeviceSupportedFormats)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