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