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