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 <windows.h>
6#include <mmsystem.h>
7
8#include "base/basictypes.h"
9#include "base/environment.h"
10#include "base/files/file_util.h"
11#include "base/memory/scoped_ptr.h"
12#include "base/message_loop/message_loop.h"
13#include "base/path_service.h"
14#include "base/test/test_timeouts.h"
15#include "base/win/scoped_com_initializer.h"
16#include "media/audio/audio_io.h"
17#include "media/audio/audio_manager_base.h"
18#include "media/audio/win/audio_low_latency_input_win.h"
19#include "media/audio/win/core_audio_util_win.h"
20#include "media/base/seekable_buffer.h"
21#include "testing/gmock/include/gmock/gmock.h"
22#include "testing/gtest/include/gtest/gtest.h"
23
24using base::win::ScopedCOMInitializer;
25using ::testing::_;
26using ::testing::AnyNumber;
27using ::testing::AtLeast;
28using ::testing::Gt;
29using ::testing::NotNull;
30
31namespace media {
32
33ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) {
34  if (++*count >= limit) {
35    loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
36  }
37}
38
39class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
40 public:
41  MOCK_METHOD4(OnData,
42               void(AudioInputStream* stream,
43                    const AudioBus* src,
44                    uint32 hardware_delay_bytes,
45                    double volume));
46  MOCK_METHOD1(OnError, void(AudioInputStream* stream));
47};
48
49class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback {
50 public:
51  FakeAudioInputCallback()
52      : error_(false),
53        data_event_(false, false),
54        num_received_audio_frames_(0) {}
55
56  bool error() const { return error_; }
57  int num_received_audio_frames() const { return num_received_audio_frames_; }
58
59  // Waits until OnData() is called on another thread.
60  void WaitForData() {
61    data_event_.Wait();
62  }
63
64  virtual void OnData(AudioInputStream* stream,
65                      const AudioBus* src,
66                      uint32 hardware_delay_bytes,
67                      double volume) OVERRIDE {
68    EXPECT_NE(hardware_delay_bytes, 0u);
69    num_received_audio_frames_ += src->frames();
70    data_event_.Signal();
71  }
72
73  virtual void OnError(AudioInputStream* stream) OVERRIDE {
74    error_ = true;
75  }
76
77 private:
78  int num_received_audio_frames_;
79  base::WaitableEvent data_event_;
80  bool error_;
81
82  DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback);
83};
84
85// This audio sink implementation should be used for manual tests only since
86// the recorded data is stored on a raw binary data file.
87class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
88 public:
89  // Allocate space for ~10 seconds of data @ 48kHz in stereo:
90  // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
91  static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10;
92
93  explicit WriteToFileAudioSink(const char* file_name, int bits_per_sample)
94      : bits_per_sample_(bits_per_sample),
95        buffer_(0, kMaxBufferSize),
96        bytes_to_write_(0) {
97    base::FilePath file_path;
98    EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path));
99    file_path = file_path.AppendASCII(file_name);
100    binary_file_ = base::OpenFile(file_path, "wb");
101    DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
102    VLOG(0) << ">> Output file: " << file_path.value() << " has been created.";
103    VLOG(0) << "bits_per_sample_:" << bits_per_sample_;
104  }
105
106  virtual ~WriteToFileAudioSink() {
107    size_t bytes_written = 0;
108    while (bytes_written < bytes_to_write_) {
109      const uint8* chunk;
110      int chunk_size;
111
112      // Stop writing if no more data is available.
113      if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
114        break;
115
116      // Write recorded data chunk to the file and prepare for next chunk.
117      fwrite(chunk, 1, chunk_size, binary_file_);
118      buffer_.Seek(chunk_size);
119      bytes_written += chunk_size;
120    }
121    base::CloseFile(binary_file_);
122  }
123
124  // AudioInputStream::AudioInputCallback implementation.
125  virtual void OnData(AudioInputStream* stream,
126                      const AudioBus* src,
127                      uint32 hardware_delay_bytes,
128                      double volume) {
129    EXPECT_EQ(bits_per_sample_, 16);
130    const int num_samples = src->frames() * src->channels();
131    scoped_ptr<int16> interleaved(new int16[num_samples]);
132    const int bytes_per_sample = sizeof(*interleaved);
133    src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get());
134
135    // Store data data in a temporary buffer to avoid making blocking
136    // fwrite() calls in the audio callback. The complete buffer will be
137    // written to file in the destructor.
138    const int size = bytes_per_sample * num_samples;
139    if (buffer_.Append((const uint8*)interleaved.get(), size)) {
140      bytes_to_write_ += size;
141    }
142  }
143
144  virtual void OnError(AudioInputStream* stream) {}
145
146 private:
147  int bits_per_sample_;
148  media::SeekableBuffer buffer_;
149  FILE* binary_file_;
150  size_t bytes_to_write_;
151};
152
153// Convenience method which ensures that we are not running on the build
154// bots and that at least one valid input device can be found. We also
155// verify that we are not running on XP since the low-latency (WASAPI-
156// based) version requires Windows Vista or higher.
157static bool CanRunAudioTests(AudioManager* audio_man) {
158  if (!CoreAudioUtil::IsSupported()) {
159    LOG(WARNING) << "This tests requires Windows Vista or higher.";
160    return false;
161  }
162  // TODO(henrika): note that we use Wave today to query the number of
163  // existing input devices.
164  bool input = audio_man->HasAudioInputDevices();
165  LOG_IF(WARNING, !input) << "No input device detected.";
166  return input;
167}
168
169// Convenience method which creates a default AudioInputStream object but
170// also allows the user to modify the default settings.
171class AudioInputStreamWrapper {
172 public:
173  explicit AudioInputStreamWrapper(AudioManager* audio_manager)
174      : com_init_(ScopedCOMInitializer::kMTA),
175        audio_man_(audio_manager),
176        default_params_(
177            audio_manager->GetInputStreamParameters(
178                  AudioManagerBase::kDefaultDeviceId)) {
179    EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
180    frames_per_buffer_ = default_params_.frames_per_buffer();
181    // We expect the default buffer size to be a 10ms buffer.
182    EXPECT_EQ(frames_per_buffer_, sample_rate() / 100);
183  }
184
185  ~AudioInputStreamWrapper() {}
186
187  // Creates AudioInputStream object using default parameters.
188  AudioInputStream* Create() {
189    return CreateInputStream();
190  }
191
192  // Creates AudioInputStream object using non-default parameters where the
193  // frame size is modified.
194  AudioInputStream* Create(int frames_per_buffer) {
195    frames_per_buffer_ = frames_per_buffer;
196    return CreateInputStream();
197  }
198
199  AudioParameters::Format format() const { return default_params_.format(); }
200  int channels() const {
201    return ChannelLayoutToChannelCount(default_params_.channel_layout());
202  }
203  int bits_per_sample() const { return default_params_.bits_per_sample(); }
204  int sample_rate() const { return default_params_.sample_rate(); }
205  int frames_per_buffer() const { return frames_per_buffer_; }
206
207 private:
208  AudioInputStream* CreateInputStream() {
209    AudioInputStream* ais = audio_man_->MakeAudioInputStream(
210        AudioParameters(format(), default_params_.channel_layout(),
211                        sample_rate(), bits_per_sample(), frames_per_buffer_,
212                        default_params_.effects()),
213        AudioManagerBase::kDefaultDeviceId);
214    EXPECT_TRUE(ais);
215    return ais;
216  }
217
218  ScopedCOMInitializer com_init_;
219  AudioManager* audio_man_;
220  const AudioParameters default_params_;
221  int frames_per_buffer_;
222};
223
224// Convenience method which creates a default AudioInputStream object.
225static AudioInputStream* CreateDefaultAudioInputStream(
226    AudioManager* audio_manager) {
227  AudioInputStreamWrapper aisw(audio_manager);
228  AudioInputStream* ais = aisw.Create();
229  return ais;
230}
231
232class ScopedAudioInputStream {
233 public:
234  explicit ScopedAudioInputStream(AudioInputStream* stream)
235      : stream_(stream) {}
236
237  ~ScopedAudioInputStream() {
238    if (stream_)
239      stream_->Close();
240  }
241
242  void Close() {
243    if (stream_)
244      stream_->Close();
245    stream_ = NULL;
246  }
247
248  AudioInputStream* operator->() {
249    return stream_;
250  }
251
252  AudioInputStream* get() const { return stream_; }
253
254  void Reset(AudioInputStream* new_stream) {
255    Close();
256    stream_ = new_stream;
257  }
258
259 private:
260  AudioInputStream* stream_;
261
262  DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
263};
264
265// Verify that we can retrieve the current hardware/mixing sample rate
266// for all available input devices.
267TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
268  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
269  if (!CanRunAudioTests(audio_manager.get()))
270    return;
271
272  ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
273
274  // Retrieve a list of all available input devices.
275  media::AudioDeviceNames device_names;
276  audio_manager->GetAudioInputDeviceNames(&device_names);
277
278  // Scan all available input devices and repeat the same test for all of them.
279  for (media::AudioDeviceNames::const_iterator it = device_names.begin();
280       it != device_names.end(); ++it) {
281    // Retrieve the hardware sample rate given a specified audio input device.
282    AudioParameters params = WASAPIAudioInputStream::GetInputStreamParameters(
283        it->unique_id);
284    EXPECT_GE(params.sample_rate(), 0);
285  }
286}
287
288// Test Create(), Close() calling sequence.
289TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
290  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
291  if (!CanRunAudioTests(audio_manager.get()))
292    return;
293  ScopedAudioInputStream ais(
294      CreateDefaultAudioInputStream(audio_manager.get()));
295  ais.Close();
296}
297
298// Test Open(), Close() calling sequence.
299TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
300  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
301  if (!CanRunAudioTests(audio_manager.get()))
302    return;
303  ScopedAudioInputStream ais(
304      CreateDefaultAudioInputStream(audio_manager.get()));
305  EXPECT_TRUE(ais->Open());
306  ais.Close();
307}
308
309// Test Open(), Start(), Close() calling sequence.
310TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
311  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
312  if (!CanRunAudioTests(audio_manager.get()))
313    return;
314  ScopedAudioInputStream ais(
315      CreateDefaultAudioInputStream(audio_manager.get()));
316  EXPECT_TRUE(ais->Open());
317  MockAudioInputCallback sink;
318  ais->Start(&sink);
319  ais.Close();
320}
321
322// Test Open(), Start(), Stop(), Close() calling sequence.
323TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
324  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
325  if (!CanRunAudioTests(audio_manager.get()))
326    return;
327  ScopedAudioInputStream ais(
328      CreateDefaultAudioInputStream(audio_manager.get()));
329  EXPECT_TRUE(ais->Open());
330  MockAudioInputCallback sink;
331  ais->Start(&sink);
332  ais->Stop();
333  ais.Close();
334}
335
336// Test some additional calling sequences.
337TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
338  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
339  if (!CanRunAudioTests(audio_manager.get()))
340    return;
341  ScopedAudioInputStream ais(
342      CreateDefaultAudioInputStream(audio_manager.get()));
343  WASAPIAudioInputStream* wais =
344      static_cast<WASAPIAudioInputStream*>(ais.get());
345
346  // Open(), Open() should fail the second time.
347  EXPECT_TRUE(ais->Open());
348  EXPECT_FALSE(ais->Open());
349
350  MockAudioInputCallback sink;
351
352  // Start(), Start() is a valid calling sequence (second call does nothing).
353  ais->Start(&sink);
354  EXPECT_TRUE(wais->started());
355  ais->Start(&sink);
356  EXPECT_TRUE(wais->started());
357
358  // Stop(), Stop() is a valid calling sequence (second call does nothing).
359  ais->Stop();
360  EXPECT_FALSE(wais->started());
361  ais->Stop();
362  EXPECT_FALSE(wais->started());
363  ais.Close();
364}
365
366TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
367  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
368  if (!CanRunAudioTests(audio_manager.get()))
369    return;
370
371  int count = 0;
372  base::MessageLoopForUI loop;
373
374  // 10 ms packet size.
375
376  // Create default WASAPI input stream which records in stereo using
377  // the shared mixing rate. The default buffer size is 10ms.
378  AudioInputStreamWrapper aisw(audio_manager.get());
379  ScopedAudioInputStream ais(aisw.Create());
380  EXPECT_TRUE(ais->Open());
381
382  MockAudioInputCallback sink;
383
384  // Derive the expected size in bytes of each recorded packet.
385  uint32 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
386      (aisw.bits_per_sample() / 8);
387
388  // We use 10ms packets and will run the test until ten packets are received.
389  // All should contain valid packets of the same size and a valid delay
390  // estimate.
391  EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
392      .Times(AtLeast(10))
393      .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
394  ais->Start(&sink);
395  loop.Run();
396  ais->Stop();
397
398  // Store current packet size (to be used in the subsequent tests).
399  int frames_per_buffer_10ms = aisw.frames_per_buffer();
400
401  ais.Close();
402
403  // 20 ms packet size.
404
405  count = 0;
406  ais.Reset(aisw.Create(2 * frames_per_buffer_10ms));
407  EXPECT_TRUE(ais->Open());
408  bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
409      (aisw.bits_per_sample() / 8);
410
411  EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
412      .Times(AtLeast(10))
413      .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
414  ais->Start(&sink);
415  loop.Run();
416  ais->Stop();
417  ais.Close();
418
419  // 5 ms packet size.
420
421  count = 0;
422  ais.Reset(aisw.Create(frames_per_buffer_10ms / 2));
423  EXPECT_TRUE(ais->Open());
424  bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
425    (aisw.bits_per_sample() / 8);
426
427  EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
428      .Times(AtLeast(10))
429      .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
430  ais->Start(&sink);
431  loop.Run();
432  ais->Stop();
433  ais.Close();
434}
435
436// Test that we can capture a stream in loopback.
437TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
438  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
439  if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported())
440    return;
441
442  AudioParameters params = audio_manager->GetInputStreamParameters(
443      AudioManagerBase::kLoopbackInputDeviceId);
444  EXPECT_EQ(params.effects(), 0);
445
446  AudioParameters output_params =
447      audio_manager->GetOutputStreamParameters(std::string());
448  EXPECT_EQ(params.sample_rate(), output_params.sample_rate());
449  EXPECT_EQ(params.channel_layout(), output_params.channel_layout());
450
451  ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream(
452      params, AudioManagerBase::kLoopbackInputDeviceId));
453  ASSERT_TRUE(stream->Open());
454  FakeAudioInputCallback sink;
455  stream->Start(&sink);
456  ASSERT_FALSE(sink.error());
457
458  sink.WaitForData();
459  stream.Close();
460
461  EXPECT_GT(sink.num_received_audio_frames(), 0);
462  EXPECT_FALSE(sink.error());
463}
464
465// This test is intended for manual tests and should only be enabled
466// when it is required to store the captured data on a local file.
467// By default, GTest will print out YOU HAVE 1 DISABLED TEST.
468// To include disabled tests in test execution, just invoke the test program
469// with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
470// environment variable to a value greater than 0.
471TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
472  scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
473  if (!CanRunAudioTests(audio_manager.get()))
474    return;
475
476  // Name of the output PCM file containing captured data. The output file
477  // will be stored in the directory containing 'media_unittests.exe'.
478  // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
479  const char* file_name = "out_stereo_10sec.pcm";
480
481  AudioInputStreamWrapper aisw(audio_manager.get());
482  ScopedAudioInputStream ais(aisw.Create());
483  EXPECT_TRUE(ais->Open());
484
485  VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
486  WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample());
487  VLOG(0) << ">> Speak into the default microphone while recording.";
488  ais->Start(&file_sink);
489  base::PlatformThread::Sleep(TestTimeouts::action_timeout());
490  ais->Stop();
491  VLOG(0) << ">> Recording has stopped.";
492  ais.Close();
493}
494
495}  // namespace media
496