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 13 using base::win::ScopedCOMInitializer; 14 15 namespace media { 16 17 class 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 40 TEST_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 52 TEST_F(CoreAudioUtilWinTest, CreateDeviceEnumerator) { 53 if (!CanRunAudioTest()) 54 return; 55 56 ScopedComPtr<IMMDeviceEnumerator> enumerator = 57 CoreAudioUtil::CreateDeviceEnumerator(); 58 EXPECT_TRUE(enumerator); 59 } 60 61 TEST_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 91 TEST_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 116 TEST_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 143 TEST_F(CoreAudioUtilWinTest, GetFriendlyName) { 144 if (!CanRunAudioTest()) 145 return; 146 147 // Get name and ID of default device used for recording. 148 ScopedComPtr<IMMDevice> audio_device = 149 CoreAudioUtil::CreateDefaultDevice(eCapture, eConsole); 150 AudioDeviceName device_name; 151 HRESULT hr = CoreAudioUtil::GetDeviceName(audio_device, &device_name); 152 EXPECT_TRUE(SUCCEEDED(hr)); 153 154 // Use unique ID as input to GetFriendlyName() and compare the result 155 // with the already obtained friendly name for the default capture device. 156 std::string friendly_name = CoreAudioUtil::GetFriendlyName( 157 device_name.unique_id); 158 EXPECT_EQ(friendly_name, device_name.device_name); 159 160 // Same test as above but for playback. 161 audio_device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); 162 hr = CoreAudioUtil::GetDeviceName(audio_device, &device_name); 163 EXPECT_TRUE(SUCCEEDED(hr)); 164 friendly_name = CoreAudioUtil::GetFriendlyName(device_name.unique_id); 165 EXPECT_EQ(friendly_name, device_name.device_name); 166 } 167 168 TEST_F(CoreAudioUtilWinTest, DeviceIsDefault) { 169 if (!CanRunAudioTest()) 170 return; 171 172 // Verify that the default render device is correctly identified as a 173 // default device. 174 ScopedComPtr<IMMDevice> audio_device = 175 CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); 176 AudioDeviceName name; 177 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device, &name))); 178 const std::string id = name.unique_id; 179 EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eRender, eConsole, id)); 180 EXPECT_FALSE(CoreAudioUtil::DeviceIsDefault(eCapture, eConsole, id)); 181 } 182 183 TEST_F(CoreAudioUtilWinTest, CreateDefaultClient) { 184 if (!CanRunAudioTest()) 185 return; 186 187 EDataFlow data[] = {eRender, eCapture}; 188 189 for (int i = 0; i < arraysize(data); ++i) { 190 ScopedComPtr<IAudioClient> client; 191 client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole); 192 EXPECT_TRUE(client); 193 } 194 } 195 196 TEST_F(CoreAudioUtilWinTest, CreateClient) { 197 if (!CanRunAudioTest()) 198 return; 199 200 EDataFlow data[] = {eRender, eCapture}; 201 202 for (int i = 0; i < arraysize(data); ++i) { 203 ScopedComPtr<IMMDevice> device; 204 ScopedComPtr<IAudioClient> client; 205 device = CoreAudioUtil::CreateDefaultDevice(data[i], eConsole); 206 EXPECT_TRUE(device); 207 EXPECT_EQ(data[i], CoreAudioUtil::GetDataFlow(device)); 208 client = CoreAudioUtil::CreateClient(device); 209 EXPECT_TRUE(client); 210 } 211 } 212 213 TEST_F(CoreAudioUtilWinTest, GetSharedModeMixFormat) { 214 if (!CanRunAudioTest()) 215 return; 216 217 ScopedComPtr<IMMDevice> device; 218 ScopedComPtr<IAudioClient> client; 219 device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole); 220 EXPECT_TRUE(device); 221 client = CoreAudioUtil::CreateClient(device); 222 EXPECT_TRUE(client); 223 224 // Perform a simple sanity test of the aquired format structure. 225 WAVEFORMATPCMEX format; 226 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 227 &format))); 228 EXPECT_GE(format.Format.nChannels, 1); 229 EXPECT_GE(format.Format.nSamplesPerSec, 8000u); 230 EXPECT_GE(format.Format.wBitsPerSample, 16); 231 EXPECT_GE(format.Samples.wValidBitsPerSample, 16); 232 EXPECT_EQ(format.Format.wFormatTag, WAVE_FORMAT_EXTENSIBLE); 233 } 234 235 TEST_F(CoreAudioUtilWinTest, IsChannelLayoutSupported) { 236 if (!CanRunAudioTest()) 237 return; 238 239 // The preferred channel layout should always be supported. Being supported 240 // means that it is possible to initialize a shared mode stream with the 241 // particular channel layout. 242 AudioParameters mix_params; 243 HRESULT hr = CoreAudioUtil::GetPreferredAudioParameters(eRender, eConsole, 244 &mix_params); 245 EXPECT_TRUE(SUCCEEDED(hr)); 246 EXPECT_TRUE(mix_params.IsValid()); 247 EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported( 248 eRender, eConsole, mix_params.channel_layout())); 249 250 // Check if it is possible to modify the channel layout to stereo for a 251 // device which reports that it prefers to be openen up in an other 252 // channel configuration. 253 if (mix_params.channel_layout() != CHANNEL_LAYOUT_STEREO) { 254 ChannelLayout channel_layout = CHANNEL_LAYOUT_STEREO; 255 // TODO(henrika): it might be too pessimistic to assume false as return 256 // value here. 257 EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported( 258 eRender, eConsole, channel_layout)); 259 } 260 } 261 262 TEST_F(CoreAudioUtilWinTest, GetDevicePeriod) { 263 if (!CanRunAudioTest()) 264 return; 265 266 EDataFlow data[] = {eRender, eCapture}; 267 268 // Verify that the device periods are valid for the default render and 269 // capture devices. 270 for (int i = 0; i < arraysize(data); ++i) { 271 ScopedComPtr<IAudioClient> client; 272 REFERENCE_TIME shared_time_period = 0; 273 REFERENCE_TIME exclusive_time_period = 0; 274 client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole); 275 EXPECT_TRUE(client); 276 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod( 277 client, AUDCLNT_SHAREMODE_SHARED, &shared_time_period))); 278 EXPECT_GT(shared_time_period, 0); 279 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod( 280 client, AUDCLNT_SHAREMODE_EXCLUSIVE, &exclusive_time_period))); 281 EXPECT_GT(exclusive_time_period, 0); 282 EXPECT_LE(exclusive_time_period, shared_time_period); 283 } 284 } 285 286 TEST_F(CoreAudioUtilWinTest, GetPreferredAudioParameters) { 287 if (!CanRunAudioTest()) 288 return; 289 290 EDataFlow data[] = {eRender, eCapture}; 291 292 // Verify that the preferred audio parameters are OK for the default render 293 // and capture devices. 294 for (int i = 0; i < arraysize(data); ++i) { 295 ScopedComPtr<IAudioClient> client; 296 AudioParameters params; 297 client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole); 298 EXPECT_TRUE(client); 299 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(client, 300 ¶ms))); 301 EXPECT_TRUE(params.IsValid()); 302 } 303 } 304 305 TEST_F(CoreAudioUtilWinTest, SharedModeInitialize) { 306 if (!CanRunAudioTest()) 307 return; 308 309 ScopedComPtr<IAudioClient> client; 310 client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); 311 EXPECT_TRUE(client); 312 313 WAVEFORMATPCMEX format; 314 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 315 &format))); 316 317 // Perform a shared-mode initialization without event-driven buffer handling. 318 uint32 endpoint_buffer_size = 0; 319 HRESULT hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 320 &endpoint_buffer_size); 321 EXPECT_TRUE(SUCCEEDED(hr)); 322 EXPECT_GT(endpoint_buffer_size, 0u); 323 324 // It is only possible to create a client once. 325 hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 326 &endpoint_buffer_size); 327 EXPECT_FALSE(SUCCEEDED(hr)); 328 EXPECT_EQ(hr, AUDCLNT_E_ALREADY_INITIALIZED); 329 330 // Verify that it is possible to reinitialize the client after releasing it. 331 client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); 332 EXPECT_TRUE(client); 333 hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 334 &endpoint_buffer_size); 335 EXPECT_TRUE(SUCCEEDED(hr)); 336 EXPECT_GT(endpoint_buffer_size, 0u); 337 338 // Use a non-supported format and verify that initialization fails. 339 // A simple way to emulate an invalid format is to use the shared-mode 340 // mixing format and modify the preferred sample. 341 client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); 342 EXPECT_TRUE(client); 343 format.Format.nSamplesPerSec = format.Format.nSamplesPerSec + 1; 344 EXPECT_FALSE(CoreAudioUtil::IsFormatSupported( 345 client, AUDCLNT_SHAREMODE_SHARED, &format)); 346 hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 347 &endpoint_buffer_size); 348 EXPECT_TRUE(FAILED(hr)); 349 EXPECT_EQ(hr, E_INVALIDARG); 350 351 // Finally, perform a shared-mode initialization using event-driven buffer 352 // handling. The event handle will be signaled when an audio buffer is ready 353 // to be processed by the client (not verified here). 354 // The event handle should be in the nonsignaled state. 355 base::win::ScopedHandle event_handle(::CreateEvent(NULL, TRUE, FALSE, NULL)); 356 client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole); 357 EXPECT_TRUE(client); 358 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 359 &format))); 360 EXPECT_TRUE(CoreAudioUtil::IsFormatSupported( 361 client, AUDCLNT_SHAREMODE_SHARED, &format)); 362 hr = CoreAudioUtil::SharedModeInitialize(client, &format, event_handle.Get(), 363 &endpoint_buffer_size); 364 EXPECT_TRUE(SUCCEEDED(hr)); 365 EXPECT_GT(endpoint_buffer_size, 0u); 366 } 367 368 TEST_F(CoreAudioUtilWinTest, CreateRenderAndCaptureClients) { 369 if (!CanRunAudioTest()) 370 return; 371 372 EDataFlow data[] = {eRender, eCapture}; 373 374 WAVEFORMATPCMEX format; 375 uint32 endpoint_buffer_size = 0; 376 377 for (int i = 0; i < arraysize(data); ++i) { 378 ScopedComPtr<IAudioClient> client; 379 ScopedComPtr<IAudioRenderClient> render_client; 380 ScopedComPtr<IAudioCaptureClient> capture_client; 381 382 client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole); 383 EXPECT_TRUE(client); 384 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 385 &format))); 386 if (data[i] == eRender) { 387 // It is not possible to create a render client using an unitialized 388 // client interface. 389 render_client = CoreAudioUtil::CreateRenderClient(client); 390 EXPECT_FALSE(render_client); 391 392 // Do a proper initialization and verify that it works this time. 393 CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 394 &endpoint_buffer_size); 395 render_client = CoreAudioUtil::CreateRenderClient(client); 396 EXPECT_TRUE(render_client); 397 EXPECT_GT(endpoint_buffer_size, 0u); 398 } else if (data[i] == eCapture) { 399 // It is not possible to create a capture client using an unitialized 400 // client interface. 401 capture_client = CoreAudioUtil::CreateCaptureClient(client); 402 EXPECT_FALSE(capture_client); 403 404 // Do a proper initialization and verify that it works this time. 405 CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 406 &endpoint_buffer_size); 407 capture_client = CoreAudioUtil::CreateCaptureClient(client); 408 EXPECT_TRUE(capture_client); 409 EXPECT_GT(endpoint_buffer_size, 0u); 410 } 411 } 412 } 413 414 TEST_F(CoreAudioUtilWinTest, FillRenderEndpointBufferWithSilence) { 415 if (!CanRunAudioTest()) 416 return; 417 418 // Create default clients using the default mixing format for shared mode. 419 ScopedComPtr<IAudioClient> client( 420 CoreAudioUtil::CreateDefaultClient(eRender, eConsole)); 421 EXPECT_TRUE(client); 422 423 WAVEFORMATPCMEX format; 424 uint32 endpoint_buffer_size = 0; 425 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client, 426 &format))); 427 CoreAudioUtil::SharedModeInitialize(client, &format, NULL, 428 &endpoint_buffer_size); 429 EXPECT_GT(endpoint_buffer_size, 0u); 430 431 ScopedComPtr<IAudioRenderClient> render_client( 432 CoreAudioUtil::CreateRenderClient(client)); 433 EXPECT_TRUE(render_client); 434 435 // The endpoint audio buffer should not be filled up by default after being 436 // created. 437 UINT32 num_queued_frames = 0; 438 client->GetCurrentPadding(&num_queued_frames); 439 EXPECT_EQ(num_queued_frames, 0u); 440 441 // Fill it up with zeros and verify that the buffer is full. 442 // It is not possible to verify that the actual data consists of zeros 443 // since we can't access data that has already been sent to the endpoint 444 // buffer. 445 EXPECT_TRUE(CoreAudioUtil::FillRenderEndpointBufferWithSilence( 446 client, render_client)); 447 client->GetCurrentPadding(&num_queued_frames); 448 EXPECT_EQ(num_queued_frames, endpoint_buffer_size); 449 } 450 451 // 452 453 } // namespace media 454