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/audio_unittest_util.h"
12 #include "media/audio/win/core_audio_util_win.h"
13 #include "testing/gmock/include/gmock/gmock.h"
14 #include "testing/gtest/include/gtest/gtest.h"
16 using base::win::ScopedCOMInitializer
;
20 class CoreAudioUtilWinTest
: public ::testing::Test
{
22 // The tests must run on a COM thread.
23 // If we don't initialize the COM library on a thread before using COM,
24 // all function calls will return CO_E_NOTINITIALIZED.
25 CoreAudioUtilWinTest() {
26 DCHECK(com_init_
.succeeded());
28 ~CoreAudioUtilWinTest() override
{}
30 bool DevicesAvailable() {
31 return CoreAudioUtil::IsSupported() &&
32 CoreAudioUtil::NumberOfActiveDevices(eCapture
) > 0 &&
33 CoreAudioUtil::NumberOfActiveDevices(eRender
) > 0;
36 ScopedCOMInitializer com_init_
;
39 TEST_F(CoreAudioUtilWinTest
, NumberOfActiveDevices
) {
40 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
42 int render_devices
= CoreAudioUtil::NumberOfActiveDevices(eRender
);
43 EXPECT_GT(render_devices
, 0);
44 int capture_devices
= CoreAudioUtil::NumberOfActiveDevices(eCapture
);
45 EXPECT_GT(capture_devices
, 0);
46 int total_devices
= CoreAudioUtil::NumberOfActiveDevices(eAll
);
47 EXPECT_EQ(total_devices
, render_devices
+ capture_devices
);
50 TEST_F(CoreAudioUtilWinTest
, CreateDeviceEnumerator
) {
51 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
53 ScopedComPtr
<IMMDeviceEnumerator
> enumerator
=
54 CoreAudioUtil::CreateDeviceEnumerator();
55 EXPECT_TRUE(enumerator
.get());
58 TEST_F(CoreAudioUtilWinTest
, CreateDefaultDevice
) {
59 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
66 {eRender
, eCommunications
},
67 {eRender
, eMultimedia
},
69 {eCapture
, eCommunications
},
70 {eCapture
, eMultimedia
}
73 // Create default devices for all flow/role combinations above.
74 ScopedComPtr
<IMMDevice
> audio_device
;
75 for (int i
= 0; i
< arraysize(data
); ++i
) {
77 CoreAudioUtil::CreateDefaultDevice(data
[i
].flow
, data
[i
].role
);
78 EXPECT_TRUE(audio_device
.get());
79 EXPECT_EQ(data
[i
].flow
, CoreAudioUtil::GetDataFlow(audio_device
.get()));
82 // Only eRender and eCapture are allowed as flow parameter.
83 audio_device
= CoreAudioUtil::CreateDefaultDevice(eAll
, eConsole
);
84 EXPECT_FALSE(audio_device
.get());
87 TEST_F(CoreAudioUtilWinTest
, CreateDevice
) {
88 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
90 // Get name and ID of default device used for playback.
91 ScopedComPtr
<IMMDevice
> default_render_device
=
92 CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
93 AudioDeviceName default_render_name
;
94 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(
95 default_render_device
.get(), &default_render_name
)));
97 // Use the uniqe ID as input to CreateDevice() and create a corresponding
99 ScopedComPtr
<IMMDevice
> audio_device
=
100 CoreAudioUtil::CreateDevice(default_render_name
.unique_id
);
101 EXPECT_TRUE(audio_device
.get());
103 // Verify that the two IMMDevice interfaces represents the same endpoint
104 // by comparing their unique IDs.
105 AudioDeviceName device_name
;
106 EXPECT_TRUE(SUCCEEDED(
107 CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
)));
108 EXPECT_EQ(default_render_name
.unique_id
, device_name
.unique_id
);
111 TEST_F(CoreAudioUtilWinTest
, GetDefaultDeviceName
) {
112 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
119 {eRender
, eCommunications
},
120 {eCapture
, eConsole
},
121 {eCapture
, eCommunications
}
124 // Get name and ID of default devices for all flow/role combinations above.
125 ScopedComPtr
<IMMDevice
> audio_device
;
126 AudioDeviceName device_name
;
127 for (int i
= 0; i
< arraysize(data
); ++i
) {
129 CoreAudioUtil::CreateDefaultDevice(data
[i
].flow
, data
[i
].role
);
130 EXPECT_TRUE(SUCCEEDED(
131 CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
)));
132 EXPECT_FALSE(device_name
.device_name
.empty());
133 EXPECT_FALSE(device_name
.unique_id
.empty());
137 TEST_F(CoreAudioUtilWinTest
, GetAudioControllerID
) {
138 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
140 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(
141 CoreAudioUtil::CreateDeviceEnumerator());
142 ASSERT_TRUE(enumerator
.get());
144 // Enumerate all active input and output devices and fetch the ID of
145 // the associated device.
146 EDataFlow flows
[] = { eRender
, eCapture
};
147 for (int i
= 0; i
< arraysize(flows
); ++i
) {
148 ScopedComPtr
<IMMDeviceCollection
> collection
;
149 ASSERT_TRUE(SUCCEEDED(enumerator
->EnumAudioEndpoints(flows
[i
],
150 DEVICE_STATE_ACTIVE
, collection
.Receive())));
152 collection
->GetCount(&count
);
153 for (UINT j
= 0; j
< count
; ++j
) {
154 ScopedComPtr
<IMMDevice
> device
;
155 collection
->Item(j
, device
.Receive());
156 std::string
controller_id(
157 CoreAudioUtil::GetAudioControllerID(device
.get(), enumerator
.get()));
158 EXPECT_FALSE(controller_id
.empty());
163 TEST_F(CoreAudioUtilWinTest
, GetFriendlyName
) {
164 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
166 // Get name and ID of default device used for recording.
167 ScopedComPtr
<IMMDevice
> audio_device
=
168 CoreAudioUtil::CreateDefaultDevice(eCapture
, eConsole
);
169 AudioDeviceName device_name
;
170 HRESULT hr
= CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
);
171 EXPECT_TRUE(SUCCEEDED(hr
));
173 // Use unique ID as input to GetFriendlyName() and compare the result
174 // with the already obtained friendly name for the default capture device.
175 std::string friendly_name
= CoreAudioUtil::GetFriendlyName(
176 device_name
.unique_id
);
177 EXPECT_EQ(friendly_name
, device_name
.device_name
);
179 // Same test as above but for playback.
180 audio_device
= CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
181 hr
= CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
);
182 EXPECT_TRUE(SUCCEEDED(hr
));
183 friendly_name
= CoreAudioUtil::GetFriendlyName(device_name
.unique_id
);
184 EXPECT_EQ(friendly_name
, device_name
.device_name
);
187 TEST_F(CoreAudioUtilWinTest
, DeviceIsDefault
) {
188 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
190 // Verify that the default render device is correctly identified as a
192 ScopedComPtr
<IMMDevice
> audio_device
=
193 CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
194 AudioDeviceName name
;
196 SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device
.get(), &name
)));
197 const std::string id
= name
.unique_id
;
198 EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eRender
, eConsole
, id
));
199 EXPECT_FALSE(CoreAudioUtil::DeviceIsDefault(eCapture
, eConsole
, id
));
202 TEST_F(CoreAudioUtilWinTest
, CreateDefaultClient
) {
203 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
205 EDataFlow data
[] = {eRender
, eCapture
};
207 for (int i
= 0; i
< arraysize(data
); ++i
) {
208 ScopedComPtr
<IAudioClient
> client
;
209 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
210 EXPECT_TRUE(client
.get());
214 TEST_F(CoreAudioUtilWinTest
, CreateClient
) {
215 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
217 EDataFlow data
[] = {eRender
, eCapture
};
219 for (int i
= 0; i
< arraysize(data
); ++i
) {
220 ScopedComPtr
<IMMDevice
> device
;
221 ScopedComPtr
<IAudioClient
> client
;
222 device
= CoreAudioUtil::CreateDefaultDevice(data
[i
], eConsole
);
223 EXPECT_TRUE(device
.get());
224 EXPECT_EQ(data
[i
], CoreAudioUtil::GetDataFlow(device
.get()));
225 client
= CoreAudioUtil::CreateClient(device
.get());
226 EXPECT_TRUE(client
.get());
230 TEST_F(CoreAudioUtilWinTest
, GetSharedModeMixFormat
) {
231 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
233 ScopedComPtr
<IMMDevice
> device
;
234 ScopedComPtr
<IAudioClient
> client
;
235 device
= CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
236 EXPECT_TRUE(device
.get());
237 client
= CoreAudioUtil::CreateClient(device
.get());
238 EXPECT_TRUE(client
.get());
240 // Perform a simple sanity test of the aquired format structure.
241 WAVEFORMATPCMEX format
;
243 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
244 EXPECT_GE(format
.Format
.nChannels
, 1);
245 EXPECT_GE(format
.Format
.nSamplesPerSec
, 8000u);
246 EXPECT_GE(format
.Format
.wBitsPerSample
, 16);
247 EXPECT_GE(format
.Samples
.wValidBitsPerSample
, 16);
248 EXPECT_EQ(format
.Format
.wFormatTag
, WAVE_FORMAT_EXTENSIBLE
);
251 TEST_F(CoreAudioUtilWinTest
, IsChannelLayoutSupported
) {
252 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
254 // The preferred channel layout should always be supported. Being supported
255 // means that it is possible to initialize a shared mode stream with the
256 // particular channel layout.
257 AudioParameters mix_params
;
258 HRESULT hr
= CoreAudioUtil::GetPreferredAudioParameters(eRender
, eConsole
,
260 EXPECT_TRUE(SUCCEEDED(hr
));
261 EXPECT_TRUE(mix_params
.IsValid());
262 EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported(
263 std::string(), eRender
, eConsole
, mix_params
.channel_layout()));
265 // Check if it is possible to modify the channel layout to stereo for a
266 // device which reports that it prefers to be openen up in an other
267 // channel configuration.
268 if (mix_params
.channel_layout() != CHANNEL_LAYOUT_STEREO
) {
269 ChannelLayout channel_layout
= CHANNEL_LAYOUT_STEREO
;
270 // TODO(henrika): it might be too pessimistic to assume false as return
272 EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported(
273 std::string(), eRender
, eConsole
, channel_layout
));
277 TEST_F(CoreAudioUtilWinTest
, GetDevicePeriod
) {
278 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
280 EDataFlow data
[] = {eRender
, eCapture
};
282 // Verify that the device periods are valid for the default render and
284 for (int i
= 0; i
< arraysize(data
); ++i
) {
285 ScopedComPtr
<IAudioClient
> client
;
286 REFERENCE_TIME shared_time_period
= 0;
287 REFERENCE_TIME exclusive_time_period
= 0;
288 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
289 EXPECT_TRUE(client
.get());
290 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod(
291 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &shared_time_period
)));
292 EXPECT_GT(shared_time_period
, 0);
293 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod(
294 client
.get(), AUDCLNT_SHAREMODE_EXCLUSIVE
, &exclusive_time_period
)));
295 EXPECT_GT(exclusive_time_period
, 0);
296 EXPECT_LE(exclusive_time_period
, shared_time_period
);
300 TEST_F(CoreAudioUtilWinTest
, GetPreferredAudioParameters
) {
301 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
303 EDataFlow data
[] = {eRender
, eCapture
};
305 // Verify that the preferred audio parameters are OK for the default render
306 // and capture devices.
307 for (int i
= 0; i
< arraysize(data
); ++i
) {
308 ScopedComPtr
<IAudioClient
> client
;
309 AudioParameters params
;
310 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
311 EXPECT_TRUE(client
.get());
312 EXPECT_TRUE(SUCCEEDED(
313 CoreAudioUtil::GetPreferredAudioParameters(client
.get(), ¶ms
)));
314 EXPECT_TRUE(params
.IsValid());
318 TEST_F(CoreAudioUtilWinTest
, SharedModeInitialize
) {
319 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
321 ScopedComPtr
<IAudioClient
> client
;
322 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
323 EXPECT_TRUE(client
.get());
325 WAVEFORMATPCMEX format
;
327 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
329 // Perform a shared-mode initialization without event-driven buffer handling.
330 uint32 endpoint_buffer_size
= 0;
331 HRESULT hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
332 &endpoint_buffer_size
, NULL
);
333 EXPECT_TRUE(SUCCEEDED(hr
));
334 EXPECT_GT(endpoint_buffer_size
, 0u);
336 // It is only possible to create a client once.
337 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
338 &endpoint_buffer_size
, NULL
);
339 EXPECT_FALSE(SUCCEEDED(hr
));
340 EXPECT_EQ(hr
, AUDCLNT_E_ALREADY_INITIALIZED
);
342 // Verify that it is possible to reinitialize the client after releasing it.
343 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
344 EXPECT_TRUE(client
.get());
345 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
346 &endpoint_buffer_size
, NULL
);
347 EXPECT_TRUE(SUCCEEDED(hr
));
348 EXPECT_GT(endpoint_buffer_size
, 0u);
350 // Use a non-supported format and verify that initialization fails.
351 // A simple way to emulate an invalid format is to use the shared-mode
352 // mixing format and modify the preferred sample.
353 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
354 EXPECT_TRUE(client
.get());
355 format
.Format
.nSamplesPerSec
= format
.Format
.nSamplesPerSec
+ 1;
356 EXPECT_FALSE(CoreAudioUtil::IsFormatSupported(
357 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &format
));
358 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
359 &endpoint_buffer_size
, NULL
);
360 EXPECT_TRUE(FAILED(hr
));
361 EXPECT_EQ(hr
, E_INVALIDARG
);
363 // Finally, perform a shared-mode initialization using event-driven buffer
364 // handling. The event handle will be signaled when an audio buffer is ready
365 // to be processed by the client (not verified here).
366 // The event handle should be in the nonsignaled state.
367 base::win::ScopedHandle
event_handle(::CreateEvent(NULL
, TRUE
, FALSE
, NULL
));
368 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
369 EXPECT_TRUE(client
.get());
371 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
372 EXPECT_TRUE(CoreAudioUtil::IsFormatSupported(
373 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &format
));
374 hr
= CoreAudioUtil::SharedModeInitialize(
375 client
.get(), &format
, event_handle
.Get(), &endpoint_buffer_size
, NULL
);
376 EXPECT_TRUE(SUCCEEDED(hr
));
377 EXPECT_GT(endpoint_buffer_size
, 0u);
380 TEST_F(CoreAudioUtilWinTest
, CreateRenderAndCaptureClients
) {
381 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
383 EDataFlow data
[] = {eRender
, eCapture
};
385 WAVEFORMATPCMEX format
;
386 uint32 endpoint_buffer_size
= 0;
388 for (int i
= 0; i
< arraysize(data
); ++i
) {
389 ScopedComPtr
<IAudioClient
> client
;
390 ScopedComPtr
<IAudioRenderClient
> render_client
;
391 ScopedComPtr
<IAudioCaptureClient
> capture_client
;
393 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
394 EXPECT_TRUE(client
.get());
395 EXPECT_TRUE(SUCCEEDED(
396 CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
397 if (data
[i
] == eRender
) {
398 // It is not possible to create a render client using an unitialized
400 render_client
= CoreAudioUtil::CreateRenderClient(client
.get());
401 EXPECT_FALSE(render_client
.get());
403 // Do a proper initialization and verify that it works this time.
404 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
405 &endpoint_buffer_size
, NULL
);
406 render_client
= CoreAudioUtil::CreateRenderClient(client
.get());
407 EXPECT_TRUE(render_client
.get());
408 EXPECT_GT(endpoint_buffer_size
, 0u);
409 } else if (data
[i
] == eCapture
) {
410 // It is not possible to create a capture client using an unitialized
412 capture_client
= CoreAudioUtil::CreateCaptureClient(client
.get());
413 EXPECT_FALSE(capture_client
.get());
415 // Do a proper initialization and verify that it works this time.
416 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
417 &endpoint_buffer_size
, NULL
);
418 capture_client
= CoreAudioUtil::CreateCaptureClient(client
.get());
419 EXPECT_TRUE(capture_client
.get());
420 EXPECT_GT(endpoint_buffer_size
, 0u);
425 TEST_F(CoreAudioUtilWinTest
, FillRenderEndpointBufferWithSilence
) {
426 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
428 // Create default clients using the default mixing format for shared mode.
429 ScopedComPtr
<IAudioClient
> client(
430 CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
));
431 EXPECT_TRUE(client
.get());
433 WAVEFORMATPCMEX format
;
434 uint32 endpoint_buffer_size
= 0;
436 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
437 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
438 &endpoint_buffer_size
, NULL
);
439 EXPECT_GT(endpoint_buffer_size
, 0u);
441 ScopedComPtr
<IAudioRenderClient
> render_client(
442 CoreAudioUtil::CreateRenderClient(client
.get()));
443 EXPECT_TRUE(render_client
.get());
445 // The endpoint audio buffer should not be filled up by default after being
447 UINT32 num_queued_frames
= 0;
448 client
->GetCurrentPadding(&num_queued_frames
);
449 EXPECT_EQ(num_queued_frames
, 0u);
451 // Fill it up with zeros and verify that the buffer is full.
452 // It is not possible to verify that the actual data consists of zeros
453 // since we can't access data that has already been sent to the endpoint
455 EXPECT_TRUE(CoreAudioUtil::FillRenderEndpointBufferWithSilence(
456 client
.get(), render_client
.get()));
457 client
->GetCurrentPadding(&num_queued_frames
);
458 EXPECT_EQ(num_queued_frames
, endpoint_buffer_size
);
461 // This test can only run on a machine that has audio hardware
462 // that has both input and output devices.
463 TEST_F(CoreAudioUtilWinTest
, GetMatchingOutputDeviceID
) {
464 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
466 bool found_a_pair
= false;
468 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(
469 CoreAudioUtil::CreateDeviceEnumerator());
470 ASSERT_TRUE(enumerator
.get());
472 // Enumerate all active input and output devices and fetch the ID of
473 // the associated device.
474 ScopedComPtr
<IMMDeviceCollection
> collection
;
475 ASSERT_TRUE(SUCCEEDED(enumerator
->EnumAudioEndpoints(eCapture
,
476 DEVICE_STATE_ACTIVE
, collection
.Receive())));
478 collection
->GetCount(&count
);
479 for (UINT i
= 0; i
< count
&& !found_a_pair
; ++i
) {
480 ScopedComPtr
<IMMDevice
> device
;
481 collection
->Item(i
, device
.Receive());
482 base::win::ScopedCoMem
<WCHAR
> wide_id
;
483 device
->GetId(&wide_id
);
485 base::WideToUTF8(wide_id
, wcslen(wide_id
), &id
);
486 found_a_pair
= !CoreAudioUtil::GetMatchingOutputDeviceID(id
).empty();
489 EXPECT_TRUE(found_a_pair
);
492 TEST_F(CoreAudioUtilWinTest
, GetDefaultOutputDeviceID
) {
493 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
495 std::string
default_device_id(CoreAudioUtil::GetDefaultOutputDeviceID());
496 EXPECT_FALSE(default_device_id
.empty());