device_enumeration_win.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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/basictypes.h" 12#include "base/logging.h" 13#include "base/strings/utf_string_conversions.h" 14#include "base/win/scoped_co_mem.h" 15#include "base/win/scoped_comptr.h" 16#include "base/win/scoped_propvariant.h" 17 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 28namespace { 29 30bool GetDeviceNamesWinImpl(EDataFlow data_flow, 31 AudioDeviceNames* device_names) { 32 // It is assumed that this method is called from a COM thread, i.e., 33 // CoInitializeEx() is not called here again to avoid STA/MTA conflicts. 34 ScopedComPtr<IMMDeviceEnumerator> enumerator; 35 HRESULT hr = enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), NULL, 36 CLSCTX_INPROC_SERVER); 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 endpoint devices. 44 // This method will succeed even if all devices are disabled. 45 ScopedComPtr<IMMDeviceCollection> collection; 46 hr = enumerator->EnumAudioEndpoints(data_flow, 47 DEVICE_STATE_ACTIVE, 48 collection.Receive()); 49 if (FAILED(hr)) 50 return false; 51 52 // Retrieve the number of active 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 AudioDeviceName device; 59 60 // Loop over all active 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 98// The waveform API is weird in that it has completely separate but 99// almost identical functions and structs for input devices vs. output 100// devices. We deal with this by implementing the logic as a templated 101// function that takes the functions and struct type to use as 102// template parameters. 103template <UINT (__stdcall *NumDevsFunc)(), 104 typename CAPSSTRUCT, 105 MMRESULT (__stdcall *DevCapsFunc)(UINT_PTR, CAPSSTRUCT*, UINT)> 106bool GetDeviceNamesWinXPImpl(AudioDeviceNames* device_names) { 107 // Retrieve the number of active waveform input devices. 108 UINT number_of_active_devices = NumDevsFunc(); 109 if (number_of_active_devices == 0) 110 return true; 111 112 AudioDeviceName device; 113 CAPSSTRUCT capabilities; 114 MMRESULT err = MMSYSERR_NOERROR; 115 116 // Loop over all active capture devices and add friendly name and 117 // unique ID to the |device_names| list. Note that, for Wave on XP, 118 // the "unique" name will simply be a copy of the friendly name since 119 // there is no safe method to retrieve a unique device name on XP. 120 for (UINT i = 0; i < number_of_active_devices; ++i) { 121 // Retrieve the capabilities of the specified waveform-audio input device. 122 err = DevCapsFunc(i, &capabilities, sizeof(capabilities)); 123 if (err != MMSYSERR_NOERROR) 124 continue; 125 126 // Store the user-friendly name. Max length is MAXPNAMELEN(=32) 127 // characters and the name cane be truncated on XP. 128 // Example: "Microphone (Realtek High Defini". 129 device.device_name = WideToUTF8(capabilities.szPname); 130 131 // Store the "unique" name (we use same as friendly name on Windows XP). 132 device.unique_id = device.device_name; 133 134 // Add combination of user-friendly and unique name to the output list. 135 device_names->push_back(device); 136 } 137 138 return true; 139} 140 141} // namespace 142 143bool GetInputDeviceNamesWin(AudioDeviceNames* device_names) { 144 return GetDeviceNamesWinImpl(eCapture, device_names); 145} 146 147bool GetOutputDeviceNamesWin(AudioDeviceNames* device_names) { 148 return GetDeviceNamesWinImpl(eRender, device_names); 149} 150 151bool GetInputDeviceNamesWinXP(AudioDeviceNames* device_names) { 152 return GetDeviceNamesWinXPImpl< 153 waveInGetNumDevs, WAVEINCAPSW, waveInGetDevCapsW>(device_names); 154} 155 156bool GetOutputDeviceNamesWinXP(AudioDeviceNames* device_names) { 157 return GetDeviceNamesWinXPImpl< 158 waveOutGetNumDevs, WAVEOUTCAPSW, waveOutGetDevCapsW>(device_names); 159} 160 161std::string ConvertToWinXPInputDeviceId(const std::string& device_id) { 162 UINT number_of_active_devices = waveInGetNumDevs(); 163 MMRESULT result = MMSYSERR_NOERROR; 164 165 UINT i = 0; 166 for (; i < number_of_active_devices; ++i) { 167 size_t size = 0; 168 // Get the size (including the terminating NULL) of the endpoint ID of the 169 // waveIn device. 170 result = waveInMessage(reinterpret_cast<HWAVEIN>(i), 171 DRV_QUERYFUNCTIONINSTANCEIDSIZE, 172 reinterpret_cast<DWORD_PTR>(&size), NULL); 173 if (result != MMSYSERR_NOERROR) 174 continue; 175 176 ScopedCoMem<WCHAR> id; 177 id.Reset(static_cast<WCHAR*>(CoTaskMemAlloc(size))); 178 if (!id) 179 continue; 180 181 // Get the endpoint ID string for this waveIn device. 182 result = waveInMessage( 183 reinterpret_cast<HWAVEIN>(i), DRV_QUERYFUNCTIONINSTANCEID, 184 reinterpret_cast<DWORD_PTR>(static_cast<WCHAR*>(id)), size); 185 if (result != MMSYSERR_NOERROR) 186 continue; 187 188 std::string utf8_id = WideToUTF8(static_cast<WCHAR*>(id)); 189 // Check whether the endpoint ID string of this waveIn device matches that 190 // of the audio endpoint device. 191 if (device_id == utf8_id) 192 break; 193 } 194 195 // If a matching waveIn device was found, convert the unique endpoint ID 196 // string to a standard friendly name with max 32 characters. 197 if (i < number_of_active_devices) { 198 WAVEINCAPS capabilities; 199 200 result = waveInGetDevCaps(i, &capabilities, sizeof(capabilities)); 201 if (result == MMSYSERR_NOERROR) 202 return WideToUTF8(capabilities.szPname); 203 } 204 205 return std::string(); 206} 207 208} // namespace media 209