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 // See header file for documentation.
     27 // {BE39AF4F-087C-423F-9303-234EC1E5B8EE}
     28 const GUID kCommunicationsSessionId = {
     29   0xbe39af4f, 0x87c, 0x423f, { 0x93, 0x3, 0x23, 0x4e, 0xc1, 0xe5, 0xb8, 0xee }
     30 };
     31 
     32 enum { 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.
     42 static 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.
     78 static 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 
    116 static 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 
    130 static 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 
    139 static 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 
    151 static 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 
    159 bool 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 
    194 base::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 
    199 AUDCLNT_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 
    206 int 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 
    234 ScopedComPtr<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 
    255 ScopedComPtr<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 
    290 std::string CoreAudioUtil::GetDefaultOutputDeviceID() {
    291   DCHECK(IsSupported());
    292   ScopedComPtr<IMMDevice> device(CreateDefaultDevice(eRender, eConsole));
    293   return device ? GetDeviceID(device) : std::string();
    294 }
    295 
    296 ScopedComPtr<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 
    316 HRESULT 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 
    348 std::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 
    395 std::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 
    430 std::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 
    444 bool 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 
    456 EDataFlow 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 
    474 ScopedComPtr<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 
    489 ScopedComPtr<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 
    497 ScopedComPtr<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 
    509 HRESULT 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 
    527 bool 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 
    548 bool 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 
    598 HRESULT 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 
    618 HRESULT 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 
    684 HRESULT 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 
    709 HRESULT 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 
    728 ChannelConfig 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 
    740 HRESULT 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 
    800 ScopedComPtr<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 
    816 ScopedComPtr<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 
    832 bool 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