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