core_audio_util_win_unittest.cc revision 58537e28ecd584eab876aee8be7156509866d23a
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/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"
14
15using base::win::ScopedCOMInitializer;
16
17namespace media {
18
19class CoreAudioUtilWinTest : public ::testing::Test {
20 protected:
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());
27  }
28  virtual ~CoreAudioUtilWinTest() {}
29
30  bool CanRunAudioTest() {
31    bool core_audio = CoreAudioUtil::IsSupported();
32    if (!core_audio)
33      return false;
34    int capture_devices = CoreAudioUtil::NumberOfActiveDevices(eCapture);
35    int render_devices = CoreAudioUtil::NumberOfActiveDevices(eRender);
36    return ((capture_devices > 0) && (render_devices > 0));
37  }
38
39  ScopedCOMInitializer com_init_;
40};
41
42TEST_F(CoreAudioUtilWinTest, NumberOfActiveDevices) {
43  if (!CanRunAudioTest())
44    return;
45
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);
52}
53
54TEST_F(CoreAudioUtilWinTest, CreateDeviceEnumerator) {
55  if (!CanRunAudioTest())
56    return;
57
58  ScopedComPtr<IMMDeviceEnumerator> enumerator =
59      CoreAudioUtil::CreateDeviceEnumerator();
60  EXPECT_TRUE(enumerator);
61}
62
63TEST_F(CoreAudioUtilWinTest, CreateDefaultDevice) {
64  if (!CanRunAudioTest())
65    return;
66
67  struct {
68    EDataFlow flow;
69    ERole role;
70  } data[] = {
71    {eRender, eConsole},
72    {eRender, eCommunications},
73    {eRender, eMultimedia},
74    {eCapture, eConsole},
75    {eCapture, eCommunications},
76    {eCapture, eMultimedia}
77  };
78
79  // Create default devices for all flow/role combinations above.
80  ScopedComPtr<IMMDevice> audio_device;
81  for (int i = 0; i < arraysize(data); ++i) {
82    audio_device =
83        CoreAudioUtil::CreateDefaultDevice(data[i].flow, data[i].role);
84    EXPECT_TRUE(audio_device);
85    EXPECT_EQ(data[i].flow, CoreAudioUtil::GetDataFlow(audio_device));
86  }
87
88  // Only eRender and eCapture are allowed as flow parameter.
89  audio_device = CoreAudioUtil::CreateDefaultDevice(eAll, eConsole);
90  EXPECT_FALSE(audio_device);
91}
92
93TEST_F(CoreAudioUtilWinTest, CreateDevice) {
94  if (!CanRunAudioTest())
95    return;
96
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)));
103
104  // Use the uniqe ID as input to CreateDevice() and create a corresponding
105  // IMMDevice.
106  ScopedComPtr<IMMDevice> audio_device =
107      CoreAudioUtil::CreateDevice(default_render_name.unique_id);
108  EXPECT_TRUE(audio_device);
109
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,
114                                                     &device_name)));
115  EXPECT_EQ(default_render_name.unique_id, device_name.unique_id);
116}
117
118TEST_F(CoreAudioUtilWinTest, GetDefaultDeviceName) {
119  if (!CanRunAudioTest())
120    return;
121
122  struct {
123    EDataFlow flow;
124    ERole role;
125  } data[] = {
126    {eRender, eConsole},
127    {eRender, eCommunications},
128    {eCapture, eConsole},
129    {eCapture, eCommunications}
130  };
131
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) {
136    audio_device =
137        CoreAudioUtil::CreateDefaultDevice(data[i].flow, data[i].role);
138    EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetDeviceName(audio_device,
139                                                       &device_name)));
140    EXPECT_FALSE(device_name.device_name.empty());
141    EXPECT_FALSE(device_name.unique_id.empty());
142  }
143}
144
145TEST_F(CoreAudioUtilWinTest, GetAudioControllerID) {
146  if (!CanRunAudioTest())
147    return;
148
149  ScopedComPtr<IMMDeviceEnumerator> enumerator(
150      CoreAudioUtil::CreateDeviceEnumerator());
151  ASSERT_TRUE(enumerator);
152
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())));
160    UINT count = 0;
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());
168    }
169  }
170}
171
172TEST_F(CoreAudioUtilWinTest, GetFriendlyName) {
173  if (!CanRunAudioTest())
174    return;
175
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));
182
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);
188
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);
195}
196
197TEST_F(CoreAudioUtilWinTest, DeviceIsDefault) {
198  if (!CanRunAudioTest())
199    return;
200
201  // Verify that the default render device is correctly identified as a
202  // default device.
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));
210}
211
212TEST_F(CoreAudioUtilWinTest, CreateDefaultClient) {
213  if (!CanRunAudioTest())
214    return;
215
216  EDataFlow data[] = {eRender, eCapture};
217
218  for (int i = 0; i < arraysize(data); ++i) {
219    ScopedComPtr<IAudioClient> client;
220    client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole);
221    EXPECT_TRUE(client);
222  }
223}
224
225TEST_F(CoreAudioUtilWinTest, CreateClient) {
226  if (!CanRunAudioTest())
227    return;
228
229  EDataFlow data[] = {eRender, eCapture};
230
231  for (int i = 0; i < arraysize(data); ++i) {
232    ScopedComPtr<IMMDevice> device;
233    ScopedComPtr<IAudioClient> client;
234    device = CoreAudioUtil::CreateDefaultDevice(data[i], eConsole);
235    EXPECT_TRUE(device);
236    EXPECT_EQ(data[i], CoreAudioUtil::GetDataFlow(device));
237    client = CoreAudioUtil::CreateClient(device);
238    EXPECT_TRUE(client);
239  }
240}
241
242TEST_F(CoreAudioUtilWinTest, GetSharedModeMixFormat) {
243  if (!CanRunAudioTest())
244    return;
245
246  ScopedComPtr<IMMDevice> device;
247  ScopedComPtr<IAudioClient> client;
248  device = CoreAudioUtil::CreateDefaultDevice(eRender, eConsole);
249  EXPECT_TRUE(device);
250  client = CoreAudioUtil::CreateClient(device);
251  EXPECT_TRUE(client);
252
253  // Perform a simple sanity test of the aquired format structure.
254  WAVEFORMATPCMEX format;
255  EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
256                                                              &format)));
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);
262}
263
264TEST_F(CoreAudioUtilWinTest, IsChannelLayoutSupported) {
265  if (!CanRunAudioTest())
266    return;
267
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,
273                                                          &mix_params);
274  EXPECT_TRUE(SUCCEEDED(hr));
275  EXPECT_TRUE(mix_params.IsValid());
276  EXPECT_TRUE(CoreAudioUtil::IsChannelLayoutSupported(
277      eRender, eConsole, mix_params.channel_layout()));
278
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
285    // value here.
286    EXPECT_FALSE(CoreAudioUtil::IsChannelLayoutSupported(
287        eRender, eConsole, channel_layout));
288  }
289}
290
291TEST_F(CoreAudioUtilWinTest, GetDevicePeriod) {
292  if (!CanRunAudioTest())
293    return;
294
295  EDataFlow data[] = {eRender, eCapture};
296
297  // Verify that the device periods are valid for the default render and
298  // capture devices.
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);
304    EXPECT_TRUE(client);
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);
312  }
313}
314
315TEST_F(CoreAudioUtilWinTest, GetPreferredAudioParameters) {
316  if (!CanRunAudioTest())
317    return;
318
319  EDataFlow data[] = {eRender, eCapture};
320
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);
327    EXPECT_TRUE(client);
328    EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(client,
329                                                                     &params)));
330    EXPECT_TRUE(params.IsValid());
331  }
332}
333
334TEST_F(CoreAudioUtilWinTest, SharedModeInitialize) {
335  if (!CanRunAudioTest())
336    return;
337
338  ScopedComPtr<IAudioClient> client;
339  client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole);
340  EXPECT_TRUE(client);
341
342  WAVEFORMATPCMEX format;
343  EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
344                                                              &format)));
345
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);
352
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);
358
359  // Verify that it is possible to reinitialize the client after releasing it.
360  client = CoreAudioUtil::CreateDefaultClient(eRender, eConsole);
361  EXPECT_TRUE(client);
362  hr = CoreAudioUtil::SharedModeInitialize(client, &format, NULL,
363                                           &endpoint_buffer_size);
364  EXPECT_TRUE(SUCCEEDED(hr));
365  EXPECT_GT(endpoint_buffer_size, 0u);
366
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);
371  EXPECT_TRUE(client);
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);
379
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);
386  EXPECT_TRUE(client);
387  EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
388                                                              &format)));
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);
395}
396
397TEST_F(CoreAudioUtilWinTest, CreateRenderAndCaptureClients) {
398  if (!CanRunAudioTest())
399    return;
400
401  EDataFlow data[] = {eRender, eCapture};
402
403  WAVEFORMATPCMEX format;
404  uint32 endpoint_buffer_size = 0;
405
406  for (int i = 0; i < arraysize(data); ++i) {
407    ScopedComPtr<IAudioClient> client;
408    ScopedComPtr<IAudioRenderClient> render_client;
409    ScopedComPtr<IAudioCaptureClient> capture_client;
410
411    client = CoreAudioUtil::CreateDefaultClient(data[i], eConsole);
412    EXPECT_TRUE(client);
413    EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
414                                                                &format)));
415    if (data[i] == eRender) {
416      // It is not possible to create a render client using an unitialized
417      // client interface.
418      render_client = CoreAudioUtil::CreateRenderClient(client);
419      EXPECT_FALSE(render_client);
420
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
429      // client interface.
430      capture_client = CoreAudioUtil::CreateCaptureClient(client);
431      EXPECT_FALSE(capture_client);
432
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);
439    }
440  }
441}
442
443TEST_F(CoreAudioUtilWinTest, FillRenderEndpointBufferWithSilence) {
444  if (!CanRunAudioTest())
445    return;
446
447  // Create default clients using the default mixing format for shared mode.
448  ScopedComPtr<IAudioClient> client(
449      CoreAudioUtil::CreateDefaultClient(eRender, eConsole));
450  EXPECT_TRUE(client);
451
452  WAVEFORMATPCMEX format;
453  uint32 endpoint_buffer_size = 0;
454  EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetSharedModeMixFormat(client,
455                                                              &format)));
456  CoreAudioUtil::SharedModeInitialize(client, &format, NULL,
457                                      &endpoint_buffer_size);
458  EXPECT_GT(endpoint_buffer_size, 0u);
459
460  ScopedComPtr<IAudioRenderClient> render_client(
461      CoreAudioUtil::CreateRenderClient(client));
462  EXPECT_TRUE(render_client);
463
464  // The endpoint audio buffer should not be filled up by default after being
465  // created.
466  UINT32 num_queued_frames = 0;
467  client->GetCurrentPadding(&num_queued_frames);
468  EXPECT_EQ(num_queued_frames, 0u);
469
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
473  // buffer.
474  EXPECT_TRUE(CoreAudioUtil::FillRenderEndpointBufferWithSilence(
475                  client, render_client));
476  client->GetCurrentPadding(&num_queued_frames);
477  EXPECT_EQ(num_queued_frames, endpoint_buffer_size);
478}
479
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.
484TEST_F(CoreAudioUtilWinTest, GetMatchingOutputDeviceID) {
485  if (!CanRunAudioTest())
486    return;
487
488  bool found_a_pair = false;
489
490  ScopedComPtr<IMMDeviceEnumerator> enumerator(
491      CoreAudioUtil::CreateDeviceEnumerator());
492  ASSERT_TRUE(enumerator);
493
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())));
499  UINT count = 0;
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);
506    std::string id;
507    WideToUTF8(wide_id, wcslen(wide_id), &id);
508    found_a_pair = !CoreAudioUtil::GetMatchingOutputDeviceID(id).empty();
509  }
510
511  EXPECT_TRUE(found_a_pair);
512}
513
514TEST_F(CoreAudioUtilWinTest, GetDefaultOutputDeviceID) {
515  if (!CanRunAudioTest())
516    return;
517
518  std::string default_device_id(CoreAudioUtil::GetDefaultOutputDeviceID());
519  EXPECT_FALSE(default_device_id.empty());
520}
521
522}  // namespace media
523