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