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
.get());
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
.get());
85 EXPECT_EQ(data
[i
].flow
, CoreAudioUtil::GetDataFlow(audio_device
.get()));
88 // Only eRender and eCapture are allowed as flow parameter.
89 audio_device
= CoreAudioUtil::CreateDefaultDevice(eAll
, eConsole
);
90 EXPECT_FALSE(audio_device
.get());
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(
102 default_render_device
.get(), &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
.get());
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(
114 CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
)));
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(
139 CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
)));
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
.get());
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(
166 CoreAudioUtil::GetAudioControllerID(device
.get(), enumerator
.get()));
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
.get(), &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
.get(), &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
;
207 SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device
.get(), &name
)));
208 const std::string id
= name
.unique_id
;
209 EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eRender
, eConsole
, id
));
210 EXPECT_FALSE(CoreAudioUtil::DeviceIsDefault(eCapture
, eConsole
, id
));
213 TEST_F(CoreAudioUtilWinTest
, CreateDefaultClient
) {
214 if (!CanRunAudioTest())
217 EDataFlow data
[] = {eRender
, eCapture
};
219 for (int i
= 0; i
< arraysize(data
); ++i
) {
220 ScopedComPtr
<IAudioClient
> client
;
221 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
222 EXPECT_TRUE(client
.get());
226 TEST_F(CoreAudioUtilWinTest
, CreateClient
) {
227 if (!CanRunAudioTest())
230 EDataFlow data
[] = {eRender
, eCapture
};
232 for (int i
= 0; i
< arraysize(data
); ++i
) {
233 ScopedComPtr
<IMMDevice
> device
;
234 ScopedComPtr
<IAudioClient
> client
;
235 device
= CoreAudioUtil::CreateDefaultDevice(data
[i
], eConsole
);
236 EXPECT_TRUE(device
.get());
237 EXPECT_EQ(data
[i
], CoreAudioUtil::GetDataFlow(device
.get()));
238 client
= CoreAudioUtil::CreateClient(device
.get());
239 EXPECT_TRUE(client
.get());
243 TEST_F(CoreAudioUtilWinTest
, GetSharedModeMixFormat
) {
244 if (!CanRunAudioTest())
247 ScopedComPtr
<IMMDevice
> device
;
248 ScopedComPtr
<IAudioClient
> client
;
249 device
= CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
250 EXPECT_TRUE(device
.get());
251 client
= CoreAudioUtil::CreateClient(device
.get());
252 EXPECT_TRUE(client
.get());
254 // Perform a simple sanity test of the aquired format structure.
255 WAVEFORMATPCMEX format
;
257 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
258 EXPECT_GE(format
.Format
.nChannels
, 1);
259 EXPECT_GE(format
.Format
.nSamplesPerSec
, 8000u);
260 EXPECT_GE(format
.Format
.wBitsPerSample
, 16);
261 EXPECT_GE(format
.Samples
.wValidBitsPerSample
, 16);
262 EXPECT_EQ(format
.Format
.wFormatTag
, WAVE_FORMAT_EXTENSIBLE
);
265 TEST_F(CoreAudioUtilWinTest
, IsChannelLayoutSupported
) {
266 if (!CanRunAudioTest())
269 // The preferred channel layout should always be supported. Being supported
270 // means that it is possible to initialize a shared mode stream with the
271 // particular channel layout.
272 AudioParameters mix_params
;
273 HRESULT hr
= CoreAudioUtil::GetPreferredAudioParameters(eRender
, eConsole
,
275 EXPECT_TRUE(SUCCEEDED(hr
));
276 EXPECT_TRUE(mix_params
.IsValid());
277 EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported(
278 std::string(), eRender
, eConsole
, mix_params
.channel_layout()));
280 // Check if it is possible to modify the channel layout to stereo for a
281 // device which reports that it prefers to be openen up in an other
282 // channel configuration.
283 if (mix_params
.channel_layout() != CHANNEL_LAYOUT_STEREO
) {
284 ChannelLayout channel_layout
= CHANNEL_LAYOUT_STEREO
;
285 // TODO(henrika): it might be too pessimistic to assume false as return
287 EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported(
288 std::string(), eRender
, eConsole
, channel_layout
));
292 TEST_F(CoreAudioUtilWinTest
, GetDevicePeriod
) {
293 if (!CanRunAudioTest())
296 EDataFlow data
[] = {eRender
, eCapture
};
298 // Verify that the device periods are valid for the default render and
300 for (int i
= 0; i
< arraysize(data
); ++i
) {
301 ScopedComPtr
<IAudioClient
> client
;
302 REFERENCE_TIME shared_time_period
= 0;
303 REFERENCE_TIME exclusive_time_period
= 0;
304 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
305 EXPECT_TRUE(client
.get());
306 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod(
307 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &shared_time_period
)));
308 EXPECT_GT(shared_time_period
, 0);
309 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod(
310 client
.get(), AUDCLNT_SHAREMODE_EXCLUSIVE
, &exclusive_time_period
)));
311 EXPECT_GT(exclusive_time_period
, 0);
312 EXPECT_LE(exclusive_time_period
, shared_time_period
);
316 TEST_F(CoreAudioUtilWinTest
, GetPreferredAudioParameters
) {
317 if (!CanRunAudioTest())
320 EDataFlow data
[] = {eRender
, eCapture
};
322 // Verify that the preferred audio parameters are OK for the default render
323 // and capture devices.
324 for (int i
= 0; i
< arraysize(data
); ++i
) {
325 ScopedComPtr
<IAudioClient
> client
;
326 AudioParameters params
;
327 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
328 EXPECT_TRUE(client
.get());
329 EXPECT_TRUE(SUCCEEDED(
330 CoreAudioUtil::GetPreferredAudioParameters(client
.get(), ¶ms
)));
331 EXPECT_TRUE(params
.IsValid());
335 TEST_F(CoreAudioUtilWinTest
, SharedModeInitialize
) {
336 if (!CanRunAudioTest())
339 ScopedComPtr
<IAudioClient
> client
;
340 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
341 EXPECT_TRUE(client
.get());
343 WAVEFORMATPCMEX format
;
345 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
347 // Perform a shared-mode initialization without event-driven buffer handling.
348 uint32 endpoint_buffer_size
= 0;
349 HRESULT hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
350 &endpoint_buffer_size
, NULL
);
351 EXPECT_TRUE(SUCCEEDED(hr
));
352 EXPECT_GT(endpoint_buffer_size
, 0u);
354 // It is only possible to create a client once.
355 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
356 &endpoint_buffer_size
, NULL
);
357 EXPECT_FALSE(SUCCEEDED(hr
));
358 EXPECT_EQ(hr
, AUDCLNT_E_ALREADY_INITIALIZED
);
360 // Verify that it is possible to reinitialize the client after releasing it.
361 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
362 EXPECT_TRUE(client
.get());
363 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
364 &endpoint_buffer_size
, NULL
);
365 EXPECT_TRUE(SUCCEEDED(hr
));
366 EXPECT_GT(endpoint_buffer_size
, 0u);
368 // Use a non-supported format and verify that initialization fails.
369 // A simple way to emulate an invalid format is to use the shared-mode
370 // mixing format and modify the preferred sample.
371 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
372 EXPECT_TRUE(client
.get());
373 format
.Format
.nSamplesPerSec
= format
.Format
.nSamplesPerSec
+ 1;
374 EXPECT_FALSE(CoreAudioUtil::IsFormatSupported(
375 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &format
));
376 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
377 &endpoint_buffer_size
, NULL
);
378 EXPECT_TRUE(FAILED(hr
));
379 EXPECT_EQ(hr
, E_INVALIDARG
);
381 // Finally, perform a shared-mode initialization using event-driven buffer
382 // handling. The event handle will be signaled when an audio buffer is ready
383 // to be processed by the client (not verified here).
384 // The event handle should be in the nonsignaled state.
385 base::win::ScopedHandle
event_handle(::CreateEvent(NULL
, TRUE
, FALSE
, NULL
));
386 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
387 EXPECT_TRUE(client
.get());
389 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
390 EXPECT_TRUE(CoreAudioUtil::IsFormatSupported(
391 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &format
));
392 hr
= CoreAudioUtil::SharedModeInitialize(
393 client
.get(), &format
, event_handle
.Get(), &endpoint_buffer_size
, NULL
);
394 EXPECT_TRUE(SUCCEEDED(hr
));
395 EXPECT_GT(endpoint_buffer_size
, 0u);
398 TEST_F(CoreAudioUtilWinTest
, CreateRenderAndCaptureClients
) {
399 if (!CanRunAudioTest())
402 EDataFlow data
[] = {eRender
, eCapture
};
404 WAVEFORMATPCMEX format
;
405 uint32 endpoint_buffer_size
= 0;
407 for (int i
= 0; i
< arraysize(data
); ++i
) {
408 ScopedComPtr
<IAudioClient
> client
;
409 ScopedComPtr
<IAudioRenderClient
> render_client
;
410 ScopedComPtr
<IAudioCaptureClient
> capture_client
;
412 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
413 EXPECT_TRUE(client
.get());
414 EXPECT_TRUE(SUCCEEDED(
415 CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
416 if (data
[i
] == eRender
) {
417 // It is not possible to create a render client using an unitialized
419 render_client
= CoreAudioUtil::CreateRenderClient(client
.get());
420 EXPECT_FALSE(render_client
.get());
422 // Do a proper initialization and verify that it works this time.
423 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
424 &endpoint_buffer_size
, NULL
);
425 render_client
= CoreAudioUtil::CreateRenderClient(client
.get());
426 EXPECT_TRUE(render_client
.get());
427 EXPECT_GT(endpoint_buffer_size
, 0u);
428 } else if (data
[i
] == eCapture
) {
429 // It is not possible to create a capture client using an unitialized
431 capture_client
= CoreAudioUtil::CreateCaptureClient(client
.get());
432 EXPECT_FALSE(capture_client
.get());
434 // Do a proper initialization and verify that it works this time.
435 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
436 &endpoint_buffer_size
, NULL
);
437 capture_client
= CoreAudioUtil::CreateCaptureClient(client
.get());
438 EXPECT_TRUE(capture_client
.get());
439 EXPECT_GT(endpoint_buffer_size
, 0u);
444 TEST_F(CoreAudioUtilWinTest
, FillRenderEndpointBufferWithSilence
) {
445 if (!CanRunAudioTest())
448 // Create default clients using the default mixing format for shared mode.
449 ScopedComPtr
<IAudioClient
> client(
450 CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
));
451 EXPECT_TRUE(client
.get());
453 WAVEFORMATPCMEX format
;
454 uint32 endpoint_buffer_size
= 0;
456 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
457 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
458 &endpoint_buffer_size
, NULL
);
459 EXPECT_GT(endpoint_buffer_size
, 0u);
461 ScopedComPtr
<IAudioRenderClient
> render_client(
462 CoreAudioUtil::CreateRenderClient(client
.get()));
463 EXPECT_TRUE(render_client
.get());
465 // The endpoint audio buffer should not be filled up by default after being
467 UINT32 num_queued_frames
= 0;
468 client
->GetCurrentPadding(&num_queued_frames
);
469 EXPECT_EQ(num_queued_frames
, 0u);
471 // Fill it up with zeros and verify that the buffer is full.
472 // It is not possible to verify that the actual data consists of zeros
473 // since we can't access data that has already been sent to the endpoint
475 EXPECT_TRUE(CoreAudioUtil::FillRenderEndpointBufferWithSilence(
476 client
.get(), render_client
.get()));
477 client
->GetCurrentPadding(&num_queued_frames
);
478 EXPECT_EQ(num_queued_frames
, endpoint_buffer_size
);
481 // This test can only succeed on a machine that has audio hardware
482 // that has both input and output devices. Currently this is the case
483 // with our test bots and the CanRunAudioTest() method should make sure
484 // that the test won't run in unsupported environments, but be warned.
485 TEST_F(CoreAudioUtilWinTest
, GetMatchingOutputDeviceID
) {
486 if (!CanRunAudioTest())
489 bool found_a_pair
= false;
491 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(
492 CoreAudioUtil::CreateDeviceEnumerator());
493 ASSERT_TRUE(enumerator
.get());
495 // Enumerate all active input and output devices and fetch the ID of
496 // the associated device.
497 ScopedComPtr
<IMMDeviceCollection
> collection
;
498 ASSERT_TRUE(SUCCEEDED(enumerator
->EnumAudioEndpoints(eCapture
,
499 DEVICE_STATE_ACTIVE
, collection
.Receive())));
501 collection
->GetCount(&count
);
502 for (UINT i
= 0; i
< count
&& !found_a_pair
; ++i
) {
503 ScopedComPtr
<IMMDevice
> device
;
504 collection
->Item(i
, device
.Receive());
505 base::win::ScopedCoMem
<WCHAR
> wide_id
;
506 device
->GetId(&wide_id
);
508 base::WideToUTF8(wide_id
, wcslen(wide_id
), &id
);
509 found_a_pair
= !CoreAudioUtil::GetMatchingOutputDeviceID(id
).empty();
512 EXPECT_TRUE(found_a_pair
);
515 TEST_F(CoreAudioUtilWinTest
, GetDefaultOutputDeviceID
) {
516 if (!CanRunAudioTest())
519 std::string
default_device_id(CoreAudioUtil::GetDefaultOutputDeviceID());
520 EXPECT_FALSE(default_device_id
.empty());