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