This source file includes following definitions.
- ChannelConfigToChannelLayout
- ChannelLayoutToChannelConfig
- LoadAudiosesDll
- CanCreateDeviceEnumerator
- GetDeviceID
- IsSupported
- RefererenceTimeToTimeDelta
- GetShareMode
- NumberOfActiveDevices
- CreateDeviceEnumerator
- CreateDefaultDevice
- GetDefaultOutputDeviceID
- CreateDevice
- GetDeviceName
- GetAudioControllerID
- GetMatchingOutputDeviceID
- GetFriendlyName
- DeviceIsDefault
- GetDataFlow
- CreateClient
- CreateDefaultClient
- CreateClient
- GetSharedModeMixFormat
- IsFormatSupported
- IsChannelLayoutSupported
- GetDevicePeriod
- GetPreferredAudioParameters
- GetPreferredAudioParameters
- GetPreferredAudioParameters
- GetChannelConfig
- SharedModeInitialize
- CreateRenderClient
- CreateCaptureClient
- FillRenderEndpointBufferWithSilence
#include "media/audio/win/core_audio_util_win.h"
#include <audioclient.h>
#include <devicetopology.h>
#include <functiondiscoverykeys_devpkey.h>
#include "base/command_line.h"
#include "base/logging.h"
#include "base/strings/stringprintf.h"
#include "base/strings/utf_string_conversions.h"
#include "base/win/scoped_co_mem.h"
#include "base/win/scoped_handle.h"
#include "base/win/scoped_propvariant.h"
#include "base/win/windows_version.h"
#include "media/base/media_switches.h"
using base::win::ScopedCoMem;
using base::win::ScopedHandle;
namespace media {
enum { KSAUDIO_SPEAKER_UNSUPPORTED = 0 };
static ChannelLayout ChannelConfigToChannelLayout(ChannelConfig config) {
switch (config) {
case KSAUDIO_SPEAKER_DIRECTOUT:
DVLOG(2) << "KSAUDIO_SPEAKER_DIRECTOUT=>CHANNEL_LAYOUT_NONE";
return CHANNEL_LAYOUT_NONE;
case KSAUDIO_SPEAKER_MONO:
DVLOG(2) << "KSAUDIO_SPEAKER_MONO=>CHANNEL_LAYOUT_MONO";
return CHANNEL_LAYOUT_MONO;
case KSAUDIO_SPEAKER_STEREO:
DVLOG(2) << "KSAUDIO_SPEAKER_STEREO=>CHANNEL_LAYOUT_STEREO";
return CHANNEL_LAYOUT_STEREO;
case KSAUDIO_SPEAKER_QUAD:
DVLOG(2) << "KSAUDIO_SPEAKER_QUAD=>CHANNEL_LAYOUT_QUAD";
return CHANNEL_LAYOUT_QUAD;
case KSAUDIO_SPEAKER_SURROUND:
DVLOG(2) << "KSAUDIO_SPEAKER_SURROUND=>CHANNEL_LAYOUT_4_0";
return CHANNEL_LAYOUT_4_0;
case KSAUDIO_SPEAKER_5POINT1:
DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1=>CHANNEL_LAYOUT_5_1_BACK";
return CHANNEL_LAYOUT_5_1_BACK;
case KSAUDIO_SPEAKER_5POINT1_SURROUND:
DVLOG(2) << "KSAUDIO_SPEAKER_5POINT1_SURROUND=>CHANNEL_LAYOUT_5_1";
return CHANNEL_LAYOUT_5_1;
case KSAUDIO_SPEAKER_7POINT1:
DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1=>CHANNEL_LAYOUT_7_1_WIDE";
return CHANNEL_LAYOUT_7_1_WIDE;
case KSAUDIO_SPEAKER_7POINT1_SURROUND:
DVLOG(2) << "KSAUDIO_SPEAKER_7POINT1_SURROUND=>CHANNEL_LAYOUT_7_1";
return CHANNEL_LAYOUT_7_1;
default:
DVLOG(2) << "Unsupported channel configuration: " << config;
return CHANNEL_LAYOUT_UNSUPPORTED;
}
}
static ChannelConfig ChannelLayoutToChannelConfig(ChannelLayout layout) {
switch (layout) {
case CHANNEL_LAYOUT_NONE:
DVLOG(2) << "CHANNEL_LAYOUT_NONE=>KSAUDIO_SPEAKER_UNSUPPORTED";
return KSAUDIO_SPEAKER_UNSUPPORTED;
case CHANNEL_LAYOUT_UNSUPPORTED:
DVLOG(2) << "CHANNEL_LAYOUT_UNSUPPORTED=>KSAUDIO_SPEAKER_UNSUPPORTED";
return KSAUDIO_SPEAKER_UNSUPPORTED;
case CHANNEL_LAYOUT_MONO:
DVLOG(2) << "CHANNEL_LAYOUT_MONO=>KSAUDIO_SPEAKER_MONO";
return KSAUDIO_SPEAKER_MONO;
case CHANNEL_LAYOUT_STEREO:
DVLOG(2) << "CHANNEL_LAYOUT_STEREO=>KSAUDIO_SPEAKER_STEREO";
return KSAUDIO_SPEAKER_STEREO;
case CHANNEL_LAYOUT_QUAD:
DVLOG(2) << "CHANNEL_LAYOUT_QUAD=>KSAUDIO_SPEAKER_QUAD";
return KSAUDIO_SPEAKER_QUAD;
case CHANNEL_LAYOUT_4_0:
DVLOG(2) << "CHANNEL_LAYOUT_4_0=>KSAUDIO_SPEAKER_SURROUND";
return KSAUDIO_SPEAKER_SURROUND;
case CHANNEL_LAYOUT_5_1_BACK:
DVLOG(2) << "CHANNEL_LAYOUT_5_1_BACK=>KSAUDIO_SPEAKER_5POINT1";
return KSAUDIO_SPEAKER_5POINT1;
case CHANNEL_LAYOUT_5_1:
DVLOG(2) << "CHANNEL_LAYOUT_5_1=>KSAUDIO_SPEAKER_5POINT1_SURROUND";
return KSAUDIO_SPEAKER_5POINT1_SURROUND;
case CHANNEL_LAYOUT_7_1_WIDE:
DVLOG(2) << "CHANNEL_LAYOUT_7_1_WIDE=>KSAUDIO_SPEAKER_7POINT1";
return KSAUDIO_SPEAKER_7POINT1;
case CHANNEL_LAYOUT_7_1:
DVLOG(2) << "CHANNEL_LAYOUT_7_1=>KSAUDIO_SPEAKER_7POINT1_SURROUND";
return KSAUDIO_SPEAKER_7POINT1_SURROUND;
default:
DVLOG(2) << "Unsupported channel layout: " << layout;
return KSAUDIO_SPEAKER_UNSUPPORTED;
}
}
static std::ostream& operator<<(std::ostream& os,
const WAVEFORMATPCMEX& format) {
os << "wFormatTag: 0x" << std::hex << format.Format.wFormatTag
<< ", nChannels: " << std::dec << format.Format.nChannels
<< ", nSamplesPerSec: " << format.Format.nSamplesPerSec
<< ", nAvgBytesPerSec: " << format.Format.nAvgBytesPerSec
<< ", nBlockAlign: " << format.Format.nBlockAlign
<< ", wBitsPerSample: " << format.Format.wBitsPerSample
<< ", cbSize: " << format.Format.cbSize
<< ", wValidBitsPerSample: " << format.Samples.wValidBitsPerSample
<< ", dwChannelMask: 0x" << std::hex << format.dwChannelMask;
return os;
}
static bool LoadAudiosesDll() {
static const wchar_t* const kAudiosesDLL =
L"%WINDIR%\\system32\\audioses.dll";
wchar_t path[MAX_PATH] = {0};
ExpandEnvironmentStringsW(kAudiosesDLL, path, arraysize(path));
return (LoadLibraryExW(path, NULL, LOAD_WITH_ALTERED_SEARCH_PATH) != NULL);
}
static bool CanCreateDeviceEnumerator() {
ScopedComPtr<IMMDeviceEnumerator> device_enumerator;
HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER);
CHECK_NE(hr, CO_E_NOTINITIALIZED);
return SUCCEEDED(hr);
}
static std::string GetDeviceID(IMMDevice* device) {
ScopedCoMem<WCHAR> device_id_com;
std::string device_id;
if (SUCCEEDED(device->GetId(&device_id_com)))
base::WideToUTF8(device_id_com, wcslen(device_id_com), &device_id);
return device_id;
}
bool CoreAudioUtil::IsSupported() {
const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
if (cmd_line->HasSwitch(switches::kForceWaveAudio)) {
DVLOG(1) << "Forcing usage of Windows WaveXxx APIs";
return false;
}
if (base::win::GetVersion() < base::win::VERSION_VISTA)
return false;
static bool g_audioses_dll_available = LoadAudiosesDll();
if (!g_audioses_dll_available)
return false;
static bool g_can_create_device_enumerator = CanCreateDeviceEnumerator();
LOG_IF(ERROR, !g_can_create_device_enumerator)
<< "Failed to create Core Audio device enumerator on thread with ID "
<< GetCurrentThreadId();
return g_can_create_device_enumerator;
}
base::TimeDelta CoreAudioUtil::RefererenceTimeToTimeDelta(REFERENCE_TIME time) {
return base::TimeDelta::FromMicroseconds(0.1 * time + 0.5);
}
AUDCLNT_SHAREMODE CoreAudioUtil::GetShareMode() {
const CommandLine* cmd_line = CommandLine::ForCurrentProcess();
if (cmd_line->HasSwitch(switches::kEnableExclusiveAudio))
return AUDCLNT_SHAREMODE_EXCLUSIVE;
return AUDCLNT_SHAREMODE_SHARED;
}
int CoreAudioUtil::NumberOfActiveDevices(EDataFlow data_flow) {
DCHECK(IsSupported());
ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
CreateDeviceEnumerator();
if (!device_enumerator)
return 0;
ScopedComPtr<IMMDeviceCollection> collection;
HRESULT hr = device_enumerator->EnumAudioEndpoints(data_flow,
DEVICE_STATE_ACTIVE,
collection.Receive());
if (FAILED(hr)) {
LOG(ERROR) << "IMMDeviceCollection::EnumAudioEndpoints: " << std::hex << hr;
return 0;
}
UINT number_of_active_devices = 0;
collection->GetCount(&number_of_active_devices);
DVLOG(2) << ((data_flow == eCapture) ? "[in ] " : "[out] ")
<< "number of devices: " << number_of_active_devices;
return static_cast<int>(number_of_active_devices);
}
ScopedComPtr<IMMDeviceEnumerator> CoreAudioUtil::CreateDeviceEnumerator() {
DCHECK(IsSupported());
ScopedComPtr<IMMDeviceEnumerator> device_enumerator;
HRESULT hr = device_enumerator.CreateInstance(__uuidof(MMDeviceEnumerator),
NULL, CLSCTX_INPROC_SERVER);
CHECK(SUCCEEDED(hr));
return device_enumerator;
}
ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDefaultDevice(EDataFlow data_flow,
ERole role) {
DCHECK(IsSupported());
ScopedComPtr<IMMDevice> endpoint_device;
ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
CreateDeviceEnumerator();
if (!device_enumerator)
return endpoint_device;
HRESULT hr = device_enumerator->GetDefaultAudioEndpoint(
data_flow, role, endpoint_device.Receive());
if (FAILED(hr)) {
DVLOG(1) << "IMMDeviceEnumerator::GetDefaultAudioEndpoint: "
<< std::hex << hr;
return endpoint_device;
}
DWORD state = DEVICE_STATE_DISABLED;
hr = endpoint_device->GetState(&state);
if (SUCCEEDED(hr)) {
if (!(state & DEVICE_STATE_ACTIVE)) {
DVLOG(1) << "Selected endpoint device is not active";
endpoint_device.Release();
}
}
return endpoint_device;
}
std::string CoreAudioUtil::GetDefaultOutputDeviceID() {
DCHECK(IsSupported());
ScopedComPtr<IMMDevice> device(CreateDefaultDevice(eRender, eConsole));
return device ? GetDeviceID(device) : std::string();
}
ScopedComPtr<IMMDevice> CoreAudioUtil::CreateDevice(
const std::string& device_id) {
DCHECK(IsSupported());
ScopedComPtr<IMMDevice> endpoint_device;
ScopedComPtr<IMMDeviceEnumerator> device_enumerator =
CreateDeviceEnumerator();
if (!device_enumerator)
return endpoint_device;
HRESULT hr = device_enumerator->GetDevice(
base::UTF8ToUTF16(device_id).c_str(), endpoint_device.Receive());
DVLOG_IF(1, FAILED(hr)) << "IMMDeviceEnumerator::GetDevice: "
<< std::hex << hr;
return endpoint_device;
}
HRESULT CoreAudioUtil::GetDeviceName(IMMDevice* device, AudioDeviceName* name) {
DCHECK(IsSupported());
AudioDeviceName device_name;
device_name.unique_id = GetDeviceID(device);
if (device_name.unique_id.empty())
return E_FAIL;
ScopedComPtr<IPropertyStore> properties;
HRESULT hr = device->OpenPropertyStore(STGM_READ, properties.Receive());
if (FAILED(hr))
return hr;
base::win::ScopedPropVariant friendly_name;
hr = properties->GetValue(PKEY_Device_FriendlyName, friendly_name.Receive());
if (FAILED(hr))
return hr;
if (friendly_name.get().vt == VT_LPWSTR && friendly_name.get().pwszVal) {
base::WideToUTF8(friendly_name.get().pwszVal,
wcslen(friendly_name.get().pwszVal),
&device_name.device_name);
}
*name = device_name;
DVLOG(2) << "friendly name: " << device_name.device_name;
DVLOG(2) << "unique id : " << device_name.unique_id;
return hr;
}
std::string CoreAudioUtil::GetAudioControllerID(IMMDevice* device,
IMMDeviceEnumerator* enumerator) {
DCHECK(IsSupported());
ScopedComPtr<IDeviceTopology> topology;
ScopedComPtr<IConnector> connector;
ScopedCoMem<WCHAR> filter_id;
if (FAILED(device->Activate(__uuidof(IDeviceTopology), CLSCTX_ALL, NULL,
topology.ReceiveVoid()) ||
FAILED(topology->GetConnector(0, connector.Receive())) ||
FAILED(connector->GetDeviceIdConnectedTo(&filter_id)))) {
DLOG(ERROR) << "Failed to get the device identifier of the audio device";
return std::string();
}
ScopedComPtr<IMMDevice> device_node;
ScopedComPtr<IPropertyStore> properties;
base::win::ScopedPropVariant instance_id;
if (FAILED(enumerator->GetDevice(filter_id, device_node.Receive())) ||
FAILED(device_node->OpenPropertyStore(STGM_READ, properties.Receive())) ||
FAILED(properties->GetValue(PKEY_Device_InstanceId,
instance_id.Receive())) ||
instance_id.get().vt != VT_LPWSTR) {
DLOG(ERROR) << "Failed to get instance id of the audio device node";
return std::string();
}
std::string controller_id;
base::WideToUTF8(instance_id.get().pwszVal,
wcslen(instance_id.get().pwszVal),
&controller_id);
return controller_id;
}
std::string CoreAudioUtil::GetMatchingOutputDeviceID(
const std::string& input_device_id) {
ScopedComPtr<IMMDevice> input_device(CreateDevice(input_device_id));
if (!input_device)
return std::string();
ScopedComPtr<IMMDeviceEnumerator> enumerator(CreateDeviceEnumerator());
std::string controller_id(GetAudioControllerID(input_device, enumerator));
if (controller_id.empty())
return std::string();
ScopedComPtr<IMMDeviceCollection> collection;
enumerator->EnumAudioEndpoints(eRender, DEVICE_STATE_ACTIVE,
collection.Receive());
if (!collection)
return std::string();
UINT count = 0;
collection->GetCount(&count);
ScopedComPtr<IMMDevice> output_device;
for (UINT i = 0; i < count; ++i) {
collection->Item(i, output_device.Receive());
std::string output_controller_id(GetAudioControllerID(
output_device, enumerator));
if (output_controller_id == controller_id)
break;
output_device = NULL;
}
return output_device ? GetDeviceID(output_device) : std::string();
}
std::string CoreAudioUtil::GetFriendlyName(const std::string& device_id) {
DCHECK(IsSupported());
ScopedComPtr<IMMDevice> audio_device = CreateDevice(device_id);
if (!audio_device)
return std::string();
AudioDeviceName device_name;
HRESULT hr = GetDeviceName(audio_device, &device_name);
if (FAILED(hr))
return std::string();
return device_name.device_name;
}
bool CoreAudioUtil::DeviceIsDefault(EDataFlow flow,
ERole role,
const std::string& device_id) {
DCHECK(IsSupported());
ScopedComPtr<IMMDevice> device = CreateDefaultDevice(flow, role);
if (!device)
return false;
std::string str_default(GetDeviceID(device));
return device_id.compare(str_default) == 0;
}
EDataFlow CoreAudioUtil::GetDataFlow(IMMDevice* device) {
DCHECK(IsSupported());
ScopedComPtr<IMMEndpoint> endpoint;
HRESULT hr = device->QueryInterface(endpoint.Receive());
if (FAILED(hr)) {
DVLOG(1) << "IMMDevice::QueryInterface: " << std::hex << hr;
return eAll;
}
EDataFlow data_flow;
hr = endpoint->GetDataFlow(&data_flow);
if (FAILED(hr)) {
DVLOG(1) << "IMMEndpoint::GetDataFlow: " << std::hex << hr;
return eAll;
}
return data_flow;
}
ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient(
IMMDevice* audio_device) {
DCHECK(IsSupported());
ScopedComPtr<IAudioClient> audio_client;
HRESULT hr = audio_device->Activate(__uuidof(IAudioClient),
CLSCTX_INPROC_SERVER,
NULL,
audio_client.ReceiveVoid());
DVLOG_IF(1, FAILED(hr)) << "IMMDevice::Activate: " << std::hex << hr;
return audio_client;
}
ScopedComPtr<IAudioClient> CoreAudioUtil::CreateDefaultClient(
EDataFlow data_flow, ERole role) {
DCHECK(IsSupported());
ScopedComPtr<IMMDevice> default_device(CreateDefaultDevice(data_flow, role));
return (default_device ? CreateClient(default_device) :
ScopedComPtr<IAudioClient>());
}
ScopedComPtr<IAudioClient> CoreAudioUtil::CreateClient(
const std::string& device_id, EDataFlow data_flow, ERole role) {
if (device_id.empty())
return CreateDefaultClient(data_flow, role);
ScopedComPtr<IMMDevice> device(CreateDevice(device_id));
if (!device)
return ScopedComPtr<IAudioClient>();
return CreateClient(device);
}
HRESULT CoreAudioUtil::GetSharedModeMixFormat(
IAudioClient* client, WAVEFORMATPCMEX* format) {
DCHECK(IsSupported());
ScopedCoMem<WAVEFORMATPCMEX> format_pcmex;
HRESULT hr = client->GetMixFormat(
reinterpret_cast<WAVEFORMATEX**>(&format_pcmex));
if (FAILED(hr))
return hr;
size_t bytes = sizeof(WAVEFORMATEX) + format_pcmex->Format.cbSize;
DCHECK_EQ(bytes, sizeof(WAVEFORMATPCMEX));
memcpy(format, format_pcmex, bytes);
DVLOG(2) << *format;
return hr;
}
bool CoreAudioUtil::IsFormatSupported(IAudioClient* client,
AUDCLNT_SHAREMODE share_mode,
const WAVEFORMATPCMEX* format) {
DCHECK(IsSupported());
ScopedCoMem<WAVEFORMATEXTENSIBLE> closest_match;
HRESULT hr = client->IsFormatSupported(
share_mode, reinterpret_cast<const WAVEFORMATEX*>(format),
reinterpret_cast<WAVEFORMATEX**>(&closest_match));
DLOG_IF(ERROR, hr == S_FALSE) << "Format is not supported "
<< "but a closest match exists.";
DLOG_IF(ERROR, hr == AUDCLNT_E_UNSUPPORTED_FORMAT) << "Unsupported format.";
if (hr == S_FALSE) {
DVLOG(2) << *closest_match;
}
return (hr == S_OK);
}
bool CoreAudioUtil::IsChannelLayoutSupported(const std::string& device_id,
EDataFlow data_flow,
ERole role,
ChannelLayout channel_layout) {
DCHECK(IsSupported());
ScopedComPtr<IAudioClient> client(CreateClient(device_id, data_flow, role));
if (!client)
return false;
WAVEFORMATPCMEX format;
HRESULT hr = GetSharedModeMixFormat(client, &format);
if (FAILED(hr))
return false;
ChannelConfig new_config = ChannelLayoutToChannelConfig(channel_layout);
if (new_config == KSAUDIO_SPEAKER_UNSUPPORTED) {
return false;
}
format.dwChannelMask = new_config;
const int channels = ChannelLayoutToChannelCount(channel_layout);
if (channels != format.Format.nChannels) {
format.Format.nChannels = channels;
format.Format.nBlockAlign = (format.Format.wBitsPerSample / 8) * channels;
format.Format.nAvgBytesPerSec = format.Format.nSamplesPerSec *
format.Format.nBlockAlign;
}
DVLOG(2) << format;
return CoreAudioUtil::IsFormatSupported(client, AUDCLNT_SHAREMODE_SHARED,
&format);
}
HRESULT CoreAudioUtil::GetDevicePeriod(IAudioClient* client,
AUDCLNT_SHAREMODE share_mode,
REFERENCE_TIME* device_period) {
DCHECK(IsSupported());
REFERENCE_TIME default_period = 0;
REFERENCE_TIME minimum_period = 0;
HRESULT hr = client->GetDevicePeriod(&default_period, &minimum_period);
if (FAILED(hr))
return hr;
*device_period = (share_mode == AUDCLNT_SHAREMODE_SHARED) ? default_period :
minimum_period;
DVLOG(2) << "device_period: "
<< RefererenceTimeToTimeDelta(*device_period).InMillisecondsF()
<< " [ms]";
return hr;
}
HRESULT CoreAudioUtil::GetPreferredAudioParameters(
IAudioClient* client, AudioParameters* params) {
DCHECK(IsSupported());
WAVEFORMATPCMEX mix_format;
HRESULT hr = GetSharedModeMixFormat(client, &mix_format);
if (FAILED(hr))
return hr;
REFERENCE_TIME default_period = 0;
hr = GetDevicePeriod(client, AUDCLNT_SHAREMODE_SHARED, &default_period);
if (FAILED(hr))
return hr;
ChannelConfig channel_config = mix_format.dwChannelMask;
ChannelLayout channel_layout = ChannelConfigToChannelLayout(channel_config);
if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) {
VLOG(1) << "Unsupported channel config: "
<< std::hex << channel_config
<< ". Guessing layout by channel count: "
<< std::dec << mix_format.Format.nChannels;
channel_layout = GuessChannelLayout(mix_format.Format.nChannels);
}
int sample_rate = mix_format.Format.nSamplesPerSec;
int bits_per_sample = 16;
int frames_per_buffer = static_cast<int>(sample_rate *
RefererenceTimeToTimeDelta(default_period).InSecondsF() + 0.5);
DVLOG(1) << "channel_layout : " << channel_layout;
DVLOG(1) << "sample_rate : " << sample_rate;
DVLOG(1) << "bits_per_sample : " << bits_per_sample;
DVLOG(1) << "frames_per_buffer: " << frames_per_buffer;
AudioParameters audio_params(AudioParameters::AUDIO_PCM_LOW_LATENCY,
channel_layout,
sample_rate,
bits_per_sample,
frames_per_buffer);
*params = audio_params;
return hr;
}
HRESULT CoreAudioUtil::GetPreferredAudioParameters(
EDataFlow data_flow, ERole role, AudioParameters* params) {
DCHECK(IsSupported());
ScopedComPtr<IAudioClient> client(CreateDefaultClient(data_flow, role));
if (!client) {
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
}
HRESULT hr = GetPreferredAudioParameters(client, params);
if (FAILED(hr))
return hr;
if (role == eCommunications) {
*params = AudioParameters(params->format(), params->channel_layout(),
params->channels(), params->input_channels(), params->sample_rate(),
params->bits_per_sample(), params->frames_per_buffer(),
params->effects() | AudioParameters::DUCKING);
}
return hr;
}
HRESULT CoreAudioUtil::GetPreferredAudioParameters(
const std::string& device_id, AudioParameters* params) {
DCHECK(IsSupported());
ScopedComPtr<IMMDevice> device(CreateDevice(device_id));
if (!device) {
return AUDCLNT_E_DEVICE_INVALIDATED;
}
ScopedComPtr<IAudioClient> client(CreateClient(device));
if (!client) {
return AUDCLNT_E_ENDPOINT_CREATE_FAILED;
}
return GetPreferredAudioParameters(client, params);
}
ChannelConfig CoreAudioUtil::GetChannelConfig(const std::string& device_id,
EDataFlow data_flow) {
ScopedComPtr<IAudioClient> client(
CreateClient(device_id, data_flow, eConsole));
WAVEFORMATPCMEX format = {0};
if (!client || FAILED(GetSharedModeMixFormat(client, &format)))
return 0;
return static_cast<ChannelConfig>(format.dwChannelMask);
}
HRESULT CoreAudioUtil::SharedModeInitialize(IAudioClient* client,
const WAVEFORMATPCMEX* format,
HANDLE event_handle,
uint32* endpoint_buffer_size) {
DCHECK(IsSupported());
DWORD stream_flags = 0;
bool use_event = (event_handle != NULL &&
event_handle != INVALID_HANDLE_VALUE);
if (use_event)
stream_flags |= AUDCLNT_STREAMFLAGS_EVENTCALLBACK;
DVLOG(2) << "stream_flags: 0x" << std::hex << stream_flags;
HRESULT hr = client->Initialize(AUDCLNT_SHAREMODE_SHARED,
stream_flags,
0,
0,
reinterpret_cast<const WAVEFORMATEX*>(format),
NULL);
if (FAILED(hr)) {
DVLOG(1) << "IAudioClient::Initialize: " << std::hex << hr;
return hr;
}
if (use_event) {
hr = client->SetEventHandle(event_handle);
if (FAILED(hr)) {
DVLOG(1) << "IAudioClient::SetEventHandle: " << std::hex << hr;
return hr;
}
}
UINT32 buffer_size_in_frames = 0;
hr = client->GetBufferSize(&buffer_size_in_frames);
if (FAILED(hr)) {
DVLOG(1) << "IAudioClient::GetBufferSize: " << std::hex << hr;
return hr;
}
*endpoint_buffer_size = buffer_size_in_frames;
DVLOG(2) << "endpoint buffer size: " << buffer_size_in_frames;
REFERENCE_TIME latency = 0;
hr = client->GetStreamLatency(&latency);
DVLOG(2) << "stream latency: "
<< RefererenceTimeToTimeDelta(latency).InMillisecondsF() << " [ms]";
return hr;
}
ScopedComPtr<IAudioRenderClient> CoreAudioUtil::CreateRenderClient(
IAudioClient* client) {
DCHECK(IsSupported());
ScopedComPtr<IAudioRenderClient> audio_render_client;
HRESULT hr = client->GetService(__uuidof(IAudioRenderClient),
audio_render_client.ReceiveVoid());
if (FAILED(hr)) {
DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr;
return ScopedComPtr<IAudioRenderClient>();
}
return audio_render_client;
}
ScopedComPtr<IAudioCaptureClient> CoreAudioUtil::CreateCaptureClient(
IAudioClient* client) {
DCHECK(IsSupported());
ScopedComPtr<IAudioCaptureClient> audio_capture_client;
HRESULT hr = client->GetService(__uuidof(IAudioCaptureClient),
audio_capture_client.ReceiveVoid());
if (FAILED(hr)) {
DVLOG(1) << "IAudioClient::GetService: " << std::hex << hr;
return ScopedComPtr<IAudioCaptureClient>();
}
return audio_capture_client;
}
bool CoreAudioUtil::FillRenderEndpointBufferWithSilence(
IAudioClient* client, IAudioRenderClient* render_client) {
DCHECK(IsSupported());
UINT32 endpoint_buffer_size = 0;
if (FAILED(client->GetBufferSize(&endpoint_buffer_size)))
return false;
UINT32 num_queued_frames = 0;
if (FAILED(client->GetCurrentPadding(&num_queued_frames)))
return false;
BYTE* data = NULL;
int num_frames_to_fill = endpoint_buffer_size - num_queued_frames;
if (FAILED(render_client->GetBuffer(num_frames_to_fill, &data)))
return false;
DVLOG(2) << "filling up " << num_frames_to_fill << " frames with silence";
return SUCCEEDED(render_client->ReleaseBuffer(num_frames_to_fill,
AUDCLNT_BUFFERFLAGS_SILENT));
}
}