15976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org/*
25976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * libjingle
35976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * Copyright 2004 Google Inc.
45976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *
55976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * Redistribution and use in source and binary forms, with or without
65976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * modification, are permitted provided that the following conditions are met:
75976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *
85976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *  1. Redistributions of source code must retain the above copyright notice,
95976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *     this list of conditions and the following disclaimer.
105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *  2. Redistributions in binary form must reproduce the above copyright notice,
115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *     this list of conditions and the following disclaimer in the documentation
125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *     and/or other materials provided with the distribution.
135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *  3. The name of the author may not be used to endorse or promote products
145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *     derived from this software without specific prior written permission.
155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org *
165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org */
275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/media/devices/win32devicemanager.h"
295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <atlbase.h>
315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <dbt.h>
325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <strmif.h>  // must come before ks.h
335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <ks.h>
345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <ksmedia.h>
355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#define INITGUID  // For PKEY_AudioEndpoint_GUID
365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <mmdeviceapi.h>
375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <mmsystem.h>
385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <functiondiscoverykeys_devpkey.h>
395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include <uuids.h>
405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/base/logging.h"
425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/base/stringutils.h"
435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/base/thread.h"
445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/base/win32.h"  // ToUtf8
455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/base/win32window.h"
465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "talk/media/base/mediacommon.h"
475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#ifdef HAVE_LOGITECH_HEADERS
485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#include "third_party/logitech/files/logitechquickcam.h"
495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#endif
505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgnamespace cricket {
525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgDeviceManagerInterface* DeviceManagerFactory::Create() {
545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return new Win32DeviceManager();
555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgclass Win32DeviceWatcher
585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    : public DeviceWatcher,
595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      public talk_base::Win32Window {
605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org public:
615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  explicit Win32DeviceWatcher(Win32DeviceManager* dm);
625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual ~Win32DeviceWatcher();
635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual bool Start();
645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual void Stop();
655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org private:
675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  HDEVNOTIFY Register(REFGUID guid);
685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  void Unregister(HDEVNOTIFY notify);
695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result);
705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  Win32DeviceManager* manager_;
725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  HDEVNOTIFY audio_notify_;
735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  HDEVNOTIFY video_notify_;
745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgstatic const char* kFilteredAudioDevicesName[] = {
775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    NULL,
785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgstatic const char* const kFilteredVideoDevicesName[] =  {
805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    "Asus virtual Camera",     // Bad Asus desktop virtual cam
815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    "Bluetooth Video",         // Bad Sony viao bluetooth sharing driver
825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    NULL,
835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};
845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgstatic const wchar_t kFriendlyName[] = L"FriendlyName";
855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgstatic const wchar_t kDevicePath[] = L"DevicePath";
865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgstatic const char kUsbDevicePathPrefix[] = "\\\\?\\usb";
875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgstatic bool GetDevices(const CLSID& catid, std::vector<Device>* out);
885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgstatic bool GetCoreAudioDevices(bool input, std::vector<Device>* devs);
895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgstatic bool GetWaveDevices(bool input, std::vector<Device>* devs);
905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgWin32DeviceManager::Win32DeviceManager()
925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    : need_couninitialize_(false) {
935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  set_watcher(new Win32DeviceWatcher(this));
945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgWin32DeviceManager::~Win32DeviceManager() {
975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (initialized()) {
985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    Terminate();
995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
1015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool Win32DeviceManager::Init() {
1035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (!initialized()) {
1045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
1055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    need_couninitialize_ = SUCCEEDED(hr);
1065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (FAILED(hr)) {
1075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr;
1085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      if (hr != RPC_E_CHANGED_MODE) {
1095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        return false;
1105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      }
1115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
1125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (!watcher()->Start()) {
1135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return false;
1145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
1155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    set_initialized(true);
1165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return true;
1185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
1195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid Win32DeviceManager::Terminate() {
1215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (initialized()) {
1225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    watcher()->Stop();
1235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (need_couninitialize_) {
1245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      CoUninitialize();
1255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      need_couninitialize_ = false;
1265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
1275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    set_initialized(false);
1285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
1305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool Win32DeviceManager::GetDefaultVideoCaptureDevice(Device* device) {
1325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  bool ret = false;
1335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // If there are multiple capture devices, we want the first USB one.
1345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // This avoids issues with defaulting to virtual cameras or grabber cards.
1355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  std::vector<Device> devices;
1365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  ret = (GetVideoCaptureDevices(&devices) && !devices.empty());
1375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (ret) {
1385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    *device = devices[0];
1395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    for (size_t i = 0; i < devices.size(); ++i) {
1405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      if (strnicmp(devices[i].id.c_str(), kUsbDevicePathPrefix,
1415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                   ARRAY_SIZE(kUsbDevicePathPrefix) - 1) == 0) {
1425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        *device = devices[i];
1435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        break;
1445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      }
1455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
1465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return ret;
1485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
1495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool Win32DeviceManager::GetAudioDevices(bool input,
1515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                                         std::vector<Device>* devs) {
1525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  devs->clear();
1535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (talk_base::IsWindowsVistaOrLater()) {
1555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (!GetCoreAudioDevices(input, devs))
1565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return false;
1575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  } else {
1585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (!GetWaveDevices(input, devs))
1595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      return false;
1605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return FilterDevices(devs, kFilteredAudioDevicesName);
1625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
1635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool Win32DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
1655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  devices->clear();
1665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (!GetDevices(CLSID_VideoInputDeviceCategory, devices)) {
1675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return false;
1685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return FilterDevices(devices, kFilteredVideoDevicesName);
1705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
1715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool GetDevices(const CLSID& catid, std::vector<Device>* devices) {
1735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  HRESULT hr;
1745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // CComPtr is a scoped pointer that will be auto released when going
1765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // out of scope. CoUninitialize must not be called before the
1775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // release.
1785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  CComPtr<ICreateDevEnum> sys_dev_enum;
1795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  CComPtr<IEnumMoniker> cam_enum;
1805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (FAILED(hr = sys_dev_enum.CoCreateInstance(CLSID_SystemDeviceEnum)) ||
1815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      FAILED(hr = sys_dev_enum->CreateClassEnumerator(catid, &cam_enum, 0))) {
1825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    LOG(LS_ERROR) << "Failed to create device enumerator, hr="  << hr;
1835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return false;
1845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
1855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
1865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // Only enum devices if CreateClassEnumerator returns S_OK. If there are no
1875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // devices available, S_FALSE will be returned, but enumMk will be NULL.
1885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (hr == S_OK) {
1895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    CComPtr<IMoniker> mk;
1905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    while (cam_enum->Next(1, &mk, NULL) == S_OK) {
1915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#ifdef HAVE_LOGITECH_HEADERS
1925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      // Initialize Logitech device if applicable
1935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      MaybeLogitechDeviceReset(mk);
1945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org#endif
1955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      CComPtr<IPropertyBag> bag;
1965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      if (SUCCEEDED(mk->BindToStorage(NULL, NULL,
1975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          __uuidof(bag), reinterpret_cast<void**>(&bag)))) {
1985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        CComVariant name, path;
1995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        std::string name_str, path_str;
2005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        if (SUCCEEDED(bag->Read(kFriendlyName, &name, 0)) &&
2015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org            name.vt == VT_BSTR) {
2025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          name_str = talk_base::ToUtf8(name.bstrVal);
2035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          // Get the device id if one exists.
2045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          if (SUCCEEDED(bag->Read(kDevicePath, &path, 0)) &&
2055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org              path.vt == VT_BSTR) {
2065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org            path_str = talk_base::ToUtf8(path.bstrVal);
2075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          }
2085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          devices->push_back(Device(name_str, path_str));
2105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        }
2115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      }
2125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      mk = NULL;
2135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
2145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return true;
2175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
2185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgHRESULT GetStringProp(IPropertyStore* bag, PROPERTYKEY key, std::string* out) {
2205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  out->clear();
2215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  PROPVARIANT var;
2225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  PropVariantInit(&var);
2235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  HRESULT hr = bag->GetValue(key, &var);
2255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (SUCCEEDED(hr)) {
2265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (var.pwszVal)
2275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      *out = talk_base::ToUtf8(var.pwszVal);
2285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    else
2295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      hr = E_FAIL;
2305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  PropVariantClear(&var);
2335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return hr;
2345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
2355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org// Adapted from http://msdn.microsoft.com/en-us/library/dd370812(v=VS.85).aspx
2375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgHRESULT CricketDeviceFromImmDevice(IMMDevice* device, Device* out) {
2385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  CComPtr<IPropertyStore> props;
2395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  HRESULT hr = device->OpenPropertyStore(STGM_READ, &props);
2415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (FAILED(hr)) {
2425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return hr;
2435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // Get the endpoint's name and id.
2465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  std::string name, guid;
2475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  hr = GetStringProp(props, PKEY_Device_FriendlyName, &name);
2485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (SUCCEEDED(hr)) {
2495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    hr = GetStringProp(props, PKEY_AudioEndpoint_GUID, &guid);
2505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (SUCCEEDED(hr)) {
2525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      out->name = name;
2535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      out->id = guid;
2545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
2555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return hr;
2575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
2585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool GetCoreAudioDevices(
2605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    bool input, std::vector<Device>* devs) {
2615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  HRESULT hr = S_OK;
2625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  CComPtr<IMMDeviceEnumerator> enumerator;
2635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
2655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&enumerator));
2665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (SUCCEEDED(hr)) {
2675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    CComPtr<IMMDeviceCollection> devices;
2685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    hr = enumerator->EnumAudioEndpoints((input ? eCapture : eRender),
2695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                                        DEVICE_STATE_ACTIVE, &devices);
2705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (SUCCEEDED(hr)) {
2715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      unsigned int count;
2725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      hr = devices->GetCount(&count);
2735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      if (SUCCEEDED(hr)) {
2755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        for (unsigned int i = 0; i < count; i++) {
2765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          CComPtr<IMMDevice> device;
2775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          // Get pointer to endpoint number i.
2795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          hr = devices->Item(i, &device);
2805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          if (FAILED(hr)) {
2815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org            break;
2825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          }
2835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          Device dev;
2855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          hr = CricketDeviceFromImmDevice(device, &dev);
2865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          if (SUCCEEDED(hr)) {
2875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org            devs->push_back(dev);
2885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          } else {
2895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org            LOG(LS_WARNING) << "Unable to query IMM Device, skipping.  HR="
2905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                            << hr;
2915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org            hr = S_FALSE;
2925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          }
2935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        }
2945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      }
2955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
2965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
2975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
2985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (FAILED(hr)) {
2995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    LOG(LS_WARNING) << "GetCoreAudioDevices failed with hr " << hr;
3005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return false;
3015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return true;
3035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
3045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3055976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool GetWaveDevices(bool input, std::vector<Device>* devs) {
3065976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // Note, we don't use the System Device Enumerator interface here since it
3075976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // adds lots of pseudo-devices to the list, such as DirectSound and Wave
3085976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  // variants of the same device.
3095976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (input) {
3105976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    int num_devs = waveInGetNumDevs();
3115976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    for (int i = 0; i < num_devs; ++i) {
3125976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      WAVEINCAPS caps;
3135976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      if (waveInGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
3145976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          caps.wChannels > 0) {
3155976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        devs->push_back(Device(talk_base::ToUtf8(caps.szPname),
3165976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                               talk_base::ToString(i)));
3175976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      }
3185976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
3195976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  } else {
3205976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    int num_devs = waveOutGetNumDevs();
3215976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    for (int i = 0; i < num_devs; ++i) {
3225976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      WAVEOUTCAPS caps;
3235976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      if (waveOutGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
3245976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          caps.wChannels > 0) {
3255976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        devs->push_back(Device(talk_base::ToUtf8(caps.szPname), i));
3265976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      }
3275976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
3285976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3295976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return true;
3305976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
3315976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3325976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgWin32DeviceWatcher::Win32DeviceWatcher(Win32DeviceManager* manager)
3335976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    : DeviceWatcher(manager),
3345976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      manager_(manager),
3355976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      audio_notify_(NULL),
3365976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      video_notify_(NULL) {
3375976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
3385976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3395976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgWin32DeviceWatcher::~Win32DeviceWatcher() {
3405976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
3415976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3425976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool Win32DeviceWatcher::Start() {
3435976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (!Create(NULL, _T("libjingle Win32DeviceWatcher Window"),
3445976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org              0, 0, 0, 0, 0, 0)) {
3455976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return false;
3465976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3475976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3485976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  audio_notify_ = Register(KSCATEGORY_AUDIO);
3495976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (!audio_notify_) {
3505976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    Stop();
3515976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return false;
3525976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3535976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3545976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_notify_ = Register(KSCATEGORY_VIDEO);
3555976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (!video_notify_) {
3565976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    Stop();
3575976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return false;
3585976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
3595976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3605976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return true;
3615976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
3625976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3635976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid Win32DeviceWatcher::Stop() {
3645976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  UnregisterDeviceNotification(video_notify_);
3655976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  video_notify_ = NULL;
3665976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  UnregisterDeviceNotification(audio_notify_);
3675976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  audio_notify_ = NULL;
3685976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  Destroy();
3695976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
3705976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3715976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgHDEVNOTIFY Win32DeviceWatcher::Register(REFGUID guid) {
3725976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  DEV_BROADCAST_DEVICEINTERFACE dbdi;
3735976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  dbdi.dbcc_size = sizeof(dbdi);
3745976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  dbdi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
3755976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  dbdi.dbcc_classguid = guid;
3765976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  dbdi.dbcc_name[0] = '\0';
3775976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return RegisterDeviceNotification(handle(), &dbdi,
3785976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                                    DEVICE_NOTIFY_WINDOW_HANDLE);
3795976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
3805976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3815976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgvoid Win32DeviceWatcher::Unregister(HDEVNOTIFY handle) {
3825976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  UnregisterDeviceNotification(handle);
3835976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
3845976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
3855976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.orgbool Win32DeviceWatcher::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
3865976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org                              LRESULT& result) {
3875976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  if (uMsg == WM_DEVICECHANGE) {
3885976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    if (wParam == DBT_DEVICEARRIVAL ||
3895976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        wParam == DBT_DEVICEREMOVECOMPLETE) {
3905976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      DEV_BROADCAST_DEVICEINTERFACE* dbdi =
3915976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org          reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lParam);
3925976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      if (dbdi->dbcc_classguid == KSCATEGORY_AUDIO ||
3935976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        dbdi->dbcc_classguid == KSCATEGORY_VIDEO) {
3945976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org        manager_->SignalDevicesChange();
3955976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org      }
3965976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    }
3975976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    result = 0;
3985976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org    return true;
3995976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  }
4005976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4015976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org  return false;
4025976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org}
4035976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org
4045976650443d68ccfadf1dea24999ee459dd2819mflodman@webrtc.org};  // namespace cricket
405