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