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