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

« back to all changes in this revision

Viewing changes to protocols/jabber/googletalk/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