• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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