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(OnClose
, void(AudioInputStream
* stream
));
45 MOCK_METHOD1(OnError
, void(AudioInputStream
* stream
));
48 // This audio sink implementation should be used for manual tests only since
49 // the recorded data is stored on a raw binary data file.
50 class WriteToFileAudioSink
: public AudioInputStream::AudioInputCallback
{
52 // Allocate space for ~10 seconds of data @ 48kHz in stereo:
53 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
54 static const size_t kMaxBufferSize
= 2 * 2 * 480 * 100 * 10;
56 explicit WriteToFileAudioSink(const char* file_name
)
57 : buffer_(0, kMaxBufferSize
),
59 base::FilePath file_path
;
60 EXPECT_TRUE(PathService::Get(base::DIR_EXE
, &file_path
));
61 file_path
= file_path
.AppendASCII(file_name
);
62 binary_file_
= file_util::OpenFile(file_path
, "wb");
63 DLOG_IF(ERROR
, !binary_file_
) << "Failed to open binary PCM data file.";
64 LOG(INFO
) << ">> Output file: " << file_path
.value()
65 << " has been created.";
68 virtual ~WriteToFileAudioSink() {
69 size_t bytes_written
= 0;
70 while (bytes_written
< bytes_to_write_
) {
74 // Stop writing if no more data is available.
75 if (!buffer_
.GetCurrentChunk(&chunk
, &chunk_size
))
78 // Write recorded data chunk to the file and prepare for next chunk.
79 fwrite(chunk
, 1, chunk_size
, binary_file_
);
80 buffer_
.Seek(chunk_size
);
81 bytes_written
+= chunk_size
;
83 file_util::CloseFile(binary_file_
);
86 // AudioInputStream::AudioInputCallback implementation.
87 virtual void OnData(AudioInputStream
* stream
,
90 uint32 hardware_delay_bytes
,
92 // Store data data in a temporary buffer to avoid making blocking
93 // fwrite() calls in the audio callback. The complete buffer will be
94 // written to file in the destructor.
95 if (buffer_
.Append(src
, size
)) {
96 bytes_to_write_
+= size
;
100 virtual void OnClose(AudioInputStream
* stream
) {}
101 virtual void OnError(AudioInputStream
* stream
) {}
104 media::SeekableBuffer buffer_
;
106 size_t bytes_to_write_
;
109 // Convenience method which ensures that we are not running on the build
110 // bots and that at least one valid input device can be found. We also
111 // verify that we are not running on XP since the low-latency (WASAPI-
112 // based) version requires Windows Vista or higher.
113 static bool CanRunAudioTests(AudioManager
* audio_man
) {
114 if (!CoreAudioUtil::IsSupported()) {
115 LOG(WARNING
) << "This tests requires Windows Vista or higher.";
118 // TODO(henrika): note that we use Wave today to query the number of
119 // existing input devices.
120 bool input
= audio_man
->HasAudioInputDevices();
121 LOG_IF(WARNING
, !input
) << "No input device detected.";
125 // Convenience method which creates a default AudioInputStream object but
126 // also allows the user to modify the default settings.
127 class AudioInputStreamWrapper
{
129 explicit AudioInputStreamWrapper(AudioManager
* audio_manager
)
130 : com_init_(ScopedCOMInitializer::kMTA
),
131 audio_man_(audio_manager
),
132 format_(AudioParameters::AUDIO_PCM_LOW_LATENCY
),
133 channel_layout_(CHANNEL_LAYOUT_STEREO
),
134 bits_per_sample_(16) {
135 // Use native/mixing sample rate and 10ms frame size as default.
136 sample_rate_
= static_cast<int>(
137 WASAPIAudioInputStream::HardwareSampleRate(
138 AudioManagerBase::kDefaultDeviceId
));
139 samples_per_packet_
= sample_rate_
/ 100;
142 ~AudioInputStreamWrapper() {}
144 // Creates AudioInputStream object using default parameters.
145 AudioInputStream
* Create() {
146 return CreateInputStream();
149 // Creates AudioInputStream object using non-default parameters where the
150 // frame size is modified.
151 AudioInputStream
* Create(int samples_per_packet
) {
152 samples_per_packet_
= samples_per_packet
;
153 return CreateInputStream();
156 AudioParameters::Format
format() const { return format_
; }
157 int channels() const {
158 return ChannelLayoutToChannelCount(channel_layout_
);
160 int bits_per_sample() const { return bits_per_sample_
; }
161 int sample_rate() const { return sample_rate_
; }
162 int samples_per_packet() const { return samples_per_packet_
; }
165 AudioInputStream
* CreateInputStream() {
166 AudioInputStream
* ais
= audio_man_
->MakeAudioInputStream(
167 AudioParameters(format_
, channel_layout_
, sample_rate_
,
168 bits_per_sample_
, samples_per_packet_
),
169 AudioManagerBase::kDefaultDeviceId
);
174 ScopedCOMInitializer com_init_
;
175 AudioManager
* audio_man_
;
176 AudioParameters::Format format_
;
177 ChannelLayout channel_layout_
;
178 int bits_per_sample_
;
180 int samples_per_packet_
;
183 // Convenience method which creates a default AudioInputStream object.
184 static AudioInputStream
* CreateDefaultAudioInputStream(
185 AudioManager
* audio_manager
) {
186 AudioInputStreamWrapper
aisw(audio_manager
);
187 AudioInputStream
* ais
= aisw
.Create();
191 // Verify that we can retrieve the current hardware/mixing sample rate
192 // for all available input devices.
193 TEST(WinAudioInputTest
, WASAPIAudioInputStreamHardwareSampleRate
) {
194 scoped_ptr
<AudioManager
> audio_manager(AudioManager::Create());
195 if (!CanRunAudioTests(audio_manager
.get()))
198 ScopedCOMInitializer
com_init(ScopedCOMInitializer::kMTA
);
200 // Retrieve a list of all available input devices.
201 media::AudioDeviceNames device_names
;
202 audio_manager
->GetAudioInputDeviceNames(&device_names
);
204 // Scan all available input devices and repeat the same test for all of them.
205 for (media::AudioDeviceNames::const_iterator it
= device_names
.begin();
206 it
!= device_names
.end(); ++it
) {
207 // Retrieve the hardware sample rate given a specified audio input device.
208 // TODO(tommi): ensure that we don't have to cast here.
209 int fs
= static_cast<int>(WASAPIAudioInputStream::HardwareSampleRate(
215 // Test Create(), Close() calling sequence.
216 TEST(WinAudioInputTest
, WASAPIAudioInputStreamCreateAndClose
) {
217 scoped_ptr
<AudioManager
> audio_manager(AudioManager::Create());
218 if (!CanRunAudioTests(audio_manager
.get()))
220 AudioInputStream
* ais
= CreateDefaultAudioInputStream(audio_manager
.get());
224 // Test Open(), Close() calling sequence.
225 TEST(WinAudioInputTest
, WASAPIAudioInputStreamOpenAndClose
) {
226 scoped_ptr
<AudioManager
> audio_manager(AudioManager::Create());
227 if (!CanRunAudioTests(audio_manager
.get()))
229 AudioInputStream
* ais
= CreateDefaultAudioInputStream(audio_manager
.get());
230 EXPECT_TRUE(ais
->Open());
234 // Test Open(), Start(), Close() calling sequence.
235 TEST(WinAudioInputTest
, WASAPIAudioInputStreamOpenStartAndClose
) {
236 scoped_ptr
<AudioManager
> audio_manager(AudioManager::Create());
237 if (!CanRunAudioTests(audio_manager
.get()))
239 AudioInputStream
* ais
= CreateDefaultAudioInputStream(audio_manager
.get());
240 EXPECT_TRUE(ais
->Open());
241 MockAudioInputCallback sink
;
243 EXPECT_CALL(sink
, OnClose(ais
))
248 // Test Open(), Start(), Stop(), Close() calling sequence.
249 TEST(WinAudioInputTest
, WASAPIAudioInputStreamOpenStartStopAndClose
) {
250 scoped_ptr
<AudioManager
> audio_manager(AudioManager::Create());
251 if (!CanRunAudioTests(audio_manager
.get()))
253 AudioInputStream
* ais
= CreateDefaultAudioInputStream(audio_manager
.get());
254 EXPECT_TRUE(ais
->Open());
255 MockAudioInputCallback sink
;
258 EXPECT_CALL(sink
, OnClose(ais
))
263 // Test some additional calling sequences.
264 TEST(WinAudioInputTest
, WASAPIAudioInputStreamMiscCallingSequences
) {
265 scoped_ptr
<AudioManager
> audio_manager(AudioManager::Create());
266 if (!CanRunAudioTests(audio_manager
.get()))
268 AudioInputStream
* ais
= CreateDefaultAudioInputStream(audio_manager
.get());
269 WASAPIAudioInputStream
* wais
= static_cast<WASAPIAudioInputStream
*>(ais
);
271 // Open(), Open() should fail the second time.
272 EXPECT_TRUE(ais
->Open());
273 EXPECT_FALSE(ais
->Open());
275 MockAudioInputCallback sink
;
277 // Start(), Start() is a valid calling sequence (second call does nothing).
279 EXPECT_TRUE(wais
->started());
281 EXPECT_TRUE(wais
->started());
283 // Stop(), Stop() is a valid calling sequence (second call does nothing).
285 EXPECT_FALSE(wais
->started());
287 EXPECT_FALSE(wais
->started());
289 EXPECT_CALL(sink
, OnClose(ais
))
294 TEST(WinAudioInputTest
, WASAPIAudioInputStreamTestPacketSizes
) {
295 scoped_ptr
<AudioManager
> audio_manager(AudioManager::Create());
296 if (!CanRunAudioTests(audio_manager
.get()))
300 base::MessageLoopForUI loop
;
302 // 10 ms packet size.
304 // Create default WASAPI input stream which records in stereo using
305 // the shared mixing rate. The default buffer size is 10ms.
306 AudioInputStreamWrapper
aisw(audio_manager
.get());
307 AudioInputStream
* ais
= aisw
.Create();
308 EXPECT_TRUE(ais
->Open());
310 MockAudioInputCallback sink
;
312 // Derive the expected size in bytes of each recorded packet.
313 uint32 bytes_per_packet
= aisw
.channels() * aisw
.samples_per_packet() *
314 (aisw
.bits_per_sample() / 8);
316 // We use 10ms packets and will run the test until ten packets are received.
317 // All should contain valid packets of the same size and a valid delay
319 EXPECT_CALL(sink
, OnData(
320 ais
, NotNull(), bytes_per_packet
, Gt(bytes_per_packet
), _
))
322 .WillRepeatedly(CheckCountAndPostQuitTask(&count
, 10, &loop
));
327 // Store current packet size (to be used in the subsequent tests).
328 int samples_per_packet_10ms
= aisw
.samples_per_packet();
330 EXPECT_CALL(sink
, OnClose(ais
))
334 // 20 ms packet size.
337 ais
= aisw
.Create(2 * samples_per_packet_10ms
);
338 EXPECT_TRUE(ais
->Open());
339 bytes_per_packet
= aisw
.channels() * aisw
.samples_per_packet() *
340 (aisw
.bits_per_sample() / 8);
342 EXPECT_CALL(sink
, OnData(
343 ais
, NotNull(), bytes_per_packet
, Gt(bytes_per_packet
), _
))
345 .WillRepeatedly(CheckCountAndPostQuitTask(&count
, 10, &loop
));
350 EXPECT_CALL(sink
, OnClose(ais
))
357 ais
= aisw
.Create(samples_per_packet_10ms
/ 2);
358 EXPECT_TRUE(ais
->Open());
359 bytes_per_packet
= aisw
.channels() * aisw
.samples_per_packet() *
360 (aisw
.bits_per_sample() / 8);
362 EXPECT_CALL(sink
, OnData(
363 ais
, NotNull(), bytes_per_packet
, Gt(bytes_per_packet
), _
))
365 .WillRepeatedly(CheckCountAndPostQuitTask(&count
, 10, &loop
));
370 EXPECT_CALL(sink
, OnClose(ais
))
375 // This test is intended for manual tests and should only be enabled
376 // when it is required to store the captured data on a local file.
377 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
378 // To include disabled tests in test execution, just invoke the test program
379 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
380 // environment variable to a value greater than 0.
381 TEST(WinAudioInputTest
, DISABLED_WASAPIAudioInputStreamRecordToFile
) {
382 scoped_ptr
<AudioManager
> audio_manager(AudioManager::Create());
383 if (!CanRunAudioTests(audio_manager
.get()))
386 // Name of the output PCM file containing captured data. The output file
387 // will be stored in the directory containing 'media_unittests.exe'.
388 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
389 const char* file_name
= "out_stereo_10sec.pcm";
391 AudioInputStreamWrapper
aisw(audio_manager
.get());
392 AudioInputStream
* ais
= aisw
.Create();
393 EXPECT_TRUE(ais
->Open());
395 LOG(INFO
) << ">> Sample rate: " << aisw
.sample_rate() << " [Hz]";
396 WriteToFileAudioSink
file_sink(file_name
);
397 LOG(INFO
) << ">> Speak into the default microphone while recording.";
398 ais
->Start(&file_sink
);
399 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
401 LOG(INFO
) << ">> Recording has stopped.";