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 ScopedComPtr
<IMMDevice
> input_device(CreateDevice(input_device_id
));
427 if (!input_device
.get())
428 return std::string();
430 // See if we can get id of the associated controller.
431 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(CreateDeviceEnumerator());
432 std::string
controller_id(
433 GetAudioControllerID(input_device
.get(), enumerator
.get()));
434 if (controller_id
.empty())
435 return std::string();
437 // Now enumerate the available (and active) output devices and see if any of
438 // them is associated with the same controller.
439 ScopedComPtr
<IMMDeviceCollection
> collection
;
440 enumerator
->EnumAudioEndpoints(eRender
, DEVICE_STATE_ACTIVE
,
441 collection
.Receive());
442 if (!collection
.get())
443 return std::string();
446 collection
->GetCount(&count
);
447 ScopedComPtr
<IMMDevice
> output_device
;
448 for (UINT i
= 0; i
< count
; ++i
) {
449 collection
->Item(i
, output_device
.Receive());
450 std::string
output_controller_id(
451 GetAudioControllerID(output_device
.get(), enumerator
.get()));
452 if (output_controller_id
== controller_id
)
454 output_device
= NULL
;
457 return output_device
.get() ? GetDeviceID(output_device
.get()) : std::string();
460 std::string
CoreAudioUtil::GetFriendlyName(const std::string
& device_id
) {
461 DCHECK(IsSupported());
462 ScopedComPtr
<IMMDevice
> audio_device
= CreateDevice(device_id
);
463 if (!audio_device
.get())
464 return std::string();
466 AudioDeviceName device_name
;
467 HRESULT hr
= GetDeviceName(audio_device
.get(), &device_name
);
469 return std::string();
471 return device_name
.device_name
;
474 bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow
,
476 const std::string
& device_id
) {
477 DCHECK(IsSupported());
478 ScopedComPtr
<IMMDevice
> device
= CreateDefaultDevice(flow
, role
);
482 std::string
str_default(GetDeviceID(device
.get()));
483 return device_id
.compare(str_default
) == 0;
486 EDataFlow
CoreAudioUtil::GetDataFlow(IMMDevice
* device
) {
487 DCHECK(IsSupported());
488 ScopedComPtr
<IMMEndpoint
> endpoint
;
489 HRESULT hr
= device
->QueryInterface(endpoint
.Receive());
491 DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex
<< hr
;
496 hr
= endpoint
->GetDataFlow(&data_flow
);
498 DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex
<< hr
;
504 ScopedComPtr
<IAudioClient
> CoreAudioUtil::CreateClient(
505 IMMDevice
* audio_device
) {
506 DCHECK(IsSupported());
508 // Creates and activates an IAudioClient COM object given the selected
510 ScopedComPtr
<IAudioClient
> audio_client
;
511 HRESULT hr
= audio_device
->Activate(__uuidof(IAudioClient
),
512 CLSCTX_INPROC_SERVER
,
514 audio_client
.ReceiveVoid());
515 DVLOG_IF(1, FAILED(hr
)) << "IMMDevice::Activate: " << std::hex
<< hr
;
519 ScopedComPtr
<IAudioClient
> CoreAudioUtil::CreateDefaultClient(
520 EDataFlow data_flow
, ERole role
) {
521 DCHECK(IsSupported());
522 ScopedComPtr
<IMMDevice
> default_device(CreateDefaultDevice(data_flow
, role
));
523 return (default_device
.get() ? CreateClient(default_device
.get())
524 : ScopedComPtr
<IAudioClient
>());
527 ScopedComPtr
<IAudioClient
> CoreAudioUtil::CreateClient(
528 const std::string
& device_id
, EDataFlow data_flow
, ERole role
) {
529 if (device_id
.empty())
530 return CreateDefaultClient(data_flow
, role
);
532 ScopedComPtr
<IMMDevice
> device(CreateDevice(device_id
));
534 return ScopedComPtr
<IAudioClient
>();
536 return CreateClient(device
.get());
539 HRESULT
CoreAudioUtil::GetSharedModeMixFormat(
540 IAudioClient
* client
, WAVEFORMATPCMEX
* format
) {
541 DCHECK(IsSupported());
542 ScopedCoMem
<WAVEFORMATPCMEX
> format_pcmex
;
543 HRESULT hr
= client
->GetMixFormat(
544 reinterpret_cast<WAVEFORMATEX
**>(&format_pcmex
));
548 size_t bytes
= sizeof(WAVEFORMATEX
) + format_pcmex
->Format
.cbSize
;
549 DCHECK_EQ(bytes
, sizeof(WAVEFORMATPCMEX
));
551 memcpy(format
, format_pcmex
, bytes
);
557 bool CoreAudioUtil::IsFormatSupported(IAudioClient
* client
,
558 AUDCLNT_SHAREMODE share_mode
,
559 const WAVEFORMATPCMEX
* format
) {
560 DCHECK(IsSupported());
561 ScopedCoMem
<WAVEFORMATEXTENSIBLE
> closest_match
;
562 HRESULT hr
= client
->IsFormatSupported(
563 share_mode
, reinterpret_cast<const WAVEFORMATEX
*>(format
),
564 reinterpret_cast<WAVEFORMATEX
**>(&closest_match
));
566 // This log can only be triggered for shared mode.
567 DLOG_IF(ERROR
, hr
== S_FALSE
) << "Format is not supported "
568 << "but a closest match exists.";
569 // This log can be triggered both for shared and exclusive modes.
570 DLOG_IF(ERROR
, hr
== AUDCLNT_E_UNSUPPORTED_FORMAT
) << "Unsupported format.";
572 DVLOG(2) << *closest_match
;
578 bool CoreAudioUtil::IsChannelLayoutSupported(const std::string
& device_id
,
581 ChannelLayout channel_layout
) {
582 DCHECK(IsSupported());
584 // First, get the preferred mixing format for shared mode streams.
586 ScopedComPtr
<IAudioClient
> client(CreateClient(device_id
, data_flow
, role
));
590 WAVEFORMATPCMEX format
;
591 HRESULT hr
= GetSharedModeMixFormat(client
.get(), &format
);
595 // Next, check if it is possible to use an alternative format where the
596 // channel layout (and possibly number of channels) is modified.
598 // Convert generic channel layout into Windows-specific channel configuration.
599 ChannelConfig new_config
= ChannelLayoutToChannelConfig(channel_layout
);
600 if (new_config
== KSAUDIO_SPEAKER_UNSUPPORTED
) {
603 format
.dwChannelMask
= new_config
;
605 // Modify the format if the new channel layout has changed the number of
606 // utilized channels.
607 const int channels
= ChannelLayoutToChannelCount(channel_layout
);
608 if (channels
!= format
.Format
.nChannels
) {
609 format
.Format
.nChannels
= channels
;
610 format
.Format
.nBlockAlign
= (format
.Format
.wBitsPerSample
/ 8) * channels
;
611 format
.Format
.nAvgBytesPerSec
= format
.Format
.nSamplesPerSec
*
612 format
.Format
.nBlockAlign
;
616 // Some devices can initialize a shared-mode stream with a format that is
617 // not identical to the mix format obtained from the GetMixFormat() method.
618 // However, chances of succeeding increases if we use the same number of
619 // channels and the same sample rate as the mix format. I.e, this call will
620 // return true only in those cases where the audio engine is able to support
621 // an even wider range of shared-mode formats where the installation package
622 // for the audio device includes a local effects (LFX) audio processing
623 // object (APO) that can handle format conversions.
624 return CoreAudioUtil::IsFormatSupported(client
.get(),
625 AUDCLNT_SHAREMODE_SHARED
, &format
);
628 HRESULT
CoreAudioUtil::GetDevicePeriod(IAudioClient
* client
,
629 AUDCLNT_SHAREMODE share_mode
,
630 REFERENCE_TIME
* device_period
) {
631 DCHECK(IsSupported());
633 // Get the period of the engine thread.
634 REFERENCE_TIME default_period
= 0;
635 REFERENCE_TIME minimum_period
= 0;
636 HRESULT hr
= client
->GetDevicePeriod(&default_period
, &minimum_period
);
640 *device_period
= (share_mode
== AUDCLNT_SHAREMODE_SHARED
) ? default_period
:
642 DVLOG(2) << "device_period: "
643 << RefererenceTimeToTimeDelta(*device_period
).InMillisecondsF()
648 HRESULT
CoreAudioUtil::GetPreferredAudioParameters(
649 IAudioClient
* client
, AudioParameters
* params
) {
650 DCHECK(IsSupported());
651 WAVEFORMATPCMEX mix_format
;
652 HRESULT hr
= GetSharedModeMixFormat(client
, &mix_format
);
656 REFERENCE_TIME default_period
= 0;
657 hr
= GetDevicePeriod(client
, AUDCLNT_SHAREMODE_SHARED
, &default_period
);
661 // Get the integer mask which corresponds to the channel layout the
662 // audio engine uses for its internal processing/mixing of shared-mode
663 // streams. This mask indicates which channels are present in the multi-
664 // channel stream. The least significant bit corresponds with the Front Left
665 // speaker, the next least significant bit corresponds to the Front Right
666 // speaker, and so on, continuing in the order defined in KsMedia.h.
667 // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083.aspx
669 ChannelConfig channel_config
= mix_format
.dwChannelMask
;
671 // Convert Microsoft's channel configuration to genric ChannelLayout.
672 ChannelLayout channel_layout
= ChannelConfigToChannelLayout(channel_config
);
674 // Some devices don't appear to set a valid channel layout, so guess based on
675 // the number of channels. See http://crbug.com/311906.
676 if (channel_layout
== CHANNEL_LAYOUT_UNSUPPORTED
) {
677 DVLOG(1) << "Unsupported channel config: "
678 << std::hex
<< channel_config
679 << ". Guessing layout by channel count: "
680 << std::dec
<< mix_format
.Format
.nChannels
;
681 channel_layout
= GuessChannelLayout(mix_format
.Format
.nChannels
);
684 // Preferred sample rate.
685 int sample_rate
= mix_format
.Format
.nSamplesPerSec
;
687 // TODO(henrika): possibly use format.Format.wBitsPerSample here instead.
688 // We use a hard-coded value of 16 bits per sample today even if most audio
689 // engines does the actual mixing in 32 bits per sample.
690 int bits_per_sample
= 16;
692 // We are using the native device period to derive the smallest possible
693 // buffer size in shared mode. Note that the actual endpoint buffer will be
694 // larger than this size but it will be possible to fill it up in two calls.
695 // TODO(henrika): ensure that this scheme works for capturing as well.
696 int frames_per_buffer
= static_cast<int>(sample_rate
*
697 RefererenceTimeToTimeDelta(default_period
).InSecondsF() + 0.5);
699 DVLOG(1) << "channel_layout : " << channel_layout
;
700 DVLOG(1) << "sample_rate : " << sample_rate
;
701 DVLOG(1) << "bits_per_sample : " << bits_per_sample
;
702 DVLOG(1) << "frames_per_buffer: " << frames_per_buffer
;
704 AudioParameters
audio_params(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
710 *params
= audio_params
;
714 HRESULT
CoreAudioUtil::GetPreferredAudioParameters(
715 EDataFlow data_flow
, ERole role
, AudioParameters
* params
) {
716 DCHECK(IsSupported());
717 ScopedComPtr
<IAudioClient
> client(CreateDefaultClient(data_flow
, role
));
719 // Map NULL-pointer to new error code which can be different from the
720 // actual error code. The exact value is not important here.
721 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
724 HRESULT hr
= GetPreferredAudioParameters(client
.get(), params
);
728 if (role
== eCommunications
) {
729 // Raise the 'DUCKING' flag for default communication devices.
730 *params
= AudioParameters(params
->format(), params
->channel_layout(),
731 params
->channels(), params
->sample_rate(), params
->bits_per_sample(),
732 params
->frames_per_buffer(),
733 params
->effects() | AudioParameters::DUCKING
);
739 HRESULT
CoreAudioUtil::GetPreferredAudioParameters(const std::string
& device_id
,
740 bool is_output_device
,
741 AudioParameters
* params
) {
742 DCHECK(IsSupported());
744 ScopedComPtr
<IMMDevice
> device
;
745 if (device_id
== AudioManagerBase::kDefaultDeviceId
) {
746 device
= CoreAudioUtil::CreateDefaultDevice(
747 is_output_device
? eRender
: eCapture
, eConsole
);
748 } else if (device_id
== AudioManagerBase::kLoopbackInputDeviceId
) {
749 DCHECK(!is_output_device
);
750 device
= CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
752 device
= CreateDevice(device_id
);
756 // Map NULL-pointer to new error code which can be different from the
757 // actual error code. The exact value is not important here.
758 return AUDCLNT_E_DEVICE_INVALIDATED
;
761 ScopedComPtr
<IAudioClient
> client(CreateClient(device
.get()));
763 // Map NULL-pointer to new error code which can be different from the
764 // actual error code. The exact value is not important here.
765 return AUDCLNT_E_ENDPOINT_CREATE_FAILED
;
768 HRESULT hr
= GetPreferredAudioParameters(client
.get(), params
);
769 if (FAILED(hr
) || is_output_device
|| !params
->IsValid())
772 // The following functionality is only for input devices.
773 DCHECK(!is_output_device
);
775 // TODO(dalecurtis): Old code rewrote != 1 channels to stereo, do we still
776 // need to do the same thing?
777 if (params
->channels() != 1) {
778 params
->Reset(params
->format(), CHANNEL_LAYOUT_STEREO
, 2,
779 params
->sample_rate(), params
->bits_per_sample(),
780 params
->frames_per_buffer());
783 ScopedComPtr
<IMMDevice
> communications_device(
784 CreateDefaultDevice(eCapture
, eCommunications
));
785 if (communications_device
&&
786 GetDeviceID(communications_device
.get()) == GetDeviceID(device
.get())) {
787 // Raise the 'DUCKING' flag for default communication devices.
789 AudioParameters(params
->format(), params
->channel_layout(),
790 params
->channels(), params
->sample_rate(),
791 params
->bits_per_sample(), params
->frames_per_buffer(),
792 params
->effects() | AudioParameters::DUCKING
);
798 ChannelConfig
CoreAudioUtil::GetChannelConfig(const std::string
& device_id
,
799 EDataFlow data_flow
) {
800 ScopedComPtr
<IAudioClient
> client(
801 CreateClient(device_id
, data_flow
, eConsole
));
803 WAVEFORMATPCMEX format
= {};
804 if (!client
.get() || FAILED(GetSharedModeMixFormat(client
.get(), &format
)))
807 return static_cast<ChannelConfig
>(format
.dwChannelMask
);
810 HRESULT
CoreAudioUtil::SharedModeInitialize(
811 IAudioClient
* client
, const WAVEFORMATPCMEX
* format
, HANDLE event_handle
,
812 uint32
* endpoint_buffer_size
, const GUID
* session_guid
) {
813 DCHECK(IsSupported());
815 // Use default flags (i.e, dont set AUDCLNT_STREAMFLAGS_NOPERSIST) to
816 // ensure that the volume level and muting state for a rendering session
817 // are persistent across system restarts. The volume level and muting
818 // state for a capture session are never persistent.
819 DWORD stream_flags
= 0;
821 // Enable event-driven streaming if a valid event handle is provided.
822 // After the stream starts, the audio engine will signal the event handle
823 // to notify the client each time a buffer becomes ready to process.
824 // Event-driven buffering is supported for both rendering and capturing.
825 // Both shared-mode and exclusive-mode streams can use event-driven buffering.
826 bool use_event
= (event_handle
!= NULL
&&
827 event_handle
!= INVALID_HANDLE_VALUE
);
829 stream_flags
|= AUDCLNT_STREAMFLAGS_EVENTCALLBACK
;
830 DVLOG(2) << "stream_flags: 0x" << std::hex
<< stream_flags
;
832 // Initialize the shared mode client for minimal delay.
833 HRESULT hr
= client
->Initialize(AUDCLNT_SHAREMODE_SHARED
,
837 reinterpret_cast<const WAVEFORMATEX
*>(format
),
840 DVLOG(1) << "IAudioClient::Initialize: " << std::hex
<< hr
;
845 hr
= client
->SetEventHandle(event_handle
);
847 DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex
<< hr
;
852 UINT32 buffer_size_in_frames
= 0;
853 hr
= client
->GetBufferSize(&buffer_size_in_frames
);
855 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex
<< hr
;
859 *endpoint_buffer_size
= buffer_size_in_frames
;
860 DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames
;
862 // TODO(henrika): utilize when delay measurements are added.
863 REFERENCE_TIME latency
= 0;
864 hr
= client
->GetStreamLatency(&latency
);
865 DVLOG(2) << "stream latency: "
866 << RefererenceTimeToTimeDelta(latency
).InMillisecondsF() << " [ms]";
870 ScopedComPtr
<IAudioRenderClient
> CoreAudioUtil::CreateRenderClient(
871 IAudioClient
* client
) {
872 DCHECK(IsSupported());
874 // Get access to the IAudioRenderClient interface. This interface
875 // enables us to write output data to a rendering endpoint buffer.
876 ScopedComPtr
<IAudioRenderClient
> audio_render_client
;
877 HRESULT hr
= client
->GetService(__uuidof(IAudioRenderClient
),
878 audio_render_client
.ReceiveVoid());
880 DVLOG(1) << "IAudioClient::GetService: " << std::hex
<< hr
;
881 return ScopedComPtr
<IAudioRenderClient
>();
883 return audio_render_client
;
886 ScopedComPtr
<IAudioCaptureClient
> CoreAudioUtil::CreateCaptureClient(
887 IAudioClient
* client
) {
888 DCHECK(IsSupported());
890 // Get access to the IAudioCaptureClient interface. This interface
891 // enables us to read input data from a capturing endpoint buffer.
892 ScopedComPtr
<IAudioCaptureClient
> audio_capture_client
;
893 HRESULT hr
= client
->GetService(__uuidof(IAudioCaptureClient
),
894 audio_capture_client
.ReceiveVoid());
896 DVLOG(1) << "IAudioClient::GetService: " << std::hex
<< hr
;
897 return ScopedComPtr
<IAudioCaptureClient
>();
899 return audio_capture_client
;
902 bool CoreAudioUtil::FillRenderEndpointBufferWithSilence(
903 IAudioClient
* client
, IAudioRenderClient
* render_client
) {
904 DCHECK(IsSupported());
906 UINT32 endpoint_buffer_size
= 0;
907 if (FAILED(client
->GetBufferSize(&endpoint_buffer_size
)))
910 UINT32 num_queued_frames
= 0;
911 if (FAILED(client
->GetCurrentPadding(&num_queued_frames
)))
915 int num_frames_to_fill
= endpoint_buffer_size
- num_queued_frames
;
916 if (FAILED(render_client
->GetBuffer(num_frames_to_fill
, &data
)))
919 // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to
920 // explicitly write silence data to the rendering buffer.
921 DVLOG(2) << "filling up " << num_frames_to_fill
<< " frames with silence";
922 return SUCCEEDED(render_client
->ReleaseBuffer(num_frames_to_fill
,
923 AUDCLNT_BUFFERFLAGS_SILENT
));