15821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Copyright (c) 2012 The Chromium Authors. All rights reserved.
25821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// Use of this source code is governed by a BSD-style license that can be
35821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)// found in the LICENSE file.
45821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
55821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "media/audio/win/core_audio_util_win.h"
65821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
73551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include <audioclient.h>
83551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include <devicetopology.h>
93551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)#include <functiondiscoverykeys_devpkey.h>
105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/command_line.h"
125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/logging.h"
13868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/stringprintf.h"
14868fa2fe829687343ffae624259930155e16dbd8Torne (Richard Coles)#include "base/strings/utf_string_conversions.h"
155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/scoped_co_mem.h"
162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/scoped_handle.h"
172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "base/win/scoped_propvariant.h"
185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)#include "base/win/windows_version.h"
192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)#include "media/base/media_switches.h"
205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)using base::win::ScopedCoMem;
222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)using base::win::ScopedHandle;
235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)namespace media {
255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
26116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// See header file for documentation.
27116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch// {BE39AF4F-087C-423F-9303-234EC1E5B8EE}
28116680a4aac90f2aa7413d9095a592090648e557Ben Murdochconst GUID kCommunicationsSessionId = {
29116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch  0xbe39af4f, 0x87c, 0x423f, { 0x93, 0x3, 0x23, 0x4e, 0xc1, 0xe5, 0xb8, 0xee }
30116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch};
31116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch
32c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 };
33c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// Converts Microsoft's channel configuration to ChannelLayout.
352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// This mapping is not perfect but the best we can do given the current
362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ChannelLayout enumerator and the Windows-specific speaker configurations
372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// defined in ksmedia.h. Don't assume that the channel ordering in
382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// ChannelLayout is exactly the same as the Windows specific configuration.
392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// As an example: KSAUDIO_SPEAKER_7POINT1_SURROUND is mapped to
402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// CHANNEL_LAYOUT_7_1 but the positions of Back L, Back R and Side L, Side R
412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)// speakers are different in these two definitions.
422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)static ChannelLayout ChannelConfigToChannelLayout(ChannelConfig config) {
432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  switch (config) {
442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_DIRECTOUT:
452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_DIRECTOUT=>CHANNEL_LAYOUT_NONE";
462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_NONE;
472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_MONO:
482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_MONO=>CHANNEL_LAYOUT_MONO";
492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_MONO;
502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_STEREO:
512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_STEREO=>CHANNEL_LAYOUT_STEREO";
522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_STEREO;
532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_QUAD:
542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_QUAD=>CHANNEL_LAYOUT_QUAD";
552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_QUAD;
562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_SURROUND:
572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_SURROUND=>CHANNEL_LAYOUT_4_0";
582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_4_0;
592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_5POINT1:
602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1=>CHANNEL_LAYOUT_5_1_BACK";
612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_5_1_BACK;
622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_5POINT1_SURROUND:
632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1_SURROUND=>CHANNEL_LAYOUT_5_1";
642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_5_1;
652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_7POINT1:
662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1=>CHANNEL_LAYOUT_7_1_WIDE";
672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_7_1_WIDE;
682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    case KSAUDIO_SPEAKER_7POINT1_SURROUND:
692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1_SURROUND=>CHANNEL_LAYOUT_7_1";
702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_7_1;
712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    default:
72c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "Unsupported channel configuration: " << config;
732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return CHANNEL_LAYOUT_UNSUPPORTED;
745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
77c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)// TODO(henrika): add mapping for all types in the ChannelLayout enumerator.
78c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static ChannelConfig ChannelLayoutToChannelConfig(ChannelLayout layout) {
79c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  switch (layout) {
80c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_NONE:
81c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_NONE=>KSAUDIO_SPEAKER_UNSUPPORTED";
82c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_UNSUPPORTED;
83c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_UNSUPPORTED:
84c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_UNSUPPORTED=>KSAUDIO_SPEAKER_UNSUPPORTED";
85c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_UNSUPPORTED;
86c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_MONO:
87c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_MONO=>KSAUDIO_SPEAKER_MONO";
88c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_MONO;
89c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_STEREO:
90c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_STEREO=>KSAUDIO_SPEAKER_STEREO";
91c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_STEREO;
92c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_QUAD:
93c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_QUAD=>KSAUDIO_SPEAKER_QUAD";
94c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_QUAD;
95c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_4_0:
96c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_4_0=>KSAUDIO_SPEAKER_SURROUND";
97c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_SURROUND;
98c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_5_1_BACK:
99c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_5_1_BACK=>KSAUDIO_SPEAKER_5POINT1";
100c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_5POINT1;
101c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_5_1:
102c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_5_1=>KSAUDIO_SPEAKER_5POINT1_SURROUND";
103c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_5POINT1_SURROUND;
104c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_7_1_WIDE:
105c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_7_1_WIDE=>KSAUDIO_SPEAKER_7POINT1";
106c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_7POINT1;
107c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    case CHANNEL_LAYOUT_7_1:
108c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "CHANNEL_LAYOUT_7_1=>KSAUDIO_SPEAKER_7POINT1_SURROUND";
109c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_7POINT1_SURROUND;
110c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    default:
111c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      DVLOG(2) << "Unsupported channel layout: " << layout;
112c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)      return KSAUDIO_SPEAKER_UNSUPPORTED;
113c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
114c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
115c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
116c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)static std::ostream& operator<<(std::ostream& os,
117c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                const WAVEFORMATPCMEX& format) {
118c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  os << "wFormatTag: 0x" << std::hex << format.Format.wFormatTag
119c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     << ", nChannels: " << std::dec << format.Format.nChannels
120c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     << ", nSamplesPerSec: " << format.Format.nSamplesPerSec
121c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     << ", nAvgBytesPerSec: " << format.Format.nAvgBytesPerSec
122c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     << ", nBlockAlign: " << format.Format.nBlockAlign
123c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     << ", wBitsPerSample: " << format.Format.wBitsPerSample
124c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     << ", cbSize: " << format.Format.cbSize
125c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     << ", wValidBitsPerSample: " << format.Samples.wValidBitsPerSample
126c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)     << ", dwChannelMask: 0x" << std::hex << format.dwChannelMask;
127c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return os;
128c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
129c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
13058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)static bool LoadAudiosesDll() {
1312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static const wchar_t* const kAudiosesDLL =
1322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      L"%WINDIR%\\system32\\audioses.dll";
1335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  wchar_t path[MAX_PATH] = {0};
1352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path));
1362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL);
1372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
13958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)static bool CanCreateDeviceEnumerator() {
1402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedComPtr<IMMDeviceEnumerator> device_enumerator;
141c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
142c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                                NULL, CLSCTX_INPROC_SERVER);
1435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // If we hit CO_E_NOTINITIALIZED, CoInitialize has not been called and it
1452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // must be called at least once for each thread that uses the COM library.
1462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  CHECK_NE(hr, CO_E_NOTINITIALIZED);
1475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
1482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return SUCCEEDED(hr);
1492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
15158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)static std::string GetDeviceID(IMMDevice* device) {
15258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ScopedCoMem<WCHAR> device_id_com;
15358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::string device_id;
15458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (SUCCEEDED(device->GetId(&device_id_com)))
1555d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::WideToUTF8(device_id_com, wcslen(device_id_com), &device_id);
15658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return device_id;
15758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
15858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
1595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CoreAudioUtil::IsSupported() {
160c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // It is possible to force usage of WaveXxx APIs by using a command line flag.
161c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
162c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (cmd_line->HasSwitch(switches::kForceWaveAudio)) {
1635d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    DVLOG(1) << "Forcing usage of Windows WaveXxx APIs";
164c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
165c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
166c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
1675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Microsoft does not plan to make the Core Audio APIs available for use
1685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // with earlier versions of Windows, including Microsoft Windows Server 2003,
1695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Windows XP, Windows Millennium Edition, Windows 2000, and Windows 98.
1702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (base::win::GetVersion() < base::win::VERSION_VISTA)
1712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // The audio core APIs are implemented in the Mmdevapi.dll and Audioses.dll
1742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // system components.
1752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Dependency Walker shows that it is enough to verify possibility to load
1762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // the Audioses DLL since it depends on Mmdevapi.dll.
1772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See http://crbug.com/166397 why this extra step is required to guarantee
1782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Core Audio support.
1792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static bool g_audioses_dll_available = LoadAudiosesDll();
1802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!g_audioses_dll_available)
1812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
1822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Being able to load the Audioses.dll does not seem to be sufficient for
1842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // all devices to guarantee Core Audio support. To be 100%, we also verify
1852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // that it is possible to a create the IMMDeviceEnumerator interface. If this
1862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // works as well we should be home free.
1872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  static bool g_can_create_device_enumerator = CanCreateDeviceEnumerator();
1882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  LOG_IF(ERROR, !g_can_create_device_enumerator)
1892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      << "Failed to create Core Audio device enumerator on thread with ID "
1902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      << GetCurrentThreadId();
1912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return g_can_create_device_enumerator;
1922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)base::TimeDelta CoreAudioUtil::RefererenceTimeToTimeDelta(REFERENCE_TIME time) {
1952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Each unit of reference time is 100 nanoseconds <=> 0.1 microsecond.
1962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return base::TimeDelta::FromMicroseconds(0.1 * time + 0.5);
1972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
1982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
1992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() {
2002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
2012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio))
2022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return AUDCLNT_SHAREMODE_EXCLUSIVE;
2032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return AUDCLNT_SHAREMODE_SHARED;
2045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)int CoreAudioUtil::NumberOfActiveDevices(EDataFlow data_flow) {
2072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
2085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the IMMDeviceEnumerator interface.
2095821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
2105821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CreateDeviceEnumerator();
2115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!device_enumerator)
2125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
2135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Generate a collection of active (present and not disabled) audio endpoint
2155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // devices for the specified data-flow direction.
2165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // This method will succeed even if all devices are disabled.
2175821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDeviceCollection> collection;
2185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device_enumerator->EnumAudioEndpoints(data_flow,
2195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     DEVICE_STATE_ACTIVE,
2205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                     collection.Receive());
2215821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
2225821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr;
2235821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return 0;
2245821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Retrieve the number of active audio devices for the specified direction
2275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  UINT number_of_active_devices = 0;
2285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  collection->GetCount(&number_of_active_devices);
2292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ")
2305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)           << "number of devices: " << number_of_active_devices;
2315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return static_cast<int>(number_of_active_devices);
2325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() {
2352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
2365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDeviceEnumerator> device_enumerator;
237c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
238c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                                NULL, CLSCTX_INPROC_SERVER);
239f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  if (hr == CO_E_NOTINITIALIZED) {
240f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    LOG(ERROR) << "CoCreateInstance fails with CO_E_NOTINITIALIZED";
241f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // We have seen crashes which indicates that this method can in fact
242f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // fail with CO_E_NOTINITIALIZED in combination with certain 3rd party
243f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // modules. Calling CoInitializeEx is an attempt to resolve the reported
244f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    // issues. See http://crbug.com/378465 for details.
245f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    hr = CoInitializeEx(NULL, COINIT_MULTITHREADED);
246f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    if (SUCCEEDED(hr)) {
247f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)      hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
248f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)                                            NULL, CLSCTX_INPROC_SERVER);
249f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)    }
250f8ee788a64d60abd8f2d742a5fdedde054ecd910Torne (Richard Coles)  }
2515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  CHECK(SUCCEEDED(hr));
2525821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return device_enumerator;
2535821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow,
2565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                                           ERole role) {
2572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
2585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDevice> endpoint_device;
2595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the IMMDeviceEnumerator interface.
2615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
2625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CreateDeviceEnumerator();
2635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!device_enumerator)
2645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return endpoint_device;
2655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Retrieve the default audio endpoint for the specified data-flow
2675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // direction and role.
2685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device_enumerator->GetDefaultAudioEndpoint(
2695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      data_flow, role, endpoint_device.Receive());
2705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
2725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(1) << "IMMDeviceEnumerator::GetDefaultAudioEndpoint: "
2735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)             << std::hex << hr;
2745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return endpoint_device;
2755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2765821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
2775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Verify that the audio endpoint device is active, i.e., that the audio
2785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // adapter that connects to the endpoint device is present and enabled.
2795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DWORD state = DEVICE_STATE_DISABLED;
2805821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = endpoint_device->GetState(&state);
2815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (SUCCEEDED(hr)) {
2825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    if (!(state & DEVICE_STATE_ACTIVE)) {
2835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      DVLOG(1) << "Selected endpoint device is not active";
2845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      endpoint_device.Release();
2855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    }
2865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
2875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return endpoint_device;
2885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
2895821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
29058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)std::string CoreAudioUtil::GetDefaultOutputDeviceID() {
29158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  DCHECK(IsSupported());
29258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ScopedComPtr<IMMDevice> device(CreateDefaultDevice(eRender, eConsole));
29358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return device ? GetDeviceID(device) : std::string();
29458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
29558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
2965821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice(
2975821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    const std::string& device_id) {
2982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
2995821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDevice> endpoint_device;
3005821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3015821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Create the IMMDeviceEnumerator interface.
3025821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
3035821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)      CreateDeviceEnumerator();
3045821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!device_enumerator)
3055821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return endpoint_device;
3065821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3075821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Retrieve an audio device specified by an endpoint device-identification
3085821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // string.
3095d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  HRESULT hr = device_enumerator->GetDevice(
3105d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)      base::UTF8ToUTF16(device_id).c_str(), endpoint_device.Receive());
3115821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: "
3125821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                          << std::hex << hr;
3135821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return endpoint_device;
3145821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3155821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3165821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) {
3172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
3185821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3195821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Retrieve unique name of endpoint device.
3205821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Example: "{0.0.1.00000000}.{8db6020f-18e3-4f25-b6f5-7726c9122574}".
3212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AudioDeviceName device_name;
32258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  device_name.unique_id = GetDeviceID(device);
32358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (device_name.unique_id.empty())
32458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return E_FAIL;
3255821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3265821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Retrieve user-friendly name of endpoint device.
3275821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Example: "Microphone (Realtek High Definition Audio)".
3285821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IPropertyStore> properties;
32958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive());
3305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
3315821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return hr;
3322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  base::win::ScopedPropVariant friendly_name;
3335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = properties->GetValue(PKEY_Device_FriendlyName, friendly_name.Receive());
3345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
3355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return hr;
3362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (friendly_name.get().vt == VT_LPWSTR && friendly_name.get().pwszVal) {
3375d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)    base::WideToUTF8(friendly_name.get().pwszVal,
3385d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     wcslen(friendly_name.get().pwszVal),
3395d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                     &device_name.device_name);
3405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
3415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  *name = device_name;
3432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(2) << "friendly name: " << device_name.device_name;
3442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(2) << "unique id    : " << device_name.unique_id;
3455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return hr;
3465821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
3475821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
3483551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device,
3493551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    IMMDeviceEnumerator* enumerator) {
3503551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  DCHECK(IsSupported());
3513551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
3523551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Fetching the controller device id could be as simple as fetching the value
3533551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // of the "{B3F8FA53-0004-438E-9003-51A46E139BFC},2" property in the property
3543551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // store of the |device|, but that key isn't defined in any header and
3553551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // according to MS should not be relied upon.
3563551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // So, instead, we go deeper, look at the device topology and fetch the
3573551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // PKEY_Device_InstanceId of the associated physical audio device.
3583551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ScopedComPtr<IDeviceTopology> topology;
3593551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ScopedComPtr<IConnector> connector;
3603551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ScopedCoMem<WCHAR> filter_id;
3613551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (FAILED(device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL,
3623551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)             topology.ReceiveVoid()) ||
3633551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      // For our purposes checking the first connected device should be enough
3643551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      // and if there are cases where there are more than one device connected
3653551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      // we're not sure how to handle that anyway. So we pass 0.
3663551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      FAILED(topology->GetConnector(0, connector.Receive())) ||
3673551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      FAILED(connector->GetDeviceIdConnectedTo(&filter_id)))) {
3683551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    DLOG(ERROR) << "Failed to get the device identifier of the audio device";
3693551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return std::string();
3703551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
3713551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
3723551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // Now look at the properties of the connected device node and fetch the
3733551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // instance id (PKEY_Device_InstanceId) of the device node that uniquely
3743551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  // identifies the controller.
3753551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ScopedComPtr<IMMDevice> device_node;
3763551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  ScopedComPtr<IPropertyStore> properties;
3773551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  base::win::ScopedPropVariant instance_id;
3783551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  if (FAILED(enumerator->GetDevice(filter_id, device_node.Receive())) ||
3793551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      FAILED(device_node->OpenPropertyStore(STGM_READ, properties.Receive())) ||
3803551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      FAILED(properties->GetValue(PKEY_Device_InstanceId,
3813551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)                                  instance_id.Receive())) ||
3823551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)      instance_id.get().vt != VT_LPWSTR) {
3833551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    DLOG(ERROR) << "Failed to get instance id of the audio device node";
3843551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)    return std::string();
3853551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  }
3863551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
3873551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  std::string controller_id;
3885d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)  base::WideToUTF8(instance_id.get().pwszVal,
3895d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                   wcslen(instance_id.get().pwszVal),
3905d1f7b1de12d16ceb2c938c56701a3e8bfa558f7Torne (Richard Coles)                   &controller_id);
3913551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
3923551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)  return controller_id;
3933551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)}
3943551c9c881056c480085172ff9840cab31610854Torne (Richard Coles)
39558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)std::string CoreAudioUtil::GetMatchingOutputDeviceID(
39658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    const std::string& input_device_id) {
39758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ScopedComPtr<IMMDevice> input_device(CreateDevice(input_device_id));
39858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!input_device)
39958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return std::string();
40058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
40158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // See if we can get id of the associated controller.
40258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ScopedComPtr<IMMDeviceEnumerator> enumerator(CreateDeviceEnumerator());
40358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::string controller_id(GetAudioControllerID(input_device, enumerator));
40458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (controller_id.empty())
40558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return std::string();
40658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
40758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // Now enumerate the available (and active) output devices and see if any of
40858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  // them is associated with the same controller.
40958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ScopedComPtr<IMMDeviceCollection> collection;
41058537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE,
41158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      collection.Receive());
41258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  if (!collection)
41358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    return std::string();
41458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
41558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  UINT count = 0;
41658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  collection->GetCount(&count);
41758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  ScopedComPtr<IMMDevice> output_device;
41858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  for (UINT i = 0; i < count; ++i) {
41958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    collection->Item(i, output_device.Receive());
4201e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    std::string output_controller_id(GetAudioControllerID(
42158537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)        output_device, enumerator));
42258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    if (output_controller_id == controller_id)
42358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)      break;
42458537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)    output_device = NULL;
42558537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  }
42658537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
42758537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return output_device ? GetDeviceID(output_device) : std::string();
42858537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)}
42958537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)
4305821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) {
4312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
4325821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id);
4335821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!audio_device)
4345821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return std::string();
4355821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4365821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  AudioDeviceName device_name;
4375821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = GetDeviceName(audio_device, &device_name);
4385821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr))
4395821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return std::string();
4405821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4415821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return device_name.device_name;
4425821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4435821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4445821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow,
4455821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                    ERole role,
446eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch                                    const std::string& device_id) {
4472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
4485821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMDevice> device = CreateDefaultDevice(flow, role);
4495821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (!device)
4505821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return false;
4515821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
45258537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  std::string str_default(GetDeviceID(device));
45358537e28ecd584eab876aee8be7156509866d23aTorne (Richard Coles)  return device_id.compare(str_default) == 0;
4545821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4555821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) {
4572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
4585821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  ScopedComPtr<IMMEndpoint> endpoint;
4595821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = device->QueryInterface(endpoint.Receive());
4605821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
4615821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex << hr;
4625821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return eAll;
4635821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4645821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4655821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  EDataFlow data_flow;
4665821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  hr = endpoint->GetDataFlow(&data_flow);
4675821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  if (FAILED(hr)) {
4685821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex << hr;
4695821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    return eAll;
4705821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  }
4715821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return data_flow;
4725821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4735821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4745821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient(
4755821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)    IMMDevice* audio_device) {
4762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
4775821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4785821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // Creates and activates an IAudioClient COM object given the selected
4795821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  // endpoint device.
4802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedComPtr<IAudioClient> audio_client;
4815821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  HRESULT hr = audio_device->Activate(__uuidof(IAudioClient),
4825821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      CLSCTX_INPROC_SERVER,
4835821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      NULL,
4845821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)                                      audio_client.ReceiveVoid());
4855821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr;
4865821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)  return audio_client;
4875821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}
4885821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)
4892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient(
4902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EDataFlow data_flow, ERole role) {
4912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
4922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedComPtr<IMMDevice> default_device(CreateDefaultDevice(data_flow, role));
4932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (default_device ? CreateClient(default_device) :
4942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      ScopedComPtr<IAudioClient>());
4952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
4962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
4971e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient(
4981e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    const std::string& device_id, EDataFlow data_flow, ERole role) {
4991e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (device_id.empty())
5001e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return CreateDefaultClient(data_flow, role);
5011e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
5021e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  ScopedComPtr<IMMDevice> device(CreateDevice(device_id));
5031e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (!device)
5041e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return ScopedComPtr<IAudioClient>();
5051e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
5061e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles) return CreateClient(device);
5071e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
5081e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
5092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)HRESULT CoreAudioUtil::GetSharedModeMixFormat(
5102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IAudioClient* client, WAVEFORMATPCMEX* format) {
5112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
5122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedCoMem<WAVEFORMATPCMEX> format_pcmex;
5132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT hr = client->GetMixFormat(
5142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      reinterpret_cast<WAVEFORMATEX**>(&format_pcmex));
5152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(hr))
5162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return hr;
5172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize;
5192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX));
5202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  memcpy(format, format_pcmex, bytes);
522c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DVLOG(2) << *format;
5232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return hr;
5252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool CoreAudioUtil::IsFormatSupported(IAudioClient* client,
5282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      AUDCLNT_SHAREMODE share_mode,
5292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                      const WAVEFORMATPCMEX* format) {
5302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
5312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedCoMem<WAVEFORMATEXTENSIBLE> closest_match;
5322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT hr = client->IsFormatSupported(
5332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      share_mode, reinterpret_cast<const WAVEFORMATEX*>(format),
5342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      reinterpret_cast<WAVEFORMATEX**>(&closest_match));
5352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This log can only be triggered for shared mode.
5372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported "
5382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                << "but a closest match exists.";
5392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // This log can be triggered both for shared and exclusive modes.
5402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format.";
5412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (hr == S_FALSE) {
542c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    DVLOG(2) << *closest_match;
5432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
5442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return (hr == S_OK);
5462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
5472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
5481e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)bool CoreAudioUtil::IsChannelLayoutSupported(const std::string& device_id,
5491e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                             EDataFlow data_flow,
5501e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                             ERole role,
551c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                             ChannelLayout channel_layout) {
552c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DCHECK(IsSupported());
553c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
554c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // First, get the preferred mixing format for shared mode streams.
555c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5561e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  ScopedComPtr<IAudioClient> client(CreateClient(device_id, data_flow, role));
557c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (!client)
558c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
559c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
560c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  WAVEFORMATPCMEX format;
5611e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  HRESULT hr = GetSharedModeMixFormat(client, &format);
562c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (FAILED(hr))
563c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
564c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
565c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Next, check if it is possible to use an alternative format where the
566c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // channel layout (and possibly number of channels) is modified.
567c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
568c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Convert generic channel layout into Windows-specific channel configuration.
569c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  ChannelConfig new_config = ChannelLayoutToChannelConfig(channel_layout);
570c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (new_config == KSAUDIO_SPEAKER_UNSUPPORTED) {
571c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    return false;
572c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
573c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  format.dwChannelMask = new_config;
574c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
575c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Modify the format if the new channel layout has changed the number of
576c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // utilized channels.
577c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  const int channels = ChannelLayoutToChannelCount(channel_layout);
578c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  if (channels != format.Format.nChannels) {
579c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    format.Format.nChannels = channels;
580c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * channels;
581c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)    format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec *
582c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                    format.Format.nBlockAlign;
583c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  }
584c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  DVLOG(2) << format;
585c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
586c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // Some devices can initialize a shared-mode stream with a format that is
587c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // not identical to the mix format obtained from the GetMixFormat() method.
588c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // However, chances of succeeding increases if we use the same number of
589c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // channels and the same sample rate as the mix format. I.e, this call will
590c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // return true only in those cases where the audio engine is able to support
591c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // an even wider range of shared-mode formats where the installation package
592c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // for the audio device includes a local effects (LFX) audio processing
593c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  // object (APO) that can handle format conversions.
594c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)  return CoreAudioUtil::IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
595c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)                                          &format);
596c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)}
597c2e0dbddbe15c98d52c4786dac06cb8952a8ae6dTorne (Richard Coles)
5982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client,
5992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       AUDCLNT_SHAREMODE share_mode,
6002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                       REFERENCE_TIME* device_period) {
6012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
6022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get the period of the engine thread.
6042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  REFERENCE_TIME default_period = 0;
6052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  REFERENCE_TIME minimum_period = 0;
6062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period);
6072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(hr))
6082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return hr;
6092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period :
6112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      minimum_period;
6122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(2) << "device_period: "
6132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << RefererenceTimeToTimeDelta(*device_period).InMillisecondsF()
6142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << " [ms]";
6152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return hr;
6162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)HRESULT CoreAudioUtil::GetPreferredAudioParameters(
6192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IAudioClient* client, AudioParameters* params) {
6202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
6212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  WAVEFORMATPCMEX mix_format;
6222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT hr = GetSharedModeMixFormat(client, &mix_format);
6232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(hr))
6242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return hr;
6252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  REFERENCE_TIME default_period = 0;
6272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period);
6282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(hr))
6292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return hr;
6302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get the integer mask which corresponds to the channel layout the
6322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // audio engine uses for its internal processing/mixing of shared-mode
6332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // streams. This mask indicates which channels are present in the multi-
6342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // channel stream. The least significant bit corresponds with the Front Left
6352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // speaker, the next least significant bit corresponds to the Front Right
6362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // speaker, and so on, continuing in the order defined in KsMedia.h.
6372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // See http://msdn.microsoft.com/en-us/library/windows/hardware/ff537083.aspx
6382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // for more details.
6392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ChannelConfig channel_config = mix_format.dwChannelMask;
6402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Convert Microsoft's channel configuration to genric ChannelLayout.
6422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ChannelLayout channel_layout = ChannelConfigToChannelLayout(channel_config);
6432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6440f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  // Some devices don't appear to set a valid channel layout, so guess based on
6450f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  // the number of channels.  See http://crbug.com/311906.
6460f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) {
6470f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    VLOG(1) << "Unsupported channel config: "
6480f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            << std::hex << channel_config
6490f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            << ".  Guessing layout by channel count: "
6500f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)            << std::dec << mix_format.Format.nChannels;
6510f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)    channel_layout = GuessChannelLayout(mix_format.Format.nChannels);
6520f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)  }
6530f1bc08d4cfcc34181b0b5cbf065c40f687bf740Torne (Richard Coles)
6542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Preferred sample rate.
6552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int sample_rate = mix_format.Format.nSamplesPerSec;
6562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(henrika): possibly use format.Format.wBitsPerSample here instead.
6582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We use a hard-coded value of 16 bits per sample today even if most audio
6592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // engines does the actual mixing in 32 bits per sample.
6602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int bits_per_sample = 16;
6612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // We are using the native device period to derive the smallest possible
6632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // buffer size in shared mode. Note that the actual endpoint buffer will be
6642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // larger than this size but it will be possible to fill it up in two calls.
6652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(henrika): ensure that this scheme works for capturing as well.
6662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int frames_per_buffer = static_cast<int>(sample_rate *
6672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      RefererenceTimeToTimeDelta(default_period).InSecondsF() + 0.5);
6682a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "channel_layout   : " << channel_layout;
6702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "sample_rate      : " << sample_rate;
6712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "bits_per_sample  : " << bits_per_sample;
6722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(1) << "frames_per_buffer: " << frames_per_buffer;
6732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  AudioParameters audio_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
6752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               channel_layout,
6762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               sample_rate,
6772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               bits_per_sample,
6782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                               frames_per_buffer);
6792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *params = audio_params;
6812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return hr;
6822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
6832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
6842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)HRESULT CoreAudioUtil::GetPreferredAudioParameters(
6852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    EDataFlow data_flow, ERole role, AudioParameters* params) {
6862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
6872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role));
6882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (!client) {
6892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // Map NULL-pointer to new error code which can be different from the
6902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    // actual error code. The exact value is not important here.
6912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
6922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
693a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
694a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  HRESULT hr = GetPreferredAudioParameters(client, params);
695a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (FAILED(hr))
696a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    return hr;
697a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
698a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  if (role == eCommunications) {
699a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    // Raise the 'DUCKING' flag for default communication devices.
700a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)    *params = AudioParameters(params->format(), params->channel_layout(),
7011320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        params->channels(), params->sample_rate(), params->bits_per_sample(),
7021320f92c476a1ad9d19dba2a48c72b75566198e9Primiano Tucci        params->frames_per_buffer(),
703a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)        params->effects() | AudioParameters::DUCKING);
704a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  }
705a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)
706a1401311d1ab56c4ed0a474bd38c108f75cb0cd9Torne (Richard Coles)  return hr;
7072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
709eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen MurdochHRESULT CoreAudioUtil::GetPreferredAudioParameters(
710eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    const std::string& device_id, AudioParameters* params) {
711eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  DCHECK(IsSupported());
712eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ScopedComPtr<IMMDevice> device(CreateDevice(device_id));
713eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!device) {
714eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Map NULL-pointer to new error code which can be different from the
715eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // actual error code. The exact value is not important here.
716eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return AUDCLNT_E_DEVICE_INVALIDATED;
717eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
718eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
719eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  ScopedComPtr<IAudioClient> client(CreateClient(device));
720eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  if (!client) {
721eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // Map NULL-pointer to new error code which can be different from the
722eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    // actual error code. The exact value is not important here.
723eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch    return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
724eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  }
725eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch  return GetPreferredAudioParameters(client, params);
726eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch}
727eb525c5499e34cc9c4b825d6d9e75bb07cc06aceBen Murdoch
7281e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)ChannelConfig CoreAudioUtil::GetChannelConfig(const std::string& device_id,
7291e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)                                              EDataFlow data_flow) {
7301e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  ScopedComPtr<IAudioClient> client(
7311e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)      CreateClient(device_id, data_flow, eConsole));
7321e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
7331e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  WAVEFORMATPCMEX format = {0};
7341e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  if (!client || FAILED(GetSharedModeMixFormat(client, &format)))
7351e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)    return 0;
7361e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
7371e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)  return static_cast<ChannelConfig>(format.dwChannelMask);
7381e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)}
7391e9bf3e0803691d0a228da41fc608347b6db4340Torne (Richard Coles)
740116680a4aac90f2aa7413d9095a592090648e557Ben MurdochHRESULT CoreAudioUtil::SharedModeInitialize(
741116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    IAudioClient* client, const WAVEFORMATPCMEX* format, HANDLE event_handle,
742116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch    uint32* endpoint_buffer_size, const GUID* session_guid) {
7432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
7442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Use default flags (i.e, dont set AUDCLNT_STREAMFLAGS_NOPERSIST) to
7462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // ensure that the volume level and muting state for a rendering session
7472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // are persistent across system restarts. The volume level and muting
7482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // state for a capture session are never persistent.
7492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DWORD stream_flags = 0;
7502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Enable event-driven streaming if a valid event handle is provided.
7522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // After the stream starts, the audio engine will signal the event handle
7532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // to notify the client each time a buffer becomes ready to process.
7542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Event-driven buffering is supported for both rendering and capturing.
7552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Both shared-mode and exclusive-mode streams can use event-driven buffering.
7562a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  bool use_event = (event_handle != NULL &&
7572a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                    event_handle != INVALID_HANDLE_VALUE);
7582a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (use_event)
7592a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
7602a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(2) << "stream_flags: 0x" << std::hex << stream_flags;
7612a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7622a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Initialize the shared mode client for minimal delay.
7632a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED,
7642a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  stream_flags,
7652a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  0,
7662a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  0,
7672a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  reinterpret_cast<const WAVEFORMATEX*>(format),
768116680a4aac90f2aa7413d9095a592090648e557Ben Murdoch                                  session_guid);
7692a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(hr)) {
7702a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "IAudioClient::Initialize: " << std::hex << hr;
7712a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return hr;
7722a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7732a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7742a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (use_event) {
7752a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    hr = client->SetEventHandle(event_handle);
7762a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    if (FAILED(hr)) {
7772a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex << hr;
7782a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)      return hr;
7792a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    }
7802a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7812a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7822a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UINT32 buffer_size_in_frames = 0;
7832a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  hr = client->GetBufferSize(&buffer_size_in_frames);
7842a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(hr)) {
7852a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr;
7862a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return hr;
7872a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
7882a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7892a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  *endpoint_buffer_size = buffer_size_in_frames;
7902a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames;
7912a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
7922a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // TODO(henrika): utilize when delay measurements are added.
7932a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  REFERENCE_TIME  latency = 0;
7942a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  hr = client->GetStreamLatency(&latency);
7952a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(2) << "stream latency: "
7962a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)           << RefererenceTimeToTimeDelta(latency).InMillisecondsF() << " [ms]";
7972a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return hr;
7982a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
7992a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8002a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ScopedComPtr<IAudioRenderClient> CoreAudioUtil::CreateRenderClient(
8012a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IAudioClient* client) {
8022a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
8032a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8042a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get access to the IAudioRenderClient interface. This interface
8052a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // enables us to write output data to a rendering endpoint buffer.
8062a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedComPtr<IAudioRenderClient> audio_render_client;
8072a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT hr = client->GetService(__uuidof(IAudioRenderClient),
8082a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  audio_render_client.ReceiveVoid());
8092a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(hr)) {
8102a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr;
8112a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return ScopedComPtr<IAudioRenderClient>();
8122a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8132a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return audio_render_client;
8142a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8152a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8162a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)ScopedComPtr<IAudioCaptureClient> CoreAudioUtil::CreateCaptureClient(
8172a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IAudioClient* client) {
8182a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
8192a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8202a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Get access to the IAudioCaptureClient interface. This interface
8212a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // enables us to read input data from a capturing endpoint buffer.
8222a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  ScopedComPtr<IAudioCaptureClient> audio_capture_client;
8232a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  HRESULT hr = client->GetService(__uuidof(IAudioCaptureClient),
8242a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                  audio_capture_client.ReceiveVoid());
8252a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(hr)) {
8262a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr;
8272a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return ScopedComPtr<IAudioCaptureClient>();
8282a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  }
8292a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return audio_capture_client;
8302a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8312a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8322a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)bool CoreAudioUtil::FillRenderEndpointBufferWithSilence(
8332a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    IAudioClient* client, IAudioRenderClient* render_client) {
8342a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DCHECK(IsSupported());
8352a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8362a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UINT32 endpoint_buffer_size = 0;
8372a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(client->GetBufferSize(&endpoint_buffer_size)))
8382a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
8392a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8402a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  UINT32 num_queued_frames = 0;
8412a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(client->GetCurrentPadding(&num_queued_frames)))
8422a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
8432a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8442a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  BYTE* data = NULL;
8452a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  int num_frames_to_fill = endpoint_buffer_size - num_queued_frames;
8462a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  if (FAILED(render_client->GetBuffer(num_frames_to_fill, &data)))
8472a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)    return false;
8482a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8492a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // Using the AUDCLNT_BUFFERFLAGS_SILENT flag eliminates the need to
8502a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  // explicitly write silence data to the rendering buffer.
8512a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence";
8522a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)  return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill,
8532a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)                                                AUDCLNT_BUFFERFLAGS_SILENT));
8542a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)}
8552a99a7e74a7f215066514fe81d2bfa6639d9edddTorne (Richard Coles)
8565821806d5e7f356e8fa4b058a389a808ea183019Torne (Richard Coles)}  // namespace media
857