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 "media/audio/win/core_audio_util_win.h" 6 7 #include <audioclient.h> 8 #include <devicetopology.h> 9 #include <functiondiscoverykeys_devpkey.h> 10 11 #include "base/command_line.h" 12 #include "base/logging.h" 13 #include "base/strings/stringprintf.h" 14 #include "base/strings/utf_string_conversions.h" 15 #include "base/win/scoped_co_mem.h" 16 #include "base/win/scoped_handle.h" 17 #include "base/win/scoped_propvariant.h" 18 #include "base/win/windows_version.h" 19 #include "media/base/media_switches.h" 20 21 using base::win::ScopedCoMem; 22 using base::win::ScopedHandle; 23 24 namespace media { 25 26 // See header file for documentation. 27 // {BE39AF4F-087C-423F-9303-234EC1E5B8EE} 28 const GUID kCommunicationsSessionId = { 29 0xbe39af4f, 0x87c, 0x423f, { 0x93, 0x3, 0x23, 0x4e, 0xc1, 0xe5, 0xb8, 0xee } 30 }; 31 32 enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 }; 33 34 // Converts Microsoft's channel configuration to ChannelLayout. 35 // This mapping is not perfect but the best we can do given the current 36 // ChannelLayout enumerator and the Windows-specific speaker configurations 37 // defined in ksmedia.h. Don't assume that the channel ordering in 38 // ChannelLayout is exactly the same as the Windows specific configuration. 39 // As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to 40 // CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R 41 // speakers are different in these two definitions. 42 static ChannelLayout ChannelConfigToChannelLayout(ChannelConfig config) { 43 switch (config) { 44 case KSAUDIO_SPEAKER_DIRECTOUT: 45 DVLOG(2) << "KSAUDIO_SPEAKER_DIRECTOUT=>CHANNEL_LAYOUT_NONE"; 46 return CHANNEL_LAYOUT_NONE; 47 case KSAUDIO_SPEAKER_MONO: 48 DVLOG(2) << "KSAUDIO_SPEAKER_MONO=>CHANNEL_LAYOUT_MONO"; 49 return CHANNEL_LAYOUT_MONO; 50 case KSAUDIO_SPEAKER_STEREO: 51 DVLOG(2) << "KSAUDIO_SPEAKER_STEREO=>CHANNEL_LAYOUT_STEREO"; 52 return CHANNEL_LAYOUT_STEREO; 53 case KSAUDIO_SPEAKER_QUAD: 54 DVLOG(2) << "KSAUDIO_SPEAKER_QUAD=>CHANNEL_LAYOUT_QUAD"; 55 return CHANNEL_LAYOUT_QUAD; 56 case KSAUDIO_SPEAKER_SURROUND: 57 DVLOG(2) << "KSAUDIO_SPEAKER_SURROUND=>CHANNEL_LAYOUT_4_0"; 58 return CHANNEL_LAYOUT_4_0; 59 case KSAUDIO_SPEAKER_5POINT1: 60 DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1=>CHANNEL_LAYOUT_5_1_BACK"; 61 return CHANNEL_LAYOUT_5_1_BACK; 62 case KSAUDIO_SPEAKER_5POINT1_SURROUND: 63 DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1_SURROUND=>CHANNEL_LAYOUT_5_1"; 64 return CHANNEL_LAYOUT_5_1; 65 case KSAUDIO_SPEAKER_7POINT1: 66 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1=>CHANNEL_LAYOUT_7_1_WIDE"; 67 return CHANNEL_LAYOUT_7_1_WIDE; 68 case KSAUDIO_SPEAKER_7POINT1_SURROUND: 69 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1_SURROUND=>CHANNEL_LAYOUT_7_1"; 70 return CHANNEL_LAYOUT_7_1; 71 default: 72 DVLOG(2) << "Unsupported channel configuration: " << config; 73 return CHANNEL_LAYOUT_UNSUPPORTED; 74 } 75 } 76 77 // TODO(henrika): add mapping for all types in the ChannelLayout enumerator. 78 static ChannelConfig ChannelLayoutToChannelConfig(ChannelLayout layout) { 79 switch (layout) { 80 case CHANNEL_LAYOUT_NONE: 81 DVLOG(2) << "CHANNEL_LAYOUT_NONE=>KSAUDIO_SPEAKER_UNSUPPORTED"; 82 return KSAUDIO_SPEAKER_UNSUPPORTED; 83 case CHANNEL_LAYOUT_UNSUPPORTED: 84 DVLOG(2) << "CHANNEL_LAYOUT_UNSUPPORTED=>KSAUDIO_SPEAKER_UNSUPPORTED"; 85 return KSAUDIO_SPEAKER_UNSUPPORTED; 86 case CHANNEL_LAYOUT_MONO: 87 DVLOG(2) << "CHANNEL_LAYOUT_MONO=>KSAUDIO_SPEAKER_MONO"; 88 return KSAUDIO_SPEAKER_MONO; 89 case CHANNEL_LAYOUT_STEREO: 90 DVLOG(2) << "CHANNEL_LAYOUT_STEREO=>KSAUDIO_SPEAKER_STEREO"; 91 return KSAUDIO_SPEAKER_STEREO; 92 case CHANNEL_LAYOUT_QUAD: 93 DVLOG(2) << "CHANNEL_LAYOUT_QUAD=>KSAUDIO_SPEAKER_QUAD"; 94 return KSAUDIO_SPEAKER_QUAD; 95 case CHANNEL_LAYOUT_4_0: 96 DVLOG(2) << "CHANNEL_LAYOUT_4_0=>KSAUDIO_SPEAKER_SURROUND"; 97 return KSAUDIO_SPEAKER_SURROUND; 98 case CHANNEL_LAYOUT_5_1_BACK: 99 DVLOG(2) << "CHANNEL_LAYOUT_5_1_BACK=>KSAUDIO_SPEAKER_5POINT1"; 100 return KSAUDIO_SPEAKER_5POINT1; 101 case CHANNEL_LAYOUT_5_1: 102 DVLOG(2) << "CHANNEL_LAYOUT_5_1=>KSAUDIO_SPEAKER_5POINT1_SURROUND"; 103 return KSAUDIO_SPEAKER_5POINT1_SURROUND; 104 case CHANNEL_LAYOUT_7_1_WIDE: 105 DVLOG(2) << "CHANNEL_LAYOUT_7_1_WIDE=>KSAUDIO_SPEAKER_7POINT1"; 106 return KSAUDIO_SPEAKER_7POINT1; 107 case CHANNEL_LAYOUT_7_1: 108 DVLOG(2) << "CHANNEL_LAYOUT_7_1=>KSAUDIO_SPEAKER_7POINT1_SURROUND"; 109 return KSAUDIO_SPEAKER_7POINT1_SURROUND; 110 default: 111 DVLOG(2) << "Unsupported channel layout: " << layout; 112 return KSAUDIO_SPEAKER_UNSUPPORTED; 113 } 114 } 115 116 static std::ostream& operator<<(std::ostream& os, 117 const WAVEFORMATPCMEX& format) { 118 os << "wFormatTag: 0x" << std::hex << format.Format.wFormatTag 119 << ", nChannels: " << std::dec << format.Format.nChannels 120 << ", nSamplesPerSec: " << format.Format.nSamplesPerSec 121 << ", nAvgBytesPerSec: " << format.Format.nAvgBytesPerSec 122 << ", nBlockAlign: " << format.Format.nBlockAlign 123 << ", wBitsPerSample: " << format.Format.wBitsPerSample 124 << ", cbSize: " << format.Format.cbSize 125 << ", wValidBitsPerSample: " << format.Samples.wValidBitsPerSample 126 << ", dwChannelMask: 0x" << std::hex << format.dwChannelMask; 127 return os; 128 } 129 130 static bool LoadAudiosesDll() { 131 static const wchar_t* const kAudiosesDLL = 132 L"%WINDIR%\\system32\\audioses.dll"; 133 134 wchar_t path[MAX_PATH] = {0}; 135 ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path)); 136 return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); 137 } 138 139 static bool CanCreateDeviceEnumerator() { 140 ScopedComPtr<IMMDeviceEnumerator> device_enumerator; 141 HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), 142 NULL, CLSCTX_INPROC_SERVER); 143 144 // If we hit CO_E_NOTINITIALIZED, CoInitialize has not been called and it 145 // must be called at least once for each thread that uses the COM library. 146 CHECK_NE(hr, CO_E_NOTINITIALIZED); 147 148 return SUCCEEDED(hr); 149 } 150 151 static std::string GetDeviceID(IMMDevice* device) { 152 ScopedCoMem<WCHAR> device_id_com; 153 std::string device_id; 154 if (SUCCEEDED(device->GetId(&device_id_com))) 155 base::WideToUTF8(device_id_com, wcslen(device_id_com), &device_id); 156 return device_id; 157 } 158 159 bool CoreAudioUtil::IsSupported() { 160 // It is possible to force usage of WaveXxx APIs by using a command line flag. 161 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 162 if (cmd_line->HasSwitch(switches::kForceWaveAudio)) { 163 DVLOG(1) << "Forcing usage of Windows WaveXxx APIs"; 164 return false; 165 } 166 167 // Microsoft does not plan to make the Core Audio APIs available for use 168 // with earlier versions of Windows, including Microsoft Windows Server 2003, 169 // Windows XP, Windows Millennium Edition, Windows 2000, and Windows 98. 170 if (base::win::GetVersion() < base::win::VERSION_VISTA) 171 return false; 172 173 // The audio core APIs are implemented in the Mmdevapi.dll and Audioses.dll 174 // system components. 175 // Dependency Walker shows that it is enough to verify possibility to load 176 // the Audioses DLL since it depends on Mmdevapi.dll. 177 // See http://crbug.com/166397 why this extra step is required to guarantee 178 // Core Audio support. 179 static bool g_audioses_dll_available = LoadAudiosesDll(); 180 if (!g_audioses_dll_available) 181 return false; 182 183 // Being able to load the Audioses.dll does not seem to be sufficient for 184 // all devices to guarantee Core Audio support. To be 100%, we also verify 185 // that it is possible to a create the IMMDeviceEnumerator interface. If this 186 // works as well we should be home free. 187 static bool g_can_create_device_enumerator = CanCreateDeviceEnumerator(); 188 LOG_IF(ERROR, !g_can_create_device_enumerator) 189 << "Failed to create Core Audio device enumerator on thread with ID " 190 << GetCurrentThreadId(); 191 return g_can_create_device_enumerator; 192 } 193 194 base::TimeDelta CoreAudioUtil::RefererenceTimeToTimeDelta(REFERENCE_TIME time) { 195 // Each unit of reference time is 100 nanoseconds <=> 0.1 microsecond. 196 return base::TimeDelta::FromMicroseconds(0.1 * time + 0.5); 197 } 198 199 AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() { 200 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 201 if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) 202 return AUDCLNT_SHAREMODE_EXCLUSIVE; 203 return AUDCLNT_SHAREMODE_SHARED; 204 } 205 206 int CoreAudioUtil::NumberOfActiveDevices(EDataFlow data_flow) { 207 DCHECK(IsSupported()); 208 // Create the IMMDeviceEnumerator interface. 209 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = 210 CreateDeviceEnumerator(); 211 if (!device_enumerator) 212 return 0; 213 214 // Generate a collection of active (present and not disabled) audio endpoint 215 // devices for the specified data-flow direction. 216 // This method will succeed even if all devices are disabled. 217 ScopedComPtr<IMMDeviceCollection> collection; 218 HRESULT hr = device_enumerator->EnumAudioEndpoints(data_flow, 219 DEVICE_STATE_ACTIVE, 220 collection.Receive()); 221 if (FAILED(hr)) { 222 LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr; 223 return 0; 224 } 225 226 // Retrieve the number of active audio devices for the specified direction 227 UINT number_of_active_devices = 0; 228 collection->GetCount(&number_of_active_devices); 229 DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ") 230 << "number of devices: " << number_of_active_devices; 231 return static_cast<int>(number_of_active_devices); 232 } 233 234 ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() { 235 DCHECK(IsSupported()); 236 ScopedComPtr<IMMDeviceEnumerator> device_enumerator; 237 HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), 238 NULL, CLSCTX_INPROC_SERVER); 239 if (hr == CO_E_NOTINITIALIZED) { 240 LOG(ERROR) << "CoCreateInstance fails with CO_E_NOTINITIALIZED"; 241 // We have seen crashes which indicates that this method can in fact 242 // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party 243 // modules. Calling CoInitializeEx is an attempt to resolve the reported 244 // issues. See http://crbug.com/378465 for details. 245 hr = CoInitializeEx(NULL, COINIT_MULTITHREADED); 246 if (SUCCEEDED(hr)) { 247 hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator), 248 NULL, CLSCTX_INPROC_SERVER); 249 } 250 } 251 CHECK(SUCCEEDED(hr)); 252 return device_enumerator; 253 } 254 255 ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow, 256 ERole role) { 257 DCHECK(IsSupported()); 258 ScopedComPtr<IMMDevice> endpoint_device; 259 260 // Create the IMMDeviceEnumerator interface. 261 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = 262 CreateDeviceEnumerator(); 263 if (!device_enumerator) 264 return endpoint_device; 265 266 // Retrieve the default audio endpoint for the specified data-flow 267 // direction and role. 268 HRESULT hr = device_enumerator->GetDefaultAudioEndpoint( 269 data_flow, role, endpoint_device.Receive()); 270 271 if (FAILED(hr)) { 272 DVLOG(1) << "IMMDeviceEnumerator::GetDefaultAudioEndpoint: " 273 << std::hex << hr; 274 return endpoint_device; 275 } 276 277 // Verify that the audio endpoint device is active, i.e., that the audio 278 // adapter that connects to the endpoint device is present and enabled. 279 DWORD state = DEVICE_STATE_DISABLED; 280 hr = endpoint_device->GetState(&state); 281 if (SUCCEEDED(hr)) { 282 if (!(state & DEVICE_STATE_ACTIVE)) { 283 DVLOG(1) << "Selected endpoint device is not active"; 284 endpoint_device.Release(); 285 } 286 } 287 return endpoint_device; 288 } 289 290 std::string CoreAudioUtil::GetDefaultOutputDeviceID() { 291 DCHECK(IsSupported()); 292 ScopedComPtr<IMMDevice> device(CreateDefaultDevice(eRender, eConsole)); 293 return device ? GetDeviceID(device) : std::string(); 294 } 295 296 ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice( 297 const std::string& device_id) { 298 DCHECK(IsSupported()); 299 ScopedComPtr<IMMDevice> endpoint_device; 300 301 // Create the IMMDeviceEnumerator interface. 302 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = 303 CreateDeviceEnumerator(); 304 if (!device_enumerator) 305 return endpoint_device; 306 307 // Retrieve an audio device specified by an endpoint device-identification 308 // string. 309 HRESULT hr = device_enumerator->GetDevice( 310 base::UTF8ToUTF16(device_id).c_str(), endpoint_device.Receive()); 311 DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: " 312 << std::hex << hr; 313 return endpoint_device; 314 } 315 316 HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { 317 DCHECK(IsSupported()); 318 319 // Retrieve unique name of endpoint device. 320 // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}". 321 AudioDeviceName device_name; 322 device_name.unique_id = GetDeviceID(device); 323 if (device_name.unique_id.empty()) 324 return E_FAIL; 325 326 // Retrieve user-friendly name of endpoint device. 327 // Example: "Microphone (Realtek High Definition Audio)". 328 ScopedComPtr<IPropertyStore> properties; 329 HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive()); 330 if (FAILED(hr)) 331 return hr; 332 base::win::ScopedPropVariant friendly_name; 333 hr = properties->GetValue(PKEY_Device_FriendlyName, friendly_name.Receive()); 334 if (FAILED(hr)) 335 return hr; 336 if (friendly_name.get().vt == VT_LPWSTR && friendly_name.get().pwszVal) { 337 base::WideToUTF8(friendly_name.get().pwszVal, 338 wcslen(friendly_name.get().pwszVal), 339 &device_name.device_name); 340 } 341 342 *name = device_name; 343 DVLOG(2) << "friendly name: " << device_name.device_name; 344 DVLOG(2) << "unique id : " << device_name.unique_id; 345 return hr; 346 } 347 348 std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device, 349 IMMDeviceEnumerator* enumerator) { 350 DCHECK(IsSupported()); 351 352 // Fetching the controller device id could be as simple as fetching the value 353 // of the "{B3F8FA53-0004-438E-9003-51A46E139BFC},2" property in the property 354 // store of the |device|, but that key isn't defined in any header and 355 // according to MS should not be relied upon. 356 // So, instead, we go deeper, look at the device topology and fetch the 357 // PKEY_Device_InstanceId of the associated physical audio device. 358 ScopedComPtr<IDeviceTopology> topology; 359 ScopedComPtr<IConnector> connector; 360 ScopedCoMem<WCHAR> filter_id; 361 if (FAILED(device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL, 362 topology.ReceiveVoid()) || 363 // For our purposes checking the first connected device should be enough 364 // and if there are cases where there are more than one device connected 365 // we're not sure how to handle that anyway. So we pass 0. 366 FAILED(topology->GetConnector(0, connector.Receive())) || 367 FAILED(connector->GetDeviceIdConnectedTo(&filter_id)))) { 368 DLOG(ERROR) << "Failed to get the device identifier of the audio device"; 369 return std::string(); 370 } 371 372 // Now look at the properties of the connected device node and fetch the 373 // instance id (PKEY_Device_InstanceId) of the device node that uniquely 374 // identifies the controller. 375 ScopedComPtr<IMMDevice> device_node; 376 ScopedComPtr<IPropertyStore> properties; 377 base::win::ScopedPropVariant instance_id; 378 if (FAILED(enumerator->GetDevice(filter_id, device_node.Receive())) || 379 FAILED(device_node->OpenPropertyStore(STGM_READ, properties.Receive())) || 380 FAILED(properties->GetValue(PKEY_Device_InstanceId, 381 instance_id.Receive())) || 382 instance_id.get().vt != VT_LPWSTR) { 383 DLOG(ERROR) << "Failed to get instance id of the audio device node"; 384 return std::string(); 385 } 386 387 std::string controller_id; 388 base::WideToUTF8(instance_id.get().pwszVal, 389 wcslen(instance_id.get().pwszVal), 390 &controller_id); 391 392 return controller_id; 393 } 394 395 std::string CoreAudioUtil::GetMatchingOutputDeviceID( 396 const std::string& input_device_id) { 397 ScopedComPtr<IMMDevice> input_device(CreateDevice(input_device_id)); 398 if (!input_device) 399 return std::string(); 400 401 // See if we can get id of the associated controller. 402 ScopedComPtr<IMMDeviceEnumerator> enumerator(CreateDeviceEnumerator()); 403 std::string controller_id(GetAudioControllerID(input_device, enumerator)); 404 if (controller_id.empty()) 405 return std::string(); 406 407 // Now enumerate the available (and active) output devices and see if any of 408 // them is associated with the same controller. 409 ScopedComPtr<IMMDeviceCollection> collection; 410 enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE, 411 collection.Receive()); 412 if (!collection) 413 return std::string(); 414 415 UINT count = 0; 416 collection->GetCount(&count); 417 ScopedComPtr<IMMDevice> output_device; 418 for (UINT i = 0; i < count; ++i) { 419 collection->Item(i, output_device.Receive()); 420 std::string output_controller_id(GetAudioControllerID( 421 output_device, enumerator)); 422 if (output_controller_id == controller_id) 423 break; 424 output_device = NULL; 425 } 426 427 return output_device ? GetDeviceID(output_device) : std::string(); 428 } 429 430 std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) { 431 DCHECK(IsSupported()); 432 ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id); 433 if (!audio_device) 434 return std::string(); 435 436 AudioDeviceName device_name; 437 HRESULT hr = GetDeviceName(audio_device, &device_name); 438 if (FAILED(hr)) 439 return std::string(); 440 441 return device_name.device_name; 442 } 443 444 bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow, 445 ERole role, 446 const std::string& device_id) { 447 DCHECK(IsSupported()); 448 ScopedComPtr<IMMDevice> device = CreateDefaultDevice(flow, role); 449 if (!device) 450 return false; 451 452 std::string str_default(GetDeviceID(device)); 453 return device_id.compare(str_default) == 0; 454 } 455 456 EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) { 457 DCHECK(IsSupported()); 458 ScopedComPtr<IMMEndpoint> endpoint; 459 HRESULT hr = device->QueryInterface(endpoint.Receive()); 460 if (FAILED(hr)) { 461 DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex << hr; 462 return eAll; 463 } 464 465 EDataFlow data_flow; 466 hr = endpoint->GetDataFlow(&data_flow); 467 if (FAILED(hr)) { 468 DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex << hr; 469 return eAll; 470 } 471 return data_flow; 472 } 473 474 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( 475 IMMDevice* audio_device) { 476 DCHECK(IsSupported()); 477 478 // Creates and activates an IAudioClient COM object given the selected 479 // endpoint device. 480 ScopedComPtr<IAudioClient> audio_client; 481 HRESULT hr = audio_device->Activate(__uuidof(IAudioClient), 482 CLSCTX_INPROC_SERVER, 483 NULL, 484 audio_client.ReceiveVoid()); 485 DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr; 486 return audio_client; 487 } 488 489 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient( 490 EDataFlow data_flow, ERole role) { 491 DCHECK(IsSupported()); 492 ScopedComPtr<IMMDevice> default_device(CreateDefaultDevice(data_flow, role)); 493 return (default_device ? CreateClient(default_device) : 494 ScopedComPtr<IAudioClient>()); 495 } 496 497 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( 498 const std::string& device_id, EDataFlow data_flow, ERole role) { 499 if (device_id.empty()) 500 return CreateDefaultClient(data_flow, role); 501 502 ScopedComPtr<IMMDevice> device(CreateDevice(device_id)); 503 if (!device) 504 return ScopedComPtr<IAudioClient>(); 505 506 return CreateClient(device); 507 } 508 509 HRESULT CoreAudioUtil::GetSharedModeMixFormat( 510 IAudioClient* client, WAVEFORMATPCMEX* format) { 511 DCHECK(IsSupported()); 512 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; 513 HRESULT hr = client->GetMixFormat( 514 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); 515 if (FAILED(hr)) 516 return hr; 517 518 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; 519 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); 520 521 memcpy(format, format_pcmex, bytes); 522 DVLOG(2) << *format; 523 524 return hr; 525 } 526 527 bool CoreAudioUtil::IsFormatSupported(IAudioClient* client, 528 AUDCLNT_SHAREMODE share_mode, 529 const WAVEFORMATPCMEX* format) { 530 DCHECK(IsSupported()); 531 ScopedCoMem<WAVEFORMATEXTENSIBLE> closest_match; 532 HRESULT hr = client->IsFormatSupported( 533 share_mode, reinterpret_cast<const WAVEFORMATEX*>(format), 534 reinterpret_cast<WAVEFORMATEX**>(&closest_match)); 535 536 // This log can only be triggered for shared mode. 537 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " 538 << "but a closest match exists."; 539 // This log can be triggered both for shared and exclusive modes. 540 DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format."; 541 if (hr == S_FALSE) { 542 DVLOG(2) << *closest_match; 543 } 544 545 return (hr == S_OK); 546 } 547 548 bool CoreAudioUtil::IsChannelLayoutSupported(const std::string& device_id, 549 EDataFlow data_flow, 550 ERole role, 551 ChannelLayout channel_layout) { 552 DCHECK(IsSupported()); 553 554 // First, get the preferred mixing format for shared mode streams. 555 556 ScopedComPtr<IAudioClient> client(CreateClient(device_id, data_flow, role)); 557 if (!client) 558 return false; 559 560 WAVEFORMATPCMEX format; 561 HRESULT hr = GetSharedModeMixFormat(client, &format); 562 if (FAILED(hr)) 563 return false; 564 565 // Next, check if it is possible to use an alternative format where the 566 // channel layout (and possibly number of channels) is modified. 567 568 // Convert generic channel layout into Windows-specific channel configuration. 569 ChannelConfig new_config = ChannelLayoutToChannelConfig(channel_layout); 570 if (new_config == KSAUDIO_SPEAKER_UNSUPPORTED) { 571 return false; 572 } 573 format.dwChannelMask = new_config; 574 575 // Modify the format if the new channel layout has changed the number of 576 // utilized channels. 577 const int channels = ChannelLayoutToChannelCount(channel_layout); 578 if (channels != format.Format.nChannels) { 579 format.Format.nChannels = channels; 580 format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * channels; 581 format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec * 582 format.Format.nBlockAlign; 583 } 584 DVLOG(2) << format; 585 586 // Some devices can initialize a shared-mode stream with a format that is 587 // not identical to the mix format obtained from the GetMixFormat() method. 588 // However, chances of succeeding increases if we use the same number of 589 // channels and the same sample rate as the mix format. I.e, this call will 590 // return true only in those cases where the audio engine is able to support 591 // an even wider range of shared-mode formats where the installation package 592 // for the audio device includes a local effects (LFX) audio processing 593 // object (APO) that can handle format conversions. 594 return CoreAudioUtil::IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED, 595 &format); 596 } 597 598 HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, 599 AUDCLNT_SHAREMODE share_mode, 600 REFERENCE_TIME* device_period) { 601 DCHECK(IsSupported()); 602 603 // Get the period of the engine thread. 604 REFERENCE_TIME default_period = 0; 605 REFERENCE_TIME minimum_period = 0; 606 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); 607 if (FAILED(hr)) 608 return hr; 609 610 *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period : 611 minimum_period; 612 DVLOG(2) << "device_period: " 613 << RefererenceTimeToTimeDelta(*device_period).InMillisecondsF() 614 << " [ms]"; 615 return hr; 616 } 617 618 HRESULT CoreAudioUtil::GetPreferredAudioParameters( 619 IAudioClient* client, AudioParameters* params) { 620 DCHECK(IsSupported()); 621 WAVEFORMATPCMEX mix_format; 622 HRESULT hr = GetSharedModeMixFormat(client, &mix_format); 623 if (FAILED(hr)) 624 return hr; 625 626 REFERENCE_TIME default_period = 0; 627 hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period); 628 if (FAILED(hr)) 629 return hr; 630 631 // Get the integer mask which corresponds to the channel layout the 632 // audio engine uses for its internal processing/mixing of shared-mode 633 // streams. This mask indicates which channels are present in the multi- 634 // channel stream. The least significant bit corresponds with the Front Left 635 // speaker, the next least significant bit corresponds to the Front Right 636 // speaker, and so on, continuing in the order defined in KsMedia.h. 637 // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083.aspx 638 // for more details. 639 ChannelConfig channel_config = mix_format.dwChannelMask; 640 641 // Convert Microsoft's channel configuration to genric ChannelLayout. 642 ChannelLayout channel_layout = ChannelConfigToChannelLayout(channel_config); 643 644 // Some devices don't appear to set a valid channel layout, so guess based on 645 // the number of channels. See http://crbug.com/311906. 646 if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) { 647 VLOG(1) << "Unsupported channel config: " 648 << std::hex << channel_config 649 << ". Guessing layout by channel count: " 650 << std::dec << mix_format.Format.nChannels; 651 channel_layout = GuessChannelLayout(mix_format.Format.nChannels); 652 } 653 654 // Preferred sample rate. 655 int sample_rate = mix_format.Format.nSamplesPerSec; 656 657 // TODO(henrika): possibly use format.Format.wBitsPerSample here instead. 658 // We use a hard-coded value of 16 bits per sample today even if most audio 659 // engines does the actual mixing in 32 bits per sample. 660 int bits_per_sample = 16; 661 662 // We are using the native device period to derive the smallest possible 663 // buffer size in shared mode. Note that the actual endpoint buffer will be 664 // larger than this size but it will be possible to fill it up in two calls. 665 // TODO(henrika): ensure that this scheme works for capturing as well. 666 int frames_per_buffer = static_cast<int>(sample_rate * 667 RefererenceTimeToTimeDelta(default_period).InSecondsF() + 0.5); 668 669 DVLOG(1) << "channel_layout : " << channel_layout; 670 DVLOG(1) << "sample_rate : " << sample_rate; 671 DVLOG(1) << "bits_per_sample : " << bits_per_sample; 672 DVLOG(1) << "frames_per_buffer: " << frames_per_buffer; 673 674 AudioParameters audio_params(AudioParameters::AUDIO_PCM_LOW_LATENCY, 675 channel_layout, 676 sample_rate, 677 bits_per_sample, 678 frames_per_buffer); 679 680 *params = audio_params; 681 return hr; 682 } 683 684 HRESULT CoreAudioUtil::GetPreferredAudioParameters( 685 EDataFlow data_flow, ERole role, AudioParameters* params) { 686 DCHECK(IsSupported()); 687 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); 688 if (!client) { 689 // Map NULL-pointer to new error code which can be different from the 690 // actual error code. The exact value is not important here. 691 return AUDCLNT_E_ENDPOINT_CREATE_FAILED; 692 } 693 694 HRESULT hr = GetPreferredAudioParameters(client, params); 695 if (FAILED(hr)) 696 return hr; 697 698 if (role == eCommunications) { 699 // Raise the 'DUCKING' flag for default communication devices. 700 *params = AudioParameters(params->format(), params->channel_layout(), 701 params->channels(), params->sample_rate(), params->bits_per_sample(), 702 params->frames_per_buffer(), 703 params->effects() | AudioParameters::DUCKING); 704 } 705 706 return hr; 707 } 708 709 HRESULT CoreAudioUtil::GetPreferredAudioParameters( 710 const std::string& device_id, AudioParameters* params) { 711 DCHECK(IsSupported()); 712 ScopedComPtr<IMMDevice> device(CreateDevice(device_id)); 713 if (!device) { 714 // Map NULL-pointer to new error code which can be different from the 715 // actual error code. The exact value is not important here. 716 return AUDCLNT_E_DEVICE_INVALIDATED; 717 } 718 719 ScopedComPtr<IAudioClient> client(CreateClient(device)); 720 if (!client) { 721 // Map NULL-pointer to new error code which can be different from the 722 // actual error code. The exact value is not important here. 723 return AUDCLNT_E_ENDPOINT_CREATE_FAILED; 724 } 725 return GetPreferredAudioParameters(client, params); 726 } 727 728 ChannelConfig CoreAudioUtil::GetChannelConfig(const std::string& device_id, 729 EDataFlow data_flow) { 730 ScopedComPtr<IAudioClient> client( 731 CreateClient(device_id, data_flow, eConsole)); 732 733 WAVEFORMATPCMEX format = {0}; 734 if (!client || FAILED(GetSharedModeMixFormat(client, &format))) 735 return 0; 736 737 return static_cast<ChannelConfig>(format.dwChannelMask); 738 } 739 740 HRESULT CoreAudioUtil::SharedModeInitialize( 741 IAudioClient* client, const WAVEFORMATPCMEX* format, HANDLE event_handle, 742 uint32* endpoint_buffer_size, const GUID* session_guid) { 743 DCHECK(IsSupported()); 744 745 // Use default flags (i.e, dont set AUDCLNT_STREAMFLAGS_NOPERSIST) to 746 // ensure that the volume level and muting state for a rendering session 747 // are persistent across system restarts. The volume level and muting 748 // state for a capture session are never persistent. 749 DWORD stream_flags = 0; 750 751 // Enable event-driven streaming if a valid event handle is provided. 752 // After the stream starts, the audio engine will signal the event handle 753 // to notify the client each time a buffer becomes ready to process. 754 // Event-driven buffering is supported for both rendering and capturing. 755 // Both shared-mode and exclusive-mode streams can use event-driven buffering. 756 bool use_event = (event_handle != NULL && 757 event_handle != INVALID_HANDLE_VALUE); 758 if (use_event) 759 stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; 760 DVLOG(2) << "stream_flags: 0x" << std::hex << stream_flags; 761 762 // Initialize the shared mode client for minimal delay. 763 HRESULT hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, 764 stream_flags, 765 0, 766 0, 767 reinterpret_cast<const WAVEFORMATEX*>(format), 768 session_guid); 769 if (FAILED(hr)) { 770 DVLOG(1) << "IAudioClient::Initialize: " << std::hex << hr; 771 return hr; 772 } 773 774 if (use_event) { 775 hr = client->SetEventHandle(event_handle); 776 if (FAILED(hr)) { 777 DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex << hr; 778 return hr; 779 } 780 } 781 782 UINT32 buffer_size_in_frames = 0; 783 hr = client->GetBufferSize(&buffer_size_in_frames); 784 if (FAILED(hr)) { 785 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr; 786 return hr; 787 } 788 789 *endpoint_buffer_size = buffer_size_in_frames; 790 DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames; 791 792 // TODO(henrika): utilize when delay measurements are added. 793 REFERENCE_TIME latency = 0; 794 hr = client->GetStreamLatency(&latency); 795 DVLOG(2) << "stream latency: " 796 << RefererenceTimeToTimeDelta(latency).InMillisecondsF() << " [ms]"; 797 return hr; 798 } 799 800 ScopedComPtr<IAudioRenderClient> CoreAudioUtil::CreateRenderClient( 801 IAudioClient* client) { 802 DCHECK(IsSupported()); 803 804 // Get access to the IAudioRenderClient interface. This interface 805 // enables us to write output data to a rendering endpoint buffer. 806 ScopedComPtr<IAudioRenderClient> audio_render_client; 807 HRESULT hr = client->GetService(__uuidof(IAudioRenderClient), 808 audio_render_client.ReceiveVoid()); 809 if (FAILED(hr)) { 810 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; 811 return ScopedComPtr<IAudioRenderClient>(); 812 } 813 return audio_render_client; 814 } 815 816 ScopedComPtr<IAudioCaptureClient> CoreAudioUtil::CreateCaptureClient( 817 IAudioClient* client) { 818 DCHECK(IsSupported()); 819 820 // Get access to the IAudioCaptureClient interface. This interface 821 // enables us to read input data from a capturing endpoint buffer. 822 ScopedComPtr<IAudioCaptureClient> audio_capture_client; 823 HRESULT hr = client->GetService(__uuidof(IAudioCaptureClient), 824 audio_capture_client.ReceiveVoid()); 825 if (FAILED(hr)) { 826 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; 827 return ScopedComPtr<IAudioCaptureClient>(); 828 } 829 return audio_capture_client; 830 } 831 832 bool CoreAudioUtil::FillRenderEndpointBufferWithSilence( 833 IAudioClient* client, IAudioRenderClient* render_client) { 834 DCHECK(IsSupported()); 835 836 UINT32 endpoint_buffer_size = 0; 837 if (FAILED(client->GetBufferSize(&endpoint_buffer_size))) 838 return false; 839 840 UINT32 num_queued_frames = 0; 841 if (FAILED(client->GetCurrentPadding(&num_queued_frames))) 842 return false; 843 844 BYTE* data = NULL; 845 int num_frames_to_fill = endpoint_buffer_size - num_queued_frames; 846 if (FAILED(render_client->GetBuffer(num_frames_to_fill, &data))) 847 return false; 848 849 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to 850 // explicitly write silence data to the rendering buffer. 851 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence"; 852 return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill, 853 AUDCLNT_BUFFERFLAGS_SILENT)); 854 } 855 856 } // namespace media 857