device_enumeration_win.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include <MMDeviceAPI.h> 6#include <mmsystem.h> 7#include <Functiondiscoverykeys_devpkey.h> // MMDeviceAPI.h must come first 8 9#include "media/audio/win/audio_manager_win.h" 10 11#include "base/logging.h" 12#include "base/utf_string_conversions.h" 13#include "base/win/scoped_co_mem.h" 14#include "base/win/scoped_comptr.h" 15#include "base/win/scoped_propvariant.h" 16 17using media::AudioDeviceNames; 18using base::win::ScopedComPtr; 19using base::win::ScopedCoMem; 20 21// Taken from Mmddk.h. 22#define DRV_RESERVED 0x0800 23#define DRV_QUERYFUNCTIONINSTANCEID (DRV_RESERVED + 17) 24#define DRV_QUERYFUNCTIONINSTANCEIDSIZE (DRV_RESERVED + 18) 25 26namespace media { 27 28bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { 29 // It is assumed that this method is called from a COM thread, i.e., 30 // CoInitializeEx() is not called here again to avoid STA/MTA conflicts. 31 ScopedComPtr<IMMDeviceEnumerator> enumerator; 32 HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), 33 NULL, 34 CLSCTX_INPROC_SERVER, 35 __uuidof(IMMDeviceEnumerator), 36 enumerator.ReceiveVoid()); 37 DCHECK_NE(CO_E_NOTINITIALIZED, hr); 38 if (FAILED(hr)) { 39 LOG(WARNING) << "Failed to create IMMDeviceEnumerator: " << std::hex << hr; 40 return false; 41 } 42 43 // Generate a collection of active audio capture endpoint devices. 44 // This method will succeed even if all devices are disabled. 45 ScopedComPtr<IMMDeviceCollection> collection; 46 hr = enumerator->EnumAudioEndpoints(eCapture, 47 DEVICE_STATE_ACTIVE, 48 collection.Receive()); 49 if (FAILED(hr)) 50 return false; 51 52 // Retrieve the number of active capture devices. 53 UINT number_of_active_devices = 0; 54 collection->GetCount(&number_of_active_devices); 55 if (number_of_active_devices == 0) 56 return true; 57 58 media::AudioDeviceName device; 59 60 // Loop over all active capture devices and add friendly name and 61 // unique ID to the |device_names| list. 62 for (UINT i = 0; i < number_of_active_devices; ++i) { 63 // Retrieve unique name of endpoint device. 64 // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}". 65 ScopedComPtr<IMMDevice> audio_device; 66 hr = collection->Item(i, audio_device.Receive()); 67 if (FAILED(hr)) 68 continue; 69 70 // Store the unique name. 71 ScopedCoMem<WCHAR> endpoint_device_id; 72 audio_device->GetId(&endpoint_device_id); 73 device.unique_id = WideToUTF8(static_cast<WCHAR*>(endpoint_device_id)); 74 75 // Retrieve user-friendly name of endpoint device. 76 // Example: "Microphone (Realtek High Definition Audio)". 77 ScopedComPtr<IPropertyStore> properties; 78 hr = audio_device->OpenPropertyStore(STGM_READ, properties.Receive()); 79 if (SUCCEEDED(hr)) { 80 base::win::ScopedPropVariant friendly_name; 81 hr = properties->GetValue(PKEY_Device_FriendlyName, 82 friendly_name.Receive()); 83 84 // Store the user-friendly name. 85 if (SUCCEEDED(hr) && 86 friendly_name.get().vt == VT_LPWSTR && friendly_name.get().pwszVal) { 87 device.device_name = WideToUTF8(friendly_name.get().pwszVal); 88 } 89 } 90 91 // Add combination of user-friendly and unique name to the output list. 92 device_names->push_back(device); 93 } 94 95 return true; 96} 97 98bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { 99 // Retrieve the number of active waveform input devices. 100 UINT number_of_active_devices = waveInGetNumDevs(); 101 if (number_of_active_devices == 0) 102 return true; 103 104 media::AudioDeviceName device; 105 WAVEINCAPS capabilities; 106 MMRESULT err = MMSYSERR_NOERROR; 107 108 // Loop over all active capture devices and add friendly name and 109 // unique ID to the |device_names| list. Note that, for Wave on XP, 110 // the "unique" name will simply be a copy of the friendly name since 111 // there is no safe method to retrieve a unique device name on XP. 112 for (UINT i = 0; i < number_of_active_devices; ++i) { 113 // Retrieve the capabilities of the specified waveform-audio input device. 114 err = waveInGetDevCaps(i, &capabilities, sizeof(capabilities)); 115 if (err != MMSYSERR_NOERROR) 116 continue; 117 118 // Store the user-friendly name. Max length is MAXPNAMELEN(=32) 119 // characters and the name cane be truncated on XP. 120 // Example: "Microphone (Realtek High Defini". 121 device.device_name = WideToUTF8(capabilities.szPname); 122 123 // Store the "unique" name (we use same as friendly name on Windows XP). 124 device.unique_id = WideToUTF8(capabilities.szPname); 125 126 // Add combination of user-friendly and unique name to the output list. 127 device_names->push_back(device); 128 } 129 130 return true; 131} 132 133std::string ConvertToWinXPDeviceId(const std::string& device_id) { 134 UINT number_of_active_devices = waveInGetNumDevs(); 135 MMRESULT result = MMSYSERR_NOERROR; 136 137 UINT i = 0; 138 for (; i < number_of_active_devices; ++i) { 139 size_t size = 0; 140 // Get the size (including the terminating NULL) of the endpoint ID of the 141 // waveIn device. 142 result = waveInMessage(reinterpret_cast<HWAVEIN>(i), 143 DRV_QUERYFUNCTIONINSTANCEIDSIZE, 144 reinterpret_cast<DWORD_PTR>(&size), NULL); 145 if (result != MMSYSERR_NOERROR) 146 continue; 147 148 ScopedCoMem<WCHAR> id; 149 id.Reset(static_cast<WCHAR*>(CoTaskMemAlloc(size))); 150 if (!id) 151 continue; 152 153 // Get the endpoint ID string for this waveIn device. 154 result = waveInMessage( 155 reinterpret_cast<HWAVEIN>(i), DRV_QUERYFUNCTIONINSTANCEID, 156 reinterpret_cast<DWORD_PTR>(static_cast<WCHAR*>(id)), size); 157 if (result != MMSYSERR_NOERROR) 158 continue; 159 160 std::string utf8_id = WideToUTF8(static_cast<WCHAR*>(id)); 161 // Check whether the endpoint ID string of this waveIn device matches that 162 // of the audio endpoint device. 163 if (device_id == utf8_id) 164 break; 165 } 166 167 // If a matching waveIn device was found, convert the unique endpoint ID 168 // string to a standard friendly name with max 32 characters. 169 if (i < number_of_active_devices) { 170 WAVEINCAPS capabilities; 171 172 result = waveInGetDevCaps(i, &capabilities, sizeof(capabilities)); 173 if (result == MMSYSERR_NOERROR) 174 return WideToUTF8(capabilities.szPname); 175 } 176 177 return std::string(); 178} 179 180} // namespace media 181