blob: d8564eaaf7eaedcdc3f76ad99d224fd528259163 [file] [log] [blame]
henrike@webrtc.org28e20752013-07-10 00:45:36 +00001/*
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
53using talk_base::Pathname;
54using talk_base::FileTimeType;
55using talk_base::scoped_ptr;
56using cricket::Device;
57using cricket::DeviceManager;
58using cricket::DeviceManagerFactory;
59using cricket::DeviceManagerInterface;
60
61const cricket::VideoFormat kVgaFormat(640, 480,
62 cricket::VideoFormat::FpsToInterval(30),
63 cricket::FOURCC_I420);
64const cricket::VideoFormat kHdFormat(1280, 720,
65 cricket::VideoFormat::FpsToInterval(30),
66 cricket::FOURCC_I420);
67
68class 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
78class 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.
98TEST(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
106TEST(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).
136TEST(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.
154TEST(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.
169TEST(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
188TEST(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
198TEST(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
220static 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
233TEST(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
272class 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
287TEST(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
314TEST(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
343TEST(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
367TEST(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
390TEST(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
410TEST_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
421TEST_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
431TEST_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
443TEST_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}