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