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 test runs on a COM thread in the multithreaded apartment (MTA).
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 : com_init_(ScopedCOMInitializer::kMTA
) {
27 DCHECK(com_init_
.succeeded());
29 ~CoreAudioUtilWinTest() override
{}
31 bool DevicesAvailable() {
32 if (!CoreAudioUtil::IsSupported())
34 return CoreAudioUtil::NumberOfActiveDevices(eCapture
) > 0 &&
35 CoreAudioUtil::NumberOfActiveDevices(eRender
) > 0;
38 ScopedCOMInitializer com_init_
;
41 TEST_F(CoreAudioUtilWinTest
, NumberOfActiveDevices
) {
42 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
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
);
52 TEST_F(CoreAudioUtilWinTest
, CreateDeviceEnumerator
) {
53 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
55 ScopedComPtr
<IMMDeviceEnumerator
> enumerator
=
56 CoreAudioUtil::CreateDeviceEnumerator();
57 EXPECT_TRUE(enumerator
.get());
60 TEST_F(CoreAudioUtilWinTest
, CreateDefaultDevice
) {
61 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
68 {eRender
, eCommunications
},
69 {eRender
, eMultimedia
},
71 {eCapture
, eCommunications
},
72 {eCapture
, eMultimedia
}
75 // Create default devices for all flow/role combinations above.
76 ScopedComPtr
<IMMDevice
> audio_device
;
77 for (int i
= 0; i
< arraysize(data
); ++i
) {
79 CoreAudioUtil::CreateDefaultDevice(data
[i
].flow
, data
[i
].role
);
80 EXPECT_TRUE(audio_device
.get());
81 EXPECT_EQ(data
[i
].flow
, CoreAudioUtil::GetDataFlow(audio_device
.get()));
84 // Only eRender and eCapture are allowed as flow parameter.
85 audio_device
= CoreAudioUtil::CreateDefaultDevice(eAll
, eConsole
);
86 EXPECT_FALSE(audio_device
.get());
89 TEST_F(CoreAudioUtilWinTest
, CreateDevice
) {
90 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
92 // Get name and ID of default device used for playback.
93 ScopedComPtr
<IMMDevice
> default_render_device
=
94 CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
95 AudioDeviceName default_render_name
;
96 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(
97 default_render_device
.get(), &default_render_name
)));
99 // Use the uniqe ID as input to CreateDevice() and create a corresponding
101 ScopedComPtr
<IMMDevice
> audio_device
=
102 CoreAudioUtil::CreateDevice(default_render_name
.unique_id
);
103 EXPECT_TRUE(audio_device
.get());
105 // Verify that the two IMMDevice interfaces represents the same endpoint
106 // by comparing their unique IDs.
107 AudioDeviceName device_name
;
108 EXPECT_TRUE(SUCCEEDED(
109 CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
)));
110 EXPECT_EQ(default_render_name
.unique_id
, device_name
.unique_id
);
113 TEST_F(CoreAudioUtilWinTest
, GetDefaultDeviceName
) {
114 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
121 {eRender
, eCommunications
},
122 {eCapture
, eConsole
},
123 {eCapture
, eCommunications
}
126 // Get name and ID of default devices for all flow/role combinations above.
127 ScopedComPtr
<IMMDevice
> audio_device
;
128 AudioDeviceName device_name
;
129 for (int i
= 0; i
< arraysize(data
); ++i
) {
131 CoreAudioUtil::CreateDefaultDevice(data
[i
].flow
, data
[i
].role
);
132 EXPECT_TRUE(SUCCEEDED(
133 CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
)));
134 EXPECT_FALSE(device_name
.device_name
.empty());
135 EXPECT_FALSE(device_name
.unique_id
.empty());
139 TEST_F(CoreAudioUtilWinTest
, GetAudioControllerID
) {
140 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
142 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(
143 CoreAudioUtil::CreateDeviceEnumerator());
144 ASSERT_TRUE(enumerator
.get());
146 // Enumerate all active input and output devices and fetch the ID of
147 // the associated device.
148 EDataFlow flows
[] = { eRender
, eCapture
};
149 for (int i
= 0; i
< arraysize(flows
); ++i
) {
150 ScopedComPtr
<IMMDeviceCollection
> collection
;
151 ASSERT_TRUE(SUCCEEDED(enumerator
->EnumAudioEndpoints(flows
[i
],
152 DEVICE_STATE_ACTIVE
, collection
.Receive())));
154 collection
->GetCount(&count
);
155 for (UINT j
= 0; j
< count
; ++j
) {
156 ScopedComPtr
<IMMDevice
> device
;
157 collection
->Item(j
, device
.Receive());
158 std::string
controller_id(
159 CoreAudioUtil::GetAudioControllerID(device
.get(), enumerator
.get()));
160 EXPECT_FALSE(controller_id
.empty());
165 TEST_F(CoreAudioUtilWinTest
, GetFriendlyName
) {
166 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
168 // Get name and ID of default device used for recording.
169 ScopedComPtr
<IMMDevice
> audio_device
=
170 CoreAudioUtil::CreateDefaultDevice(eCapture
, eConsole
);
171 AudioDeviceName device_name
;
172 HRESULT hr
= CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
);
173 EXPECT_TRUE(SUCCEEDED(hr
));
175 // Use unique ID as input to GetFriendlyName() and compare the result
176 // with the already obtained friendly name for the default capture device.
177 std::string friendly_name
= CoreAudioUtil::GetFriendlyName(
178 device_name
.unique_id
);
179 EXPECT_EQ(friendly_name
, device_name
.device_name
);
181 // Same test as above but for playback.
182 audio_device
= CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
183 hr
= CoreAudioUtil::GetDeviceName(audio_device
.get(), &device_name
);
184 EXPECT_TRUE(SUCCEEDED(hr
));
185 friendly_name
= CoreAudioUtil::GetFriendlyName(device_name
.unique_id
);
186 EXPECT_EQ(friendly_name
, device_name
.device_name
);
189 TEST_F(CoreAudioUtilWinTest
, DeviceIsDefault
) {
190 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
192 // Verify that the default render device is correctly identified as a
194 ScopedComPtr
<IMMDevice
> audio_device
=
195 CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
196 AudioDeviceName name
;
198 SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device
.get(), &name
)));
199 const std::string id
= name
.unique_id
;
200 EXPECT_TRUE(CoreAudioUtil::DeviceIsDefault(eRender
, eConsole
, id
));
201 EXPECT_FALSE(CoreAudioUtil::DeviceIsDefault(eCapture
, eConsole
, id
));
204 TEST_F(CoreAudioUtilWinTest
, CreateDefaultClient
) {
205 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
207 EDataFlow data
[] = {eRender
, eCapture
};
209 for (int i
= 0; i
< arraysize(data
); ++i
) {
210 ScopedComPtr
<IAudioClient
> client
;
211 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
212 EXPECT_TRUE(client
.get());
216 TEST_F(CoreAudioUtilWinTest
, CreateClient
) {
217 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
219 EDataFlow data
[] = {eRender
, eCapture
};
221 for (int i
= 0; i
< arraysize(data
); ++i
) {
222 ScopedComPtr
<IMMDevice
> device
;
223 ScopedComPtr
<IAudioClient
> client
;
224 device
= CoreAudioUtil::CreateDefaultDevice(data
[i
], eConsole
);
225 EXPECT_TRUE(device
.get());
226 EXPECT_EQ(data
[i
], CoreAudioUtil::GetDataFlow(device
.get()));
227 client
= CoreAudioUtil::CreateClient(device
.get());
228 EXPECT_TRUE(client
.get());
232 TEST_F(CoreAudioUtilWinTest
, GetSharedModeMixFormat
) {
233 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
235 ScopedComPtr
<IMMDevice
> device
;
236 ScopedComPtr
<IAudioClient
> client
;
237 device
= CoreAudioUtil::CreateDefaultDevice(eRender
, eConsole
);
238 EXPECT_TRUE(device
.get());
239 client
= CoreAudioUtil::CreateClient(device
.get());
240 EXPECT_TRUE(client
.get());
242 // Perform a simple sanity test of the aquired format structure.
243 WAVEFORMATPCMEX format
;
245 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
246 EXPECT_GE(format
.Format
.nChannels
, 1);
247 EXPECT_GE(format
.Format
.nSamplesPerSec
, 8000u);
248 EXPECT_GE(format
.Format
.wBitsPerSample
, 16);
249 EXPECT_GE(format
.Samples
.wValidBitsPerSample
, 16);
250 EXPECT_EQ(format
.Format
.wFormatTag
, WAVE_FORMAT_EXTENSIBLE
);
253 TEST_F(CoreAudioUtilWinTest
, IsChannelLayoutSupported
) {
254 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
256 // The preferred channel layout should always be supported. Being supported
257 // means that it is possible to initialize a shared mode stream with the
258 // particular channel layout.
259 AudioParameters mix_params
;
260 HRESULT hr
= CoreAudioUtil::GetPreferredAudioParameters(eRender
, eConsole
,
262 EXPECT_TRUE(SUCCEEDED(hr
));
263 EXPECT_TRUE(mix_params
.IsValid());
264 EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported(
265 std::string(), eRender
, eConsole
, mix_params
.channel_layout()));
267 // Check if it is possible to modify the channel layout to stereo for a
268 // device which reports that it prefers to be openen up in an other
269 // channel configuration.
270 if (mix_params
.channel_layout() != CHANNEL_LAYOUT_STEREO
) {
271 ChannelLayout channel_layout
= CHANNEL_LAYOUT_STEREO
;
272 // TODO(henrika): it might be too pessimistic to assume false as return
274 EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported(
275 std::string(), eRender
, eConsole
, channel_layout
));
279 TEST_F(CoreAudioUtilWinTest
, GetDevicePeriod
) {
280 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
282 EDataFlow data
[] = {eRender
, eCapture
};
284 // Verify that the device periods are valid for the default render and
286 for (int i
= 0; i
< arraysize(data
); ++i
) {
287 ScopedComPtr
<IAudioClient
> client
;
288 REFERENCE_TIME shared_time_period
= 0;
289 REFERENCE_TIME exclusive_time_period
= 0;
290 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
291 EXPECT_TRUE(client
.get());
292 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod(
293 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &shared_time_period
)));
294 EXPECT_GT(shared_time_period
, 0);
295 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDevicePeriod(
296 client
.get(), AUDCLNT_SHAREMODE_EXCLUSIVE
, &exclusive_time_period
)));
297 EXPECT_GT(exclusive_time_period
, 0);
298 EXPECT_LE(exclusive_time_period
, shared_time_period
);
302 TEST_F(CoreAudioUtilWinTest
, GetPreferredAudioParameters
) {
303 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
305 EDataFlow data
[] = {eRender
, eCapture
};
307 // Verify that the preferred audio parameters are OK for the default render
308 // and capture devices.
309 for (int i
= 0; i
< arraysize(data
); ++i
) {
310 ScopedComPtr
<IAudioClient
> client
;
311 AudioParameters params
;
312 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
313 EXPECT_TRUE(client
.get());
314 EXPECT_TRUE(SUCCEEDED(
315 CoreAudioUtil::GetPreferredAudioParameters(client
.get(), ¶ms
)));
316 EXPECT_TRUE(params
.IsValid());
320 TEST_F(CoreAudioUtilWinTest
, SharedModeInitialize
) {
321 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
323 ScopedComPtr
<IAudioClient
> client
;
324 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
325 EXPECT_TRUE(client
.get());
327 WAVEFORMATPCMEX format
;
329 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
331 // Perform a shared-mode initialization without event-driven buffer handling.
332 uint32 endpoint_buffer_size
= 0;
333 HRESULT hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
334 &endpoint_buffer_size
, NULL
);
335 EXPECT_TRUE(SUCCEEDED(hr
));
336 EXPECT_GT(endpoint_buffer_size
, 0u);
338 // It is only possible to create a client once.
339 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
340 &endpoint_buffer_size
, NULL
);
341 EXPECT_FALSE(SUCCEEDED(hr
));
342 EXPECT_EQ(hr
, AUDCLNT_E_ALREADY_INITIALIZED
);
344 // Verify that it is possible to reinitialize the client after releasing it.
345 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
346 EXPECT_TRUE(client
.get());
347 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
348 &endpoint_buffer_size
, NULL
);
349 EXPECT_TRUE(SUCCEEDED(hr
));
350 EXPECT_GT(endpoint_buffer_size
, 0u);
352 // Use a non-supported format and verify that initialization fails.
353 // A simple way to emulate an invalid format is to use the shared-mode
354 // mixing format and modify the preferred sample.
355 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
356 EXPECT_TRUE(client
.get());
357 format
.Format
.nSamplesPerSec
= format
.Format
.nSamplesPerSec
+ 1;
358 EXPECT_FALSE(CoreAudioUtil::IsFormatSupported(
359 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &format
));
360 hr
= CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
361 &endpoint_buffer_size
, NULL
);
362 EXPECT_TRUE(FAILED(hr
));
363 EXPECT_EQ(hr
, E_INVALIDARG
);
365 // Finally, perform a shared-mode initialization using event-driven buffer
366 // handling. The event handle will be signaled when an audio buffer is ready
367 // to be processed by the client (not verified here).
368 // The event handle should be in the nonsignaled state.
369 base::win::ScopedHandle
event_handle(::CreateEvent(NULL
, TRUE
, FALSE
, NULL
));
370 client
= CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
);
371 EXPECT_TRUE(client
.get());
373 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
374 EXPECT_TRUE(CoreAudioUtil::IsFormatSupported(
375 client
.get(), AUDCLNT_SHAREMODE_SHARED
, &format
));
376 hr
= CoreAudioUtil::SharedModeInitialize(
377 client
.get(), &format
, event_handle
.Get(), &endpoint_buffer_size
, NULL
);
378 EXPECT_TRUE(SUCCEEDED(hr
));
379 EXPECT_GT(endpoint_buffer_size
, 0u);
382 TEST_F(CoreAudioUtilWinTest
, CreateRenderAndCaptureClients
) {
383 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
385 EDataFlow data
[] = {eRender
, eCapture
};
387 WAVEFORMATPCMEX format
;
388 uint32 endpoint_buffer_size
= 0;
390 for (int i
= 0; i
< arraysize(data
); ++i
) {
391 ScopedComPtr
<IAudioClient
> client
;
392 ScopedComPtr
<IAudioRenderClient
> render_client
;
393 ScopedComPtr
<IAudioCaptureClient
> capture_client
;
395 client
= CoreAudioUtil::CreateDefaultClient(data
[i
], eConsole
);
396 EXPECT_TRUE(client
.get());
397 EXPECT_TRUE(SUCCEEDED(
398 CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
399 if (data
[i
] == eRender
) {
400 // It is not possible to create a render client using an unitialized
402 render_client
= CoreAudioUtil::CreateRenderClient(client
.get());
403 EXPECT_FALSE(render_client
.get());
405 // Do a proper initialization and verify that it works this time.
406 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
407 &endpoint_buffer_size
, NULL
);
408 render_client
= CoreAudioUtil::CreateRenderClient(client
.get());
409 EXPECT_TRUE(render_client
.get());
410 EXPECT_GT(endpoint_buffer_size
, 0u);
411 } else if (data
[i
] == eCapture
) {
412 // It is not possible to create a capture client using an unitialized
414 capture_client
= CoreAudioUtil::CreateCaptureClient(client
.get());
415 EXPECT_FALSE(capture_client
.get());
417 // Do a proper initialization and verify that it works this time.
418 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
419 &endpoint_buffer_size
, NULL
);
420 capture_client
= CoreAudioUtil::CreateCaptureClient(client
.get());
421 EXPECT_TRUE(capture_client
.get());
422 EXPECT_GT(endpoint_buffer_size
, 0u);
427 TEST_F(CoreAudioUtilWinTest
, FillRenderEndpointBufferWithSilence
) {
428 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
430 // Create default clients using the default mixing format for shared mode.
431 ScopedComPtr
<IAudioClient
> client(
432 CoreAudioUtil::CreateDefaultClient(eRender
, eConsole
));
433 EXPECT_TRUE(client
.get());
435 WAVEFORMATPCMEX format
;
436 uint32 endpoint_buffer_size
= 0;
438 SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client
.get(), &format
)));
439 CoreAudioUtil::SharedModeInitialize(client
.get(), &format
, NULL
,
440 &endpoint_buffer_size
, NULL
);
441 EXPECT_GT(endpoint_buffer_size
, 0u);
443 ScopedComPtr
<IAudioRenderClient
> render_client(
444 CoreAudioUtil::CreateRenderClient(client
.get()));
445 EXPECT_TRUE(render_client
.get());
447 // The endpoint audio buffer should not be filled up by default after being
449 UINT32 num_queued_frames
= 0;
450 client
->GetCurrentPadding(&num_queued_frames
);
451 EXPECT_EQ(num_queued_frames
, 0u);
453 // Fill it up with zeros and verify that the buffer is full.
454 // It is not possible to verify that the actual data consists of zeros
455 // since we can't access data that has already been sent to the endpoint
457 EXPECT_TRUE(CoreAudioUtil::FillRenderEndpointBufferWithSilence(
458 client
.get(), render_client
.get()));
459 client
->GetCurrentPadding(&num_queued_frames
);
460 EXPECT_EQ(num_queued_frames
, endpoint_buffer_size
);
463 // This test can only run on a machine that has audio hardware
464 // that has both input and output devices.
465 TEST_F(CoreAudioUtilWinTest
, GetMatchingOutputDeviceID
) {
466 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
468 bool found_a_pair
= false;
470 ScopedComPtr
<IMMDeviceEnumerator
> enumerator(
471 CoreAudioUtil::CreateDeviceEnumerator());
472 ASSERT_TRUE(enumerator
.get());
474 // Enumerate all active input and output devices and fetch the ID of
475 // the associated device.
476 ScopedComPtr
<IMMDeviceCollection
> collection
;
477 ASSERT_TRUE(SUCCEEDED(enumerator
->EnumAudioEndpoints(eCapture
,
478 DEVICE_STATE_ACTIVE
, collection
.Receive())));
480 collection
->GetCount(&count
);
481 for (UINT i
= 0; i
< count
&& !found_a_pair
; ++i
) {
482 ScopedComPtr
<IMMDevice
> device
;
483 collection
->Item(i
, device
.Receive());
484 base::win::ScopedCoMem
<WCHAR
> wide_id
;
485 device
->GetId(&wide_id
);
487 base::WideToUTF8(wide_id
, wcslen(wide_id
), &id
);
488 found_a_pair
= !CoreAudioUtil::GetMatchingOutputDeviceID(id
).empty();
491 EXPECT_TRUE(found_a_pair
);
494 TEST_F(CoreAudioUtilWinTest
, GetDefaultOutputDeviceID
) {
495 ABORT_AUDIO_TEST_IF_NOT(DevicesAvailable());
497 std::string
default_device_id(CoreAudioUtil::GetDefaultOutputDeviceID());
498 EXPECT_FALSE(default_device_id
.empty());