Merge Chromium + Blink git repositories
[chromium-blink-merge.git] / media / audio / win / audio_low_latency_input_win_unittest.cc
blob5b8f46e551c933f87f1c33e4f2bcb07bc0ef4cf1
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 <windows.h>
6 #include <mmsystem.h>
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/audio_unittest_util.h"
19 #include "media/audio/win/audio_low_latency_input_win.h"
20 #include "media/audio/win/core_audio_util_win.h"
21 #include "media/base/seekable_buffer.h"
22 #include "testing/gmock/include/gmock/gmock.h"
23 #include "testing/gtest/include/gtest/gtest.h"
25 using ::testing::_;
26 using ::testing::AnyNumber;
27 using ::testing::AtLeast;
28 using ::testing::Gt;
29 using ::testing::NotNull;
31 namespace media {
33 ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) {
34 if (++*count >= limit) {
35 loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
39 class 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));
49 class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback {
50 public:
51 FakeAudioInputCallback()
52 : num_received_audio_frames_(0),
53 data_event_(false, false),
54 error_(false) {}
56 bool error() const { return error_; }
57 int num_received_audio_frames() const { return num_received_audio_frames_; }
59 // Waits until OnData() is called on another thread.
60 void WaitForData() {
61 data_event_.Wait();
64 void OnData(AudioInputStream* stream,
65 const AudioBus* src,
66 uint32 hardware_delay_bytes,
67 double volume) override {
68 EXPECT_GE(hardware_delay_bytes, 0u);
69 EXPECT_LT(hardware_delay_bytes, 0xFFFFu); // Arbitrarily picked.
70 num_received_audio_frames_ += src->frames();
71 data_event_.Signal();
74 void OnError(AudioInputStream* stream) override {
75 error_ = true;
78 private:
79 int num_received_audio_frames_;
80 base::WaitableEvent data_event_;
81 bool error_;
83 DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback);
86 // This audio sink implementation should be used for manual tests only since
87 // the recorded data is stored on a raw binary data file.
88 class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
89 public:
90 // Allocate space for ~10 seconds of data @ 48kHz in stereo:
91 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
92 static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10;
94 explicit WriteToFileAudioSink(const char* file_name, int bits_per_sample)
95 : bits_per_sample_(bits_per_sample),
96 buffer_(0, kMaxBufferSize),
97 bytes_to_write_(0) {
98 base::FilePath file_path;
99 EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path));
100 file_path = file_path.AppendASCII(file_name);
101 binary_file_ = base::OpenFile(file_path, "wb");
102 DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
103 VLOG(0) << ">> Output file: " << file_path.value() << " has been created.";
104 VLOG(0) << "bits_per_sample_:" << bits_per_sample_;
107 ~WriteToFileAudioSink() override {
108 size_t bytes_written = 0;
109 while (bytes_written < bytes_to_write_) {
110 const uint8* chunk;
111 int chunk_size;
113 // Stop writing if no more data is available.
114 if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
115 break;
117 // Write recorded data chunk to the file and prepare for next chunk.
118 fwrite(chunk, 1, chunk_size, binary_file_);
119 buffer_.Seek(chunk_size);
120 bytes_written += chunk_size;
122 base::CloseFile(binary_file_);
125 // AudioInputStream::AudioInputCallback implementation.
126 void OnData(AudioInputStream* stream,
127 const AudioBus* src,
128 uint32 hardware_delay_bytes,
129 double volume) override {
130 EXPECT_EQ(bits_per_sample_, 16);
131 const int num_samples = src->frames() * src->channels();
132 scoped_ptr<int16> interleaved(new int16[num_samples]);
133 const int bytes_per_sample = sizeof(*interleaved);
134 src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get());
136 // Store data data in a temporary buffer to avoid making blocking
137 // fwrite() calls in the audio callback. The complete buffer will be
138 // written to file in the destructor.
139 const int size = bytes_per_sample * num_samples;
140 if (buffer_.Append((const uint8*)interleaved.get(), size)) {
141 bytes_to_write_ += size;
145 void OnError(AudioInputStream* stream) override {}
147 private:
148 int bits_per_sample_;
149 media::SeekableBuffer buffer_;
150 FILE* binary_file_;
151 size_t bytes_to_write_;
154 static bool HasCoreAudioAndInputDevices(AudioManager* audio_man) {
155 // The low-latency (WASAPI-based) version requires Windows Vista or higher.
156 // TODO(henrika): note that we use Wave today to query the number of
157 // existing input devices.
158 return CoreAudioUtil::IsSupported() && audio_man->HasAudioInputDevices();
161 // Convenience method which creates a default AudioInputStream object but
162 // also allows the user to modify the default settings.
163 class AudioInputStreamWrapper {
164 public:
165 explicit AudioInputStreamWrapper(AudioManager* audio_manager)
166 : audio_man_(audio_manager) {
167 EXPECT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
168 AudioManagerBase::kDefaultDeviceId, false, &default_params_)));
169 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
170 frames_per_buffer_ = default_params_.frames_per_buffer();
173 ~AudioInputStreamWrapper() {}
175 // Creates AudioInputStream object using default parameters.
176 AudioInputStream* Create() {
177 return CreateInputStream();
180 // Creates AudioInputStream object using non-default parameters where the
181 // frame size is modified.
182 AudioInputStream* Create(int frames_per_buffer) {
183 frames_per_buffer_ = frames_per_buffer;
184 return CreateInputStream();
187 AudioParameters::Format format() const { return default_params_.format(); }
188 int channels() const {
189 return ChannelLayoutToChannelCount(default_params_.channel_layout());
191 int bits_per_sample() const { return default_params_.bits_per_sample(); }
192 int sample_rate() const { return default_params_.sample_rate(); }
193 int frames_per_buffer() const { return frames_per_buffer_; }
195 private:
196 AudioInputStream* CreateInputStream() {
197 AudioParameters params = default_params_;
198 params.set_frames_per_buffer(frames_per_buffer_);
199 AudioInputStream* ais = audio_man_->MakeAudioInputStream(
200 params, AudioManagerBase::kDefaultDeviceId);
201 EXPECT_TRUE(ais);
202 return ais;
205 AudioManager* audio_man_;
206 AudioParameters default_params_;
207 int frames_per_buffer_;
210 // Convenience method which creates a default AudioInputStream object.
211 static AudioInputStream* CreateDefaultAudioInputStream(
212 AudioManager* audio_manager) {
213 AudioInputStreamWrapper aisw(audio_manager);
214 AudioInputStream* ais = aisw.Create();
215 return ais;
218 class ScopedAudioInputStream {
219 public:
220 explicit ScopedAudioInputStream(AudioInputStream* stream)
221 : stream_(stream) {}
223 ~ScopedAudioInputStream() {
224 if (stream_)
225 stream_->Close();
228 void Close() {
229 if (stream_)
230 stream_->Close();
231 stream_ = NULL;
234 AudioInputStream* operator->() {
235 return stream_;
238 AudioInputStream* get() const { return stream_; }
240 void Reset(AudioInputStream* new_stream) {
241 Close();
242 stream_ = new_stream;
245 private:
246 AudioInputStream* stream_;
248 DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
251 // Verify that we can retrieve the current hardware/mixing sample rate
252 // for all available input devices.
253 TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
254 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
255 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get()));
257 // Retrieve a list of all available input devices.
258 media::AudioDeviceNames device_names;
259 audio_manager->GetAudioInputDeviceNames(&device_names);
261 // Scan all available input devices and repeat the same test for all of them.
262 for (media::AudioDeviceNames::const_iterator it = device_names.begin();
263 it != device_names.end(); ++it) {
264 // Retrieve the hardware sample rate given a specified audio input device.
265 AudioParameters params;
266 ASSERT_TRUE(SUCCEEDED(CoreAudioUtil::GetPreferredAudioParameters(
267 it->unique_id, false, &params)));
268 EXPECT_GE(params.sample_rate(), 0);
272 // Test Create(), Close() calling sequence.
273 TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
274 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
275 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get()));
276 ScopedAudioInputStream ais(
277 CreateDefaultAudioInputStream(audio_manager.get()));
278 ais.Close();
281 // Test Open(), Close() calling sequence.
282 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
283 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
284 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get()));
285 ScopedAudioInputStream ais(
286 CreateDefaultAudioInputStream(audio_manager.get()));
287 EXPECT_TRUE(ais->Open());
288 ais.Close();
291 // Test Open(), Start(), Close() calling sequence.
292 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
293 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
294 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get()));
295 ScopedAudioInputStream ais(
296 CreateDefaultAudioInputStream(audio_manager.get()));
297 EXPECT_TRUE(ais->Open());
298 MockAudioInputCallback sink;
299 ais->Start(&sink);
300 ais.Close();
303 // Test Open(), Start(), Stop(), Close() calling sequence.
304 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
305 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
306 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get()));
307 ScopedAudioInputStream ais(
308 CreateDefaultAudioInputStream(audio_manager.get()));
309 EXPECT_TRUE(ais->Open());
310 MockAudioInputCallback sink;
311 ais->Start(&sink);
312 ais->Stop();
313 ais.Close();
316 // Test some additional calling sequences.
317 TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
318 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
319 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get()));
320 ScopedAudioInputStream ais(
321 CreateDefaultAudioInputStream(audio_manager.get()));
322 WASAPIAudioInputStream* wais =
323 static_cast<WASAPIAudioInputStream*>(ais.get());
325 // Open(), Open() should fail the second time.
326 EXPECT_TRUE(ais->Open());
327 EXPECT_FALSE(ais->Open());
329 MockAudioInputCallback sink;
331 // Start(), Start() is a valid calling sequence (second call does nothing).
332 ais->Start(&sink);
333 EXPECT_TRUE(wais->started());
334 ais->Start(&sink);
335 EXPECT_TRUE(wais->started());
337 // Stop(), Stop() is a valid calling sequence (second call does nothing).
338 ais->Stop();
339 EXPECT_FALSE(wais->started());
340 ais->Stop();
341 EXPECT_FALSE(wais->started());
342 ais.Close();
345 TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
346 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
347 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get()));
349 int count = 0;
350 base::MessageLoopForUI loop;
352 // 10 ms packet size.
354 // Create default WASAPI input stream which records in stereo using
355 // the shared mixing rate. The default buffer size is 10ms.
356 AudioInputStreamWrapper aisw(audio_manager.get());
357 ScopedAudioInputStream ais(aisw.Create());
358 EXPECT_TRUE(ais->Open());
360 MockAudioInputCallback sink;
362 // Derive the expected size in bytes of each recorded packet.
363 uint32 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
364 (aisw.bits_per_sample() / 8);
366 // We use 10ms packets and will run the test until ten packets are received.
367 // All should contain valid packets of the same size and a valid delay
368 // estimate.
369 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _))
370 .Times(AtLeast(10))
371 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
372 ais->Start(&sink);
373 loop.Run();
374 ais->Stop();
376 // Store current packet size (to be used in the subsequent tests).
377 int frames_per_buffer_10ms = aisw.frames_per_buffer();
379 ais.Close();
381 // 20 ms packet size.
383 count = 0;
384 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms));
385 EXPECT_TRUE(ais->Open());
386 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
387 (aisw.bits_per_sample() / 8);
389 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _))
390 .Times(AtLeast(10))
391 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
392 ais->Start(&sink);
393 loop.Run();
394 ais->Stop();
395 ais.Close();
397 // 5 ms packet size.
399 count = 0;
400 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2));
401 EXPECT_TRUE(ais->Open());
402 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
403 (aisw.bits_per_sample() / 8);
405 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), _, _))
406 .Times(AtLeast(10))
407 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
408 ais->Start(&sink);
409 loop.Run();
410 ais->Stop();
411 ais.Close();
414 // Test that we can capture a stream in loopback.
415 TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
416 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
417 ABORT_AUDIO_TEST_IF_NOT(audio_manager->HasAudioOutputDevices() &&
418 CoreAudioUtil::IsSupported());
420 AudioParameters params = audio_manager->GetInputStreamParameters(
421 AudioManagerBase::kLoopbackInputDeviceId);
422 EXPECT_EQ(params.effects(), 0);
424 AudioParameters output_params =
425 audio_manager->GetOutputStreamParameters(std::string());
426 EXPECT_EQ(params.sample_rate(), output_params.sample_rate());
427 EXPECT_EQ(params.channel_layout(), output_params.channel_layout());
429 ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream(
430 params, AudioManagerBase::kLoopbackInputDeviceId));
431 ASSERT_TRUE(stream->Open());
432 FakeAudioInputCallback sink;
433 stream->Start(&sink);
434 ASSERT_FALSE(sink.error());
436 sink.WaitForData();
437 stream.Close();
439 EXPECT_GT(sink.num_received_audio_frames(), 0);
440 EXPECT_FALSE(sink.error());
443 // This test is intended for manual tests and should only be enabled
444 // when it is required to store the captured data on a local file.
445 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
446 // To include disabled tests in test execution, just invoke the test program
447 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
448 // environment variable to a value greater than 0.
449 TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
450 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
451 ABORT_AUDIO_TEST_IF_NOT(HasCoreAudioAndInputDevices(audio_manager.get()));
453 // Name of the output PCM file containing captured data. The output file
454 // will be stored in the directory containing 'media_unittests.exe'.
455 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
456 const char* file_name = "out_stereo_10sec.pcm";
458 AudioInputStreamWrapper aisw(audio_manager.get());
459 ScopedAudioInputStream ais(aisw.Create());
460 EXPECT_TRUE(ais->Open());
462 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
463 WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample());
464 VLOG(0) << ">> Speak into the default microphone while recording.";
465 ais->Start(&file_sink);
466 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
467 ais->Stop();
468 VLOG(0) << ">> Recording has stopped.";
469 ais.Close();
472 } // namespace media