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.
8 #include "base/basictypes.h"
9 #include "base/environment.h"
10 #include "base/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;
26 using ::testing::AnyNumber;
27 using ::testing::AtLeast;
29 using ::testing::NotNull;
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 {
41 MOCK_METHOD5(OnData, void(AudioInputStream* stream,
42 const uint8* src, uint32 size,
43 uint32 hardware_delay_bytes, double volume));
44 MOCK_METHOD1(OnError, void(AudioInputStream* stream));
47 class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback {
49 FakeAudioInputCallback()
51 data_event_(false, false) {
54 const std::vector<uint8>& received_data() const { return received_data_; }
55 bool error() const { return error_; }
57 // Waits until OnData() is called on another thread.
62 virtual void OnData(AudioInputStream* stream,
63 const uint8* src, uint32 size,
64 uint32 hardware_delay_bytes, double volume) OVERRIDE {
65 received_data_.insert(received_data_.end(), src, src + size);
69 virtual void OnError(AudioInputStream* stream) OVERRIDE {
74 std::vector<uint8> received_data_;
75 base::WaitableEvent data_event_;
78 DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback);
81 // This audio sink implementation should be used for manual tests only since
82 // the recorded data is stored on a raw binary data file.
83 class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
85 // Allocate space for ~10 seconds of data @ 48kHz in stereo:
86 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
87 static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10;
89 explicit WriteToFileAudioSink(const char* file_name)
90 : buffer_(0, kMaxBufferSize),
92 base::FilePath file_path;
93 EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path));
94 file_path = file_path.AppendASCII(file_name);
95 binary_file_ = base::OpenFile(file_path, "wb");
96 DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
97 VLOG(0) << ">> Output file: " << file_path.value() << " has been created.";
100 virtual ~WriteToFileAudioSink() {
101 size_t bytes_written = 0;
102 while (bytes_written < bytes_to_write_) {
106 // Stop writing if no more data is available.
107 if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
110 // Write recorded data chunk to the file and prepare for next chunk.
111 fwrite(chunk, 1, chunk_size, binary_file_);
112 buffer_.Seek(chunk_size);
113 bytes_written += chunk_size;
115 base::CloseFile(binary_file_);
118 // AudioInputStream::AudioInputCallback implementation.
119 virtual void OnData(AudioInputStream* stream,
122 uint32 hardware_delay_bytes,
124 // Store data data in a temporary buffer to avoid making blocking
125 // fwrite() calls in the audio callback. The complete buffer will be
126 // written to file in the destructor.
127 if (buffer_.Append(src, size)) {
128 bytes_to_write_ += size;
132 virtual void OnError(AudioInputStream* stream) {}
135 media::SeekableBuffer buffer_;
137 size_t bytes_to_write_;
140 // Convenience method which ensures that we are not running on the build
141 // bots and that at least one valid input device can be found. We also
142 // verify that we are not running on XP since the low-latency (WASAPI-
143 // based) version requires Windows Vista or higher.
144 static bool CanRunAudioTests(AudioManager* audio_man) {
145 if (!CoreAudioUtil::IsSupported()) {
146 LOG(WARNING) << "This tests requires Windows Vista or higher.";
149 // TODO(henrika): note that we use Wave today to query the number of
150 // existing input devices.
151 bool input = audio_man->HasAudioInputDevices();
152 LOG_IF(WARNING, !input) << "No input device detected.";
156 // Convenience method which creates a default AudioInputStream object but
157 // also allows the user to modify the default settings.
158 class AudioInputStreamWrapper {
160 explicit AudioInputStreamWrapper(AudioManager* audio_manager)
161 : com_init_(ScopedCOMInitializer::kMTA),
162 audio_man_(audio_manager),
164 audio_manager->GetInputStreamParameters(
165 AudioManagerBase::kDefaultDeviceId)) {
166 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
167 frames_per_buffer_ = default_params_.frames_per_buffer();
168 // We expect the default buffer size to be a 10ms buffer.
169 EXPECT_EQ(frames_per_buffer_, sample_rate() / 100);
172 ~AudioInputStreamWrapper() {}
174 // Creates AudioInputStream object using default parameters.
175 AudioInputStream* Create() {
176 return CreateInputStream();
179 // Creates AudioInputStream object using non-default parameters where the
180 // frame size is modified.
181 AudioInputStream* Create(int frames_per_buffer) {
182 frames_per_buffer_ = frames_per_buffer;
183 return CreateInputStream();
186 AudioParameters::Format format() const { return default_params_.format(); }
187 int channels() const {
188 return ChannelLayoutToChannelCount(default_params_.channel_layout());
190 int bits_per_sample() const { return default_params_.bits_per_sample(); }
191 int sample_rate() const { return default_params_.sample_rate(); }
192 int frames_per_buffer() const { return frames_per_buffer_; }
195 AudioInputStream* CreateInputStream() {
196 AudioInputStream* ais = audio_man_->MakeAudioInputStream(
197 AudioParameters(format(), default_params_.channel_layout(),
198 default_params_.input_channels(),
199 sample_rate(), bits_per_sample(), frames_per_buffer_,
200 default_params_.effects()),
201 AudioManagerBase::kDefaultDeviceId);
206 ScopedCOMInitializer com_init_;
207 AudioManager* audio_man_;
208 const AudioParameters default_params_;
209 int frames_per_buffer_;
212 // Convenience method which creates a default AudioInputStream object.
213 static AudioInputStream* CreateDefaultAudioInputStream(
214 AudioManager* audio_manager) {
215 AudioInputStreamWrapper aisw(audio_manager);
216 AudioInputStream* ais = aisw.Create();
220 class ScopedAudioInputStream {
222 explicit ScopedAudioInputStream(AudioInputStream* stream)
225 ~ScopedAudioInputStream() {
236 AudioInputStream* operator->() {
240 AudioInputStream* get() const { return stream_; }
242 void Reset(AudioInputStream* new_stream) {
244 stream_ = new_stream;
248 AudioInputStream* stream_;
250 DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
253 // Verify that we can retrieve the current hardware/mixing sample rate
254 // for all available input devices.
255 TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
256 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
257 if (!CanRunAudioTests(audio_manager.get()))
260 ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
262 // Retrieve a list of all available input devices.
263 media::AudioDeviceNames device_names;
264 audio_manager->GetAudioInputDeviceNames(&device_names);
266 // Scan all available input devices and repeat the same test for all of them.
267 for (media::AudioDeviceNames::const_iterator it = device_names.begin();
268 it != device_names.end(); ++it) {
269 // Retrieve the hardware sample rate given a specified audio input device.
270 AudioParameters params = WASAPIAudioInputStream::GetInputStreamParameters(
272 EXPECT_GE(params.sample_rate(), 0);
276 // Test Create(), Close() calling sequence.
277 TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
278 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
279 if (!CanRunAudioTests(audio_manager.get()))
281 ScopedAudioInputStream ais(
282 CreateDefaultAudioInputStream(audio_manager.get()));
286 // Test Open(), Close() calling sequence.
287 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
288 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
289 if (!CanRunAudioTests(audio_manager.get()))
291 ScopedAudioInputStream ais(
292 CreateDefaultAudioInputStream(audio_manager.get()));
293 EXPECT_TRUE(ais->Open());
297 // Test Open(), Start(), Close() calling sequence.
298 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
299 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
300 if (!CanRunAudioTests(audio_manager.get()))
302 ScopedAudioInputStream ais(
303 CreateDefaultAudioInputStream(audio_manager.get()));
304 EXPECT_TRUE(ais->Open());
305 MockAudioInputCallback sink;
310 // Test Open(), Start(), Stop(), Close() calling sequence.
311 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
312 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
313 if (!CanRunAudioTests(audio_manager.get()))
315 ScopedAudioInputStream ais(
316 CreateDefaultAudioInputStream(audio_manager.get()));
317 EXPECT_TRUE(ais->Open());
318 MockAudioInputCallback sink;
324 // Test some additional calling sequences.
325 TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
326 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
327 if (!CanRunAudioTests(audio_manager.get()))
329 ScopedAudioInputStream ais(
330 CreateDefaultAudioInputStream(audio_manager.get()));
331 WASAPIAudioInputStream* wais =
332 static_cast<WASAPIAudioInputStream*>(ais.get());
334 // Open(), Open() should fail the second time.
335 EXPECT_TRUE(ais->Open());
336 EXPECT_FALSE(ais->Open());
338 MockAudioInputCallback sink;
340 // Start(), Start() is a valid calling sequence (second call does nothing).
342 EXPECT_TRUE(wais->started());
344 EXPECT_TRUE(wais->started());
346 // Stop(), Stop() is a valid calling sequence (second call does nothing).
348 EXPECT_FALSE(wais->started());
350 EXPECT_FALSE(wais->started());
354 TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
355 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
356 if (!CanRunAudioTests(audio_manager.get()))
360 base::MessageLoopForUI loop;
362 // 10 ms packet size.
364 // Create default WASAPI input stream which records in stereo using
365 // the shared mixing rate. The default buffer size is 10ms.
366 AudioInputStreamWrapper aisw(audio_manager.get());
367 ScopedAudioInputStream ais(aisw.Create());
368 EXPECT_TRUE(ais->Open());
370 MockAudioInputCallback sink;
372 // Derive the expected size in bytes of each recorded packet.
373 uint32 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
374 (aisw.bits_per_sample() / 8);
376 // We use 10ms packets and will run the test until ten packets are received.
377 // All should contain valid packets of the same size and a valid delay
379 EXPECT_CALL(sink, OnData(
380 ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
382 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
387 // Store current packet size (to be used in the subsequent tests).
388 int frames_per_buffer_10ms = aisw.frames_per_buffer();
392 // 20 ms packet size.
395 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms));
396 EXPECT_TRUE(ais->Open());
397 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
398 (aisw.bits_per_sample() / 8);
400 EXPECT_CALL(sink, OnData(
401 ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
403 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
412 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2));
413 EXPECT_TRUE(ais->Open());
414 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
415 (aisw.bits_per_sample() / 8);
417 EXPECT_CALL(sink, OnData(
418 ais.get(), NotNull(), bytes_per_packet, Gt(bytes_per_packet), _))
420 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
427 // Test that we can capture loopback stream.
428 TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
429 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
430 if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported())
433 AudioParameters params = audio_manager->GetInputStreamParameters(
434 AudioManagerBase::kLoopbackInputDeviceId);
435 EXPECT_EQ(params.effects(), 0);
437 AudioParameters output_params =
438 audio_manager->GetOutputStreamParameters(std::string());
439 EXPECT_EQ(params.sample_rate(), output_params.sample_rate());
440 EXPECT_EQ(params.channel_layout(), output_params.channel_layout());
442 ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream(
443 params, AudioManagerBase::kLoopbackInputDeviceId));
444 ASSERT_TRUE(stream->Open());
445 FakeAudioInputCallback sink;
446 stream->Start(&sink);
447 ASSERT_FALSE(sink.error());
452 EXPECT_FALSE(sink.received_data().empty());
453 EXPECT_FALSE(sink.error());
456 // This test is intended for manual tests and should only be enabled
457 // when it is required to store the captured data on a local file.
458 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
459 // To include disabled tests in test execution, just invoke the test program
460 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
461 // environment variable to a value greater than 0.
462 TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
463 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
464 if (!CanRunAudioTests(audio_manager.get()))
467 // Name of the output PCM file containing captured data. The output file
468 // will be stored in the directory containing 'media_unittests.exe'.
469 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
470 const char* file_name = "out_stereo_10sec.pcm";
472 AudioInputStreamWrapper aisw(audio_manager.get());
473 ScopedAudioInputStream ais(aisw.Create());
474 EXPECT_TRUE(ais->Open());
476 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
477 WriteToFileAudioSink file_sink(file_name);
478 VLOG(0) << ">> Speak into the default microphone while recording.";
479 ais->Start(&file_sink);
480 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
482 VLOG(0) << ">> Recording has stopped.";