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 "base/basictypes.h"
6 #include "base/environment.h"
7 #include "base/message_loop/message_loop.h"
8 #include "base/run_loop.h"
9 #include "base/test/test_timeouts.h"
10 #include "base/threading/platform_thread.h"
11 #include "media/audio/audio_io.h"
12 #include "media/audio/audio_manager_base.h"
13 #include "media/audio/audio_unittest_util.h"
14 #include "media/audio/mac/audio_low_latency_input_mac.h"
15 #include "media/base/seekable_buffer.h"
16 #include "testing/gmock/include/gmock/gmock.h"
17 #include "testing/gtest/include/gtest/gtest.h"
20 using ::testing::AnyNumber
;
21 using ::testing::AtLeast
;
23 using ::testing::NotNull
;
27 ACTION_P4(CheckCountAndPostQuitTask
, count
, limit
, loop
, closure
) {
28 if (++*count
>= limit
) {
29 loop
->PostTask(FROM_HERE
, closure
);
33 class MockAudioInputCallback
: public AudioInputStream::AudioInputCallback
{
36 void(AudioInputStream
* stream
,
38 uint32 hardware_delay_bytes
,
40 MOCK_METHOD1(OnError
, void(AudioInputStream
* stream
));
43 // This audio sink implementation should be used for manual tests only since
44 // the recorded data is stored on a raw binary data file.
45 // The last test (WriteToFileAudioSink) - which is disabled by default -
46 // can use this audio sink to store the captured data on a file for offline
48 class WriteToFileAudioSink
: public AudioInputStream::AudioInputCallback
{
50 // Allocate space for ~10 seconds of data @ 48kHz in stereo:
51 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
52 static const int kMaxBufferSize
= 2 * 2 * 480 * 100 * 10;
54 explicit WriteToFileAudioSink(const char* file_name
)
55 : buffer_(0, kMaxBufferSize
),
56 file_(fopen(file_name
, "wb")),
60 ~WriteToFileAudioSink() override
{
61 int bytes_written
= 0;
62 while (bytes_written
< bytes_to_write_
) {
66 // Stop writing if no more data is available.
67 if (!buffer_
.GetCurrentChunk(&chunk
, &chunk_size
))
70 // Write recorded data chunk to the file and prepare for next chunk.
71 fwrite(chunk
, 1, chunk_size
, file_
);
72 buffer_
.Seek(chunk_size
);
73 bytes_written
+= chunk_size
;
78 // AudioInputStream::AudioInputCallback implementation.
79 void OnData(AudioInputStream
* stream
,
81 uint32 hardware_delay_bytes
,
82 double volume
) override
{
83 const int num_samples
= src
->frames() * src
->channels();
84 scoped_ptr
<int16
> interleaved(new int16
[num_samples
]);
85 const int bytes_per_sample
= sizeof(*interleaved
);
86 src
->ToInterleaved(src
->frames(), bytes_per_sample
, interleaved
.get());
88 // Store data data in a temporary buffer to avoid making blocking
89 // fwrite() calls in the audio callback. The complete buffer will be
90 // written to file in the destructor.
91 const int size
= bytes_per_sample
* num_samples
;
92 if (buffer_
.Append((const uint8
*)interleaved
.get(), size
)) {
93 bytes_to_write_
+= size
;
97 void OnError(AudioInputStream
* stream
) override
{}
100 media::SeekableBuffer buffer_
;
105 class MacAudioInputTest
: public testing::Test
{
108 : message_loop_(base::MessageLoop::TYPE_UI
),
109 audio_manager_(AudioManager::CreateForTesting()) {
110 // Wait for the AudioManager to finish any initialization on the audio loop.
111 base::RunLoop().RunUntilIdle();
114 ~MacAudioInputTest() override
{ base::RunLoop().RunUntilIdle(); }
116 bool InputDevicesAvailable() {
117 return audio_manager_
->HasAudioInputDevices();
120 // Convenience method which creates a default AudioInputStream object using
121 // a 10ms frame size and a sample rate which is set to the hardware sample
123 AudioInputStream
* CreateDefaultAudioInputStream() {
124 int fs
= static_cast<int>(AUAudioInputStream::HardwareSampleRate());
125 int samples_per_packet
= fs
/ 100;
126 AudioInputStream
* ais
= audio_manager_
->MakeAudioInputStream(
127 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
128 CHANNEL_LAYOUT_STEREO
, fs
, 16, samples_per_packet
),
129 AudioManagerBase::kDefaultDeviceId
);
134 // Convenience method which creates an AudioInputStream object with a
135 // specified channel layout.
136 AudioInputStream
* CreateAudioInputStream(ChannelLayout channel_layout
) {
137 int fs
= static_cast<int>(AUAudioInputStream::HardwareSampleRate());
138 int samples_per_packet
= fs
/ 100;
139 AudioInputStream
* ais
= audio_manager_
->MakeAudioInputStream(
140 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
141 channel_layout
, fs
, 16, samples_per_packet
),
142 AudioManagerBase::kDefaultDeviceId
);
147 base::MessageLoop message_loop_
;
148 scoped_ptr
<AudioManager
> audio_manager_
;
151 // Test Create(), Close().
152 TEST_F(MacAudioInputTest
, AUAudioInputStreamCreateAndClose
) {
153 ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
154 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
158 // Test Open(), Close().
159 TEST_F(MacAudioInputTest
, AUAudioInputStreamOpenAndClose
) {
160 ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
161 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
162 EXPECT_TRUE(ais
->Open());
166 // Test Open(), Start(), Close().
167 TEST_F(MacAudioInputTest
, AUAudioInputStreamOpenStartAndClose
) {
168 ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
169 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
170 EXPECT_TRUE(ais
->Open());
171 MockAudioInputCallback sink
;
176 // Test Open(), Start(), Stop(), Close().
177 TEST_F(MacAudioInputTest
, AUAudioInputStreamOpenStartStopAndClose
) {
178 ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
179 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
180 EXPECT_TRUE(ais
->Open());
181 MockAudioInputCallback sink
;
187 // Test some additional calling sequences.
188 TEST_F(MacAudioInputTest
, AUAudioInputStreamMiscCallingSequences
) {
189 ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
190 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
191 AUAudioInputStream
* auais
= static_cast<AUAudioInputStream
*>(ais
);
193 // Open(), Open() should fail the second time.
194 EXPECT_TRUE(ais
->Open());
195 EXPECT_FALSE(ais
->Open());
197 MockAudioInputCallback sink
;
199 // Start(), Start() is a valid calling sequence (second call does nothing).
201 EXPECT_TRUE(auais
->started());
203 EXPECT_TRUE(auais
->started());
205 // Stop(), Stop() is a valid calling sequence (second call does nothing).
207 EXPECT_FALSE(auais
->started());
209 EXPECT_FALSE(auais
->started());
214 // Verify that recording starts and stops correctly in mono using mocked sink.
215 TEST_F(MacAudioInputTest
, AUAudioInputStreamVerifyMonoRecording
) {
216 ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
220 // Create an audio input stream which records in mono.
221 AudioInputStream
* ais
= CreateAudioInputStream(CHANNEL_LAYOUT_MONO
);
222 EXPECT_TRUE(ais
->Open());
224 MockAudioInputCallback sink
;
226 // We use 10ms packets and will run the test until ten packets are received.
227 // All should contain valid packets of the same size and a valid delay
229 base::RunLoop run_loop
;
230 EXPECT_CALL(sink
, OnData(ais
, NotNull(), _
, _
))
232 .WillRepeatedly(CheckCountAndPostQuitTask(
233 &count
, 10, &message_loop_
, run_loop
.QuitClosure()));
240 // Verify that recording starts and stops correctly in mono using mocked sink.
241 TEST_F(MacAudioInputTest
, AUAudioInputStreamVerifyStereoRecording
) {
242 ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
246 // Create an audio input stream which records in stereo.
247 AudioInputStream
* ais
= CreateAudioInputStream(CHANNEL_LAYOUT_STEREO
);
248 EXPECT_TRUE(ais
->Open());
250 MockAudioInputCallback sink
;
252 // We use 10ms packets and will run the test until ten packets are received.
253 // All should contain valid packets of the same size and a valid delay
255 // TODO(henrika): http://crbug.com/154352 forced us to run the capture side
256 // using a native buffer size of 128 audio frames and combine it with a FIFO
257 // to match the requested size by the client. This change might also have
258 // modified the delay estimates since the existing Ge(bytes_per_packet) for
259 // parameter #4 does no longer pass. I am removing this restriction here to
260 // ensure that we can land the patch but will revisit this test again when
261 // more analysis of the delay estimates are done.
262 base::RunLoop run_loop
;
263 EXPECT_CALL(sink
, OnData(ais
, NotNull(), _
, _
))
265 .WillRepeatedly(CheckCountAndPostQuitTask(
266 &count
, 10, &message_loop_
, run_loop
.QuitClosure()));
273 // This test is intended for manual tests and should only be enabled
274 // when it is required to store the captured data on a local file.
275 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
276 // To include disabled tests in test execution, just invoke the test program
277 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
278 // environment variable to a value greater than 0.
279 TEST_F(MacAudioInputTest
, DISABLED_AUAudioInputStreamRecordToFile
) {
280 ABORT_AUDIO_TEST_IF_NOT(InputDevicesAvailable());
281 const char* file_name
= "out_stereo_10sec.pcm";
283 int fs
= static_cast<int>(AUAudioInputStream::HardwareSampleRate());
284 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
285 EXPECT_TRUE(ais
->Open());
287 fprintf(stderr
, " File name : %s\n", file_name
);
288 fprintf(stderr
, " Sample rate: %d\n", fs
);
289 WriteToFileAudioSink
file_sink(file_name
);
290 fprintf(stderr
, " >> Speak into the mic while recording...\n");
291 ais
->Start(&file_sink
);
292 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
294 fprintf(stderr
, " >> Recording has stopped.\n");