Blink roll 25b6bd3a7a131ffe68d809546ad1a20707915cdc:3a503f41ae42e5b79cfcd2ff10e65afde...
[chromium-blink-merge.git] / media / audio / win / audio_low_latency_input_win_unittest.cc
blob4a4939ed68b92f7c1a4c956e8ca70be668d10716
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/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"
24 using base::win::ScopedCOMInitializer;
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 : error_(false),
53 data_event_(false, false),
54 num_received_audio_frames_(0) {}
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 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();
73 virtual void OnError(AudioInputStream* stream) override {
74 error_ = true;
77 private:
78 int num_received_audio_frames_;
79 base::WaitableEvent data_event_;
80 bool error_;
82 DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback);
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.
87 class 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;
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_;
106 virtual ~WriteToFileAudioSink() {
107 size_t bytes_written = 0;
108 while (bytes_written < bytes_to_write_) {
109 const uint8* chunk;
110 int chunk_size;
112 // Stop writing if no more data is available.
113 if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
114 break;
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;
121 base::CloseFile(binary_file_);
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());
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;
144 virtual void OnError(AudioInputStream* stream) {}
146 private:
147 int bits_per_sample_;
148 media::SeekableBuffer buffer_;
149 FILE* binary_file_;
150 size_t bytes_to_write_;
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.
157 static bool CanRunAudioTests(AudioManager* audio_man) {
158 if (!CoreAudioUtil::IsSupported()) {
159 LOG(WARNING) << "This tests requires Windows Vista or higher.";
160 return false;
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;
169 // Convenience method which creates a default AudioInputStream object but
170 // also allows the user to modify the default settings.
171 class 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);
185 ~AudioInputStreamWrapper() {}
187 // Creates AudioInputStream object using default parameters.
188 AudioInputStream* Create() {
189 return CreateInputStream();
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();
199 AudioParameters::Format format() const { return default_params_.format(); }
200 int channels() const {
201 return ChannelLayoutToChannelCount(default_params_.channel_layout());
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_; }
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;
218 ScopedCOMInitializer com_init_;
219 AudioManager* audio_man_;
220 const AudioParameters default_params_;
221 int frames_per_buffer_;
224 // Convenience method which creates a default AudioInputStream object.
225 static AudioInputStream* CreateDefaultAudioInputStream(
226 AudioManager* audio_manager) {
227 AudioInputStreamWrapper aisw(audio_manager);
228 AudioInputStream* ais = aisw.Create();
229 return ais;
232 class ScopedAudioInputStream {
233 public:
234 explicit ScopedAudioInputStream(AudioInputStream* stream)
235 : stream_(stream) {}
237 ~ScopedAudioInputStream() {
238 if (stream_)
239 stream_->Close();
242 void Close() {
243 if (stream_)
244 stream_->Close();
245 stream_ = NULL;
248 AudioInputStream* operator->() {
249 return stream_;
252 AudioInputStream* get() const { return stream_; }
254 void Reset(AudioInputStream* new_stream) {
255 Close();
256 stream_ = new_stream;
259 private:
260 AudioInputStream* stream_;
262 DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
265 // Verify that we can retrieve the current hardware/mixing sample rate
266 // for all available input devices.
267 TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
268 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
269 if (!CanRunAudioTests(audio_manager.get()))
270 return;
272 ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
274 // Retrieve a list of all available input devices.
275 media::AudioDeviceNames device_names;
276 audio_manager->GetAudioInputDeviceNames(&device_names);
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);
288 // Test Create(), Close() calling sequence.
289 TEST(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();
298 // Test Open(), Close() calling sequence.
299 TEST(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();
309 // Test Open(), Start(), Close() calling sequence.
310 TEST(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();
322 // Test Open(), Start(), Stop(), Close() calling sequence.
323 TEST(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();
336 // Test some additional calling sequences.
337 TEST(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());
346 // Open(), Open() should fail the second time.
347 EXPECT_TRUE(ais->Open());
348 EXPECT_FALSE(ais->Open());
350 MockAudioInputCallback sink;
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());
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();
366 TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
367 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
368 if (!CanRunAudioTests(audio_manager.get()))
369 return;
371 int count = 0;
372 base::MessageLoopForUI loop;
374 // 10 ms packet size.
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());
382 MockAudioInputCallback sink;
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);
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();
398 // Store current packet size (to be used in the subsequent tests).
399 int frames_per_buffer_10ms = aisw.frames_per_buffer();
401 ais.Close();
403 // 20 ms packet size.
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);
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();
419 // 5 ms packet size.
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);
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();
436 // Test that we can capture a stream in loopback.
437 TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
438 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
439 if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported())
440 return;
442 AudioParameters params = audio_manager->GetInputStreamParameters(
443 AudioManagerBase::kLoopbackInputDeviceId);
444 EXPECT_EQ(params.effects(), 0);
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());
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());
458 sink.WaitForData();
459 stream.Close();
461 EXPECT_GT(sink.num_received_audio_frames(), 0);
462 EXPECT_FALSE(sink.error());
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.
471 TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
472 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
473 if (!CanRunAudioTests(audio_manager.get()))
474 return;
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";
481 AudioInputStreamWrapper aisw(audio_manager.get());
482 ScopedAudioInputStream ais(aisw.Create());
483 EXPECT_TRUE(ais->Open());
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();
495 } // namespace media