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