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