1 /* 2 * libjingle 3 * Copyright 2004 Google Inc. 4 * 5 * Redistribution and use in source and binary forms, with or without 6 * modification, are permitted provided that the following conditions are met: 7 * 8 * 1. Redistributions of source code must retain the above copyright notice, 9 * this list of conditions and the following disclaimer. 10 * 2. Redistributions in binary form must reproduce the above copyright notice, 11 * this list of conditions and the following disclaimer in the documentation 12 * and/or other materials provided with the distribution. 13 * 3. The name of the author may not be used to endorse or promote products 14 * derived from this software without specific prior written permission. 15 * 16 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED 17 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF 18 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO 19 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, 20 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 21 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; 22 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, 23 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR 24 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF 25 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 26 */ 27 28 #include "talk/media/devices/devicemanager.h" 29 30 #ifdef WIN32 31 #include "talk/base/win32.h" 32 #include <objbase.h> 33 #endif 34 #include <string> 35 36 #include "talk/base/fileutils.h" 37 #include "talk/base/gunit.h" 38 #include "talk/base/logging.h" 39 #include "talk/base/pathutils.h" 40 #include "talk/base/scoped_ptr.h" 41 #include "talk/base/stream.h" 42 #include "talk/base/windowpickerfactory.h" 43 #include "talk/media/base/fakevideocapturer.h" 44 #include "talk/media/base/testutils.h" 45 #include "talk/media/devices/filevideocapturer.h" 46 #include "talk/media/devices/v4llookup.h" 47 48 #ifdef LINUX 49 // TODO(juberti): Figure out why this doesn't compile on Windows. 50 #include "talk/base/fileutils_mock.h" 51 #endif // LINUX 52 53 using talk_base::Pathname; 54 using talk_base::FileTimeType; 55 using talk_base::scoped_ptr; 56 using cricket::Device; 57 using cricket::DeviceManager; 58 using cricket::DeviceManagerFactory; 59 using cricket::DeviceManagerInterface; 60 61 const cricket::VideoFormat kVgaFormat(640, 480, 62 cricket::VideoFormat::FpsToInterval(30), 63 cricket::FOURCC_I420); 64 const cricket::VideoFormat kHdFormat(1280, 720, 65 cricket::VideoFormat::FpsToInterval(30), 66 cricket::FOURCC_I420); 67 68 class FakeVideoCapturerFactory : public cricket::VideoCapturerFactory { 69 public: 70 FakeVideoCapturerFactory() {} 71 virtual ~FakeVideoCapturerFactory() {} 72 73 virtual cricket::VideoCapturer* Create(const cricket::Device& device) { 74 return new cricket::FakeVideoCapturer; 75 } 76 }; 77 78 class DeviceManagerTestFake : public testing::Test { 79 public: 80 virtual void SetUp() { 81 dm_.reset(DeviceManagerFactory::Create()); 82 EXPECT_TRUE(dm_->Init()); 83 DeviceManager* device_manager = static_cast<DeviceManager*>(dm_.get()); 84 device_manager->set_device_video_capturer_factory( 85 new FakeVideoCapturerFactory()); 86 } 87 88 virtual void TearDown() { 89 dm_->Terminate(); 90 } 91 92 protected: 93 scoped_ptr<DeviceManagerInterface> dm_; 94 }; 95 96 97 // Test that we startup/shutdown properly. 98 TEST(DeviceManagerTest, StartupShutdown) { 99 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 100 EXPECT_TRUE(dm->Init()); 101 dm->Terminate(); 102 } 103 104 // Test CoInitEx behavior 105 #ifdef WIN32 106 TEST(DeviceManagerTest, CoInitialize) { 107 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 108 std::vector<Device> devices; 109 // Ensure that calls to video device work if COM is not yet initialized. 110 EXPECT_TRUE(dm->Init()); 111 EXPECT_TRUE(dm->GetVideoCaptureDevices(&devices)); 112 dm->Terminate(); 113 // Ensure that the ref count is correct. 114 EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED)); 115 CoUninitialize(); 116 // Ensure that Init works in COINIT_APARTMENTTHREADED setting. 117 EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); 118 EXPECT_TRUE(dm->Init()); 119 dm->Terminate(); 120 CoUninitialize(); 121 // Ensure that the ref count is correct. 122 EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_APARTMENTTHREADED)); 123 CoUninitialize(); 124 // Ensure that Init works in COINIT_MULTITHREADED setting. 125 EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED)); 126 EXPECT_TRUE(dm->Init()); 127 dm->Terminate(); 128 CoUninitialize(); 129 // Ensure that the ref count is correct. 130 EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED)); 131 CoUninitialize(); 132 } 133 #endif 134 135 // Test enumerating devices (although we may not find any). 136 TEST(DeviceManagerTest, GetDevices) { 137 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 138 std::vector<Device> audio_ins, audio_outs, video_ins; 139 std::vector<cricket::Device> video_in_devs; 140 cricket::Device def_video; 141 EXPECT_TRUE(dm->Init()); 142 EXPECT_TRUE(dm->GetAudioInputDevices(&audio_ins)); 143 EXPECT_TRUE(dm->GetAudioOutputDevices(&audio_outs)); 144 EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins)); 145 EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_in_devs)); 146 EXPECT_EQ(video_ins.size(), video_in_devs.size()); 147 // If we have any video devices, we should be able to pick a default. 148 EXPECT_TRUE(dm->GetVideoCaptureDevice( 149 cricket::DeviceManagerInterface::kDefaultDeviceName, &def_video) 150 != video_ins.empty()); 151 } 152 153 // Test that we return correct ids for default and bogus devices. 154 TEST(DeviceManagerTest, GetAudioDeviceIds) { 155 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 156 Device device; 157 EXPECT_TRUE(dm->Init()); 158 EXPECT_TRUE(dm->GetAudioInputDevice( 159 cricket::DeviceManagerInterface::kDefaultDeviceName, &device)); 160 EXPECT_EQ("-1", device.id); 161 EXPECT_TRUE(dm->GetAudioOutputDevice( 162 cricket::DeviceManagerInterface::kDefaultDeviceName, &device)); 163 EXPECT_EQ("-1", device.id); 164 EXPECT_FALSE(dm->GetAudioInputDevice("_NOT A REAL DEVICE_", &device)); 165 EXPECT_FALSE(dm->GetAudioOutputDevice("_NOT A REAL DEVICE_", &device)); 166 } 167 168 // Test that we get the video capture device by name properly. 169 TEST(DeviceManagerTest, GetVideoDeviceIds) { 170 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 171 Device device; 172 EXPECT_TRUE(dm->Init()); 173 EXPECT_FALSE(dm->GetVideoCaptureDevice("_NOT A REAL DEVICE_", &device)); 174 std::vector<Device> video_ins; 175 EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins)); 176 if (!video_ins.empty()) { 177 // Get the default device with the parameter kDefaultDeviceName. 178 EXPECT_TRUE(dm->GetVideoCaptureDevice( 179 cricket::DeviceManagerInterface::kDefaultDeviceName, &device)); 180 181 // Get the first device with the parameter video_ins[0].name. 182 EXPECT_TRUE(dm->GetVideoCaptureDevice(video_ins[0].name, &device)); 183 EXPECT_EQ(device.name, video_ins[0].name); 184 EXPECT_EQ(device.id, video_ins[0].id); 185 } 186 } 187 188 TEST(DeviceManagerTest, GetVideoDeviceIds_File) { 189 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 190 EXPECT_TRUE(dm->Init()); 191 Device device; 192 const std::string test_file = 193 cricket::GetTestFilePath("captured-320x240-2s-48.frames"); 194 EXPECT_TRUE(dm->GetVideoCaptureDevice(test_file, &device)); 195 EXPECT_TRUE(cricket::FileVideoCapturer::IsFileVideoCapturerDevice(device)); 196 } 197 198 TEST(DeviceManagerTest, VerifyDevicesListsAreCleared) { 199 const std::string imaginary("_NOT A REAL DEVICE_"); 200 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 201 std::vector<Device> audio_ins, audio_outs, video_ins; 202 audio_ins.push_back(Device(imaginary, imaginary)); 203 audio_outs.push_back(Device(imaginary, imaginary)); 204 video_ins.push_back(Device(imaginary, imaginary)); 205 EXPECT_TRUE(dm->Init()); 206 EXPECT_TRUE(dm->GetAudioInputDevices(&audio_ins)); 207 EXPECT_TRUE(dm->GetAudioOutputDevices(&audio_outs)); 208 EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins)); 209 for (size_t i = 0; i < audio_ins.size(); ++i) { 210 EXPECT_NE(imaginary, audio_ins[i].name); 211 } 212 for (size_t i = 0; i < audio_outs.size(); ++i) { 213 EXPECT_NE(imaginary, audio_outs[i].name); 214 } 215 for (size_t i = 0; i < video_ins.size(); ++i) { 216 EXPECT_NE(imaginary, video_ins[i].name); 217 } 218 } 219 220 static bool CompareDeviceList(std::vector<Device>& devices, 221 const char* const device_list[], int list_size) { 222 if (list_size != static_cast<int>(devices.size())) { 223 return false; 224 } 225 for (int i = 0; i < list_size; ++i) { 226 if (devices[i].name.compare(device_list[i]) != 0) { 227 return false; 228 } 229 } 230 return true; 231 } 232 233 TEST(DeviceManagerTest, VerifyFilterDevices) { 234 static const char* const kTotalDevicesName[] = { 235 "Google Camera Adapters are tons of fun.", 236 "device1", 237 "device2", 238 "device3", 239 "device4", 240 "device5", 241 "Google Camera Adapter 0", 242 "Google Camera Adapter 1", 243 }; 244 static const char* const kFilteredDevicesName[] = { 245 "device2", 246 "device4", 247 "Google Camera Adapter", 248 NULL, 249 }; 250 static const char* const kDevicesName[] = { 251 "device1", 252 "device3", 253 "device5", 254 }; 255 std::vector<Device> devices; 256 for (int i = 0; i < ARRAY_SIZE(kTotalDevicesName); ++i) { 257 devices.push_back(Device(kTotalDevicesName[i], i)); 258 } 259 EXPECT_TRUE(CompareDeviceList(devices, kTotalDevicesName, 260 ARRAY_SIZE(kTotalDevicesName))); 261 // Return false if given NULL as the exclusion list. 262 EXPECT_TRUE(DeviceManager::FilterDevices(&devices, NULL)); 263 // The devices should not change. 264 EXPECT_TRUE(CompareDeviceList(devices, kTotalDevicesName, 265 ARRAY_SIZE(kTotalDevicesName))); 266 EXPECT_TRUE(DeviceManager::FilterDevices(&devices, kFilteredDevicesName)); 267 EXPECT_TRUE(CompareDeviceList(devices, kDevicesName, 268 ARRAY_SIZE(kDevicesName))); 269 } 270 271 #ifdef LINUX 272 class FakeV4LLookup : public cricket::V4LLookup { 273 public: 274 explicit FakeV4LLookup(std::vector<std::string> device_paths) 275 : device_paths_(device_paths) {} 276 277 protected: 278 bool CheckIsV4L2Device(const std::string& device) { 279 return std::find(device_paths_.begin(), device_paths_.end(), device) 280 != device_paths_.end(); 281 } 282 283 private: 284 std::vector<std::string> device_paths_; 285 }; 286 287 TEST(DeviceManagerTest, GetVideoCaptureDevices_K2_6) { 288 std::vector<std::string> devices; 289 devices.push_back("/dev/video0"); 290 devices.push_back("/dev/video5"); 291 cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices)); 292 293 std::vector<talk_base::FakeFileSystem::File> files; 294 files.push_back(talk_base::FakeFileSystem::File("/dev/video0", "")); 295 files.push_back(talk_base::FakeFileSystem::File("/dev/video5", "")); 296 files.push_back(talk_base::FakeFileSystem::File( 297 "/sys/class/video4linux/video0/name", "Video Device 1")); 298 files.push_back(talk_base::FakeFileSystem::File( 299 "/sys/class/video4linux/video1/model", "Bad Device")); 300 files.push_back( 301 talk_base::FakeFileSystem::File("/sys/class/video4linux/video5/model", 302 "Video Device 2")); 303 talk_base::FilesystemScope fs(new talk_base::FakeFileSystem(files)); 304 305 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 306 std::vector<Device> video_ins; 307 EXPECT_TRUE(dm->Init()); 308 EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins)); 309 EXPECT_EQ(2u, video_ins.size()); 310 EXPECT_EQ("Video Device 1", video_ins.at(0).name); 311 EXPECT_EQ("Video Device 2", video_ins.at(1).name); 312 } 313 314 TEST(DeviceManagerTest, GetVideoCaptureDevices_K2_4) { 315 std::vector<std::string> devices; 316 devices.push_back("/dev/video0"); 317 devices.push_back("/dev/video5"); 318 cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices)); 319 320 std::vector<talk_base::FakeFileSystem::File> files; 321 files.push_back(talk_base::FakeFileSystem::File("/dev/video0", "")); 322 files.push_back(talk_base::FakeFileSystem::File("/dev/video5", "")); 323 files.push_back(talk_base::FakeFileSystem::File( 324 "/proc/video/dev/video0", 325 "param1: value1\nname: Video Device 1\n param2: value2\n")); 326 files.push_back(talk_base::FakeFileSystem::File( 327 "/proc/video/dev/video1", 328 "param1: value1\nname: Bad Device\n param2: value2\n")); 329 files.push_back(talk_base::FakeFileSystem::File( 330 "/proc/video/dev/video5", 331 "param1: value1\nname: Video Device 2\n param2: value2\n")); 332 talk_base::FilesystemScope fs(new talk_base::FakeFileSystem(files)); 333 334 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 335 std::vector<Device> video_ins; 336 EXPECT_TRUE(dm->Init()); 337 EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins)); 338 EXPECT_EQ(2u, video_ins.size()); 339 EXPECT_EQ("Video Device 1", video_ins.at(0).name); 340 EXPECT_EQ("Video Device 2", video_ins.at(1).name); 341 } 342 343 TEST(DeviceManagerTest, GetVideoCaptureDevices_KUnknown) { 344 std::vector<std::string> devices; 345 devices.push_back("/dev/video0"); 346 devices.push_back("/dev/video5"); 347 cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices)); 348 349 std::vector<talk_base::FakeFileSystem::File> files; 350 files.push_back(talk_base::FakeFileSystem::File("/dev/video0", "")); 351 files.push_back(talk_base::FakeFileSystem::File("/dev/video1", "")); 352 files.push_back(talk_base::FakeFileSystem::File("/dev/video5", "")); 353 talk_base::FilesystemScope fs(new talk_base::FakeFileSystem(files)); 354 355 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 356 std::vector<Device> video_ins; 357 EXPECT_TRUE(dm->Init()); 358 EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins)); 359 EXPECT_EQ(2u, video_ins.size()); 360 EXPECT_EQ("/dev/video0", video_ins.at(0).name); 361 EXPECT_EQ("/dev/video5", video_ins.at(1).name); 362 } 363 #endif // LINUX 364 365 // TODO(noahric): These are flaky on windows on headless machines. 366 #ifndef WIN32 367 TEST(DeviceManagerTest, GetWindows) { 368 if (!talk_base::WindowPickerFactory::IsSupported()) { 369 LOG(LS_INFO) << "skipping test: window capturing is not supported with " 370 << "current configuration."; 371 return; 372 } 373 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 374 std::vector<talk_base::WindowDescription> descriptions; 375 EXPECT_TRUE(dm->Init()); 376 if (!dm->GetWindows(&descriptions) || descriptions.empty()) { 377 LOG(LS_INFO) << "skipping test: window capturing. Does not have any " 378 << "windows to capture."; 379 return; 380 } 381 scoped_ptr<cricket::VideoCapturer> capturer(dm->CreateWindowCapturer( 382 descriptions.front().id())); 383 EXPECT_FALSE(capturer.get() == NULL); 384 // TODO(hellner): creating a window capturer and immediately deleting it 385 // results in "Continuous Build and Test Mainline - Mac opt" failure (crash). 386 // Remove the following line as soon as this has been resolved. 387 talk_base::Thread::Current()->ProcessMessages(1); 388 } 389 390 TEST(DeviceManagerTest, GetDesktops) { 391 if (!talk_base::WindowPickerFactory::IsSupported()) { 392 LOG(LS_INFO) << "skipping test: desktop capturing is not supported with " 393 << "current configuration."; 394 return; 395 } 396 scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create()); 397 std::vector<talk_base::DesktopDescription> descriptions; 398 EXPECT_TRUE(dm->Init()); 399 if (!dm->GetDesktops(&descriptions) || descriptions.empty()) { 400 LOG(LS_INFO) << "skipping test: desktop capturing. Does not have any " 401 << "desktops to capture."; 402 return; 403 } 404 scoped_ptr<cricket::VideoCapturer> capturer(dm->CreateDesktopCapturer( 405 descriptions.front().id())); 406 EXPECT_FALSE(capturer.get() == NULL); 407 } 408 #endif // !WIN32 409 410 TEST_F(DeviceManagerTestFake, CaptureConstraintsWhitelisted) { 411 const Device device("white", "white_id"); 412 dm_->SetVideoCaptureDeviceMaxFormat(device.name, kHdFormat); 413 scoped_ptr<cricket::VideoCapturer> capturer( 414 dm_->CreateVideoCapturer(device)); 415 cricket::VideoFormat best_format; 416 capturer->set_enable_camera_list(true); 417 EXPECT_TRUE(capturer->GetBestCaptureFormat(kHdFormat, &best_format)); 418 EXPECT_EQ(kHdFormat, best_format); 419 } 420 421 TEST_F(DeviceManagerTestFake, CaptureConstraintsNotWhitelisted) { 422 const Device device("regular", "regular_id"); 423 scoped_ptr<cricket::VideoCapturer> capturer( 424 dm_->CreateVideoCapturer(device)); 425 cricket::VideoFormat best_format; 426 capturer->set_enable_camera_list(true); 427 EXPECT_TRUE(capturer->GetBestCaptureFormat(kHdFormat, &best_format)); 428 EXPECT_EQ(kHdFormat, best_format); 429 } 430 431 TEST_F(DeviceManagerTestFake, CaptureConstraintsUnWhitelisted) { 432 const Device device("un_white", "un_white_id"); 433 dm_->SetVideoCaptureDeviceMaxFormat(device.name, kHdFormat); 434 dm_->ClearVideoCaptureDeviceMaxFormat(device.name); 435 scoped_ptr<cricket::VideoCapturer> capturer( 436 dm_->CreateVideoCapturer(device)); 437 cricket::VideoFormat best_format; 438 capturer->set_enable_camera_list(true); 439 EXPECT_TRUE(capturer->GetBestCaptureFormat(kHdFormat, &best_format)); 440 EXPECT_EQ(kHdFormat, best_format); 441 } 442 443 TEST_F(DeviceManagerTestFake, CaptureConstraintsWildcard) { 444 const Device device("any_device", "any_device"); 445 dm_->SetVideoCaptureDeviceMaxFormat("*", kHdFormat); 446 scoped_ptr<cricket::VideoCapturer> capturer( 447 dm_->CreateVideoCapturer(device)); 448 cricket::VideoFormat best_format; 449 capturer->set_enable_camera_list(true); 450 EXPECT_TRUE(capturer->GetBestCaptureFormat(kHdFormat, &best_format)); 451 EXPECT_EQ(kHdFormat, best_format); 452 } 453