core_audio_util_win_unittest.cc revision 3551c9c881056c480085172ff9840cab31610854
1// Copyright (c) 2012 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5#include "base/memory/scoped_ptr.h" 6#include "base/synchronization/waitable_event.h" 7#include "base/win/scoped_com_initializer.h" 8#include "base/win/scoped_handle.h" 9#include "media/audio/win/core_audio_util_win.h" 10#include "testing/gmock/include/gmock/gmock.h" 11#include "testing/gtest/include/gtest/gtest.h" 12 13using base::win::ScopedCOMInitializer; 14 15namespace media { 16 17class CoreAudioUtilWinTest : public ::testing::Test { 18 protected: 19 // The test runs on a COM thread in the multithreaded apartment (MTA). 20 // If we don't initialize the COM library on a thread before using COM, 21 // all function calls will return CO_E_NOTINITIALIZED. 22 CoreAudioUtilWinTest() 23 : com_init_(ScopedCOMInitializer::kMTA) { 24 DCHECK(com_init_.succeeded()); 25 } 26 virtual ~CoreAudioUtilWinTest() {} 27 28 bool CanRunAudioTest() { 29 bool core_audio = CoreAudioUtil::IsSupported(); 30 if (!core_audio) 31 return false; 32 int capture_devices = CoreAudioUtil::NumberOfActiveDevices(eCapture); 33 int render_devices = CoreAudioUtil::NumberOfActiveDevices(eRender); 34 return ((capture_devices > 0) && (render_devices > 0)); 35 } 36 37 ScopedCOMInitializer com_init_; 38}; 39 40TEST_F(CoreAudioUtilWinTest, NumberOfActiveDevices) { 41 if (!CanRunAudioTest()) 42 return; 43 44 int render_devices = CoreAudioUtil::NumberOfActiveDevices(eRender); 45 EXPECT_GT(render_devices, 0); 46 int capture_devices = CoreAudioUtil::NumberOfActiveDevices(eCapture); 47 EXPECT_GT(capture_devices, 0); 48 int total_devices = CoreAudioUtil::NumberOfActiveDevices(eAll); 49 EXPECT_EQ(total_devices, render_devices + capture_devices); 50} 51 52TEST_F(CoreAudioUtilWinTest, CreateDeviceEnumerator) { 53 if (!CanRunAudioTest()) 54 return; 55 56 ScopedComPtr<IMMDeviceEnumerator> enumerator = 57 CoreAudioUtil::CreateDeviceEnumerator(); 58 EXPECT_TRUE(enumerator); 59} 60 61TEST_F(CoreAudioUtilWinTest, CreateDefaultDevice) { 62 if (!CanRunAudioTest()) 63 return; 64 65 struct { 66 EDataFlow flow; 67 ERole role; 68 } data[] = { 69 {eRender, eConsole}, 70 {eRender, eCommunications}, 71 {eRender, eMultimedia}, 72 {eCapture, eConsole}, 73 {eCapture, eCommunications}, 74 {eCapture, eMultimedia} 75 }; 76 77 // Create default devices for all flow/role combinations above. 78 ScopedComPtr<IMMDevice> audio_device; 79 for (int i = 0; i < arraysize(data); ++i) { 80 audio_device = 81 CoreAudioUtil::CreateDefaultDevice(data[i].flow, data[i].role); 82 EXPECT_TRUE(audio_device); 83 EXPECT_EQ(data[i].flow, CoreAudioUtil::GetDataFlow(audio_device)); 84 } 85 86 // Only eRender and eCapture are allowed as flow parameter. 87 audio_device = CoreAudioUtil::CreateDefaultDevice(eAll, eConsole); 88 EXPECT_FALSE(audio_device); 89} 90 91TEST_F(CoreAudioUtilWinTest, CreateDevice) { 92 if (!CanRunAudioTest()) 93 return; 94 95 // Get name and ID of default device used for playback. 96 ScopedComPtr<IMMDevice> default_render_device = 97 CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); 98 AudioDeviceName default_render_name; 99 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(default_render_device, 100 &default_render_name))); 101 102 // Use the uniqe ID as input to CreateDevice() and create a corresponding 103 // IMMDevice. 104 ScopedComPtr<IMMDevice> audio_device = 105 CoreAudioUtil::CreateDevice(default_render_name.unique_id); 106 EXPECT_TRUE(audio_device); 107 108 // Verify that the two IMMDevice interfaces represents the same endpoint 109 // by comparing their unique IDs. 110 AudioDeviceName device_name; 111 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, 112 &device_name))); 113 EXPECT_EQ(default_render_name.unique_id, device_name.unique_id); 114} 115 116TEST_F(CoreAudioUtilWinTest, GetDefaultDeviceName) { 117 if (!CanRunAudioTest()) 118 return; 119 120 struct { 121 EDataFlow flow; 122 ERole role; 123 } data[] = { 124 {eRender, eConsole}, 125 {eRender, eCommunications}, 126 {eCapture, eConsole}, 127 {eCapture, eCommunications} 128 }; 129 130 // Get name and ID of default devices for all flow/role combinations above. 131 ScopedComPtr<IMMDevice> audio_device; 132 AudioDeviceName device_name; 133 for (int i = 0; i < arraysize(data); ++i) { 134 audio_device = 135 CoreAudioUtil::CreateDefaultDevice(data[i].flow, data[i].role); 136 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, 137 &device_name))); 138 EXPECT_FALSE(device_name.device_name.empty()); 139 EXPECT_FALSE(device_name.unique_id.empty()); 140 } 141} 142 143TEST_F(CoreAudioUtilWinTest, GetAudioControllerID) { 144 if (!CanRunAudioTest()) 145 return; 146 147 ScopedComPtr<IMMDeviceEnumerator> enumerator( 148 CoreAudioUtil::CreateDeviceEnumerator()); 149 ASSERT_TRUE(enumerator); 150 151 // Enumerate all active input and output devices and fetch the ID of 152 // the associated device. 153 EDataFlow flows[] = { eRender , eCapture }; 154 for (int i = 0; i < arraysize(flows); ++i) { 155 ScopedComPtr<IMMDeviceCollection> collection; 156 ASSERT_TRUE(SUCCEEDED(enumerator->EnumAudioEndpoints(flows[i], 157 DEVICE_STATE_ACTIVE, collection.Receive()))); 158 UINT count = 0; 159 collection->GetCount(&count); 160 for (UINT j = 0; j < count; ++j) { 161 ScopedComPtr<IMMDevice> device; 162 collection->Item(j, device.Receive()); 163 std::string controller_id(CoreAudioUtil::GetAudioControllerID( 164 device, enumerator)); 165 EXPECT_FALSE(controller_id.empty()); 166 } 167 } 168} 169 170TEST_F(CoreAudioUtilWinTest, GetFriendlyName) { 171 if (!CanRunAudioTest()) 172 return; 173 174 // Get name and ID of default device used for recording. 175 ScopedComPtr<IMMDevice> audio_device = 176 CoreAudioUtil::CreateDefaultDevice(eCapture, eConsole); 177 AudioDeviceName device_name; 178 HRESULT hr = CoreAudioUtil::GetDeviceName(audio_device, &device_name); 179 EXPECT_TRUE(SUCCEEDED(hr)); 180 181 // Use unique ID as input to GetFriendlyName() and compare the result 182 // with the already obtained friendly name for the default capture device. 183 std::string friendly_name = CoreAudioUtil::GetFriendlyName( 184 device_name.unique_id); 185 EXPECT_EQ(friendly_name, device_name.device_name); 186 187 // Same test as above but for playback. 188 audio_device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); 189 hr = CoreAudioUtil::GetDeviceName(audio_device, &device_name); 190 EXPECT_TRUE(SUCCEEDED(hr)); 191 friendly_name = CoreAudioUtil::GetFriendlyName(device_name.unique_id); 192 EXPECT_EQ(friendly_name, device_name.device_name); 193} 194 195TEST_F(CoreAudioUtilWinTest, DeviceIsDefault) { 196 if (!CanRunAudioTest()) 197 return; 198 199 // Verify that the default render device is correctly identified as a 200 // default device. 201 ScopedComPtr<IMMDevice> audio_device = 202 CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); 203 AudioDeviceName name; 204 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, &name))); 205 const std::string id = name.unique_id; 206 EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eRender, eConsole, id)); 207 EXPECT_FALSE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole, id)); 208} 209 210TEST_F(CoreAudioUtilWinTest, CreateDefaultClient) { 211 if (!CanRunAudioTest()) 212 return; 213 214 EDataFlow data[] = {eRender, eCapture}; 215 216 for (int i = 0; i < arraysize(data); ++i) { 217 ScopedComPtr<IAudioClient> client; 218 client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole); 219 EXPECT_TRUE(client); 220 } 221} 222 223TEST_F(CoreAudioUtilWinTest, CreateClient) { 224 if (!CanRunAudioTest()) 225 return; 226 227 EDataFlow data[] = {eRender, eCapture}; 228 229 for (int i = 0; i < arraysize(data); ++i) { 230 ScopedComPtr<IMMDevice> device; 231 ScopedComPtr<IAudioClient> client; 232 device = CoreAudioUtil::CreateDefaultDevice(data[i], eConsole); 233 EXPECT_TRUE(device); 234 EXPECT_EQ(data[i], CoreAudioUtil::GetDataFlow(device)); 235 client = CoreAudioUtil::CreateClient(device); 236 EXPECT_TRUE(client); 237 } 238} 239 240TEST_F(CoreAudioUtilWinTest, GetSharedModeMixFormat) { 241 if (!CanRunAudioTest()) 242 return; 243 244 ScopedComPtr<IMMDevice> device; 245 ScopedComPtr<IAudioClient> client; 246 device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); 247 EXPECT_TRUE(device); 248 client = CoreAudioUtil::CreateClient(device); 249 EXPECT_TRUE(client); 250 251 // Perform a simple sanity test of the aquired format structure. 252 WAVEFORMATPCMEX format; 253 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 254 &format))); 255 EXPECT_GE(format.Format.nChannels, 1); 256 EXPECT_GE(format.Format.nSamplesPerSec, 8000u); 257 EXPECT_GE(format.Format.wBitsPerSample, 16); 258 EXPECT_GE(format.Samples.wValidBitsPerSample, 16); 259 EXPECT_EQ(format.Format.wFormatTag, WAVE_FORMAT_EXTENSIBLE); 260} 261 262TEST_F(CoreAudioUtilWinTest, IsChannelLayoutSupported) { 263 if (!CanRunAudioTest()) 264 return; 265 266 // The preferred channel layout should always be supported. Being supported 267 // means that it is possible to initialize a shared mode stream with the 268 // particular channel layout. 269 AudioParameters mix_params; 270 HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters(eRender, eConsole, 271 &mix_params); 272 EXPECT_TRUE(SUCCEEDED(hr)); 273 EXPECT_TRUE(mix_params.IsValid()); 274 EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported( 275 eRender, eConsole, mix_params.channel_layout())); 276 277 // Check if it is possible to modify the channel layout to stereo for a 278 // device which reports that it prefers to be openen up in an other 279 // channel configuration. 280 if (mix_params.channel_layout() != CHANNEL_LAYOUT_STEREO) { 281 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 282 // TODO(henrika): it might be too pessimistic to assume false as return 283 // value here. 284 EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported( 285 eRender, eConsole, channel_layout)); 286 } 287} 288 289TEST_F(CoreAudioUtilWinTest, GetDevicePeriod) { 290 if (!CanRunAudioTest()) 291 return; 292 293 EDataFlow data[] = {eRender, eCapture}; 294 295 // Verify that the device periods are valid for the default render and 296 // capture devices. 297 for (int i = 0; i < arraysize(data); ++i) { 298 ScopedComPtr<IAudioClient> client; 299 REFERENCE_TIME shared_time_period = 0; 300 REFERENCE_TIME exclusive_time_period = 0; 301 client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole); 302 EXPECT_TRUE(client); 303 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod( 304 client, AUDCLNT_SHAREMODE_SHARED, &shared_time_period))); 305 EXPECT_GT(shared_time_period, 0); 306 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod( 307 client, AUDCLNT_SHAREMODE_EXCLUSIVE, &exclusive_time_period))); 308 EXPECT_GT(exclusive_time_period, 0); 309 EXPECT_LE(exclusive_time_period, shared_time_period); 310 } 311} 312 313TEST_F(CoreAudioUtilWinTest, GetPreferredAudioParameters) { 314 if (!CanRunAudioTest()) 315 return; 316 317 EDataFlow data[] = {eRender, eCapture}; 318 319 // Verify that the preferred audio parameters are OK for the default render 320 // and capture devices. 321 for (int i = 0; i < arraysize(data); ++i) { 322 ScopedComPtr<IAudioClient> client; 323 AudioParameters params; 324 client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole); 325 EXPECT_TRUE(client); 326 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(client, 327 ¶ms))); 328 EXPECT_TRUE(params.IsValid()); 329 } 330} 331 332TEST_F(CoreAudioUtilWinTest, SharedModeInitialize) { 333 if (!CanRunAudioTest()) 334 return; 335 336 ScopedComPtr<IAudioClient> client; 337 client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); 338 EXPECT_TRUE(client); 339 340 WAVEFORMATPCMEX format; 341 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 342 &format))); 343 344 // Perform a shared-mode initialization without event-driven buffer handling. 345 uint32 endpoint_buffer_size = 0; 346 HRESULT hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 347 &endpoint_buffer_size); 348 EXPECT_TRUE(SUCCEEDED(hr)); 349 EXPECT_GT(endpoint_buffer_size, 0u); 350 351 // It is only possible to create a client once. 352 hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 353 &endpoint_buffer_size); 354 EXPECT_FALSE(SUCCEEDED(hr)); 355 EXPECT_EQ(hr, AUDCLNT_E_ALREADY_INITIALIZED); 356 357 // Verify that it is possible to reinitialize the client after releasing it. 358 client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); 359 EXPECT_TRUE(client); 360 hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 361 &endpoint_buffer_size); 362 EXPECT_TRUE(SUCCEEDED(hr)); 363 EXPECT_GT(endpoint_buffer_size, 0u); 364 365 // Use a non-supported format and verify that initialization fails. 366 // A simple way to emulate an invalid format is to use the shared-mode 367 // mixing format and modify the preferred sample. 368 client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); 369 EXPECT_TRUE(client); 370 format.Format.nSamplesPerSec = format.Format.nSamplesPerSec + 1; 371 EXPECT_FALSE(CoreAudioUtil::IsFormatSupported( 372 client, AUDCLNT_SHAREMODE_SHARED, &format)); 373 hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 374 &endpoint_buffer_size); 375 EXPECT_TRUE(FAILED(hr)); 376 EXPECT_EQ(hr, E_INVALIDARG); 377 378 // Finally, perform a shared-mode initialization using event-driven buffer 379 // handling. The event handle will be signaled when an audio buffer is ready 380 // to be processed by the client (not verified here). 381 // The event handle should be in the nonsignaled state. 382 base::win::ScopedHandle event_handle(::CreateEvent(NULL, TRUE, FALSE, NULL)); 383 client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); 384 EXPECT_TRUE(client); 385 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 386 &format))); 387 EXPECT_TRUE(CoreAudioUtil::IsFormatSupported( 388 client, AUDCLNT_SHAREMODE_SHARED, &format)); 389 hr = CoreAudioUtil::SharedModeInitialize(client, &format, event_handle.Get(), 390 &endpoint_buffer_size); 391 EXPECT_TRUE(SUCCEEDED(hr)); 392 EXPECT_GT(endpoint_buffer_size, 0u); 393} 394 395TEST_F(CoreAudioUtilWinTest, CreateRenderAndCaptureClients) { 396 if (!CanRunAudioTest()) 397 return; 398 399 EDataFlow data[] = {eRender, eCapture}; 400 401 WAVEFORMATPCMEX format; 402 uint32 endpoint_buffer_size = 0; 403 404 for (int i = 0; i < arraysize(data); ++i) { 405 ScopedComPtr<IAudioClient> client; 406 ScopedComPtr<IAudioRenderClient> render_client; 407 ScopedComPtr<IAudioCaptureClient> capture_client; 408 409 client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole); 410 EXPECT_TRUE(client); 411 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 412 &format))); 413 if (data[i] == eRender) { 414 // It is not possible to create a render client using an unitialized 415 // client interface. 416 render_client = CoreAudioUtil::CreateRenderClient(client); 417 EXPECT_FALSE(render_client); 418 419 // Do a proper initialization and verify that it works this time. 420 CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 421 &endpoint_buffer_size); 422 render_client = CoreAudioUtil::CreateRenderClient(client); 423 EXPECT_TRUE(render_client); 424 EXPECT_GT(endpoint_buffer_size, 0u); 425 } else if (data[i] == eCapture) { 426 // It is not possible to create a capture client using an unitialized 427 // client interface. 428 capture_client = CoreAudioUtil::CreateCaptureClient(client); 429 EXPECT_FALSE(capture_client); 430 431 // Do a proper initialization and verify that it works this time. 432 CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 433 &endpoint_buffer_size); 434 capture_client = CoreAudioUtil::CreateCaptureClient(client); 435 EXPECT_TRUE(capture_client); 436 EXPECT_GT(endpoint_buffer_size, 0u); 437 } 438 } 439} 440 441TEST_F(CoreAudioUtilWinTest, FillRenderEndpointBufferWithSilence) { 442 if (!CanRunAudioTest()) 443 return; 444 445 // Create default clients using the default mixing format for shared mode. 446 ScopedComPtr<IAudioClient> client( 447 CoreAudioUtil::CreateDefaultClient(eRender, eConsole)); 448 EXPECT_TRUE(client); 449 450 WAVEFORMATPCMEX format; 451 uint32 endpoint_buffer_size = 0; 452 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 453 &format))); 454 CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 455 &endpoint_buffer_size); 456 EXPECT_GT(endpoint_buffer_size, 0u); 457 458 ScopedComPtr<IAudioRenderClient> render_client( 459 CoreAudioUtil::CreateRenderClient(client)); 460 EXPECT_TRUE(render_client); 461 462 // The endpoint audio buffer should not be filled up by default after being 463 // created. 464 UINT32 num_queued_frames = 0; 465 client->GetCurrentPadding(&num_queued_frames); 466 EXPECT_EQ(num_queued_frames, 0u); 467 468 // Fill it up with zeros and verify that the buffer is full. 469 // It is not possible to verify that the actual data consists of zeros 470 // since we can't access data that has already been sent to the endpoint 471 // buffer. 472 EXPECT_TRUE(CoreAudioUtil::FillRenderEndpointBufferWithSilence( 473 client, render_client)); 474 client->GetCurrentPadding(&num_queued_frames); 475 EXPECT_EQ(num_queued_frames, endpoint_buffer_size); 476} 477 478// 479 480} // namespace media 481