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.
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"
15 using base::win::ScopedCOMInitializer
;
19 class CoreAudioUtilWinTest
: public ::testing::Test
{
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());
28 virtual ~CoreAudioUtilWinTest() {}
30 bool CanRunAudioTest() {
31 bool core_audio
= CoreAudioUtil::IsSupported();
34 int capture_devices
= CoreAudioUtil::NumberOfActiveDevices(eCapture
);
35 int render_devices
= CoreAudioUtil::NumberOfActiveDevices(eRender
);
36 return ((capture_devices
> 0) && (render_devices
> 0));
39 ScopedCOMInitializer com_init_
;
42 TEST_F(CoreAudioUtilWinTest
, NumberOfActiveDevices
) {
43 if (!CanRunAudioTest())
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
);
54 TEST_F(CoreAudioUtilWinTest
, CreateDeviceEnumerator
) {
55 if (!CanRunAudioTest())
58 ScopedComPtr
<IMMDeviceEnumerator
> enumerator
=
59 CoreAudioUtil::CreateDeviceEnumerator();
60 EXPECT_TRUE(enumerator
);
63 TEST_F(CoreAudioUtilWinTest
, CreateDefaultDevice
) {
64 if (!CanRunAudioTest())
72 {eRender
, eCommunications
},
73 {eRender
, eMultimedia
},
75 {eCapture
, eCommunications
},
76 {eCapture
, eMultimedia
}
79 // Create default devices for all flow/role combinations above.
80 ScopedComPtr
<IMMDevice
> audio_device
;
81 for (int i
= 0; i
< arraysize(data
); ++i
) {
83 CoreAudioUtil::CreateDefaultDevice(data
[i
].flow
, data
[i
].role
);
84 EXPECT_TRUE(audio_device
);
85 EXPECT_EQ(data
[i
].flow
, CoreAudioUtil::GetDataFlow(audio_device
));
88 // Only eRender and eCapture are allowed as flow parameter.
89 audio_device
= CoreAudioUtil::CreateDefaultDevice(eAll
, eConsole
);
90 EXPECT_FALSE(audio_device
);
93 TEST_F(CoreAudioUtilWinTest
, CreateDevice
) {
94 if (!CanRunAudioTest())
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
)));
104 // Use the uniqe ID as input to CreateDevice() and create a corresponding
106 ScopedComPtr
<IMMDevice
> audio_device
=
107 CoreAudioUtil::CreateDevice(default_render_name
.unique_id
);
108 EXPECT_TRUE(audio_device
);
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
,
115 EXPECT_EQ(default_render_name
.unique_id
, device_name
.unique_id
);
118 TEST_F(CoreAudioUtilWinTest
, GetDefaultDeviceName
) {
119 if (!CanRunAudioTest())
127 {eRender
, eCommunications
},
128 {eCapture
, eConsole
},
129 {eCapture
, eCommunications
}
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
) {
137 CoreAudioUtil::CreateDefaultDevice(data
[i
].flow
, data
[i
].role
);
138 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device
,
140 EXPECT_FALSE(device_name
.device_name
.empty());
141 EXPECT_FALSE(device_name
.unique_id
.empty());
145 TEST_F(CoreAudioUtilWinTest
, GetAudioControllerID
) {
146 if (!CanRunAudioTest())
149 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(
150 CoreAudioUtil::CreateDeviceEnumerator());
151 ASSERT_TRUE(enumerator
);
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())));
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());
172 TEST_F(CoreAudioUtilWinTest
, GetFriendlyName
) {
173 if (!CanRunAudioTest())
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
));
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
);
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
);
197 TEST_F(CoreAudioUtilWinTest
, DeviceIsDefault
) {
198 if (!CanRunAudioTest())
201 // Verify that the default render device is correctly identified as a
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
));
212 TEST_F(CoreAudioUtilWinTest
, CreateDefaultClient
) {
213 if (!CanRunAudioTest())
216 EDataFlow data
[] = {eRender
, eCapture
};
218 for (int i
= 0; i
< arraysize(data
); ++i
) {
219 ScopedComPtr
<IAudioClient
> client
;
220 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
225 TEST_F(CoreAudioUtilWinTest
, CreateClient
) {
226 if (!CanRunAudioTest())
229 EDataFlow data
[] = {eRender
, eCapture
};
231 for (int i
= 0; i
< arraysize(data
); ++i
) {
232 ScopedComPtr
<IMMDevice
> device
;
233 ScopedComPtr
<IAudioClient
> client
;
234 device
= CoreAudioUtil::CreateDefaultDevice(data
[i
], eConsole
);
236 EXPECT_EQ(data
[i
], CoreAudioUtil::GetDataFlow(device
));
237 client
= CoreAudioUtil::CreateClient(device
);
242 TEST_F(CoreAudioUtilWinTest
, GetSharedModeMixFormat
) {
243 if (!CanRunAudioTest())
246 ScopedComPtr
<IMMDevice
> device
;
247 ScopedComPtr
<IAudioClient
> client
;
248 device
= CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
250 client
= CoreAudioUtil::CreateClient(device
);
253 // Perform a simple sanity test of the aquired format structure.
254 WAVEFORMATPCMEX format
;
255 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
,
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
);
264 TEST_F(CoreAudioUtilWinTest
, IsChannelLayoutSupported
) {
265 if (!CanRunAudioTest())
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
,
274 EXPECT_TRUE(SUCCEEDED(hr
));
275 EXPECT_TRUE(mix_params
.IsValid());
276 EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported(
277 eRender
, eConsole
, mix_params
.channel_layout()));
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
286 EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported(
287 eRender
, eConsole
, channel_layout
));
291 TEST_F(CoreAudioUtilWinTest
, GetDevicePeriod
) {
292 if (!CanRunAudioTest())
295 EDataFlow data
[] = {eRender
, eCapture
};
297 // Verify that the device periods are valid for the default render and
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
);
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
);
315 TEST_F(CoreAudioUtilWinTest
, GetPreferredAudioParameters
) {
316 if (!CanRunAudioTest())
319 EDataFlow data
[] = {eRender
, eCapture
};
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
);
328 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(client
,
330 EXPECT_TRUE(params
.IsValid());
334 TEST_F(CoreAudioUtilWinTest
, SharedModeInitialize
) {
335 if (!CanRunAudioTest())
338 ScopedComPtr
<IAudioClient
> client
;
339 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
342 WAVEFORMATPCMEX format
;
343 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
,
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
);
350 EXPECT_TRUE(SUCCEEDED(hr
));
351 EXPECT_GT(endpoint_buffer_size
, 0u);
353 // It is only possible to create a client once.
354 hr
= CoreAudioUtil::SharedModeInitialize(client
, &format
, NULL
,
355 &endpoint_buffer_size
);
356 EXPECT_FALSE(SUCCEEDED(hr
));
357 EXPECT_EQ(hr
, AUDCLNT_E_ALREADY_INITIALIZED
);
359 // Verify that it is possible to reinitialize the client after releasing it.
360 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
362 hr
= CoreAudioUtil::SharedModeInitialize(client
, &format
, NULL
,
363 &endpoint_buffer_size
);
364 EXPECT_TRUE(SUCCEEDED(hr
));
365 EXPECT_GT(endpoint_buffer_size
, 0u);
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
);
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
);
377 EXPECT_TRUE(FAILED(hr
));
378 EXPECT_EQ(hr
, E_INVALIDARG
);
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
);
387 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
,
389 EXPECT_TRUE(CoreAudioUtil::IsFormatSupported(
390 client
, AUDCLNT_SHAREMODE_SHARED
, &format
));
391 hr
= CoreAudioUtil::SharedModeInitialize(client
, &format
, event_handle
.Get(),
392 &endpoint_buffer_size
);
393 EXPECT_TRUE(SUCCEEDED(hr
));
394 EXPECT_GT(endpoint_buffer_size
, 0u);
397 TEST_F(CoreAudioUtilWinTest
, CreateRenderAndCaptureClients
) {
398 if (!CanRunAudioTest())
401 EDataFlow data
[] = {eRender
, eCapture
};
403 WAVEFORMATPCMEX format
;
404 uint32 endpoint_buffer_size
= 0;
406 for (int i
= 0; i
< arraysize(data
); ++i
) {
407 ScopedComPtr
<IAudioClient
> client
;
408 ScopedComPtr
<IAudioRenderClient
> render_client
;
409 ScopedComPtr
<IAudioCaptureClient
> capture_client
;
411 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
413 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
,
415 if (data
[i
] == eRender
) {
416 // It is not possible to create a render client using an unitialized
418 render_client
= CoreAudioUtil::CreateRenderClient(client
);
419 EXPECT_FALSE(render_client
);
421 // Do a proper initialization and verify that it works this time.
422 CoreAudioUtil::SharedModeInitialize(client
, &format
, NULL
,
423 &endpoint_buffer_size
);
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
430 capture_client
= CoreAudioUtil::CreateCaptureClient(client
);
431 EXPECT_FALSE(capture_client
);
433 // Do a proper initialization and verify that it works this time.
434 CoreAudioUtil::SharedModeInitialize(client
, &format
, NULL
,
435 &endpoint_buffer_size
);
436 capture_client
= CoreAudioUtil::CreateCaptureClient(client
);
437 EXPECT_TRUE(capture_client
);
438 EXPECT_GT(endpoint_buffer_size
, 0u);
443 TEST_F(CoreAudioUtilWinTest
, FillRenderEndpointBufferWithSilence
) {
444 if (!CanRunAudioTest())
447 // Create default clients using the default mixing format for shared mode.
448 ScopedComPtr
<IAudioClient
> client(
449 CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
));
452 WAVEFORMATPCMEX format
;
453 uint32 endpoint_buffer_size
= 0;
454 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
,
456 CoreAudioUtil::SharedModeInitialize(client
, &format
, NULL
,
457 &endpoint_buffer_size
);
458 EXPECT_GT(endpoint_buffer_size
, 0u);
460 ScopedComPtr
<IAudioRenderClient
> render_client(
461 CoreAudioUtil::CreateRenderClient(client
));
462 EXPECT_TRUE(render_client
);
464 // The endpoint audio buffer should not be filled up by default after being
466 UINT32 num_queued_frames
= 0;
467 client
->GetCurrentPadding(&num_queued_frames
);
468 EXPECT_EQ(num_queued_frames
, 0u);
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
474 EXPECT_TRUE(CoreAudioUtil::FillRenderEndpointBufferWithSilence(
475 client
, render_client
));
476 client
->GetCurrentPadding(&num_queued_frames
);
477 EXPECT_EQ(num_queued_frames
, endpoint_buffer_size
);
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.
484 TEST_F(CoreAudioUtilWinTest
, GetMatchingOutputDeviceID
) {
485 if (!CanRunAudioTest())
488 bool found_a_pair
= false;
490 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(
491 CoreAudioUtil::CreateDeviceEnumerator());
492 ASSERT_TRUE(enumerator
);
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())));
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
);
507 WideToUTF8(wide_id
, wcslen(wide_id
), &id
);
508 found_a_pair
= !CoreAudioUtil::GetMatchingOutputDeviceID(id
).empty();
511 EXPECT_TRUE(found_a_pair
);