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