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.
5 #include "media/audio/win/core_audio_util_win.h"
7 #include <devicetopology.h>
8 #include <functiondiscoverykeys_devpkey.h>
10 #include "base/command_line.h"
11 #include "base/logging.h"
12 #include "base/strings/stringprintf.h"
13 #include "base/strings/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/audio/audio_manager_base.h"
19 #include "media/base/media_switches.h"
21 using base::win::ScopedCoMem
;
22 using base::win::ScopedHandle
;
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 }
32 enum { KSAUDIO_SPEAKER_UNSUPPORTED
= 0 };
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
) {
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
;
72 DVLOG(2) << "Unsupported channel configuration: " << config
;
73 return CHANNEL_LAYOUT_UNSUPPORTED
;
77 // TODO(henrika): add mapping for all types in the ChannelLayout enumerator.
78 static ChannelConfig
ChannelLayoutToChannelConfig(ChannelLayout 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
;
111 DVLOG(2) << "Unsupported channel layout: " << layout
;
112 return KSAUDIO_SPEAKER_UNSUPPORTED
;
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
;
130 static bool LoadAudiosesDll() {
131 static const wchar_t* const kAudiosesDLL
=
132 L
"%WINDIR%\\system32\\audioses.dll";
134 wchar_t path
[MAX_PATH
] = {0};
135 ExpandEnvironmentStringsW(kAudiosesDLL
, path
, arraysize(path
));
136 return (LoadLibraryExW(path
, NULL
, LOAD_WITH_ALTERED_SEARCH_PATH
) != NULL
);
139 static std::string
GetDeviceID(IMMDevice
* device
) {
140 ScopedCoMem
<WCHAR
> device_id_com
;
141 std::string device_id
;
142 if (SUCCEEDED(device
->GetId(&device_id_com
)))
143 base::WideToUTF8(device_id_com
, wcslen(device_id_com
), &device_id
);
147 static bool IsDeviceActive(IMMDevice
* device
) {
148 DWORD state
= DEVICE_STATE_DISABLED
;
149 return SUCCEEDED(device
->GetState(&state
)) && (state
& DEVICE_STATE_ACTIVE
);
152 static HRESULT
GetDeviceFriendlyNameInternal(IMMDevice
* device
,
153 std::string
* friendly_name
) {
154 // Retrieve user-friendly name of endpoint device.
155 // Example: "Microphone (Realtek High Definition Audio)".
156 ScopedComPtr
<IPropertyStore
> properties
;
157 HRESULT hr
= device
->OpenPropertyStore(STGM_READ
, properties
.Receive());
161 base::win::ScopedPropVariant friendly_name_pv
;
162 hr
= properties
->GetValue(PKEY_Device_FriendlyName
,
163 friendly_name_pv
.Receive());
167 if (friendly_name_pv
.get().vt
== VT_LPWSTR
&&
168 friendly_name_pv
.get().pwszVal
) {
169 base::WideToUTF8(friendly_name_pv
.get().pwszVal
,
170 wcslen(friendly_name_pv
.get().pwszVal
), friendly_name
);
176 static ScopedComPtr
<IMMDeviceEnumerator
> CreateDeviceEnumeratorInternal(
177 bool allow_reinitialize
) {
178 ScopedComPtr
<IMMDeviceEnumerator
> device_enumerator
;
179 HRESULT hr
= device_enumerator
.CreateInstance(__uuidof(MMDeviceEnumerator
),
180 NULL
, CLSCTX_INPROC_SERVER
);
181 if (hr
== CO_E_NOTINITIALIZED
&& allow_reinitialize
) {
182 LOG(ERROR
) << "CoCreateInstance fails with CO_E_NOTINITIALIZED";
183 // We have seen crashes which indicates that this method can in fact
184 // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party
185 // modules. Calling CoInitializeEx is an attempt to resolve the reported
186 // issues. See http://crbug.com/378465 for details.
187 hr
= CoInitializeEx(NULL
, COINIT_MULTITHREADED
);
189 hr
= device_enumerator
.CreateInstance(__uuidof(MMDeviceEnumerator
),
190 NULL
, CLSCTX_INPROC_SERVER
);
193 return device_enumerator
;
196 static bool IsSupportedInternal() {
197 // It is possible to force usage of WaveXxx APIs by using a command line flag.
198 const base::CommandLine
* cmd_line
= base::CommandLine::ForCurrentProcess();
199 if (cmd_line
->HasSwitch(switches::kForceWaveAudio
)) {
200 DVLOG(1) << "Forcing usage of Windows WaveXxx APIs";
204 // Microsoft does not plan to make the Core Audio APIs available for use
205 // with earlier versions of Windows, including Microsoft Windows Server 2003,
206 // Windows XP, Windows Millennium Edition, Windows 2000, and Windows 98.
207 if (base::win::GetVersion() < base::win::VERSION_VISTA
)
210 // The audio core APIs are implemented in the Mmdevapi.dll and Audioses.dll
211 // system components.
212 // Dependency Walker shows that it is enough to verify possibility to load
213 // the Audioses DLL since it depends on Mmdevapi.dll.
214 // See http://crbug.com/166397 why this extra step is required to guarantee
215 // Core Audio support.
216 if (!LoadAudiosesDll())
219 // Being able to load the Audioses.dll does not seem to be sufficient for
220 // all devices to guarantee Core Audio support. To be 100%, we also verify
221 // that it is possible to a create the IMMDeviceEnumerator interface. If this
222 // works as well we should be home free.
223 ScopedComPtr
<IMMDeviceEnumerator
> device_enumerator
=
224 CreateDeviceEnumeratorInternal(false);
225 if (!device_enumerator
) {
227 << "Failed to create Core Audio device enumerator on thread with ID "
228 << GetCurrentThreadId();
235 bool CoreAudioUtil::IsSupported() {
236 static bool g_is_supported
= IsSupportedInternal();
237 return g_is_supported
;
240 base::TimeDelta
CoreAudioUtil::RefererenceTimeToTimeDelta(REFERENCE_TIME time
) {
241 // Each unit of reference time is 100 nanoseconds <=> 0.1 microsecond.
242 return base::TimeDelta::FromMicroseconds(0.1 * time
+ 0.5);
245 AUDCLNT_SHAREMODE
CoreAudioUtil::GetShareMode() {
246 const base::CommandLine
* cmd_line
= base::CommandLine::ForCurrentProcess();
247 if (cmd_line
->HasSwitch(switches::kEnableExclusiveAudio
))
248 return AUDCLNT_SHAREMODE_EXCLUSIVE
;
249 return AUDCLNT_SHAREMODE_SHARED
;
252 int CoreAudioUtil::NumberOfActiveDevices(EDataFlow data_flow
) {
253 DCHECK(IsSupported());
254 // Create the IMMDeviceEnumerator interface.
255 ScopedComPtr
<IMMDeviceEnumerator
> device_enumerator
=
256 CreateDeviceEnumerator();
257 if (!device_enumerator
.get())
260 // Generate a collection of active (present and not disabled) audio endpoint
261 // devices for the specified data-flow direction.
262 // This method will succeed even if all devices are disabled.
263 ScopedComPtr
<IMMDeviceCollection
> collection
;
264 HRESULT hr
= device_enumerator
->EnumAudioEndpoints(data_flow
,
266 collection
.Receive());
268 LOG(ERROR
) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex
<< hr
;
272 // Retrieve the number of active audio devices for the specified direction
273 UINT number_of_active_devices
= 0;
274 collection
->GetCount(&number_of_active_devices
);
275 DVLOG(2) << ((data_flow
== eCapture
) ? "[in ] " : "[out] ")
276 << "number of devices: " << number_of_active_devices
;
277 return static_cast<int>(number_of_active_devices
);
280 ScopedComPtr
<IMMDeviceEnumerator
> CoreAudioUtil::CreateDeviceEnumerator() {
281 DCHECK(IsSupported());
282 ScopedComPtr
<IMMDeviceEnumerator
> device_enumerator
=
283 CreateDeviceEnumeratorInternal(true);
284 CHECK(device_enumerator
);
285 return device_enumerator
;
288 ScopedComPtr
<IMMDevice
> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow
,
290 DCHECK(IsSupported());
291 ScopedComPtr
<IMMDevice
> endpoint_device
;
293 // Create the IMMDeviceEnumerator interface.
294 ScopedComPtr
<IMMDeviceEnumerator
> device_enumerator
=
295 CreateDeviceEnumerator();
296 if (!device_enumerator
.get())
297 return endpoint_device
;
299 // Retrieve the default audio endpoint for the specified data-flow
300 // direction and role.
301 HRESULT hr
= device_enumerator
->GetDefaultAudioEndpoint(
302 data_flow
, role
, endpoint_device
.Receive());
305 DVLOG(1) << "IMMDeviceEnumerator::GetDefaultAudioEndpoint: "
307 return endpoint_device
;
310 // Verify that the audio endpoint device is active, i.e., that the audio
311 // adapter that connects to the endpoint device is present and enabled.
312 if (!IsDeviceActive(endpoint_device
.get())) {
313 DVLOG(1) << "Selected endpoint device is not active";
314 endpoint_device
.Release();
316 return endpoint_device
;
319 std::string
CoreAudioUtil::GetDefaultOutputDeviceID() {
320 DCHECK(IsSupported());
321 ScopedComPtr
<IMMDevice
> device(CreateDefaultDevice(eRender
, eConsole
));
322 return device
.get() ? GetDeviceID(device
.get()) : std::string();
325 ScopedComPtr
<IMMDevice
> CoreAudioUtil::CreateDevice(
326 const std::string
& device_id
) {
327 DCHECK(IsSupported());
328 ScopedComPtr
<IMMDevice
> endpoint_device
;
330 // Create the IMMDeviceEnumerator interface.
331 ScopedComPtr
<IMMDeviceEnumerator
> device_enumerator
=
332 CreateDeviceEnumerator();
333 if (!device_enumerator
.get())
334 return endpoint_device
;
336 // Retrieve an audio device specified by an endpoint device-identification
338 HRESULT hr
= device_enumerator
->GetDevice(
339 base::UTF8ToUTF16(device_id
).c_str(), endpoint_device
.Receive());
340 DVLOG_IF(1, FAILED(hr
)) << "IMMDeviceEnumerator::GetDevice: "
344 DVLOG(1) << "IMMDeviceEnumerator::GetDevice: " << std::hex
<< hr
;
345 return endpoint_device
;
348 // Verify that the audio endpoint device is active, i.e., that the audio
349 // adapter that connects to the endpoint device is present and enabled.
350 if (!IsDeviceActive(endpoint_device
.get())) {
351 DVLOG(1) << "Selected endpoint device is not active";
352 endpoint_device
.Release();
354 return endpoint_device
;
357 HRESULT
CoreAudioUtil::GetDeviceName(IMMDevice
* device
, AudioDeviceName
* name
) {
358 DCHECK(IsSupported());
360 // Retrieve unique name of endpoint device.
361 // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}".
362 AudioDeviceName device_name
;
363 device_name
.unique_id
= GetDeviceID(device
);
364 if (device_name
.unique_id
.empty())
367 HRESULT hr
= GetDeviceFriendlyNameInternal(device
, &device_name
.device_name
);
372 DVLOG(2) << "friendly name: " << device_name
.device_name
;
373 DVLOG(2) << "unique id : " << device_name
.unique_id
;
377 std::string
CoreAudioUtil::GetAudioControllerID(IMMDevice
* device
,
378 IMMDeviceEnumerator
* enumerator
) {
379 DCHECK(IsSupported());
381 // Fetching the controller device id could be as simple as fetching the value
382 // of the "{B3F8FA53-0004-438E-9003-51A46E139BFC},2" property in the property
383 // store of the |device|, but that key isn't defined in any header and
384 // according to MS should not be relied upon.
385 // So, instead, we go deeper, look at the device topology and fetch the
386 // PKEY_Device_InstanceId of the associated physical audio device.
387 ScopedComPtr
<IDeviceTopology
> topology
;
388 ScopedComPtr
<IConnector
> connector
;
389 ScopedCoMem
<WCHAR
> filter_id
;
390 if (FAILED(device
->Activate(__uuidof(IDeviceTopology
), CLSCTX_ALL
, NULL
,
391 topology
.ReceiveVoid())) ||
392 // For our purposes checking the first connected device should be enough
393 // and if there are cases where there are more than one device connected
394 // we're not sure how to handle that anyway. So we pass 0.
395 FAILED(topology
->GetConnector(0, connector
.Receive())) ||
396 FAILED(connector
->GetDeviceIdConnectedTo(&filter_id
))) {
397 DLOG(ERROR
) << "Failed to get the device identifier of the audio device";
398 return std::string();
401 // Now look at the properties of the connected device node and fetch the
402 // instance id (PKEY_Device_InstanceId) of the device node that uniquely
403 // identifies the controller.
404 ScopedComPtr
<IMMDevice
> device_node
;
405 ScopedComPtr
<IPropertyStore
> properties
;
406 base::win::ScopedPropVariant instance_id
;
407 if (FAILED(enumerator
->GetDevice(filter_id
, device_node
.Receive())) ||
408 FAILED(device_node
->OpenPropertyStore(STGM_READ
, properties
.Receive())) ||
409 FAILED(properties
->GetValue(PKEY_Device_InstanceId
,
410 instance_id
.Receive())) ||
411 instance_id
.get().vt
!= VT_LPWSTR
) {
412 DLOG(ERROR
) << "Failed to get instance id of the audio device node";
413 return std::string();
416 std::string controller_id
;
417 base::WideToUTF8(instance_id
.get().pwszVal
,
418 wcslen(instance_id
.get().pwszVal
),
421 return controller_id
;
424 std::string
CoreAudioUtil::GetMatchingOutputDeviceID(
425 const std::string
& input_device_id
) {
426 // Special handling for the default communications device.
427 // We always treat the configured communications devices, as a pair.
428 // If we didn't do that and the user has e.g. configured a mic of a headset
429 // as the default comms input device and a different device (not the speakers
430 // of the headset) as the default comms output device, then we would otherwise
431 // here pick the headset as the matched output device. That's technically
432 // correct, but the user experience would be that any audio played out to
433 // the matched device, would get ducked since it's not the default comms
434 // device. So here, we go with the user's configuration.
435 if (input_device_id
== AudioManagerBase::kCommunicationsDeviceId
)
436 return AudioManagerBase::kCommunicationsDeviceId
;
438 ScopedComPtr
<IMMDevice
> input_device
;
439 if (input_device_id
.empty() ||
440 input_device_id
== AudioManagerBase::kDefaultDeviceId
) {
441 input_device
= CreateDefaultDevice(eCapture
, eConsole
);
443 input_device
= CreateDevice(input_device_id
);
446 if (!input_device
.get())
447 return std::string();
449 // See if we can get id of the associated controller.
450 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(CreateDeviceEnumerator());
451 std::string
controller_id(
452 GetAudioControllerID(input_device
.get(), enumerator
.get()));
453 if (controller_id
.empty())
454 return std::string();
456 // Now enumerate the available (and active) output devices and see if any of
457 // them is associated with the same controller.
458 ScopedComPtr
<IMMDeviceCollection
> collection
;
459 enumerator
->EnumAudioEndpoints(eRender
, DEVICE_STATE_ACTIVE
,
460 collection
.Receive());
461 if (!collection
.get())
462 return std::string();
465 collection
->GetCount(&count
);
466 ScopedComPtr
<IMMDevice
> output_device
;
467 for (UINT i
= 0; i
< count
; ++i
) {
468 collection
->Item(i
, output_device
.Receive());
469 std::string
output_controller_id(
470 GetAudioControllerID(output_device
.get(), enumerator
.get()));
471 if (output_controller_id
== controller_id
)
473 output_device
= NULL
;
476 return output_device
.get() ? GetDeviceID(output_device
.get()) : std::string();
479 std::string
CoreAudioUtil::GetFriendlyName(const std::string
& device_id
) {
480 DCHECK(IsSupported());
481 ScopedComPtr
<IMMDevice
> audio_device
= CreateDevice(device_id
);
482 if (!audio_device
.get())
483 return std::string();
485 AudioDeviceName device_name
;
486 HRESULT hr
= GetDeviceName(audio_device
.get(), &device_name
);
488 return std::string();
490 return device_name
.device_name
;
493 bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow
,
495 const std::string
& device_id
) {
496 DCHECK(IsSupported());
497 ScopedComPtr
<IMMDevice
> device
= CreateDefaultDevice(flow
, role
);
501 std::string
str_default(GetDeviceID(device
.get()));
502 return device_id
.compare(str_default
) == 0;
505 EDataFlow
CoreAudioUtil::GetDataFlow(IMMDevice
* device
) {
506 DCHECK(IsSupported());
507 ScopedComPtr
<IMMEndpoint
> endpoint
;
508 HRESULT hr
= device
->QueryInterface(endpoint
.Receive());
510 DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex
<< hr
;
515 hr
= endpoint
->GetDataFlow(&data_flow
);
517 DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex
<< hr
;
523 ScopedComPtr
<IAudioClient
> CoreAudioUtil::CreateClient(
524 IMMDevice
* audio_device
) {
525 DCHECK(IsSupported());
527 // Creates and activates an IAudioClient COM object given the selected
529 ScopedComPtr
<IAudioClient
> audio_client
;
530 HRESULT hr
= audio_device
->Activate(__uuidof(IAudioClient
),
531 CLSCTX_INPROC_SERVER
,
533 audio_client
.ReceiveVoid());
534 DVLOG_IF(1, FAILED(hr
)) << "IMMDevice::Activate: " << std::hex
<< hr
;
538 ScopedComPtr
<IAudioClient
> CoreAudioUtil::CreateDefaultClient(
539 EDataFlow data_flow
, ERole role
) {
540 DCHECK(IsSupported());
541 ScopedComPtr
<IMMDevice
> default_device(CreateDefaultDevice(data_flow
, role
));
542 return (default_device
.get() ? CreateClient(default_device
.get())
543 : ScopedComPtr
<IAudioClient
>());
546 ScopedComPtr
<IAudioClient
> CoreAudioUtil::CreateClient(
547 const std::string
& device_id
, EDataFlow data_flow
, ERole role
) {
548 if (device_id
.empty())
549 return CreateDefaultClient(data_flow
, role
);
551 ScopedComPtr
<IMMDevice
> device(CreateDevice(device_id
));
553 return ScopedComPtr
<IAudioClient
>();
555 return CreateClient(device
.get());
558 HRESULT
CoreAudioUtil::GetSharedModeMixFormat(
559 IAudioClient
* client
, WAVEFORMATPCMEX
* format
) {
560 DCHECK(IsSupported());
561 ScopedCoMem
<WAVEFORMATPCMEX
> format_pcmex
;
562 HRESULT hr
= client
->GetMixFormat(
563 reinterpret_cast<WAVEFORMATEX
**>(&format_pcmex
));
567 size_t bytes
= sizeof(WAVEFORMATEX
) + format_pcmex
->Format
.cbSize
;
568 DCHECK_EQ(bytes
, sizeof(WAVEFORMATPCMEX
));
570 memcpy(format
, format_pcmex
, bytes
);
576 bool CoreAudioUtil::IsFormatSupported(IAudioClient
* client
,
577 AUDCLNT_SHAREMODE share_mode
,
578 const WAVEFORMATPCMEX
* format
) {
579 DCHECK(IsSupported());
580 ScopedCoMem
<WAVEFORMATEXTENSIBLE
> closest_match
;
581 HRESULT hr
= client
->IsFormatSupported(
582 share_mode
, reinterpret_cast<const WAVEFORMATEX
*>(format
),
583 reinterpret_cast<WAVEFORMATEX
**>(&closest_match
));
585 // This log can only be triggered for shared mode.
586 DLOG_IF(ERROR
, hr
== S_FALSE
) << "Format is not supported "
587 << "but a closest match exists.";
588 // This log can be triggered both for shared and exclusive modes.
589 DLOG_IF(ERROR
, hr
== AUDCLNT_E_UNSUPPORTED_FORMAT
) << "Unsupported format.";
591 DVLOG(2) << *closest_match
;
597 bool CoreAudioUtil::IsChannelLayoutSupported(const std::string
& device_id
,
600 ChannelLayout channel_layout
) {
601 DCHECK(IsSupported());
603 // First, get the preferred mixing format for shared mode streams.
605 ScopedComPtr
<IAudioClient
> client(CreateClient(device_id
, data_flow
, role
));
609 WAVEFORMATPCMEX format
;
610 HRESULT hr
= GetSharedModeMixFormat(client
.get(), &format
);
614 // Next, check if it is possible to use an alternative format where the
615 // channel layout (and possibly number of channels) is modified.
617 // Convert generic channel layout into Windows-specific channel configuration.
618 ChannelConfig new_config
= ChannelLayoutToChannelConfig(channel_layout
);
619 if (new_config
== KSAUDIO_SPEAKER_UNSUPPORTED
) {
622 format
.dwChannelMask
= new_config
;
624 // Modify the format if the new channel layout has changed the number of
625 // utilized channels.
626 const int channels
= ChannelLayoutToChannelCount(channel_layout
);
627 if (channels
!= format
.Format
.nChannels
) {
628 format
.Format
.nChannels
= channels
;
629 format
.Format
.nBlockAlign
= (format
.Format
.wBitsPerSample
/ 8) * channels
;
630 format
.Format
.nAvgBytesPerSec
= format
.Format
.nSamplesPerSec
*
631 format
.Format
.nBlockAlign
;
635 // Some devices can initialize a shared-mode stream with a format that is
636 // not identical to the mix format obtained from the GetMixFormat() method.
637 // However, chances of succeeding increases if we use the same number of
638 // channels and the same sample rate as the mix format. I.e, this call will
639 // return true only in those cases where the audio engine is able to support
640 // an even wider range of shared-mode formats where the installation package
641 // for the audio device includes a local effects (LFX) audio processing
642 // object (APO) that can handle format conversions.
643 return CoreAudioUtil::IsFormatSupported(client
.get(),
644 AUDCLNT_SHAREMODE_SHARED
, &format
);
647 HRESULT
CoreAudioUtil::GetDevicePeriod(IAudioClient
* client
,
648 AUDCLNT_SHAREMODE share_mode
,
649 REFERENCE_TIME
* device_period
) {
650 DCHECK(IsSupported());
652 // Get the period of the engine thread.
653 REFERENCE_TIME default_period
= 0;
654 REFERENCE_TIME minimum_period
= 0;
655 HRESULT hr
= client
->GetDevicePeriod(&default_period
, &minimum_period
);
659 *device_period
= (share_mode
== AUDCLNT_SHAREMODE_SHARED
) ? default_period
:
661 DVLOG(2) << "device_period: "
662 << RefererenceTimeToTimeDelta(*device_period
).InMillisecondsF()
667 HRESULT
CoreAudioUtil::GetPreferredAudioParameters(
668 IAudioClient
* client
, AudioParameters
* params
) {
669 DCHECK(IsSupported());
670 WAVEFORMATPCMEX mix_format
;
671 HRESULT hr
= GetSharedModeMixFormat(client
, &mix_format
);
675 REFERENCE_TIME default_period
= 0;
676 hr
= GetDevicePeriod(client
, AUDCLNT_SHAREMODE_SHARED
, &default_period
);
680 // Get the integer mask which corresponds to the channel layout the
681 // audio engine uses for its internal processing/mixing of shared-mode
682 // streams. This mask indicates which channels are present in the multi-
683 // channel stream. The least significant bit corresponds with the Front Left
684 // speaker, the next least significant bit corresponds to the Front Right
685 // speaker, and so on, continuing in the order defined in KsMedia.h.
686 // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083.aspx
688 ChannelConfig channel_config
= mix_format
.dwChannelMask
;
690 // Convert Microsoft's channel configuration to genric ChannelLayout.
691 ChannelLayout channel_layout
= ChannelConfigToChannelLayout(channel_config
);
693 // Some devices don't appear to set a valid channel layout, so guess based on
694 // the number of channels. See http://crbug.com/311906.
695 if (channel_layout
== CHANNEL_LAYOUT_UNSUPPORTED
) {
696 DVLOG(1) << "Unsupported channel config: "
697 << std::hex
<< channel_config
698 << ". Guessing layout by channel count: "
699 << std::dec
<< mix_format
.Format
.nChannels
;
700 channel_layout
= GuessChannelLayout(mix_format
.Format
.nChannels
);
703 // Preferred sample rate.
704 int sample_rate
= mix_format
.Format
.nSamplesPerSec
;
706 // TODO(henrika): possibly use format.Format.wBitsPerSample here instead.
707 // We use a hard-coded value of 16 bits per sample today even if most audio
708 // engines does the actual mixing in 32 bits per sample.
709 int bits_per_sample
= 16;
711 // We are using the native device period to derive the smallest possible
712 // buffer size in shared mode. Note that the actual endpoint buffer will be
713 // larger than this size but it will be possible to fill it up in two calls.
714 // TODO(henrika): ensure that this scheme works for capturing as well.
715 int frames_per_buffer
= static_cast<int>(sample_rate
*
716 RefererenceTimeToTimeDelta(default_period
).InSecondsF() + 0.5);
718 DVLOG(1) << "channel_layout : " << channel_layout
;
719 DVLOG(1) << "sample_rate : " << sample_rate
;
720 DVLOG(1) << "bits_per_sample : " << bits_per_sample
;
721 DVLOG(1) << "frames_per_buffer: " << frames_per_buffer
;
723 AudioParameters
audio_params(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
729 *params
= audio_params
;
733 HRESULT
CoreAudioUtil::GetPreferredAudioParameters(const std::string
& device_id
,
734 bool is_output_device
,
735 AudioParameters
* params
) {
736 DCHECK(IsSupported());
738 ScopedComPtr
<IMMDevice
> device
;
739 if (device_id
== AudioManagerBase::kDefaultDeviceId
) {
740 device
= CoreAudioUtil::CreateDefaultDevice(
741 is_output_device
? eRender
: eCapture
, eConsole
);
742 } else if (device_id
== AudioManagerBase::kLoopbackInputDeviceId
) {
743 DCHECK(!is_output_device
);
744 device
= CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
745 } else if (device_id
== AudioManagerBase::kCommunicationsDeviceId
) {
746 device
= CoreAudioUtil::CreateDefaultDevice(
747 is_output_device
? eRender
: eCapture
, eCommunications
);
749 device
= CreateDevice(device_id
);
753 // Map NULL-pointer to new error code which can be different from the
754 // actual error code. The exact value is not important here.
755 return AUDCLNT_E_DEVICE_INVALIDATED
;
758 ScopedComPtr
<IAudioClient
> client(CreateClient(device
.get()));
760 // Map NULL-pointer to new error code which can be different from the
761 // actual error code. The exact value is not important here.
762 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
765 HRESULT hr
= GetPreferredAudioParameters(client
.get(), params
);
766 if (FAILED(hr
) || is_output_device
|| !params
->IsValid())
769 // The following functionality is only for input devices.
770 DCHECK(!is_output_device
);
772 // TODO(dalecurtis): Old code rewrote != 1 channels to stereo, do we still
773 // need to do the same thing?
774 if (params
->channels() != 1) {
775 params
->Reset(params
->format(), CHANNEL_LAYOUT_STEREO
,
776 params
->sample_rate(), params
->bits_per_sample(),
777 params
->frames_per_buffer());
783 ChannelConfig
CoreAudioUtil::GetChannelConfig(const std::string
& device_id
,
784 EDataFlow data_flow
) {
785 ScopedComPtr
<IAudioClient
> client(
786 CreateClient(device_id
, data_flow
, eConsole
));
788 WAVEFORMATPCMEX format
= {};
789 if (!client
.get() || FAILED(GetSharedModeMixFormat(client
.get(), &format
)))
792 return static_cast<ChannelConfig
>(format
.dwChannelMask
);
795 HRESULT
CoreAudioUtil::SharedModeInitialize(
796 IAudioClient
* client
, const WAVEFORMATPCMEX
* format
, HANDLE event_handle
,
797 uint32
* endpoint_buffer_size
, const GUID
* session_guid
) {
798 DCHECK(IsSupported());
800 // Use default flags (i.e, dont set AUDCLNT_STREAMFLAGS_NOPERSIST) to
801 // ensure that the volume level and muting state for a rendering session
802 // are persistent across system restarts. The volume level and muting
803 // state for a capture session are never persistent.
804 DWORD stream_flags
= 0;
806 // Enable event-driven streaming if a valid event handle is provided.
807 // After the stream starts, the audio engine will signal the event handle
808 // to notify the client each time a buffer becomes ready to process.
809 // Event-driven buffering is supported for both rendering and capturing.
810 // Both shared-mode and exclusive-mode streams can use event-driven buffering.
811 bool use_event
= (event_handle
!= NULL
&&
812 event_handle
!= INVALID_HANDLE_VALUE
);
814 stream_flags
|= AUDCLNT_STREAMFLAGS_EVENTCALLBACK
;
815 DVLOG(2) << "stream_flags: 0x" << std::hex
<< stream_flags
;
817 // Initialize the shared mode client for minimal delay.
818 HRESULT hr
= client
->Initialize(AUDCLNT_SHAREMODE_SHARED
,
822 reinterpret_cast<const WAVEFORMATEX
*>(format
),
825 DVLOG(1) << "IAudioClient::Initialize: " << std::hex
<< hr
;
830 hr
= client
->SetEventHandle(event_handle
);
832 DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex
<< hr
;
837 UINT32 buffer_size_in_frames
= 0;
838 hr
= client
->GetBufferSize(&buffer_size_in_frames
);
840 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex
<< hr
;
844 *endpoint_buffer_size
= buffer_size_in_frames
;
845 DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames
;
847 // TODO(henrika): utilize when delay measurements are added.
848 REFERENCE_TIME latency
= 0;
849 hr
= client
->GetStreamLatency(&latency
);
850 DVLOG(2) << "stream latency: "
851 << RefererenceTimeToTimeDelta(latency
).InMillisecondsF() << " [ms]";
855 ScopedComPtr
<IAudioRenderClient
> CoreAudioUtil::CreateRenderClient(
856 IAudioClient
* client
) {
857 DCHECK(IsSupported());
859 // Get access to the IAudioRenderClient interface. This interface
860 // enables us to write output data to a rendering endpoint buffer.
861 ScopedComPtr
<IAudioRenderClient
> audio_render_client
;
862 HRESULT hr
= client
->GetService(__uuidof(IAudioRenderClient
),
863 audio_render_client
.ReceiveVoid());
865 DVLOG(1) << "IAudioClient::GetService: " << std::hex
<< hr
;
866 return ScopedComPtr
<IAudioRenderClient
>();
868 return audio_render_client
;
871 ScopedComPtr
<IAudioCaptureClient
> CoreAudioUtil::CreateCaptureClient(
872 IAudioClient
* client
) {
873 DCHECK(IsSupported());
875 // Get access to the IAudioCaptureClient interface. This interface
876 // enables us to read input data from a capturing endpoint buffer.
877 ScopedComPtr
<IAudioCaptureClient
> audio_capture_client
;
878 HRESULT hr
= client
->GetService(__uuidof(IAudioCaptureClient
),
879 audio_capture_client
.ReceiveVoid());
881 DVLOG(1) << "IAudioClient::GetService: " << std::hex
<< hr
;
882 return ScopedComPtr
<IAudioCaptureClient
>();
884 return audio_capture_client
;
887 bool CoreAudioUtil::FillRenderEndpointBufferWithSilence(
888 IAudioClient
* client
, IAudioRenderClient
* render_client
) {
889 DCHECK(IsSupported());
891 UINT32 endpoint_buffer_size
= 0;
892 if (FAILED(client
->GetBufferSize(&endpoint_buffer_size
)))
895 UINT32 num_queued_frames
= 0;
896 if (FAILED(client
->GetCurrentPadding(&num_queued_frames
)))
900 int num_frames_to_fill
= endpoint_buffer_size
- num_queued_frames
;
901 if (FAILED(render_client
->GetBuffer(num_frames_to_fill
, &data
)))
904 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to
905 // explicitly write silence data to the rendering buffer.
906 DVLOG(2) << "filling up " << num_frames_to_fill
<< " frames with silence";
907 return SUCCEEDED(render_client
->ReleaseBuffer(num_frames_to_fill
,
908 AUDCLNT_BUFFERFLAGS_SILENT
));