core_audio_util_win.cc revision 2a99a7e74a7f215066514fe81d2bfa6639d9eddd
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "media/audio/win/core_audio_util_win.h" 6 7#include <Audioclient.h> 8#include <Functiondiscoverykeys_devpkey.h> 9 10#include "base/command_line.h" 11#include "base/logging.h" 12#include "base/stringprintf.h" 13#include "base/utf_string_conversions.h" 14#include "base/win/scoped_co_mem.h" 15#include "base/win/scoped_handle.h" 16#include "base/win/scoped_propvariant.h" 17#include "base/win/windows_version.h" 18#include "media/base/media_switches.h" 19 20using base::win::ScopedCoMem; 21using base::win::ScopedHandle; 22 23namespace media { 24 25typedef uint32 ChannelConfig; 26 27// Converts Microsoft's channel configuration to ChannelLayout. 28// This mapping is not perfect but the best we can do given the current 29// ChannelLayout enumerator and the Windows-specific speaker configurations 30// defined in ksmedia.h. Don't assume that the channel ordering in 31// ChannelLayout is exactly the same as the Windows specific configuration. 32// As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to 33// CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R 34// speakers are different in these two definitions. 35static ChannelLayout ChannelConfigToChannelLayout(ChannelConfig config) { 36 switch (config) { 37 case KSAUDIO_SPEAKER_DIRECTOUT: 38 DVLOG(2) << "KSAUDIO_SPEAKER_DIRECTOUT=>CHANNEL_LAYOUT_NONE"; 39 return CHANNEL_LAYOUT_NONE; 40 case KSAUDIO_SPEAKER_MONO: 41 DVLOG(2) << "KSAUDIO_SPEAKER_MONO=>CHANNEL_LAYOUT_MONO"; 42 return CHANNEL_LAYOUT_MONO; 43 case KSAUDIO_SPEAKER_STEREO: 44 DVLOG(2) << "KSAUDIO_SPEAKER_STEREO=>CHANNEL_LAYOUT_STEREO"; 45 return CHANNEL_LAYOUT_STEREO; 46 case KSAUDIO_SPEAKER_QUAD: 47 DVLOG(2) << "KSAUDIO_SPEAKER_QUAD=>CHANNEL_LAYOUT_QUAD"; 48 return CHANNEL_LAYOUT_QUAD; 49 case KSAUDIO_SPEAKER_SURROUND: 50 DVLOG(2) << "KSAUDIO_SPEAKER_SURROUND=>CHANNEL_LAYOUT_4_0"; 51 return CHANNEL_LAYOUT_4_0; 52 case KSAUDIO_SPEAKER_5POINT1: 53 DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1=>CHANNEL_LAYOUT_5_1_BACK"; 54 return CHANNEL_LAYOUT_5_1_BACK; 55 case KSAUDIO_SPEAKER_5POINT1_SURROUND: 56 DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1_SURROUND=>CHANNEL_LAYOUT_5_1"; 57 return CHANNEL_LAYOUT_5_1; 58 case KSAUDIO_SPEAKER_7POINT1: 59 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1=>CHANNEL_LAYOUT_7_1_WIDE"; 60 return CHANNEL_LAYOUT_7_1_WIDE; 61 case KSAUDIO_SPEAKER_7POINT1_SURROUND: 62 DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1_SURROUND=>CHANNEL_LAYOUT_7_1"; 63 return CHANNEL_LAYOUT_7_1; 64 default: 65 DVLOG(2) << "Unsupported channel layout: " << config; 66 return CHANNEL_LAYOUT_UNSUPPORTED; 67 } 68} 69 70bool LoadAudiosesDll() { 71 static const wchar_t* const kAudiosesDLL = 72 L"%WINDIR%\\system32\\audioses.dll"; 73 74 wchar_t path[MAX_PATH] = {0}; 75 ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path)); 76 return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL); 77} 78 79bool CanCreateDeviceEnumerator() { 80 ScopedComPtr<IMMDeviceEnumerator> device_enumerator; 81 HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), 82 NULL, 83 CLSCTX_INPROC_SERVER, 84 __uuidof(IMMDeviceEnumerator), 85 device_enumerator.ReceiveVoid()); 86 87 // If we hit CO_E_NOTINITIALIZED, CoInitialize has not been called and it 88 // must be called at least once for each thread that uses the COM library. 89 CHECK_NE(hr, CO_E_NOTINITIALIZED); 90 91 return SUCCEEDED(hr); 92} 93 94bool CoreAudioUtil::IsSupported() { 95 // Microsoft does not plan to make the Core Audio APIs available for use 96 // with earlier versions of Windows, including Microsoft Windows Server 2003, 97 // Windows XP, Windows Millennium Edition, Windows 2000, and Windows 98. 98 if (base::win::GetVersion() < base::win::VERSION_VISTA) 99 return false; 100 101 // The audio core APIs are implemented in the Mmdevapi.dll and Audioses.dll 102 // system components. 103 // Dependency Walker shows that it is enough to verify possibility to load 104 // the Audioses DLL since it depends on Mmdevapi.dll. 105 // See http://crbug.com/166397 why this extra step is required to guarantee 106 // Core Audio support. 107 static bool g_audioses_dll_available = LoadAudiosesDll(); 108 if (!g_audioses_dll_available) 109 return false; 110 111 // Being able to load the Audioses.dll does not seem to be sufficient for 112 // all devices to guarantee Core Audio support. To be 100%, we also verify 113 // that it is possible to a create the IMMDeviceEnumerator interface. If this 114 // works as well we should be home free. 115 static bool g_can_create_device_enumerator = CanCreateDeviceEnumerator(); 116 LOG_IF(ERROR, !g_can_create_device_enumerator) 117 << "Failed to create Core Audio device enumerator on thread with ID " 118 << GetCurrentThreadId(); 119 return g_can_create_device_enumerator; 120} 121 122base::TimeDelta CoreAudioUtil::RefererenceTimeToTimeDelta(REFERENCE_TIME time) { 123 // Each unit of reference time is 100 nanoseconds <=> 0.1 microsecond. 124 return base::TimeDelta::FromMicroseconds(0.1 * time + 0.5); 125} 126 127AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() { 128 const CommandLine* cmd_line = CommandLine::ForCurrentProcess(); 129 if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio)) 130 return AUDCLNT_SHAREMODE_EXCLUSIVE; 131 return AUDCLNT_SHAREMODE_SHARED; 132} 133 134int CoreAudioUtil::NumberOfActiveDevices(EDataFlow data_flow) { 135 DCHECK(IsSupported()); 136 // Create the IMMDeviceEnumerator interface. 137 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = 138 CreateDeviceEnumerator(); 139 if (!device_enumerator) 140 return 0; 141 142 // Generate a collection of active (present and not disabled) audio endpoint 143 // devices for the specified data-flow direction. 144 // This method will succeed even if all devices are disabled. 145 ScopedComPtr<IMMDeviceCollection> collection; 146 HRESULT hr = device_enumerator->EnumAudioEndpoints(data_flow, 147 DEVICE_STATE_ACTIVE, 148 collection.Receive()); 149 if (FAILED(hr)) { 150 LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr; 151 return 0; 152 } 153 154 // Retrieve the number of active audio devices for the specified direction 155 UINT number_of_active_devices = 0; 156 collection->GetCount(&number_of_active_devices); 157 DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ") 158 << "number of devices: " << number_of_active_devices; 159 return static_cast<int>(number_of_active_devices); 160} 161 162ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() { 163 DCHECK(IsSupported()); 164 ScopedComPtr<IMMDeviceEnumerator> device_enumerator; 165 HRESULT hr = CoCreateInstance(__uuidof(MMDeviceEnumerator), 166 NULL, 167 CLSCTX_INPROC_SERVER, 168 __uuidof(IMMDeviceEnumerator), 169 device_enumerator.ReceiveVoid()); 170 CHECK(SUCCEEDED(hr)); 171 return device_enumerator; 172} 173 174ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow, 175 ERole role) { 176 DCHECK(IsSupported()); 177 ScopedComPtr<IMMDevice> endpoint_device; 178 179 // Create the IMMDeviceEnumerator interface. 180 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = 181 CreateDeviceEnumerator(); 182 if (!device_enumerator) 183 return endpoint_device; 184 185 // Retrieve the default audio endpoint for the specified data-flow 186 // direction and role. 187 HRESULT hr = device_enumerator->GetDefaultAudioEndpoint( 188 data_flow, role, endpoint_device.Receive()); 189 190 if (FAILED(hr)) { 191 DVLOG(1) << "IMMDeviceEnumerator::GetDefaultAudioEndpoint: " 192 << std::hex << hr; 193 return endpoint_device; 194 } 195 196 // Verify that the audio endpoint device is active, i.e., that the audio 197 // adapter that connects to the endpoint device is present and enabled. 198 DWORD state = DEVICE_STATE_DISABLED; 199 hr = endpoint_device->GetState(&state); 200 if (SUCCEEDED(hr)) { 201 if (!(state & DEVICE_STATE_ACTIVE)) { 202 DVLOG(1) << "Selected endpoint device is not active"; 203 endpoint_device.Release(); 204 } 205 } 206 return endpoint_device; 207} 208 209ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice( 210 const std::string& device_id) { 211 DCHECK(IsSupported()); 212 ScopedComPtr<IMMDevice> endpoint_device; 213 214 // Create the IMMDeviceEnumerator interface. 215 ScopedComPtr<IMMDeviceEnumerator> device_enumerator = 216 CreateDeviceEnumerator(); 217 if (!device_enumerator) 218 return endpoint_device; 219 220 // Retrieve an audio device specified by an endpoint device-identification 221 // string. 222 HRESULT hr = device_enumerator->GetDevice(UTF8ToUTF16(device_id).c_str(), 223 endpoint_device.Receive()); 224 DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: " 225 << std::hex << hr; 226 return endpoint_device; 227} 228 229HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) { 230 DCHECK(IsSupported()); 231 232 // Retrieve unique name of endpoint device. 233 // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}". 234 AudioDeviceName device_name; 235 ScopedCoMem<WCHAR> endpoint_device_id; 236 HRESULT hr = device->GetId(&endpoint_device_id); 237 if (FAILED(hr)) 238 return hr; 239 WideToUTF8(endpoint_device_id, wcslen(endpoint_device_id), 240 &device_name.unique_id); 241 242 // Retrieve user-friendly name of endpoint device. 243 // Example: "Microphone (Realtek High Definition Audio)". 244 ScopedComPtr<IPropertyStore> properties; 245 hr = device->OpenPropertyStore(STGM_READ, properties.Receive()); 246 if (FAILED(hr)) 247 return hr; 248 base::win::ScopedPropVariant friendly_name; 249 hr = properties->GetValue(PKEY_Device_FriendlyName, friendly_name.Receive()); 250 if (FAILED(hr)) 251 return hr; 252 if (friendly_name.get().vt == VT_LPWSTR && friendly_name.get().pwszVal) { 253 WideToUTF8(friendly_name.get().pwszVal, 254 wcslen(friendly_name.get().pwszVal), 255 &device_name.device_name); 256 } 257 258 *name = device_name; 259 DVLOG(2) << "friendly name: " << device_name.device_name; 260 DVLOG(2) << "unique id : " << device_name.unique_id; 261 return hr; 262} 263 264std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) { 265 DCHECK(IsSupported()); 266 ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id); 267 if (!audio_device) 268 return std::string(); 269 270 AudioDeviceName device_name; 271 HRESULT hr = GetDeviceName(audio_device, &device_name); 272 if (FAILED(hr)) 273 return std::string(); 274 275 return device_name.device_name; 276} 277 278bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow, 279 ERole role, 280 std::string device_id) { 281 DCHECK(IsSupported()); 282 ScopedComPtr<IMMDevice> device = CreateDefaultDevice(flow, role); 283 if (!device) 284 return false; 285 286 ScopedCoMem<WCHAR> default_device_id; 287 HRESULT hr = device->GetId(&default_device_id); 288 if (FAILED(hr)) 289 return false; 290 291 std::string str_default; 292 WideToUTF8(default_device_id, wcslen(default_device_id), &str_default); 293 if (device_id.compare(str_default) != 0) 294 return false; 295 return true; 296} 297 298EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) { 299 DCHECK(IsSupported()); 300 ScopedComPtr<IMMEndpoint> endpoint; 301 HRESULT hr = device->QueryInterface(endpoint.Receive()); 302 if (FAILED(hr)) { 303 DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex << hr; 304 return eAll; 305 } 306 307 EDataFlow data_flow; 308 hr = endpoint->GetDataFlow(&data_flow); 309 if (FAILED(hr)) { 310 DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex << hr; 311 return eAll; 312 } 313 return data_flow; 314} 315 316ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient( 317 IMMDevice* audio_device) { 318 DCHECK(IsSupported()); 319 320 // Creates and activates an IAudioClient COM object given the selected 321 // endpoint device. 322 ScopedComPtr<IAudioClient> audio_client; 323 HRESULT hr = audio_device->Activate(__uuidof(IAudioClient), 324 CLSCTX_INPROC_SERVER, 325 NULL, 326 audio_client.ReceiveVoid()); 327 DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr; 328 return audio_client; 329} 330 331ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient( 332 EDataFlow data_flow, ERole role) { 333 DCHECK(IsSupported()); 334 ScopedComPtr<IMMDevice> default_device(CreateDefaultDevice(data_flow, role)); 335 return (default_device ? CreateClient(default_device) : 336 ScopedComPtr<IAudioClient>()); 337} 338 339HRESULT CoreAudioUtil::GetSharedModeMixFormat( 340 IAudioClient* client, WAVEFORMATPCMEX* format) { 341 DCHECK(IsSupported()); 342 ScopedCoMem<WAVEFORMATPCMEX> format_pcmex; 343 HRESULT hr = client->GetMixFormat( 344 reinterpret_cast<WAVEFORMATEX**>(&format_pcmex)); 345 if (FAILED(hr)) 346 return hr; 347 348 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize; 349 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX)); 350 351 memcpy(format, format_pcmex, bytes); 352 353 DVLOG(2) << "wFormatTag: 0x" << std::hex << format->Format.wFormatTag 354 << ", nChannels: " << std::dec << format->Format.nChannels 355 << ", nSamplesPerSec: " << format->Format.nSamplesPerSec 356 << ", nAvgBytesPerSec: " << format->Format.nAvgBytesPerSec 357 << ", nBlockAlign: " << format->Format.nBlockAlign 358 << ", wBitsPerSample: " << format->Format.wBitsPerSample 359 << ", cbSize: " << format->Format.cbSize 360 << ", wValidBitsPerSample: " << format->Samples.wValidBitsPerSample 361 << ", dwChannelMask: 0x" << std::hex << format->dwChannelMask; 362 363 return hr; 364} 365 366HRESULT CoreAudioUtil::GetDefaultSharedModeMixFormat( 367 EDataFlow data_flow, ERole role, WAVEFORMATPCMEX* format) { 368 DCHECK(IsSupported()); 369 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); 370 if (!client) { 371 // Map NULL-pointer to new error code which can be different from the 372 // actual error code. The exact value is not important here. 373 return AUDCLNT_E_ENDPOINT_CREATE_FAILED; 374 } 375 return CoreAudioUtil::GetSharedModeMixFormat(client, format); 376} 377 378bool CoreAudioUtil::IsFormatSupported(IAudioClient* client, 379 AUDCLNT_SHAREMODE share_mode, 380 const WAVEFORMATPCMEX* format) { 381 DCHECK(IsSupported()); 382 ScopedCoMem<WAVEFORMATEXTENSIBLE> closest_match; 383 HRESULT hr = client->IsFormatSupported( 384 share_mode, reinterpret_cast<const WAVEFORMATEX*>(format), 385 reinterpret_cast<WAVEFORMATEX**>(&closest_match)); 386 387 // This log can only be triggered for shared mode. 388 DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported " 389 << "but a closest match exists."; 390 // This log can be triggered both for shared and exclusive modes. 391 DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format."; 392 if (hr == S_FALSE) { 393 DVLOG(2) << "wFormatTag: " << closest_match->Format.wFormatTag 394 << ", nChannels: " << closest_match->Format.nChannels 395 << ", nSamplesPerSec: " << closest_match->Format.nSamplesPerSec 396 << ", wBitsPerSample: " << closest_match->Format.wBitsPerSample; 397 } 398 399 return (hr == S_OK); 400} 401 402HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client, 403 AUDCLNT_SHAREMODE share_mode, 404 REFERENCE_TIME* device_period) { 405 DCHECK(IsSupported()); 406 407 // Get the period of the engine thread. 408 REFERENCE_TIME default_period = 0; 409 REFERENCE_TIME minimum_period = 0; 410 HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period); 411 if (FAILED(hr)) 412 return hr; 413 414 *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period : 415 minimum_period; 416 DVLOG(2) << "device_period: " 417 << RefererenceTimeToTimeDelta(*device_period).InMillisecondsF() 418 << " [ms]"; 419 return hr; 420} 421 422HRESULT CoreAudioUtil::GetPreferredAudioParameters( 423 IAudioClient* client, AudioParameters* params) { 424 DCHECK(IsSupported()); 425 WAVEFORMATPCMEX mix_format; 426 HRESULT hr = GetSharedModeMixFormat(client, &mix_format); 427 if (FAILED(hr)) 428 return hr; 429 430 REFERENCE_TIME default_period = 0; 431 hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period); 432 if (FAILED(hr)) 433 return hr; 434 435 // Get the integer mask which corresponds to the channel layout the 436 // audio engine uses for its internal processing/mixing of shared-mode 437 // streams. This mask indicates which channels are present in the multi- 438 // channel stream. The least significant bit corresponds with the Front Left 439 // speaker, the next least significant bit corresponds to the Front Right 440 // speaker, and so on, continuing in the order defined in KsMedia.h. 441 // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083.aspx 442 // for more details. 443 ChannelConfig channel_config = mix_format.dwChannelMask; 444 445 // Convert Microsoft's channel configuration to genric ChannelLayout. 446 ChannelLayout channel_layout = ChannelConfigToChannelLayout(channel_config); 447 448 // Preferred sample rate. 449 int sample_rate = mix_format.Format.nSamplesPerSec; 450 451 // TODO(henrika): possibly use format.Format.wBitsPerSample here instead. 452 // We use a hard-coded value of 16 bits per sample today even if most audio 453 // engines does the actual mixing in 32 bits per sample. 454 int bits_per_sample = 16; 455 456 // We are using the native device period to derive the smallest possible 457 // buffer size in shared mode. Note that the actual endpoint buffer will be 458 // larger than this size but it will be possible to fill it up in two calls. 459 // TODO(henrika): ensure that this scheme works for capturing as well. 460 int frames_per_buffer = static_cast<int>(sample_rate * 461 RefererenceTimeToTimeDelta(default_period).InSecondsF() + 0.5); 462 463 DVLOG(1) << "channel_layout : " << channel_layout; 464 DVLOG(1) << "sample_rate : " << sample_rate; 465 DVLOG(1) << "bits_per_sample : " << bits_per_sample; 466 DVLOG(1) << "frames_per_buffer: " << frames_per_buffer; 467 468 AudioParameters audio_params(AudioParameters::AUDIO_PCM_LOW_LATENCY, 469 channel_layout, 470 sample_rate, 471 bits_per_sample, 472 frames_per_buffer); 473 474 *params = audio_params; 475 return hr; 476} 477 478HRESULT CoreAudioUtil::GetPreferredAudioParameters( 479 EDataFlow data_flow, ERole role, AudioParameters* params) { 480 DCHECK(IsSupported()); 481 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role)); 482 if (!client) { 483 // Map NULL-pointer to new error code which can be different from the 484 // actual error code. The exact value is not important here. 485 return AUDCLNT_E_ENDPOINT_CREATE_FAILED; 486 } 487 return GetPreferredAudioParameters(client, params); 488} 489 490HRESULT CoreAudioUtil::SharedModeInitialize(IAudioClient* client, 491 const WAVEFORMATPCMEX* format, 492 HANDLE event_handle, 493 uint32* endpoint_buffer_size) { 494 DCHECK(IsSupported()); 495 496 // Use default flags (i.e, dont set AUDCLNT_STREAMFLAGS_NOPERSIST) to 497 // ensure that the volume level and muting state for a rendering session 498 // are persistent across system restarts. The volume level and muting 499 // state for a capture session are never persistent. 500 DWORD stream_flags = 0; 501 502 // Enable event-driven streaming if a valid event handle is provided. 503 // After the stream starts, the audio engine will signal the event handle 504 // to notify the client each time a buffer becomes ready to process. 505 // Event-driven buffering is supported for both rendering and capturing. 506 // Both shared-mode and exclusive-mode streams can use event-driven buffering. 507 bool use_event = (event_handle != NULL && 508 event_handle != INVALID_HANDLE_VALUE); 509 if (use_event) 510 stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK; 511 DVLOG(2) << "stream_flags: 0x" << std::hex << stream_flags; 512 513 // Initialize the shared mode client for minimal delay. 514 HRESULT hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED, 515 stream_flags, 516 0, 517 0, 518 reinterpret_cast<const WAVEFORMATEX*>(format), 519 NULL); 520 if (FAILED(hr)) { 521 DVLOG(1) << "IAudioClient::Initialize: " << std::hex << hr; 522 return hr; 523 } 524 525 if (use_event) { 526 hr = client->SetEventHandle(event_handle); 527 if (FAILED(hr)) { 528 DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex << hr; 529 return hr; 530 } 531 } 532 533 UINT32 buffer_size_in_frames = 0; 534 hr = client->GetBufferSize(&buffer_size_in_frames); 535 if (FAILED(hr)) { 536 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr; 537 return hr; 538 } 539 540 *endpoint_buffer_size = buffer_size_in_frames; 541 DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames; 542 543 // TODO(henrika): utilize when delay measurements are added. 544 REFERENCE_TIME latency = 0; 545 hr = client->GetStreamLatency(&latency); 546 DVLOG(2) << "stream latency: " 547 << RefererenceTimeToTimeDelta(latency).InMillisecondsF() << " [ms]"; 548 return hr; 549} 550 551ScopedComPtr<IAudioRenderClient> CoreAudioUtil::CreateRenderClient( 552 IAudioClient* client) { 553 DCHECK(IsSupported()); 554 555 // Get access to the IAudioRenderClient interface. This interface 556 // enables us to write output data to a rendering endpoint buffer. 557 ScopedComPtr<IAudioRenderClient> audio_render_client; 558 HRESULT hr = client->GetService(__uuidof(IAudioRenderClient), 559 audio_render_client.ReceiveVoid()); 560 if (FAILED(hr)) { 561 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; 562 return ScopedComPtr<IAudioRenderClient>(); 563 } 564 return audio_render_client; 565} 566 567ScopedComPtr<IAudioCaptureClient> CoreAudioUtil::CreateCaptureClient( 568 IAudioClient* client) { 569 DCHECK(IsSupported()); 570 571 // Get access to the IAudioCaptureClient interface. This interface 572 // enables us to read input data from a capturing endpoint buffer. 573 ScopedComPtr<IAudioCaptureClient> audio_capture_client; 574 HRESULT hr = client->GetService(__uuidof(IAudioCaptureClient), 575 audio_capture_client.ReceiveVoid()); 576 if (FAILED(hr)) { 577 DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr; 578 return ScopedComPtr<IAudioCaptureClient>(); 579 } 580 return audio_capture_client; 581} 582 583bool CoreAudioUtil::FillRenderEndpointBufferWithSilence( 584 IAudioClient* client, IAudioRenderClient* render_client) { 585 DCHECK(IsSupported()); 586 587 UINT32 endpoint_buffer_size = 0; 588 if (FAILED(client->GetBufferSize(&endpoint_buffer_size))) 589 return false; 590 591 UINT32 num_queued_frames = 0; 592 if (FAILED(client->GetCurrentPadding(&num_queued_frames))) 593 return false; 594 595 BYTE* data = NULL; 596 int num_frames_to_fill = endpoint_buffer_size - num_queued_frames; 597 if (FAILED(render_client->GetBuffer(num_frames_to_fill, &data))) 598 return false; 599 600 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to 601 // explicitly write silence data to the rendering buffer. 602 DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence"; 603 return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill, 604 AUDCLNT_BUFFERFLAGS_SILENT)); 605} 606 607} // namespace media 608