1 /*
2  * libjingle
3  * Copyright 2004--2008, 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 #ifdef WIN32
29 #include "talk/base/win32.h"
30 #include <objbase.h>
31 #endif
32 #include <string>
33 
34 #include "talk/base/fileutils.h"
35 #include "talk/base/gunit.h"
36 #include "talk/base/logging.h"
37 #include "talk/base/pathutils.h"
38 #include "talk/base/scoped_ptr.h"
39 #include "talk/base/stream.h"
40 #include "talk/session/phone/devicemanager.h"
41 #include "talk/session/phone/filevideocapturer.h"
42 #include "talk/session/phone/testutils.h"
43 #include "talk/session/phone/v4llookup.h"
44 
45 #ifdef LINUX
46 // TODO: Figure out why this doesn't compile on Windows.
47 #include "talk/base/fileutils_mock.h"
48 #endif  // LINUX
49 
50 #include "talk/session/phone/devicemanager.h"
51 
52 using talk_base::Pathname;
53 using talk_base::FileTimeType;
54 using talk_base::scoped_ptr;
55 using cricket::Device;
56 using cricket::DeviceManager;
57 using cricket::DeviceManagerFactory;
58 using cricket::DeviceManagerInterface;
59 
60 // Test that we startup/shutdown properly.
TEST(DeviceManagerTest,StartupShutdown)61 TEST(DeviceManagerTest, StartupShutdown) {
62   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
63   EXPECT_TRUE(dm->Init());
64   dm->Terminate();
65 }
66 
67 // Test CoInitEx behavior
68 #ifdef WIN32
TEST(DeviceManagerTest,CoInitialize)69 TEST(DeviceManagerTest, CoInitialize) {
70   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
71   std::vector<Device> devices;
72   // Ensure that calls to video device work if COM is not yet initialized.
73   EXPECT_TRUE(dm->Init());
74   EXPECT_TRUE(dm->GetVideoCaptureDevices(&devices));
75   dm->Terminate();
76   // Ensure that the ref count is correct.
77   EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED));
78   CoUninitialize();
79   // Ensure that Init works in COINIT_APARTMENTTHREADED setting.
80   EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
81   EXPECT_TRUE(dm->Init());
82   dm->Terminate();
83   CoUninitialize();
84   // Ensure that the ref count is correct.
85   EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_APARTMENTTHREADED));
86   CoUninitialize();
87   // Ensure that Init works in COINIT_MULTITHREADED setting.
88   EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED));
89   EXPECT_TRUE(dm->Init());
90   dm->Terminate();
91   CoUninitialize();
92   // Ensure that the ref count is correct.
93   EXPECT_EQ(S_OK, CoInitializeEx(NULL, COINIT_MULTITHREADED));
94   CoUninitialize();
95 }
96 #endif
97 
98 // Test enumerating devices (although we may not find any).
TEST(DeviceManagerTest,GetDevices)99 TEST(DeviceManagerTest, GetDevices) {
100   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
101   std::vector<Device> audio_ins, audio_outs, video_ins;
102   std::vector<cricket::Device> video_in_devs;
103   cricket::Device def_video;
104   EXPECT_TRUE(dm->Init());
105   EXPECT_TRUE(dm->GetAudioInputDevices(&audio_ins));
106   EXPECT_TRUE(dm->GetAudioOutputDevices(&audio_outs));
107   EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
108   EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_in_devs));
109   EXPECT_EQ(video_ins.size(), video_in_devs.size());
110   // If we have any video devices, we should be able to pick a default.
111   EXPECT_TRUE(dm->GetVideoCaptureDevice(
112       cricket::DeviceManagerInterface::kDefaultDeviceName, &def_video)
113       != video_ins.empty());
114 }
115 
116 // Test that we return correct ids for default and bogus devices.
TEST(DeviceManagerTest,GetAudioDeviceIds)117 TEST(DeviceManagerTest, GetAudioDeviceIds) {
118   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
119   Device device;
120   EXPECT_TRUE(dm->Init());
121   EXPECT_TRUE(dm->GetAudioInputDevice(
122       cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
123   EXPECT_EQ("-1", device.id);
124   EXPECT_TRUE(dm->GetAudioOutputDevice(
125       cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
126   EXPECT_EQ("-1", device.id);
127   EXPECT_FALSE(dm->GetAudioInputDevice("_NOT A REAL DEVICE_", &device));
128   EXPECT_FALSE(dm->GetAudioOutputDevice("_NOT A REAL DEVICE_", &device));
129 }
130 
131 // Test that we get the video capture device by name properly.
TEST(DeviceManagerTest,GetVideoDeviceIds)132 TEST(DeviceManagerTest, GetVideoDeviceIds) {
133   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
134   Device device;
135   EXPECT_TRUE(dm->Init());
136   EXPECT_FALSE(dm->GetVideoCaptureDevice("_NOT A REAL DEVICE_", &device));
137   std::vector<Device> video_ins;
138   EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
139   if (!video_ins.empty()) {
140     // Get the default device with the parameter kDefaultDeviceName.
141     EXPECT_TRUE(dm->GetVideoCaptureDevice(
142         cricket::DeviceManagerInterface::kDefaultDeviceName, &device));
143 
144     // Get the first device with the parameter video_ins[0].name.
145     EXPECT_TRUE(dm->GetVideoCaptureDevice(video_ins[0].name, &device));
146     EXPECT_EQ(device.name, video_ins[0].name);
147     EXPECT_EQ(device.id, video_ins[0].id);
148   }
149 }
150 
TEST(DeviceManagerTest,GetVideoDeviceIds_File)151 TEST(DeviceManagerTest, GetVideoDeviceIds_File) {
152   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
153   EXPECT_TRUE(dm->Init());
154   Device device;
155   const std::string test_file =
156       cricket::GetTestFilePath("captured-320x240-2s-48.frames");
157   EXPECT_TRUE(dm->GetVideoCaptureDevice(test_file, &device));
158   EXPECT_TRUE(cricket::FileVideoCapturer::IsFileVideoCapturerDevice(device));
159 }
160 
TEST(DeviceManagerTest,VerifyDevicesListsAreCleared)161 TEST(DeviceManagerTest, VerifyDevicesListsAreCleared) {
162   const std::string imaginary("_NOT A REAL DEVICE_");
163   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
164   std::vector<Device> audio_ins, audio_outs, video_ins;
165   audio_ins.push_back(Device(imaginary, imaginary));
166   audio_outs.push_back(Device(imaginary, imaginary));
167   video_ins.push_back(Device(imaginary, imaginary));
168   EXPECT_TRUE(dm->Init());
169   EXPECT_TRUE(dm->GetAudioInputDevices(&audio_ins));
170   EXPECT_TRUE(dm->GetAudioOutputDevices(&audio_outs));
171   EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
172   for (size_t i = 0; i < audio_ins.size(); ++i) {
173     EXPECT_NE(imaginary, audio_ins[i].name);
174   }
175   for (size_t i = 0; i < audio_outs.size(); ++i) {
176     EXPECT_NE(imaginary, audio_outs[i].name);
177   }
178   for (size_t i = 0; i < video_ins.size(); ++i) {
179     EXPECT_NE(imaginary, video_ins[i].name);
180   }
181 }
182 
CompareDeviceList(std::vector<Device> & devices,const char * const device_list[],int list_size)183 static bool CompareDeviceList(std::vector<Device>& devices,
184     const char* const device_list[], int list_size) {
185   if (list_size != static_cast<int>(devices.size())) {
186     return false;
187   }
188   for (int i = 0; i < list_size; ++i) {
189     if (devices[i].name.compare(device_list[i]) != 0) {
190       return false;
191     }
192   }
193   return true;
194 }
195 
TEST(DeviceManagerTest,VerifyFilterDevices)196 TEST(DeviceManagerTest, VerifyFilterDevices) {
197   static const char* const kTotalDevicesName[] = {
198       "Google Camera Adapters are tons of fun.",
199       "device1",
200       "device2",
201       "device3",
202       "device4",
203       "device5",
204       "Google Camera Adapter 0",
205       "Google Camera Adapter 1",
206   };
207   static const char* const kFilteredDevicesName[] = {
208       "device2",
209       "device4",
210       "Google Camera Adapter",
211       NULL,
212   };
213   static const char* const kDevicesName[] = {
214       "device1",
215       "device3",
216       "device5",
217   };
218   std::vector<Device> devices;
219   for (int i = 0; i < ARRAY_SIZE(kTotalDevicesName); ++i) {
220     devices.push_back(Device(kTotalDevicesName[i], i));
221   }
222   EXPECT_TRUE(CompareDeviceList(devices, kTotalDevicesName,
223                                 ARRAY_SIZE(kTotalDevicesName)));
224   // Return false if given NULL as the exclusion list.
225   EXPECT_TRUE(DeviceManager::FilterDevices(&devices, NULL));
226   // The devices should not change.
227   EXPECT_TRUE(CompareDeviceList(devices, kTotalDevicesName,
228                                 ARRAY_SIZE(kTotalDevicesName)));
229   EXPECT_TRUE(DeviceManager::FilterDevices(&devices, kFilteredDevicesName));
230   EXPECT_TRUE(CompareDeviceList(devices, kDevicesName,
231                                 ARRAY_SIZE(kDevicesName)));
232 }
233 
234 #ifdef LINUX
235 class FakeV4LLookup : public cricket::V4LLookup {
236  public:
FakeV4LLookup(std::vector<std::string> device_paths)237   explicit FakeV4LLookup(std::vector<std::string> device_paths)
238       : device_paths_(device_paths) {}
239 
240  protected:
CheckIsV4L2Device(const std::string & device)241   bool CheckIsV4L2Device(const std::string& device) {
242     return std::find(device_paths_.begin(), device_paths_.end(), device)
243         != device_paths_.end();
244   }
245 
246  private:
247   std::vector<std::string> device_paths_;
248 };
249 
TEST(DeviceManagerTest,GetVideoCaptureDevices_K2_6)250 TEST(DeviceManagerTest, GetVideoCaptureDevices_K2_6) {
251   std::vector<std::string> devices;
252   devices.push_back("/dev/video0");
253   devices.push_back("/dev/video5");
254   cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices));
255 
256   std::vector<talk_base::FakeFileSystem::File> files;
257   files.push_back(talk_base::FakeFileSystem::File("/dev/video0", ""));
258   files.push_back(talk_base::FakeFileSystem::File("/dev/video5", ""));
259   files.push_back(talk_base::FakeFileSystem::File(
260       "/sys/class/video4linux/video0/name", "Video Device 1"));
261   files.push_back(talk_base::FakeFileSystem::File(
262       "/sys/class/video4linux/video1/model", "Bad Device"));
263   files.push_back(
264       talk_base::FakeFileSystem::File("/sys/class/video4linux/video5/model",
265                                       "Video Device 2"));
266   talk_base::FilesystemScope fs(new talk_base::FakeFileSystem(files));
267 
268   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
269   std::vector<Device> video_ins;
270   EXPECT_TRUE(dm->Init());
271   EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
272   EXPECT_EQ(2u, video_ins.size());
273   EXPECT_EQ("Video Device 1", video_ins.at(0).name);
274   EXPECT_EQ("Video Device 2", video_ins.at(1).name);
275 }
276 
TEST(DeviceManagerTest,GetVideoCaptureDevices_K2_4)277 TEST(DeviceManagerTest, GetVideoCaptureDevices_K2_4) {
278   std::vector<std::string> devices;
279   devices.push_back("/dev/video0");
280   devices.push_back("/dev/video5");
281   cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices));
282 
283   std::vector<talk_base::FakeFileSystem::File> files;
284   files.push_back(talk_base::FakeFileSystem::File("/dev/video0", ""));
285   files.push_back(talk_base::FakeFileSystem::File("/dev/video5", ""));
286   files.push_back(talk_base::FakeFileSystem::File(
287           "/proc/video/dev/video0",
288           "param1: value1\nname: Video Device 1\n param2: value2\n"));
289   files.push_back(talk_base::FakeFileSystem::File(
290           "/proc/video/dev/video1",
291           "param1: value1\nname: Bad Device\n param2: value2\n"));
292   files.push_back(talk_base::FakeFileSystem::File(
293           "/proc/video/dev/video5",
294           "param1: value1\nname:   Video Device 2\n param2: value2\n"));
295   talk_base::FilesystemScope fs(new talk_base::FakeFileSystem(files));
296 
297   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
298   std::vector<Device> video_ins;
299   EXPECT_TRUE(dm->Init());
300   EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
301   EXPECT_EQ(2u, video_ins.size());
302   EXPECT_EQ("Video Device 1", video_ins.at(0).name);
303   EXPECT_EQ("Video Device 2", video_ins.at(1).name);
304 }
305 
TEST(DeviceManagerTest,GetVideoCaptureDevices_KUnknown)306 TEST(DeviceManagerTest, GetVideoCaptureDevices_KUnknown) {
307   std::vector<std::string> devices;
308   devices.push_back("/dev/video0");
309   devices.push_back("/dev/video5");
310   cricket::V4LLookup::SetV4LLookup(new FakeV4LLookup(devices));
311 
312   std::vector<talk_base::FakeFileSystem::File> files;
313   files.push_back(talk_base::FakeFileSystem::File("/dev/video0", ""));
314   files.push_back(talk_base::FakeFileSystem::File("/dev/video1", ""));
315   files.push_back(talk_base::FakeFileSystem::File("/dev/video5", ""));
316   talk_base::FilesystemScope fs(new talk_base::FakeFileSystem(files));
317 
318   scoped_ptr<DeviceManagerInterface> dm(DeviceManagerFactory::Create());
319   std::vector<Device> video_ins;
320   EXPECT_TRUE(dm->Init());
321   EXPECT_TRUE(dm->GetVideoCaptureDevices(&video_ins));
322   EXPECT_EQ(2u, video_ins.size());
323   EXPECT_EQ("/dev/video0", video_ins.at(0).name);
324   EXPECT_EQ("/dev/video5", video_ins.at(1).name);
325 }
326 #endif  // LINUX
327