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