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