blob: b1edf4704544b95dfdd4457d8f5d890048733d27 [file] [log] [blame]
// Copyright (c) 2012 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/memory/scoped_ptr.h"
#include "base/synchronization/waitable_event.h"
#include "base/win/scoped_com_initializer.h"
#include "base/win/scoped_handle.h"
#include "media/audio/win/core_audio_util_win.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using base::win::ScopedCOMInitializer;
namespace media {
class CoreAudioUtilWinTest : public ::testing::Test {
protected:
// The test runs on a COM thread in the multithreaded apartment (MTA).
// If we don't initialize the COM library on a thread before using COM,
// all function calls will return CO_E_NOTINITIALIZED.
CoreAudioUtilWinTest()
: com_init_(ScopedCOMInitializer::kMTA) {
DCHECK(com_init_.succeeded());
}
virtual ~CoreAudioUtilWinTest() {}
bool CanRunAudioTest() {
bool core_audio = CoreAudioUtil::IsSupported();
if (!core_audio)
return false;
int capture_devices = CoreAudioUtil::NumberOfActiveDevices(eCapture);
int render_devices = CoreAudioUtil::NumberOfActiveDevices(eRender);
return ((capture_devices > 0) && (render_devices > 0));
}
ScopedCOMInitializer com_init_;
};
TEST_F(CoreAudioUtilWinTest, NumberOfActiveDevices) {
if (!CanRunAudioTest())
return;
int render_devices = CoreAudioUtil::NumberOfActiveDevices(eRender);
EXPECT_GT(render_devices, 0);
int capture_devices = CoreAudioUtil::NumberOfActiveDevices(eCapture);
EXPECT_GT(capture_devices, 0);
int total_devices = CoreAudioUtil::NumberOfActiveDevices(eAll);
EXPECT_EQ(total_devices, render_devices + capture_devices);
}
TEST_F(CoreAudioUtilWinTest, CreateDeviceEnumerator) {
if (!CanRunAudioTest())
return;
ScopedComPtr<IMMDeviceEnumerator> enumerator =
CoreAudioUtil::CreateDeviceEnumerator();
EXPECT_TRUE(enumerator);
}
TEST_F(CoreAudioUtilWinTest, CreateDefaultDevice) {
if (!CanRunAudioTest())
return;
struct {
EDataFlow flow;
ERole role;
} data[] = {
{eRender, eConsole},
{eRender, eCommunications},
{eRender, eMultimedia},
{eCapture, eConsole},
{eCapture, eCommunications},
{eCapture, eMultimedia}
};
// Create default devices for all flow/role combinations above.
ScopedComPtr<IMMDevice> audio_device;
for (int i = 0; i < arraysize(data); ++i) {
audio_device =
CoreAudioUtil::CreateDefaultDevice(data[i].flow, data[i].role);
EXPECT_TRUE(audio_device);
EXPECT_EQ(data[i].flow, CoreAudioUtil::GetDataFlow(audio_device));
}
// Only eRender and eCapture are allowed as flow parameter.
audio_device = CoreAudioUtil::CreateDefaultDevice(eAll, eConsole);
EXPECT_FALSE(audio_device);
}
TEST_F(CoreAudioUtilWinTest, CreateDevice) {
if (!CanRunAudioTest())
return;
// Get name and ID of default device used for playback.
ScopedComPtr<IMMDevice> default_render_device =
CoreAudioUtil::CreateDefaultDevice(eRender, eConsole);
AudioDeviceName default_render_name;
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(default_render_device,
&default_render_name)));
// Use the uniqe ID as input to CreateDevice() and create a corresponding
// IMMDevice.
ScopedComPtr<IMMDevice> audio_device =
CoreAudioUtil::CreateDevice(default_render_name.unique_id);
EXPECT_TRUE(audio_device);
// Verify that the two IMMDevice interfaces represents the same endpoint
// by comparing their unique IDs.
AudioDeviceName device_name;
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device,
&device_name)));
EXPECT_EQ(default_render_name.unique_id, device_name.unique_id);
}
TEST_F(CoreAudioUtilWinTest, GetDefaultDeviceName) {
if (!CanRunAudioTest())
return;
struct {
EDataFlow flow;
ERole role;
} data[] = {
{eRender, eConsole},
{eRender, eCommunications},
{eCapture, eConsole},
{eCapture, eCommunications}
};
// Get name and ID of default devices for all flow/role combinations above.
ScopedComPtr<IMMDevice> audio_device;
AudioDeviceName device_name;
for (int i = 0; i < arraysize(data); ++i) {
audio_device =
CoreAudioUtil::CreateDefaultDevice(data[i].flow, data[i].role);
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device,
&device_name)));
EXPECT_FALSE(device_name.device_name.empty());
EXPECT_FALSE(device_name.unique_id.empty());
}
}
TEST_F(CoreAudioUtilWinTest, GetFriendlyName) {
if (!CanRunAudioTest())
return;
// Get name and ID of default device used for recording.
ScopedComPtr<IMMDevice> audio_device =
CoreAudioUtil::CreateDefaultDevice(eCapture, eConsole);
AudioDeviceName device_name;
HRESULT hr = CoreAudioUtil::GetDeviceName(audio_device, &device_name);
EXPECT_TRUE(SUCCEEDED(hr));
// Use unique ID as input to GetFriendlyName() and compare the result
// with the already obtained friendly name for the default capture device.
std::string friendly_name = CoreAudioUtil::GetFriendlyName(
device_name.unique_id);
EXPECT_EQ(friendly_name, device_name.device_name);
// Same test as above but for playback.
audio_device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole);
hr = CoreAudioUtil::GetDeviceName(audio_device, &device_name);
EXPECT_TRUE(SUCCEEDED(hr));
friendly_name = CoreAudioUtil::GetFriendlyName(device_name.unique_id);
EXPECT_EQ(friendly_name, device_name.device_name);
}
TEST_F(CoreAudioUtilWinTest, DeviceIsDefault) {
if (!CanRunAudioTest())
return;
// Verify that the default render device is correctly identified as a
// default device.
ScopedComPtr<IMMDevice> audio_device =
CoreAudioUtil::CreateDefaultDevice(eRender, eConsole);
AudioDeviceName name;
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, &name)));
const std::string id = name.unique_id;
EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eRender, eConsole, id));
EXPECT_FALSE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole, id));
}
TEST_F(CoreAudioUtilWinTest, CreateDefaultClient) {
if (!CanRunAudioTest())
return;
EDataFlow data[] = {eRender, eCapture};
for (int i = 0; i < arraysize(data); ++i) {
ScopedComPtr<IAudioClient> client;
client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole);
EXPECT_TRUE(client);
}
}
TEST_F(CoreAudioUtilWinTest, CreateClient) {
if (!CanRunAudioTest())
return;
EDataFlow data[] = {eRender, eCapture};
for (int i = 0; i < arraysize(data); ++i) {
ScopedComPtr<IMMDevice> device;
ScopedComPtr<IAudioClient> client;
device = CoreAudioUtil::CreateDefaultDevice(data[i], eConsole);
EXPECT_TRUE(device);
EXPECT_EQ(data[i], CoreAudioUtil::GetDataFlow(device));
client = CoreAudioUtil::CreateClient(device);
EXPECT_TRUE(client);
}
}
TEST_F(CoreAudioUtilWinTest, GetSharedModeMixFormat) {
if (!CanRunAudioTest())
return;
ScopedComPtr<IMMDevice> device;
ScopedComPtr<IAudioClient> client;
device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole);
EXPECT_TRUE(device);
client = CoreAudioUtil::CreateClient(device);
EXPECT_TRUE(client);
// Perform a simple sanity test of the aquired format structure.
WAVEFORMATPCMEX format;
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
&format)));
EXPECT_GE(format.Format.nChannels, 1);
EXPECT_GE(format.Format.nSamplesPerSec, 8000u);
EXPECT_GE(format.Format.wBitsPerSample, 16);
EXPECT_GE(format.Samples.wValidBitsPerSample, 16);
EXPECT_EQ(format.Format.wFormatTag, WAVE_FORMAT_EXTENSIBLE);
}
TEST_F(CoreAudioUtilWinTest, GetDevicePeriod) {
if (!CanRunAudioTest())
return;
EDataFlow data[] = {eRender, eCapture};
// Verify that the device periods are valid for the default render and
// capture devices.
for (int i = 0; i < arraysize(data); ++i) {
ScopedComPtr<IAudioClient> client;
REFERENCE_TIME shared_time_period = 0;
REFERENCE_TIME exclusive_time_period = 0;
client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole);
EXPECT_TRUE(client);
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod(
client, AUDCLNT_SHAREMODE_SHARED, &shared_time_period)));
EXPECT_GT(shared_time_period, 0);
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod(
client, AUDCLNT_SHAREMODE_EXCLUSIVE, &exclusive_time_period)));
EXPECT_GT(exclusive_time_period, 0);
EXPECT_LE(exclusive_time_period, shared_time_period);
}
}
TEST_F(CoreAudioUtilWinTest, GetPreferredAudioParameters) {
if (!CanRunAudioTest())
return;
EDataFlow data[] = {eRender, eCapture};
// Verify that the preferred audio parameters are OK for the default render
// and capture devices.
for (int i = 0; i < arraysize(data); ++i) {
ScopedComPtr<IAudioClient> client;
AudioParameters params;
client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole);
EXPECT_TRUE(client);
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(client,
&params)));
EXPECT_TRUE(params.IsValid());
}
}
TEST_F(CoreAudioUtilWinTest, SharedModeInitialize) {
if (!CanRunAudioTest())
return;
ScopedComPtr<IAudioClient> client;
client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole);
EXPECT_TRUE(client);
WAVEFORMATPCMEX format;
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
&format)));
// Perform a shared-mode initialization without event-driven buffer handling.
size_t endpoint_buffer_size = 0;
HRESULT hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL,
&endpoint_buffer_size);
EXPECT_TRUE(SUCCEEDED(hr));
EXPECT_GT(endpoint_buffer_size, 0u);
// It is only possible to create a client once.
hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL,
&endpoint_buffer_size);
EXPECT_FALSE(SUCCEEDED(hr));
EXPECT_EQ(hr, AUDCLNT_E_ALREADY_INITIALIZED);
// Verify that it is possible to reinitialize the client after releasing it.
client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole);
EXPECT_TRUE(client);
hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL,
&endpoint_buffer_size);
EXPECT_TRUE(SUCCEEDED(hr));
EXPECT_GT(endpoint_buffer_size, 0u);
// Use a non-supported format and verify that initialization fails.
// A simple way to emulate an invalid format is to use the shared-mode
// mixing format and modify the preferred sample.
client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole);
EXPECT_TRUE(client);
format.Format.nSamplesPerSec = format.Format.nSamplesPerSec + 1;
EXPECT_FALSE(CoreAudioUtil::IsFormatSupported(
client, AUDCLNT_SHAREMODE_SHARED, &format));
hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL,
&endpoint_buffer_size);
EXPECT_TRUE(FAILED(hr));
EXPECT_EQ(hr, E_INVALIDARG);
// Finally, perform a shared-mode initialization using event-driven buffer
// handling. The event handle will be signaled when an audio buffer is ready
// to be processed by the client (not verified here).
// The event handle should be in the nonsignaled state.
base::win::ScopedHandle event_handle(::CreateEvent(NULL, TRUE, FALSE, NULL));
client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole);
EXPECT_TRUE(client);
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
&format)));
EXPECT_TRUE(CoreAudioUtil::IsFormatSupported(
client, AUDCLNT_SHAREMODE_SHARED, &format));
hr = CoreAudioUtil::SharedModeInitialize(client, &format, event_handle.Get(),
&endpoint_buffer_size);
EXPECT_TRUE(SUCCEEDED(hr));
EXPECT_GT(endpoint_buffer_size, 0u);
}
TEST_F(CoreAudioUtilWinTest, CreateRenderAndCaptureClients) {
if (!CanRunAudioTest())
return;
EDataFlow data[] = {eRender, eCapture};
WAVEFORMATPCMEX format;
size_t endpoint_buffer_size = 0;
for (int i = 0; i < arraysize(data); ++i) {
ScopedComPtr<IAudioClient> client;
ScopedComPtr<IAudioRenderClient> render_client;
ScopedComPtr<IAudioCaptureClient> capture_client;
client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole);
EXPECT_TRUE(client);
EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
&format)));
if (data[i] == eRender) {
// It is not possible to create a render client using an unitialized
// client interface.
render_client = CoreAudioUtil::CreateRenderClient(client);
EXPECT_FALSE(render_client);
// Do a proper initialization and verify that it works this time.
CoreAudioUtil::SharedModeInitialize(client, &format, NULL,
&endpoint_buffer_size);
render_client = CoreAudioUtil::CreateRenderClient(client);
EXPECT_TRUE(render_client);
EXPECT_GT(endpoint_buffer_size, 0u);
} else if (data[i] == eCapture) {
// It is not possible to create a capture client using an unitialized
// client interface.
capture_client = CoreAudioUtil::CreateCaptureClient(client);
EXPECT_FALSE(capture_client);
// Do a proper initialization and verify that it works this time.
CoreAudioUtil::SharedModeInitialize(client, &format, NULL,
&endpoint_buffer_size);
capture_client = CoreAudioUtil::CreateCaptureClient(client);
EXPECT_TRUE(capture_client);
EXPECT_GT(endpoint_buffer_size, 0u);
}
}
}
//
} // namespace media