~ubuntu-branches/debian/experimental/kopete/experimental

« back to all changes in this revision

Viewing changes to protocols/jabber/libjingle/talk/session/phone/win32devicemanager.cc

  • Committer: Package Import Robot
  • Author(s): Maximiliano Curia
  • Date: 2015-02-24 11:32:57 UTC
  • mfrom: (1.1.41 vivid)
  • Revision ID: package-import@ubuntu.com-20150224113257-gnupg4v7lzz18ij0
Tags: 4:14.12.2-1
* New upstream release (14.12.2).
* Bump Standards-Version to 3.9.6, no changes needed.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/*
 
2
 * libjingle
 
3
 * Copyright 2004 Google Inc.
 
4
 *
 
5
 * Redistribution and use in source and binary forms, with or without
 
6
 * modification, are permitted provided that the following conditions are met:
 
7
 *
 
8
 *  1. Redistributions of source code must retain the above copyright notice,
 
9
 *     this list of conditions and the following disclaimer.
 
10
 *  2. Redistributions in binary form must reproduce the above copyright notice,
 
11
 *     this list of conditions and the following disclaimer in the documentation
 
12
 *     and/or other materials provided with the distribution.
 
13
 *  3. The name of the author may not be used to endorse or promote products
 
14
 *     derived from this software without specific prior written permission.
 
15
 *
 
16
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 
17
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
 
18
 * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
 
19
 * EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
 
20
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
 
21
 * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
 
22
 * OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
 
23
 * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
 
24
 * OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
 
25
 * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 
26
 */
 
27
 
 
28
#include "talk/session/phone/win32devicemanager.h"
 
29
 
 
30
#include <atlbase.h>
 
31
#include <dbt.h>
 
32
#include <strmif.h>  // must come before ks.h
 
33
#include <ks.h>
 
34
#include <ksmedia.h>
 
35
#define INITGUID  // For PKEY_AudioEndpoint_GUID
 
36
#include <mmdeviceapi.h>
 
37
#include <mmsystem.h>
 
38
#include <functiondiscoverykeys_devpkey.h>
 
39
#include <uuids.h>
 
40
 
 
41
#include "talk/base/win32.h"  // ToUtf8
 
42
#include "talk/base/win32window.h"
 
43
#include "talk/base/logging.h"
 
44
#include "talk/base/stringutils.h"
 
45
#include "talk/base/thread.h"
 
46
#include "talk/session/phone/mediacommon.h"
 
47
#ifdef HAVE_LOGITECH_HEADERS
 
48
#include "third_party/logitech/files/logitechquickcam.h"
 
49
#endif
 
50
 
 
51
namespace cricket {
 
52
 
 
53
DeviceManagerInterface* DeviceManagerFactory::Create() {
 
54
  return new Win32DeviceManager();
 
55
}
 
56
 
 
57
class Win32DeviceWatcher
 
58
    : public DeviceWatcher,
 
59
      public talk_base::Win32Window {
 
60
 public:
 
61
  explicit Win32DeviceWatcher(Win32DeviceManager* dm);
 
62
  virtual ~Win32DeviceWatcher();
 
63
  virtual bool Start();
 
64
  virtual void Stop();
 
65
 
 
66
 private:
 
67
  HDEVNOTIFY Register(REFGUID guid);
 
68
  void Unregister(HDEVNOTIFY notify);
 
69
  virtual bool OnMessage(UINT msg, WPARAM wp, LPARAM lp, LRESULT& result);
 
70
 
 
71
  Win32DeviceManager* manager_;
 
72
  HDEVNOTIFY audio_notify_;
 
73
  HDEVNOTIFY video_notify_;
 
74
};
 
75
 
 
76
static const char* kFilteredAudioDevicesName[] = {
 
77
    NULL,
 
78
};
 
79
static const char* const kFilteredVideoDevicesName[] =  {
 
80
    "Google Camera Adapter",   // Our own magiccams
 
81
    "Asus virtual Camera",     // Bad Asus desktop virtual cam
 
82
    "Bluetooth Video",         // Bad Sony viao bluetooth sharing driver
 
83
    NULL,
 
84
};
 
85
static const wchar_t kFriendlyName[] = L"FriendlyName";
 
86
static const wchar_t kDevicePath[] = L"DevicePath";
 
87
static const char kUsbDevicePathPrefix[] = "\\\\?\\usb";
 
88
static bool GetDevices(const CLSID& catid, std::vector<Device>* out);
 
89
static bool GetCoreAudioDevices(bool input, std::vector<Device>* devs);
 
90
static bool GetWaveDevices(bool input, std::vector<Device>* devs);
 
91
 
 
92
Win32DeviceManager::Win32DeviceManager()
 
93
    : need_couninitialize_(false) {
 
94
  set_watcher(new Win32DeviceWatcher(this));
 
95
}
 
96
 
 
97
Win32DeviceManager::~Win32DeviceManager() {
 
98
  if (initialized()) {
 
99
    Terminate();
 
100
  }
 
101
}
 
102
 
 
103
bool Win32DeviceManager::Init() {
 
104
  if (!initialized()) {
 
105
    HRESULT hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
 
106
    need_couninitialize_ = SUCCEEDED(hr);
 
107
    if (FAILED(hr)) {
 
108
      LOG(LS_ERROR) << "CoInitialize failed, hr=" << hr;
 
109
      if (hr != RPC_E_CHANGED_MODE) {
 
110
        return false;
 
111
      }
 
112
    }
 
113
    if (!watcher()->Start()) {
 
114
      return false;
 
115
    }
 
116
    set_initialized(true);
 
117
  }
 
118
  return true;
 
119
}
 
120
 
 
121
void Win32DeviceManager::Terminate() {
 
122
  if (initialized()) {
 
123
    watcher()->Stop();
 
124
    if (need_couninitialize_) {
 
125
      CoUninitialize();
 
126
      need_couninitialize_ = false;
 
127
    }
 
128
    set_initialized(false);
 
129
  }
 
130
}
 
131
 
 
132
bool Win32DeviceManager::GetDefaultVideoCaptureDevice(Device* device) {
 
133
  bool ret = false;
 
134
  // If there are multiple capture devices, we want the first USB one.
 
135
  // This avoids issues with defaulting to virtual cameras or grabber cards.
 
136
  std::vector<Device> devices;
 
137
  ret = (GetVideoCaptureDevices(&devices) && !devices.empty());
 
138
  if (ret) {
 
139
    *device = devices[0];
 
140
    for (size_t i = 0; i < devices.size(); ++i) {
 
141
      if (strnicmp(devices[i].id.c_str(), kUsbDevicePathPrefix,
 
142
                   ARRAY_SIZE(kUsbDevicePathPrefix) - 1) == 0) {
 
143
        *device = devices[i];
 
144
        break;
 
145
      }
 
146
    }
 
147
  }
 
148
  return ret;
 
149
}
 
150
 
 
151
bool Win32DeviceManager::GetAudioDevices(bool input,
 
152
                                         std::vector<Device>* devs) {
 
153
  devs->clear();
 
154
 
 
155
  if (talk_base::IsWindowsVistaOrLater()) {
 
156
    if (!GetCoreAudioDevices(input, devs))
 
157
      return false;
 
158
  } else {
 
159
    if (!GetWaveDevices(input, devs))
 
160
      return false;
 
161
  }
 
162
  return FilterDevices(devs, kFilteredAudioDevicesName);
 
163
}
 
164
 
 
165
bool Win32DeviceManager::GetVideoCaptureDevices(std::vector<Device>* devices) {
 
166
  devices->clear();
 
167
  if (!GetDevices(CLSID_VideoInputDeviceCategory, devices)) {
 
168
    return false;
 
169
  }
 
170
  return FilterDevices(devices, kFilteredVideoDevicesName);
 
171
}
 
172
 
 
173
bool GetDevices(const CLSID& catid, std::vector<Device>* devices) {
 
174
  HRESULT hr;
 
175
 
 
176
  // CComPtr is a scoped pointer that will be auto released when going
 
177
  // out of scope. CoUninitialize must not be called before the
 
178
  // release.
 
179
  CComPtr<ICreateDevEnum> sys_dev_enum;
 
180
  CComPtr<IEnumMoniker> cam_enum;
 
181
  if (FAILED(hr = sys_dev_enum.CoCreateInstance(CLSID_SystemDeviceEnum)) ||
 
182
      FAILED(hr = sys_dev_enum->CreateClassEnumerator(catid, &cam_enum, 0))) {
 
183
    LOG(LS_ERROR) << "Failed to create device enumerator, hr="  << hr;
 
184
    return false;
 
185
  }
 
186
 
 
187
  // Only enum devices if CreateClassEnumerator returns S_OK. If there are no
 
188
  // devices available, S_FALSE will be returned, but enumMk will be NULL.
 
189
  if (hr == S_OK) {
 
190
    CComPtr<IMoniker> mk;
 
191
    while (cam_enum->Next(1, &mk, NULL) == S_OK) {
 
192
#ifdef HAVE_LOGITECH_HEADERS
 
193
      // Initialize Logitech device if applicable
 
194
      MaybeLogitechDeviceReset(mk);
 
195
#endif
 
196
      CComPtr<IPropertyBag> bag;
 
197
      if (SUCCEEDED(mk->BindToStorage(NULL, NULL,
 
198
          __uuidof(bag), reinterpret_cast<void**>(&bag)))) {
 
199
        CComVariant name, path;
 
200
        std::string name_str, path_str;
 
201
        if (SUCCEEDED(bag->Read(kFriendlyName, &name, 0)) &&
 
202
            name.vt == VT_BSTR) {
 
203
          name_str = talk_base::ToUtf8(name.bstrVal);
 
204
          // Get the device id if one exists.
 
205
          if (SUCCEEDED(bag->Read(kDevicePath, &path, 0)) &&
 
206
              path.vt == VT_BSTR) {
 
207
            path_str = talk_base::ToUtf8(path.bstrVal);
 
208
          }
 
209
 
 
210
          devices->push_back(Device(name_str, path_str));
 
211
        }
 
212
      }
 
213
      mk = NULL;
 
214
    }
 
215
  }
 
216
 
 
217
  return true;
 
218
}
 
219
 
 
220
HRESULT GetStringProp(IPropertyStore* bag, PROPERTYKEY key, std::string* out) {
 
221
  out->clear();
 
222
  PROPVARIANT var;
 
223
  PropVariantInit(&var);
 
224
 
 
225
  HRESULT hr = bag->GetValue(key, &var);
 
226
  if (SUCCEEDED(hr)) {
 
227
    if (var.pwszVal)
 
228
      *out = talk_base::ToUtf8(var.pwszVal);
 
229
    else
 
230
      hr = E_FAIL;
 
231
  }
 
232
 
 
233
  PropVariantClear(&var);
 
234
  return hr;
 
235
}
 
236
 
 
237
// Adapted from http://msdn.microsoft.com/en-us/library/dd370812(v=VS.85).aspx
 
238
HRESULT CricketDeviceFromImmDevice(IMMDevice* device, Device* out) {
 
239
  CComPtr<IPropertyStore> props;
 
240
 
 
241
  HRESULT hr = device->OpenPropertyStore(STGM_READ, &props);
 
242
  if (FAILED(hr)) {
 
243
    return hr;
 
244
  }
 
245
 
 
246
  // Get the endpoint's name and id.
 
247
  std::string name, guid;
 
248
  hr = GetStringProp(props, PKEY_Device_FriendlyName, &name);
 
249
  if (SUCCEEDED(hr)) {
 
250
    hr = GetStringProp(props, PKEY_AudioEndpoint_GUID, &guid);
 
251
 
 
252
    if (SUCCEEDED(hr)) {
 
253
      out->name = name;
 
254
      out->id = guid;
 
255
    }
 
256
  }
 
257
  return hr;
 
258
}
 
259
 
 
260
bool GetCoreAudioDevices(
 
261
    bool input, std::vector<Device>* devs) {
 
262
  HRESULT hr = S_OK;
 
263
  CComPtr<IMMDeviceEnumerator> enumerator;
 
264
 
 
265
  hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), NULL, CLSCTX_ALL,
 
266
      __uuidof(IMMDeviceEnumerator), reinterpret_cast<void**>(&enumerator));
 
267
  if (SUCCEEDED(hr)) {
 
268
    CComPtr<IMMDeviceCollection> devices;
 
269
    hr = enumerator->EnumAudioEndpoints((input ? eCapture : eRender),
 
270
                                        DEVICE_STATE_ACTIVE, &devices);
 
271
    if (SUCCEEDED(hr)) {
 
272
      unsigned int count;
 
273
      hr = devices->GetCount(&count);
 
274
 
 
275
      if (SUCCEEDED(hr)) {
 
276
        for (unsigned int i = 0; i < count; i++) {
 
277
          CComPtr<IMMDevice> device;
 
278
 
 
279
          // Get pointer to endpoint number i.
 
280
          hr = devices->Item(i, &device);
 
281
          if (FAILED(hr)) {
 
282
            break;
 
283
          }
 
284
 
 
285
          Device dev;
 
286
          hr = CricketDeviceFromImmDevice(device, &dev);
 
287
          if (SUCCEEDED(hr)) {
 
288
            devs->push_back(dev);
 
289
          } else {
 
290
            LOG(LS_WARNING) << "Unable to query IMM Device, skipping.  HR="
 
291
                            << hr;
 
292
            hr = S_FALSE;
 
293
          }
 
294
        }
 
295
      }
 
296
    }
 
297
  }
 
298
 
 
299
  if (FAILED(hr)) {
 
300
    LOG(LS_WARNING) << "GetCoreAudioDevices failed with hr " << hr;
 
301
    return false;
 
302
  }
 
303
  return true;
 
304
}
 
305
 
 
306
bool GetWaveDevices(bool input, std::vector<Device>* devs) {
 
307
  // Note, we don't use the System Device Enumerator interface here since it
 
308
  // adds lots of pseudo-devices to the list, such as DirectSound and Wave
 
309
  // variants of the same device.
 
310
  if (input) {
 
311
    int num_devs = waveInGetNumDevs();
 
312
    for (int i = 0; i < num_devs; ++i) {
 
313
      WAVEINCAPS caps;
 
314
      if (waveInGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
 
315
          caps.wChannels > 0) {
 
316
        devs->push_back(Device(talk_base::ToUtf8(caps.szPname),
 
317
                               talk_base::ToString(i)));
 
318
      }
 
319
    }
 
320
  } else {
 
321
    int num_devs = waveOutGetNumDevs();
 
322
    for (int i = 0; i < num_devs; ++i) {
 
323
      WAVEOUTCAPS caps;
 
324
      if (waveOutGetDevCaps(i, &caps, sizeof(caps)) == MMSYSERR_NOERROR &&
 
325
          caps.wChannels > 0) {
 
326
        devs->push_back(Device(talk_base::ToUtf8(caps.szPname), i));
 
327
      }
 
328
    }
 
329
  }
 
330
  return true;
 
331
}
 
332
 
 
333
Win32DeviceWatcher::Win32DeviceWatcher(Win32DeviceManager* manager)
 
334
    : DeviceWatcher(manager),
 
335
      manager_(manager),
 
336
      audio_notify_(NULL),
 
337
      video_notify_(NULL) {
 
338
}
 
339
 
 
340
Win32DeviceWatcher::~Win32DeviceWatcher() {
 
341
}
 
342
 
 
343
bool Win32DeviceWatcher::Start() {
 
344
  if (!Create(NULL, _T("libjingle Win32DeviceWatcher Window"),
 
345
              0, 0, 0, 0, 0, 0)) {
 
346
    return false;
 
347
  }
 
348
 
 
349
  audio_notify_ = Register(KSCATEGORY_AUDIO);
 
350
  if (!audio_notify_) {
 
351
    Stop();
 
352
    return false;
 
353
  }
 
354
 
 
355
  video_notify_ = Register(KSCATEGORY_VIDEO);
 
356
  if (!video_notify_) {
 
357
    Stop();
 
358
    return false;
 
359
  }
 
360
 
 
361
  return true;
 
362
}
 
363
 
 
364
void Win32DeviceWatcher::Stop() {
 
365
  UnregisterDeviceNotification(video_notify_);
 
366
  video_notify_ = NULL;
 
367
  UnregisterDeviceNotification(audio_notify_);
 
368
  audio_notify_ = NULL;
 
369
  Destroy();
 
370
}
 
371
 
 
372
HDEVNOTIFY Win32DeviceWatcher::Register(REFGUID guid) {
 
373
  DEV_BROADCAST_DEVICEINTERFACE dbdi;
 
374
  dbdi.dbcc_size = sizeof(dbdi);
 
375
  dbdi.dbcc_devicetype = DBT_DEVTYP_DEVICEINTERFACE;
 
376
  dbdi.dbcc_classguid = guid;
 
377
  dbdi.dbcc_name[0] = '\0';
 
378
  return RegisterDeviceNotification(handle(), &dbdi,
 
379
                                    DEVICE_NOTIFY_WINDOW_HANDLE);
 
380
}
 
381
 
 
382
void Win32DeviceWatcher::Unregister(HDEVNOTIFY handle) {
 
383
  UnregisterDeviceNotification(handle);
 
384
}
 
385
 
 
386
bool Win32DeviceWatcher::OnMessage(UINT uMsg, WPARAM wParam, LPARAM lParam,
 
387
                              LRESULT& result) {
 
388
  if (uMsg == WM_DEVICECHANGE) {
 
389
    if (wParam == DBT_DEVICEARRIVAL ||
 
390
        wParam == DBT_DEVICEREMOVECOMPLETE) {
 
391
      DEV_BROADCAST_DEVICEINTERFACE* dbdi =
 
392
          reinterpret_cast<DEV_BROADCAST_DEVICEINTERFACE*>(lParam);
 
393
      if (dbdi->dbcc_classguid == KSCATEGORY_AUDIO ||
 
394
        dbdi->dbcc_classguid == KSCATEGORY_VIDEO) {
 
395
        manager_->SignalDevicesChange();
 
396
      }
 
397
    }
 
398
    result = 0;
 
399
    return true;
 
400
  }
 
401
 
 
402
  return false;
 
403
}
 
404
 
 
405
};  // namespace cricket