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 #include "talk/session/phone/devicemanager.h"
29
30 #if WIN32
31 #include <atlbase.h>
32 #include <dbt.h>
33 #include <strmif.h> // must come before ks.h
34 #include <ks.h>
35 #include <ksmedia.h>
36 #define INITGUID // For PKEY_AudioEndpoint_GUID
37 #include <mmdeviceapi.h>
38 #include <functiondiscoverykeys_devpkey.h>
39 #include <uuids.h>
40 #include "talk/base/win32.h" // ToUtf8
41 #include "talk/base/win32window.h"
42 #elif OSX
43 #include <CoreAudio/CoreAudio.h>
44 #include <QuickTime/QuickTime.h>
45 #elif LINUX
46 #include <libudev.h>
47 #include <unistd.h>
48 #include "talk/base/linux.h"
49 #include "talk/base/fileutils.h"
50 #include "talk/base/pathutils.h"
51 #include "talk/base/physicalsocketserver.h"
52 #include "talk/base/stream.h"
53 #include "talk/session/phone/libudevsymboltable.h"
54 #include "talk/session/phone/v4llookup.h"
55 #include "talk/sound/platformsoundsystem.h"
56 #include "talk/sound/platformsoundsystemfactory.h"
57 #include "talk/sound/sounddevicelocator.h"
58 #include "talk/sound/soundsysteminterface.h"
59 #endif
60
61 #include "talk/base/logging.h"
62 #include "talk/base/stringutils.h"
63 #include "talk/session/phone/mediaengine.h"
64
65 namespace cricket {
66 // Initialize to empty string.
67 const std::string DeviceManager::kDefaultDeviceName;
68
69 #ifdef WIN32
70 class DeviceWatcher : public talk_base::Win32Window {
71 public:
72 explicit DeviceWatcher(DeviceManager* dm);
73 bool Start();
74 void Stop();
75
76 private:
77 HDEVNOTIFY Register(REFGUID guid);
78 void Unregister(HDEVNOTIFY notify);
79 virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result);
80
81 DeviceManager* manager_;
82 HDEVNOTIFY audio_notify_;
83 HDEVNOTIFY video_notify_;
84 };
85 #elif defined(LINUX)
86 class DeviceWatcher : private talk_base::Dispatcher {
87 public:
88 explicit DeviceWatcher(DeviceManager* dm);
89 bool Start();
90 void Stop();
91
92 private:
93 virtual uint32 GetRequestedEvents();
94 virtual void OnPreEvent(uint32 ff);
95 virtual void OnEvent(uint32 ff, int err);
96 virtual int GetDescriptor();
97 virtual bool IsDescriptorClosed();
98
99 DeviceManager* manager_;
100 LibUDevSymbolTable libudev_;
101 struct udev* udev_;
102 struct udev_monitor* udev_monitor_;
103 bool registered_;
104 };
105 #define LATE(sym) LATESYM_GET(LibUDevSymbolTable, &libudev_, sym)
106 #elif defined(OSX)
107 class DeviceWatcher {
108 public:
109 explicit DeviceWatcher(DeviceManager* dm);
110 bool Start();
111 void Stop();
112 private:
113 DeviceManager* manager_;
114 void* impl_;
115 };
116 #elif defined(IOS) || defined(ANDROID)
117 // We don't use DeviceWatcher on iOS or Android, so just stub out a noop class.
118 class DeviceWatcher {
119 public:
DeviceWatcher(DeviceManager * dm)120 explicit DeviceWatcher(DeviceManager* dm) {}
Start()121 bool Start() { return true; }
Stop()122 void Stop() {}
123 };
124 #endif
125
126 #if !defined(LINUX) && !defined(IOS)
127 static bool ShouldDeviceBeIgnored(const std::string& device_name);
128 #endif
129 #ifndef OSX
130 static bool GetVideoDevices(std::vector<Device>* out);
131 #endif
132 #if WIN32
133 static const wchar_t kFriendlyName[] = L"FriendlyName";
134 static const wchar_t kDevicePath[] = L"DevicePath";
135 static const char kUsbDevicePathPrefix[] = "\\\\?\\usb";
136 static bool GetDevices(const CLSID& catid, std::vector<Device>* out);
137 static bool GetCoreAudioDevices(bool input, std::vector<Device>* devs);
138 static bool GetWaveDevices(bool input, std::vector<Device>* devs);
139 #elif OSX
140 static const int kVideoDeviceOpenAttempts = 3;
141 static const UInt32 kAudioDeviceNameLength = 64;
142 // Obj-C functions defined in devicemanager-mac.mm
143 extern void* CreateDeviceWatcherCallback(DeviceManager* dm);
144 extern void ReleaseDeviceWatcherCallback(void* impl);
145 extern bool GetQTKitVideoDevices(std::vector<Device>* out);
146 static bool GetAudioDeviceIDs(bool inputs, std::vector<AudioDeviceID>* out);
147 static bool GetAudioDeviceName(AudioDeviceID id, bool input, std::string* out);
148 #endif
149
DeviceManager()150 DeviceManager::DeviceManager()
151 : initialized_(false),
152 #if defined(WIN32)
153 need_couninitialize_(false),
154 #endif
155 watcher_(new DeviceWatcher(this))
156 #ifdef LINUX
157 , sound_system_(new PlatformSoundSystemFactory())
158 #endif
159 {
160 }
161
~DeviceManager()162 DeviceManager::~DeviceManager() {
163 if (initialized_) {
164 Terminate();
165 }
166 delete watcher_;
167 }
168
Init()169 bool DeviceManager::Init() {
170 if (!initialized_) {
171 #if defined(WIN32)
172 HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
173 need_couninitialize_ = SUCCEEDED(hr);
174 if (FAILED(hr)) {
175 LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr;
176 if (hr != RPC_E_CHANGED_MODE) {
177 return false;
178 }
179 }
180 #endif
181 if (!watcher_->Start()) {
182 return false;
183 }
184 initialized_ = true;
185 }
186 return true;
187 }
188
Terminate()189 void DeviceManager::Terminate() {
190 if (initialized_) {
191 watcher_->Stop();
192 #if defined(WIN32)
193 if (need_couninitialize_) {
194 CoUninitialize();
195 need_couninitialize_ = false;
196 }
197 #endif
198 initialized_ = false;
199 }
200 }
201
GetCapabilities()202 int DeviceManager::GetCapabilities() {
203 std::vector<Device> devices;
204 int caps = MediaEngine::VIDEO_RECV;
205 if (GetAudioInputDevices(&devices) && !devices.empty()) {
206 caps |= MediaEngine::AUDIO_SEND;
207 }
208 if (GetAudioOutputDevices(&devices) && !devices.empty()) {
209 caps |= MediaEngine::AUDIO_RECV;
210 }
211 if (GetVideoCaptureDevices(&devices) && !devices.empty()) {
212 caps |= MediaEngine::VIDEO_SEND;
213 }
214 return caps;
215 }
216
GetAudioInputDevices(std::vector<Device> * devices)217 bool DeviceManager::GetAudioInputDevices(std::vector<Device>* devices) {
218 return GetAudioDevicesByPlatform(true, devices);
219 }
220
GetAudioOutputDevices(std::vector<Device> * devices)221 bool DeviceManager::GetAudioOutputDevices(std::vector<Device>* devices) {
222 return GetAudioDevicesByPlatform(false, devices);
223 }
224
GetAudioInputDevice(const std::string & name,Device * out)225 bool DeviceManager::GetAudioInputDevice(const std::string& name, Device* out) {
226 return GetAudioDevice(true, name, out);
227 }
228
GetAudioOutputDevice(const std::string & name,Device * out)229 bool DeviceManager::GetAudioOutputDevice(const std::string& name, Device* out) {
230 return GetAudioDevice(false, name, out);
231 }
232
233 #ifdef OSX
FilterDevice(const Device & d)234 static bool FilterDevice(const Device& d) {
235 return ShouldDeviceBeIgnored(d.name);
236 }
237 #endif
238
GetVideoCaptureDevices(std::vector<Device> * devices)239 bool DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
240 devices->clear();
241 #ifdef OSX
242 if (GetQTKitVideoDevices(devices)) {
243 // Now filter out any known incompatible devices
244 devices->erase(remove_if(devices->begin(), devices->end(), FilterDevice),
245 devices->end());
246 return true;
247 }
248 return false;
249 #else
250 return GetVideoDevices(devices);
251 #endif
252 }
253
GetDefaultVideoCaptureDevice(Device * device)254 bool DeviceManager::GetDefaultVideoCaptureDevice(Device* device) {
255 bool ret = false;
256 #if WIN32
257 // If there are multiple capture devices, we want the first USB one.
258 // This avoids issues with defaulting to virtual cameras or grabber cards.
259 std::vector<Device> devices;
260 ret = (GetVideoDevices(&devices) && !devices.empty());
261 if (ret) {
262 *device = devices[0];
263 for (size_t i = 0; i < devices.size(); ++i) {
264 if (strnicmp(devices[i].id.c_str(), kUsbDevicePathPrefix,
265 ARRAY_SIZE(kUsbDevicePathPrefix) - 1) == 0) {
266 *device = devices[i];
267 break;
268 }
269 }
270 }
271 #else
272 // We just return the first device.
273 std::vector<Device> devices;
274 ret = (GetVideoCaptureDevices(&devices) && !devices.empty());
275 if (ret) {
276 *device = devices[0];
277 }
278 #endif
279 return ret;
280 }
281
GetVideoCaptureDevice(const std::string & name,Device * out)282 bool DeviceManager::GetVideoCaptureDevice(const std::string& name,
283 Device* out) {
284 // If the name is empty, return the default device.
285 if (name.empty() || name == kDefaultDeviceName) {
286 return GetDefaultVideoCaptureDevice(out);
287 }
288
289 std::vector<Device> devices;
290 if (!GetVideoCaptureDevices(&devices)) {
291 return false;
292 }
293
294 for (std::vector<Device>::const_iterator it = devices.begin();
295 it != devices.end(); ++it) {
296 if (name == it->name) {
297 *out = *it;
298 return true;
299 }
300 }
301
302 return false;
303 }
304
GetAudioDevice(bool is_input,const std::string & name,Device * out)305 bool DeviceManager::GetAudioDevice(bool is_input, const std::string& name,
306 Device* out) {
307 // If the name is empty, return the default device id.
308 if (name.empty() || name == kDefaultDeviceName) {
309 *out = Device(name, -1);
310 return true;
311 }
312
313 std::vector<Device> devices;
314 bool ret = is_input ? GetAudioInputDevices(&devices) :
315 GetAudioOutputDevices(&devices);
316 if (ret) {
317 ret = false;
318 for (size_t i = 0; i < devices.size(); ++i) {
319 if (devices[i].name == name) {
320 *out = devices[i];
321 ret = true;
322 break;
323 }
324 }
325 }
326 return ret;
327 }
328
GetAudioDevicesByPlatform(bool input,std::vector<Device> * devs)329 bool DeviceManager::GetAudioDevicesByPlatform(bool input,
330 std::vector<Device>* devs) {
331 devs->clear();
332
333 #if defined(LINUX)
334 if (!sound_system_.get()) {
335 return false;
336 }
337 SoundSystemInterface::SoundDeviceLocatorList list;
338 bool success;
339 if (input) {
340 success = sound_system_->EnumerateCaptureDevices(&list);
341 } else {
342 success = sound_system_->EnumeratePlaybackDevices(&list);
343 }
344 if (!success) {
345 LOG(LS_ERROR) << "Can't enumerate devices";
346 sound_system_.release();
347 return false;
348 }
349 int index = 0;
350 for (SoundSystemInterface::SoundDeviceLocatorList::iterator i = list.begin();
351 i != list.end();
352 ++i, ++index) {
353 devs->push_back(Device((*i)->name(), index));
354 }
355 SoundSystemInterface::ClearSoundDeviceLocatorList(&list);
356 sound_system_.release();
357 return true;
358
359 #elif defined(WIN32)
360 if (talk_base::IsWindowsVistaOrLater()) {
361 return GetCoreAudioDevices(input, devs);
362 } else {
363 return GetWaveDevices(input, devs);
364 }
365
366 #elif defined(OSX)
367 std::vector<AudioDeviceID> dev_ids;
368 bool ret = GetAudioDeviceIDs(input, &dev_ids);
369 if (ret) {
370 for (size_t i = 0; i < dev_ids.size(); ++i) {
371 std::string name;
372 if (GetAudioDeviceName(dev_ids[i], input, &name)) {
373 devs->push_back(Device(name, dev_ids[i]));
374 }
375 }
376 }
377 return ret;
378
379 #else
380 return false;
381 #endif
382 }
383
384 #if defined(WIN32)
GetVideoDevices(std::vector<Device> * devices)385 bool GetVideoDevices(std::vector<Device>* devices) {
386 return GetDevices(CLSID_VideoInputDeviceCategory, devices);
387 }
388
GetDevices(const CLSID & catid,std::vector<Device> * devices)389 bool GetDevices(const CLSID& catid, std::vector<Device>* devices) {
390 HRESULT hr;
391
392 // CComPtr is a scoped pointer that will be auto released when going
393 // out of scope. CoUninitialize must not be called before the
394 // release.
395 CComPtr<ICreateDevEnum> sys_dev_enum;
396 CComPtr<IEnumMoniker> cam_enum;
397 if (FAILED(hr = sys_dev_enum.CoCreateInstance(CLSID_SystemDeviceEnum)) ||
398 FAILED(hr = sys_dev_enum->CreateClassEnumerator(catid, &cam_enum, 0))) {
399 LOG(LS_ERROR) << "Failed to create device enumerator, hr=" << hr;
400 return false;
401 }
402
403 // Only enum devices if CreateClassEnumerator returns S_OK. If there are no
404 // devices available, S_FALSE will be returned, but enumMk will be NULL.
405 if (hr == S_OK) {
406 CComPtr<IMoniker> mk;
407 while (cam_enum->Next(1, &mk, NULL) == S_OK) {
408 CComPtr<IPropertyBag> bag;
409 if (SUCCEEDED(mk->BindToStorage(NULL, NULL,
410 __uuidof(bag), reinterpret_cast<void**>(&bag)))) {
411 CComVariant name, path;
412 std::string name_str, path_str;
413 if (SUCCEEDED(bag->Read(kFriendlyName, &name, 0)) &&
414 name.vt == VT_BSTR) {
415 name_str = talk_base::ToUtf8(name.bstrVal);
416 if (!ShouldDeviceBeIgnored(name_str)) {
417 // Get the device id if one exists.
418 if (SUCCEEDED(bag->Read(kDevicePath, &path, 0)) &&
419 path.vt == VT_BSTR) {
420 path_str = talk_base::ToUtf8(path.bstrVal);
421 }
422
423 devices->push_back(Device(name_str, path_str));
424 }
425 }
426 }
427 mk = NULL;
428 }
429 }
430
431 return true;
432 }
433
GetStringProp(IPropertyStore * bag,PROPERTYKEY key,std::string * out)434 HRESULT GetStringProp(IPropertyStore* bag, PROPERTYKEY key, std::string* out) {
435 out->clear();
436 PROPVARIANT var;
437 PropVariantInit(&var);
438
439 HRESULT hr = bag->GetValue(key, &var);
440 if (SUCCEEDED(hr)) {
441 if (var.pwszVal)
442 *out = talk_base::ToUtf8(var.pwszVal);
443 else
444 hr = E_FAIL;
445 }
446
447 PropVariantClear(&var);
448 return hr;
449 }
450
451 // Adapted from http://msdn.microsoft.com/en-us/library/dd370812(v=VS.85).aspx
CricketDeviceFromImmDevice(IMMDevice * device,Device * out)452 HRESULT CricketDeviceFromImmDevice(IMMDevice* device, Device* out) {
453 CComPtr<IPropertyStore> props;
454
455 HRESULT hr = device->OpenPropertyStore(STGM_READ, &props);
456 if (FAILED(hr)) {
457 return hr;
458 }
459
460 // Get the endpoint's name and id.
461 std::string name, guid;
462 hr = GetStringProp(props, PKEY_Device_FriendlyName, &name);
463 if (SUCCEEDED(hr)) {
464 hr = GetStringProp(props, PKEY_AudioEndpoint_GUID, &guid);
465
466 if (SUCCEEDED(hr)) {
467 out->name = name;
468 out->id = guid;
469 }
470 }
471 return hr;
472 }
473
GetCoreAudioDevices(bool input,std::vector<Device> * devs)474 bool GetCoreAudioDevices(bool input, std::vector<Device>* devs) {
475 HRESULT hr = S_OK;
476 CComPtr<IMMDeviceEnumerator> enumerator;
477
478 hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
479 __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&enumerator));
480 if (SUCCEEDED(hr)) {
481 CComPtr<IMMDeviceCollection> devices;
482 hr = enumerator->EnumAudioEndpoints((input ? eCapture : eRender),
483 DEVICE_STATE_ACTIVE, &devices);
484 if (SUCCEEDED(hr)) {
485 unsigned int count;
486 hr = devices->GetCount(&count);
487
488 if (SUCCEEDED(hr)) {
489 for (unsigned int i = 0; i < count; i++) {
490 CComPtr<IMMDevice> device;
491
492 // Get pointer to endpoint number i.
493 hr = devices->Item(i, &device);
494 if (FAILED(hr)) {
495 break;
496 }
497
498 Device dev;
499 hr = CricketDeviceFromImmDevice(device, &dev);
500 if (SUCCEEDED(hr)) {
501 devs->push_back(dev);
502 } else {
503 LOG(LS_WARNING) << "Unable to query IMM Device, skipping. HR="
504 << hr;
505 hr = S_FALSE;
506 }
507 }
508 }
509 }
510 }
511
512 if (!SUCCEEDED(hr)) {
513 LOG(LS_WARNING) << "GetCoreAudioDevices failed with hr " << hr;
514 return false;
515 }
516 return true;
517 }
518
GetWaveDevices(bool input,std::vector<Device> * devs)519 bool GetWaveDevices(bool input, std::vector<Device>* devs) {
520 // Note, we don't use the System Device Enumerator interface here since it
521 // adds lots of pseudo-devices to the list, such as DirectSound and Wave
522 // variants of the same device.
523 if (input) {
524 int num_devs = waveInGetNumDevs();
525 for (int i = 0; i < num_devs; ++i) {
526 WAVEINCAPS caps;
527 if (waveInGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
528 caps.wChannels > 0) {
529 devs->push_back(Device(talk_base::ToUtf8(caps.szPname),
530 talk_base::ToString(i)));
531 }
532 }
533 } else {
534 int num_devs = waveOutGetNumDevs();
535 for (int i = 0; i < num_devs; ++i) {
536 WAVEOUTCAPS caps;
537 if (waveOutGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
538 caps.wChannels > 0) {
539 devs->push_back(Device(talk_base::ToUtf8(caps.szPname), i));
540 }
541 }
542 }
543 return true;
544 }
545
DeviceWatcher(DeviceManager * manager)546 DeviceWatcher::DeviceWatcher(DeviceManager* manager)
547 : manager_(manager), audio_notify_(NULL), video_notify_(NULL) {
548 }
549
Start()550 bool DeviceWatcher::Start() {
551 if (!Create(NULL, _T("libjingle DeviceWatcher Window"),
552 0, 0, 0, 0, 0, 0)) {
553 return false;
554 }
555
556 audio_notify_ = Register(KSCATEGORY_AUDIO);
557 if (!audio_notify_) {
558 Stop();
559 return false;
560 }
561
562 video_notify_ = Register(KSCATEGORY_VIDEO);
563 if (!video_notify_) {
564 Stop();
565 return false;
566 }
567
568 return true;
569 }
570
Stop()571 void DeviceWatcher::Stop() {
572 UnregisterDeviceNotification(video_notify_);
573 video_notify_ = NULL;
574 UnregisterDeviceNotification(audio_notify_);
575 audio_notify_ = NULL;
576 Destroy();
577 }
578
Register(REFGUID guid)579 HDEVNOTIFY DeviceWatcher::Register(REFGUID guid) {
580 DEV_BROADCAST_DEVICEINTERFACE dbdi;
581 dbdi.dbcc_size = sizeof(dbdi);
582 dbdi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
583 dbdi.dbcc_classguid = guid;
584 dbdi.dbcc_name[0] = '\0';
585 return RegisterDeviceNotification(handle(), &dbdi,
586 DEVICE_NOTIFY_WINDOW_HANDLE);
587 }
588
Unregister(HDEVNOTIFY handle)589 void DeviceWatcher::Unregister(HDEVNOTIFY handle) {
590 UnregisterDeviceNotification(handle);
591 }
592
OnMessage(UINT uMsg,WPARAM wParam,LPARAM lParam,LRESULT & result)593 bool DeviceWatcher::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
594 LRESULT& result) {
595 if (uMsg == WM_DEVICECHANGE) {
596 if (wParam == DBT_DEVICEARRIVAL ||
597 wParam == DBT_DEVICEREMOVECOMPLETE) {
598 DEV_BROADCAST_DEVICEINTERFACE* dbdi =
599 reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lParam);
600 if (dbdi->dbcc_classguid == KSCATEGORY_AUDIO ||
601 dbdi->dbcc_classguid == KSCATEGORY_VIDEO) {
602 manager_->OnDevicesChange();
603 }
604 }
605 result = 0;
606 return true;
607 }
608
609 return false;
610 }
611 #elif defined(OSX)
GetAudioDeviceIDs(bool input,std::vector<AudioDeviceID> * out_dev_ids)612 static bool GetAudioDeviceIDs(bool input,
613 std::vector<AudioDeviceID>* out_dev_ids) {
614 UInt32 propsize;
615 OSErr err = AudioHardwareGetPropertyInfo(kAudioHardwarePropertyDevices,
616 &propsize, NULL);
617 if (0 != err) {
618 LOG(LS_ERROR) << "Couldn't get information about property, "
619 << "so no device list acquired.";
620 return false;
621 }
622
623 size_t num_devices = propsize / sizeof(AudioDeviceID);
624 talk_base::scoped_array<AudioDeviceID> device_ids(
625 new AudioDeviceID[num_devices]);
626
627 err = AudioHardwareGetProperty(kAudioHardwarePropertyDevices,
628 &propsize, device_ids.get());
629 if (0 != err) {
630 LOG(LS_ERROR) << "Failed to get device ids, "
631 << "so no device listing acquired.";
632 return false;
633 }
634
635 for (size_t i = 0; i < num_devices; ++i) {
636 AudioDeviceID an_id = device_ids[i];
637 // find out the number of channels for this direction
638 // (input/output) on this device -
639 // we'll ignore anything with no channels.
640 err = AudioDeviceGetPropertyInfo(an_id, 0, input,
641 kAudioDevicePropertyStreams,
642 &propsize, NULL);
643 if (0 == err) {
644 unsigned num_channels = propsize / sizeof(AudioStreamID);
645 if (0 < num_channels) {
646 out_dev_ids->push_back(an_id);
647 }
648 } else {
649 LOG(LS_ERROR) << "No property info for stream property for device id "
650 << an_id << "(is_input == " << input
651 << "), so not including it in the list.";
652 }
653 }
654
655 return true;
656 }
657
GetAudioDeviceName(AudioDeviceID id,bool input,std::string * out_name)658 static bool GetAudioDeviceName(AudioDeviceID id,
659 bool input,
660 std::string* out_name) {
661 UInt32 nameLength = kAudioDeviceNameLength;
662 char name[kAudioDeviceNameLength + 1];
663 OSErr err = AudioDeviceGetProperty(id, 0, input,
664 kAudioDevicePropertyDeviceName,
665 &nameLength, name);
666 if (0 != err) {
667 LOG(LS_ERROR) << "No name acquired for device id " << id;
668 return false;
669 }
670
671 *out_name = name;
672 return true;
673 }
674
DeviceWatcher(DeviceManager * manager)675 DeviceWatcher::DeviceWatcher(DeviceManager* manager)
676 : manager_(manager), impl_(NULL) {
677 }
678
Start()679 bool DeviceWatcher::Start() {
680 if (!impl_) {
681 impl_ = CreateDeviceWatcherCallback(manager_);
682 }
683 return impl_ != NULL;
684 }
685
Stop()686 void DeviceWatcher::Stop() {
687 if (impl_) {
688 ReleaseDeviceWatcherCallback(impl_);
689 impl_ = NULL;
690 }
691 }
692
693 #elif defined(LINUX)
694 static const std::string kVideoMetaPathK2_4("/proc/video/dev/");
695 static const std::string kVideoMetaPathK2_6("/sys/class/video4linux/");
696
697 enum MetaType { M2_4, M2_6, NONE };
698
ScanDeviceDirectory(const std::string & devdir,std::vector<Device> * devices)699 static void ScanDeviceDirectory(const std::string& devdir,
700 std::vector<Device>* devices) {
701 talk_base::scoped_ptr<talk_base::DirectoryIterator> directoryIterator(
702 talk_base::Filesystem::IterateDirectory());
703
704 if (directoryIterator->Iterate(talk_base::Pathname(devdir))) {
705 do {
706 std::string filename = directoryIterator->Name();
707 std::string device_name = devdir + filename;
708 if (!directoryIterator->IsDots()) {
709 if (filename.find("video") == 0 &&
710 V4LLookup::IsV4L2Device(device_name)) {
711 devices->push_back(Device(device_name, device_name));
712 }
713 }
714 } while (directoryIterator->Next());
715 }
716 }
717
GetVideoDeviceNameK2_6(const std::string & device_meta_path)718 static std::string GetVideoDeviceNameK2_6(const std::string& device_meta_path) {
719 std::string device_name;
720
721 talk_base::scoped_ptr<talk_base::FileStream> device_meta_stream(
722 talk_base::Filesystem::OpenFile(device_meta_path, "r"));
723
724 if (device_meta_stream.get() != NULL) {
725 if (device_meta_stream->ReadLine(&device_name) != talk_base::SR_SUCCESS) {
726 LOG(LS_ERROR) << "Failed to read V4L2 device meta " << device_meta_path;
727 }
728 device_meta_stream->Close();
729 }
730
731 return device_name;
732 }
733
Trim(const std::string & s,const std::string & drop=" \\t")734 static std::string Trim(const std::string& s, const std::string& drop = " \t") {
735 std::string::size_type first = s.find_first_not_of(drop);
736 std::string::size_type last = s.find_last_not_of(drop);
737
738 if (first == std::string::npos || last == std::string::npos)
739 return std::string("");
740
741 return s.substr(first, last - first + 1);
742 }
743
GetVideoDeviceNameK2_4(const std::string & device_meta_path)744 static std::string GetVideoDeviceNameK2_4(const std::string& device_meta_path) {
745 talk_base::ConfigParser::MapVector all_values;
746
747 talk_base::ConfigParser config_parser;
748 talk_base::FileStream* file_stream =
749 talk_base::Filesystem::OpenFile(device_meta_path, "r");
750
751 if (file_stream == NULL) return "";
752
753 config_parser.Attach(file_stream);
754 config_parser.Parse(&all_values);
755
756 for (talk_base::ConfigParser::MapVector::iterator i = all_values.begin();
757 i != all_values.end(); ++i) {
758 talk_base::ConfigParser::SimpleMap::iterator device_name_i =
759 i->find("name");
760
761 if (device_name_i != i->end()) {
762 return device_name_i->second;
763 }
764 }
765
766 return "";
767 }
768
GetVideoDeviceName(MetaType meta,const std::string & device_file_name)769 static std::string GetVideoDeviceName(MetaType meta,
770 const std::string& device_file_name) {
771 std::string device_meta_path;
772 std::string device_name;
773 std::string meta_file_path;
774
775 if (meta == M2_6) {
776 meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/name";
777
778 LOG(LS_INFO) << "Trying " + meta_file_path;
779 device_name = GetVideoDeviceNameK2_6(meta_file_path);
780
781 if (device_name.empty()) {
782 meta_file_path = kVideoMetaPathK2_6 + device_file_name + "/model";
783
784 LOG(LS_INFO) << "Trying " << meta_file_path;
785 device_name = GetVideoDeviceNameK2_6(meta_file_path);
786 }
787 } else {
788 meta_file_path = kVideoMetaPathK2_4 + device_file_name;
789 LOG(LS_INFO) << "Trying " << meta_file_path;
790 device_name = GetVideoDeviceNameK2_4(meta_file_path);
791 }
792
793 if (device_name.empty()) {
794 device_name = "/dev/" + device_file_name;
795 LOG(LS_ERROR)
796 << "Device name not found, defaulting to device path " << device_name;
797 }
798
799 LOG(LS_INFO) << "Name for " << device_file_name << " is " << device_name;
800
801 return Trim(device_name);
802 }
803
ScanV4L2Devices(std::vector<Device> * devices)804 static void ScanV4L2Devices(std::vector<Device>* devices) {
805 LOG(LS_INFO) << ("Enumerating V4L2 devices");
806
807 MetaType meta;
808 std::string metadata_dir;
809
810 talk_base::scoped_ptr<talk_base::DirectoryIterator> directoryIterator(
811 talk_base::Filesystem::IterateDirectory());
812
813 // Try and guess kernel version
814 if (directoryIterator->Iterate(kVideoMetaPathK2_6)) {
815 meta = M2_6;
816 metadata_dir = kVideoMetaPathK2_6;
817 } else if (directoryIterator->Iterate(kVideoMetaPathK2_4)) {
818 meta = M2_4;
819 metadata_dir = kVideoMetaPathK2_4;
820 } else {
821 meta = NONE;
822 }
823
824 if (meta != NONE) {
825 LOG(LS_INFO) << "V4L2 device metadata found at " << metadata_dir;
826
827 do {
828 std::string filename = directoryIterator->Name();
829
830 if (filename.find("video") == 0) {
831 std::string device_path = "/dev/" + filename;
832
833 if (V4LLookup::IsV4L2Device(device_path)) {
834 devices->push_back(
835 Device(GetVideoDeviceName(meta, filename), device_path));
836 }
837 }
838 } while (directoryIterator->Next());
839 } else {
840 LOG(LS_ERROR) << "Unable to detect v4l2 metadata directory";
841 }
842
843 if (devices->size() == 0) {
844 LOG(LS_INFO) << "Plan B. Scanning all video devices in /dev directory";
845 ScanDeviceDirectory("/dev/", devices);
846 }
847
848 LOG(LS_INFO) << "Total V4L2 devices found : " << devices->size();
849 }
850
GetVideoDevices(std::vector<Device> * devices)851 static bool GetVideoDevices(std::vector<Device>* devices) {
852 ScanV4L2Devices(devices);
853 return true;
854 }
855
DeviceWatcher(DeviceManager * dm)856 DeviceWatcher::DeviceWatcher(DeviceManager* dm)
857 : manager_(dm), udev_(NULL), udev_monitor_(NULL), registered_(false) {}
858
Start()859 bool DeviceWatcher::Start() {
860 // We deliberately return true in the failure paths here because libudev is
861 // not a critical component of a Linux system so it may not be present/usable,
862 // and we don't want to halt DeviceManager initialization in such a case.
863 if (!libudev_.Load()) {
864 LOG(LS_WARNING) << "libudev not present/usable; DeviceWatcher disabled";
865 return true;
866 }
867 udev_ = LATE(udev_new)();
868 if (!udev_) {
869 LOG_ERR(LS_ERROR) << "udev_new()";
870 return true;
871 }
872 // The second argument here is the event source. It can be either "kernel" or
873 // "udev", but "udev" is the only correct choice. Apps listen on udev and the
874 // udev daemon in turn listens on the kernel.
875 udev_monitor_ = LATE(udev_monitor_new_from_netlink)(udev_, "udev");
876 if (!udev_monitor_) {
877 LOG_ERR(LS_ERROR) << "udev_monitor_new_from_netlink()";
878 return true;
879 }
880 // We only listen for changes in the video devices. Audio devices are more or
881 // less unimportant because receiving device change notifications really only
882 // matters for broadcasting updated send/recv capabilities based on whether
883 // there is at least one device available, and almost all computers have at
884 // least one audio device. Also, PulseAudio device notifications don't come
885 // from the udev daemon, they come from the PulseAudio daemon, so we'd only
886 // want to listen for audio device changes from udev if using ALSA. For
887 // simplicity, we don't bother with any audio stuff at all.
888 if (LATE(udev_monitor_filter_add_match_subsystem_devtype)(udev_monitor_,
889 "video4linux",
890 NULL) < 0) {
891 LOG_ERR(LS_ERROR) << "udev_monitor_filter_add_match_subsystem_devtype()";
892 return true;
893 }
894 if (LATE(udev_monitor_enable_receiving)(udev_monitor_) < 0) {
895 LOG_ERR(LS_ERROR) << "udev_monitor_enable_receiving()";
896 return true;
897 }
898 static_cast<talk_base::PhysicalSocketServer*>(
899 talk_base::Thread::Current()->socketserver())->Add(this);
900 registered_ = true;
901 return true;
902 }
903
Stop()904 void DeviceWatcher::Stop() {
905 if (registered_) {
906 static_cast<talk_base::PhysicalSocketServer*>(
907 talk_base::Thread::Current()->socketserver())->Remove(this);
908 registered_ = false;
909 }
910 if (udev_monitor_) {
911 LATE(udev_monitor_unref)(udev_monitor_);
912 udev_monitor_ = NULL;
913 }
914 if (udev_) {
915 LATE(udev_unref)(udev_);
916 udev_ = NULL;
917 }
918 libudev_.Unload();
919 }
920
GetRequestedEvents()921 uint32 DeviceWatcher::GetRequestedEvents() {
922 return talk_base::DE_READ;
923 }
924
OnPreEvent(uint32 ff)925 void DeviceWatcher::OnPreEvent(uint32 ff) {
926 // Nothing to do.
927 }
928
OnEvent(uint32 ff,int err)929 void DeviceWatcher::OnEvent(uint32 ff, int err) {
930 udev_device* device = LATE(udev_monitor_receive_device)(udev_monitor_);
931 if (!device) {
932 // Probably the socket connection to the udev daemon was terminated (perhaps
933 // the daemon crashed or is being restarted?).
934 LOG_ERR(LS_WARNING) << "udev_monitor_receive_device()";
935 // Stop listening to avoid potential livelock (an fd with EOF in it is
936 // always considered readable).
937 static_cast<talk_base::PhysicalSocketServer*>(
938 talk_base::Thread::Current()->socketserver())->Remove(this);
939 registered_ = false;
940 return;
941 }
942 // Else we read the device successfully.
943
944 // Since we already have our own filesystem-based device enumeration code, we
945 // simply re-enumerate rather than inspecting the device event.
946 LATE(udev_device_unref)(device);
947 manager_->OnDevicesChange();
948 }
949
GetDescriptor()950 int DeviceWatcher::GetDescriptor() {
951 return LATE(udev_monitor_get_fd)(udev_monitor_);
952 }
953
IsDescriptorClosed()954 bool DeviceWatcher::IsDescriptorClosed() {
955 // If it is closed then we will just get an error in
956 // udev_monitor_receive_device and unregister, so we don't need to check for
957 // it separately.
958 return false;
959 }
960
961 #endif
962
963 // TODO: Try to get hold of a copy of Final Cut to understand why we
964 // crash while scanning their components on OS X.
965 #if !defined(LINUX) && !defined(IOS)
ShouldDeviceBeIgnored(const std::string & device_name)966 static bool ShouldDeviceBeIgnored(const std::string& device_name) {
967 static const char* const kFilteredDevices[] = {
968 "Google Camera Adapter", // Our own magiccams
969 #ifdef WIN32
970 "Asus virtual Camera", // Bad Asus desktop virtual cam
971 "Bluetooth Video", // Bad Sony viao bluetooth sharing driver
972 #elif OSX
973 "DVCPRO HD", // Final cut
974 "Sonix SN9C201p", // Crashes in OpenAComponent and CloseComponent
975 #endif
976 };
977
978 for (int i = 0; i < ARRAY_SIZE(kFilteredDevices); ++i) {
979 if (strnicmp(device_name.c_str(), kFilteredDevices[i],
980 strlen(kFilteredDevices[i])) == 0) {
981 LOG(LS_INFO) << "Ignoring device " << device_name;
982 return true;
983 }
984 }
985 return false;
986 }
987 #endif
988
989 }; // namespace cricket
990