Home | History | Annotate | Download | only in phone
      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:
    120   explicit DeviceWatcher(DeviceManager* dm) {}
    121   bool Start() { return true; }
    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 
    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 
    162 DeviceManager::~DeviceManager() {
    163   if (initialized_) {
    164     Terminate();
    165   }
    166   delete watcher_;
    167 }
    168 
    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 
    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 
    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 
    217 bool DeviceManager::GetAudioInputDevices(std::vector<Device>* devices) {
    218   return GetAudioDevicesByPlatform(true, devices);
    219 }
    220 
    221 bool DeviceManager::GetAudioOutputDevices(std::vector<Device>* devices) {
    222   return GetAudioDevicesByPlatform(false, devices);
    223 }
    224 
    225 bool DeviceManager::GetAudioInputDevice(const std::string& name, Device* out) {
    226   return GetAudioDevice(true, name, out);
    227 }
    228 
    229 bool DeviceManager::GetAudioOutputDevice(const std::string& name, Device* out) {
    230   return GetAudioDevice(false, name, out);
    231 }
    232 
    233 #ifdef OSX
    234 static bool FilterDevice(const Device& d) {
    235   return ShouldDeviceBeIgnored(d.name);
    236 }
    237 #endif
    238 
    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 
    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 
    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 
    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 
    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)
    385 bool GetVideoDevices(std::vector<Device>* devices) {
    386   return GetDevices(CLSID_VideoInputDeviceCategory, devices);
    387 }
    388 
    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 
    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
    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 
    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 
    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 
    546 DeviceWatcher::DeviceWatcher(DeviceManager* manager)
    547     : manager_(manager), audio_notify_(NULL), video_notify_(NULL) {
    548 }
    549 
    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 
    571 void DeviceWatcher::Stop() {
    572   UnregisterDeviceNotification(video_notify_);
    573   video_notify_ = NULL;
    574   UnregisterDeviceNotification(audio_notify_);
    575   audio_notify_ = NULL;
    576   Destroy();
    577 }
    578 
    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 
    589 void DeviceWatcher::Unregister(HDEVNOTIFY handle) {
    590   UnregisterDeviceNotification(handle);
    591 }
    592 
    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)
    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 
    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 
    675 DeviceWatcher::DeviceWatcher(DeviceManager* manager)
    676     : manager_(manager), impl_(NULL) {
    677 }
    678 
    679 bool DeviceWatcher::Start() {
    680   if (!impl_) {
    681     impl_ = CreateDeviceWatcherCallback(manager_);
    682   }
    683   return impl_ != NULL;
    684 }
    685 
    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 
    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 
    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 
    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 
    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 
    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 
    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 
    851 static bool GetVideoDevices(std::vector<Device>* devices) {
    852   ScanV4L2Devices(devices);
    853   return true;
    854 }
    855 
    856 DeviceWatcher::DeviceWatcher(DeviceManager* dm)
    857     : manager_(dm), udev_(NULL), udev_monitor_(NULL), registered_(false) {}
    858 
    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 
    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 
    921 uint32 DeviceWatcher::GetRequestedEvents() {
    922   return talk_base::DE_READ;
    923 }
    924 
    925 void DeviceWatcher::OnPreEvent(uint32 ff) {
    926   // Nothing to do.
    927 }
    928 
    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 
    950 int DeviceWatcher::GetDescriptor() {
    951   return LATE(udev_monitor_get_fd)(udev_monitor_);
    952 }
    953 
    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)
    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