Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / media / audio / win / core_audio_util_win.cc
blobce1793e0c28d157d81500966fcc7c0c3e7c285d7
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;
24 namespace media {
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) {
43 switch (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;
71 default:
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) {
79 switch (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;
110 default:
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;
127 return os;
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);
144 return 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());
158 if (FAILED(hr))
159 return hr;
161 base::win::ScopedPropVariant friendly_name_pv;
162 hr = properties->GetValue(PKEY_Device_FriendlyName,
163 friendly_name_pv.Receive());
164 if (FAILED(hr))
165 return hr;
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);
173 return hr;
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);
188 if (SUCCEEDED(hr)) {
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";
201 return false;
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)
208 return false;
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())
217 return false;
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) {
226 LOG(ERROR)
227 << "Failed to create Core Audio device enumerator on thread with ID "
228 << GetCurrentThreadId();
229 return false;
232 return true;
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())
258 return 0;
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,
265 DEVICE_STATE_ACTIVE,
266 collection.Receive());
267 if (FAILED(hr)) {
268 LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr;
269 return 0;
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,
289 ERole role) {
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());
304 if (FAILED(hr)) {
305 DVLOG(1) << "IMMDeviceEnumerator::GetDefaultAudioEndpoint: "
306 << std::hex << hr;
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
337 // string.
338 HRESULT hr = device_enumerator->GetDevice(
339 base::UTF8ToUTF16(device_id).c_str(), endpoint_device.Receive());
340 DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: "
341 << std::hex << hr;
343 if (FAILED(hr)) {
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())
365 return E_FAIL;
367 HRESULT hr = GetDeviceFriendlyNameInternal(device, &device_name.device_name);
368 if (FAILED(hr))
369 return hr;
371 *name = device_name;
372 DVLOG(2) << "friendly name: " << device_name.device_name;
373 DVLOG(2) << "unique id : " << device_name.unique_id;
374 return hr;
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),
419 &controller_id);
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();
445 UINT count = 0;
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)
453 break;
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);
468 if (FAILED(hr))
469 return std::string();
471 return device_name.device_name;
474 bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow,
475 ERole role,
476 const std::string& device_id) {
477 DCHECK(IsSupported());
478 ScopedComPtr<IMMDevice> device = CreateDefaultDevice(flow, role);
479 if (!device.get())
480 return false;
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());
490 if (FAILED(hr)) {
491 DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex << hr;
492 return eAll;
495 EDataFlow data_flow;
496 hr = endpoint->GetDataFlow(&data_flow);
497 if (FAILED(hr)) {
498 DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex << hr;
499 return eAll;
501 return data_flow;
504 ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient(
505 IMMDevice* audio_device) {
506 DCHECK(IsSupported());
508 // Creates and activates an IAudioClient COM object given the selected
509 // endpoint device.
510 ScopedComPtr<IAudioClient> audio_client;
511 HRESULT hr = audio_device->Activate(__uuidof(IAudioClient),
512 CLSCTX_INPROC_SERVER,
513 NULL,
514 audio_client.ReceiveVoid());
515 DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr;
516 return audio_client;
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));
533 if (!device.get())
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));
545 if (FAILED(hr))
546 return hr;
548 size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize;
549 DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX));
551 memcpy(format, format_pcmex, bytes);
552 DVLOG(2) << *format;
554 return hr;
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.";
571 if (hr == S_FALSE) {
572 DVLOG(2) << *closest_match;
575 return (hr == S_OK);
578 bool CoreAudioUtil::IsChannelLayoutSupported(const std::string& device_id,
579 EDataFlow data_flow,
580 ERole role,
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));
587 if (!client.get())
588 return false;
590 WAVEFORMATPCMEX format;
591 HRESULT hr = GetSharedModeMixFormat(client.get(), &format);
592 if (FAILED(hr))
593 return false;
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) {
601 return false;
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;
614 DVLOG(2) << format;
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);
637 if (FAILED(hr))
638 return hr;
640 *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period :
641 minimum_period;
642 DVLOG(2) << "device_period: "
643 << RefererenceTimeToTimeDelta(*device_period).InMillisecondsF()
644 << " [ms]";
645 return hr;
648 HRESULT CoreAudioUtil::GetPreferredAudioParameters(
649 IAudioClient* client, AudioParameters* params) {
650 DCHECK(IsSupported());
651 WAVEFORMATPCMEX mix_format;
652 HRESULT hr = GetSharedModeMixFormat(client, &mix_format);
653 if (FAILED(hr))
654 return hr;
656 REFERENCE_TIME default_period = 0;
657 hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period);
658 if (FAILED(hr))
659 return hr;
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
668 // for more details.
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,
705 channel_layout,
706 sample_rate,
707 bits_per_sample,
708 frames_per_buffer);
710 *params = audio_params;
711 return hr;
714 HRESULT CoreAudioUtil::GetPreferredAudioParameters(
715 EDataFlow data_flow, ERole role, AudioParameters* params) {
716 DCHECK(IsSupported());
717 ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role));
718 if (!client.get()) {
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);
725 if (FAILED(hr))
726 return hr;
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);
736 return hr;
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);
751 } else {
752 device = CreateDevice(device_id);
755 if (!device.get()) {
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()));
762 if (!client.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())
770 return hr;
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.
788 *params =
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);
795 return hr;
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)))
805 return 0;
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);
828 if (use_event)
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,
834 stream_flags,
837 reinterpret_cast<const WAVEFORMATEX*>(format),
838 session_guid);
839 if (FAILED(hr)) {
840 DVLOG(1) << "IAudioClient::Initialize: " << std::hex << hr;
841 return hr;
844 if (use_event) {
845 hr = client->SetEventHandle(event_handle);
846 if (FAILED(hr)) {
847 DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex << hr;
848 return hr;
852 UINT32 buffer_size_in_frames = 0;
853 hr = client->GetBufferSize(&buffer_size_in_frames);
854 if (FAILED(hr)) {
855 DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr;
856 return 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]";
867 return hr;
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());
879 if (FAILED(hr)) {
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());
895 if (FAILED(hr)) {
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)))
908 return false;
910 UINT32 num_queued_frames = 0;
911 if (FAILED(client->GetCurrentPadding(&num_queued_frames)))
912 return false;
914 BYTE* data = NULL;
915 int num_frames_to_fill = endpoint_buffer_size - num_queued_frames;
916 if (FAILED(render_client->GetBuffer(num_frames_to_fill, &data)))
917 return false;
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));
926 } // namespace media