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