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