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