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/mac/audio_low_latency_input_mac.h"
14 #include "media/base/seekable_buffer.h"
15 #include "testing/gmock/include/gmock/gmock.h"
16 #include "testing/gtest/include/gtest/gtest.h"
19 using ::testing::AnyNumber
;
20 using ::testing::AtLeast
;
22 using ::testing::NotNull
;
26 ACTION_P4(CheckCountAndPostQuitTask
, count
, limit
, loop
, closure
) {
27 if (++*count
>= limit
) {
28 loop
->PostTask(FROM_HERE
, closure
);
32 class MockAudioInputCallback
: public AudioInputStream::AudioInputCallback
{
35 void(AudioInputStream
* stream
,
37 uint32 hardware_delay_bytes
,
39 MOCK_METHOD1(OnError
, void(AudioInputStream
* stream
));
42 // This audio sink implementation should be used for manual tests only since
43 // the recorded data is stored on a raw binary data file.
44 // The last test (WriteToFileAudioSink) - which is disabled by default -
45 // can use this audio sink to store the captured data on a file for offline
47 class WriteToFileAudioSink
: public AudioInputStream::AudioInputCallback
{
49 // Allocate space for ~10 seconds of data @ 48kHz in stereo:
50 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
51 static const int kMaxBufferSize
= 2 * 2 * 480 * 100 * 10;
53 explicit WriteToFileAudioSink(const char* file_name
)
54 : buffer_(0, kMaxBufferSize
),
55 file_(fopen(file_name
, "wb")),
59 ~WriteToFileAudioSink() override
{
60 int bytes_written
= 0;
61 while (bytes_written
< bytes_to_write_
) {
65 // Stop writing if no more data is available.
66 if (!buffer_
.GetCurrentChunk(&chunk
, &chunk_size
))
69 // Write recorded data chunk to the file and prepare for next chunk.
70 fwrite(chunk
, 1, chunk_size
, file_
);
71 buffer_
.Seek(chunk_size
);
72 bytes_written
+= chunk_size
;
77 // AudioInputStream::AudioInputCallback implementation.
78 void OnData(AudioInputStream
* stream
,
80 uint32 hardware_delay_bytes
,
81 double volume
) override
{
82 const int num_samples
= src
->frames() * src
->channels();
83 scoped_ptr
<int16
> interleaved(new int16
[num_samples
]);
84 const int bytes_per_sample
= sizeof(*interleaved
);
85 src
->ToInterleaved(src
->frames(), bytes_per_sample
, interleaved
.get());
87 // Store data data in a temporary buffer to avoid making blocking
88 // fwrite() calls in the audio callback. The complete buffer will be
89 // written to file in the destructor.
90 const int size
= bytes_per_sample
* num_samples
;
91 if (buffer_
.Append((const uint8
*)interleaved
.get(), size
)) {
92 bytes_to_write_
+= size
;
96 void OnError(AudioInputStream
* stream
) override
{}
99 media::SeekableBuffer buffer_
;
104 class MacAudioInputTest
: public testing::Test
{
107 : message_loop_(base::MessageLoop::TYPE_UI
),
108 audio_manager_(AudioManager::CreateForTesting()) {
109 // Wait for the AudioManager to finish any initialization on the audio loop.
110 base::RunLoop().RunUntilIdle();
113 ~MacAudioInputTest() override
{ base::RunLoop().RunUntilIdle(); }
115 // Convenience method which ensures that we are not running on the build
116 // bots and that at least one valid input device can be found.
117 bool CanRunAudioTests() {
118 bool has_input
= audio_manager_
->HasAudioInputDevices();
120 LOG(WARNING
) << "No input devices detected";
124 // Convenience method which creates a default AudioInputStream object using
125 // a 10ms frame size and a sample rate which is set to the hardware sample
127 AudioInputStream
* CreateDefaultAudioInputStream() {
128 int fs
= static_cast<int>(AUAudioInputStream::HardwareSampleRate());
129 int samples_per_packet
= fs
/ 100;
130 AudioInputStream
* ais
= audio_manager_
->MakeAudioInputStream(
131 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
132 CHANNEL_LAYOUT_STEREO
, fs
, 16, samples_per_packet
),
133 AudioManagerBase::kDefaultDeviceId
);
138 // Convenience method which creates an AudioInputStream object with a
139 // specified channel layout.
140 AudioInputStream
* CreateAudioInputStream(ChannelLayout channel_layout
) {
141 int fs
= static_cast<int>(AUAudioInputStream::HardwareSampleRate());
142 int samples_per_packet
= fs
/ 100;
143 AudioInputStream
* ais
= audio_manager_
->MakeAudioInputStream(
144 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY
,
145 channel_layout
, fs
, 16, samples_per_packet
),
146 AudioManagerBase::kDefaultDeviceId
);
151 base::MessageLoop message_loop_
;
152 scoped_ptr
<AudioManager
> audio_manager_
;
155 // Test Create(), Close().
156 TEST_F(MacAudioInputTest
, AUAudioInputStreamCreateAndClose
) {
157 if (!CanRunAudioTests())
159 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
163 // Test Open(), Close().
164 TEST_F(MacAudioInputTest
, AUAudioInputStreamOpenAndClose
) {
165 if (!CanRunAudioTests())
167 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
168 EXPECT_TRUE(ais
->Open());
172 // Test Open(), Start(), Close().
173 TEST_F(MacAudioInputTest
, AUAudioInputStreamOpenStartAndClose
) {
174 if (!CanRunAudioTests())
176 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
177 EXPECT_TRUE(ais
->Open());
178 MockAudioInputCallback sink
;
183 // Test Open(), Start(), Stop(), Close().
184 TEST_F(MacAudioInputTest
, AUAudioInputStreamOpenStartStopAndClose
) {
185 if (!CanRunAudioTests())
187 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
188 EXPECT_TRUE(ais
->Open());
189 MockAudioInputCallback sink
;
195 // Test some additional calling sequences.
196 TEST_F(MacAudioInputTest
, AUAudioInputStreamMiscCallingSequences
) {
197 if (!CanRunAudioTests())
199 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
200 AUAudioInputStream
* auais
= static_cast<AUAudioInputStream
*>(ais
);
202 // Open(), Open() should fail the second time.
203 EXPECT_TRUE(ais
->Open());
204 EXPECT_FALSE(ais
->Open());
206 MockAudioInputCallback sink
;
208 // Start(), Start() is a valid calling sequence (second call does nothing).
210 EXPECT_TRUE(auais
->started());
212 EXPECT_TRUE(auais
->started());
214 // Stop(), Stop() is a valid calling sequence (second call does nothing).
216 EXPECT_FALSE(auais
->started());
218 EXPECT_FALSE(auais
->started());
223 // Verify that recording starts and stops correctly in mono using mocked sink.
224 TEST_F(MacAudioInputTest
, AUAudioInputStreamVerifyMonoRecording
) {
225 if (!CanRunAudioTests())
230 // Create an audio input stream which records in mono.
231 AudioInputStream
* ais
= CreateAudioInputStream(CHANNEL_LAYOUT_MONO
);
232 EXPECT_TRUE(ais
->Open());
234 MockAudioInputCallback sink
;
236 // We use 10ms packets and will run the test until ten packets are received.
237 // All should contain valid packets of the same size and a valid delay
239 base::RunLoop run_loop
;
240 EXPECT_CALL(sink
, OnData(ais
, NotNull(), _
, _
))
242 .WillRepeatedly(CheckCountAndPostQuitTask(
243 &count
, 10, &message_loop_
, run_loop
.QuitClosure()));
250 // Verify that recording starts and stops correctly in mono using mocked sink.
251 TEST_F(MacAudioInputTest
, AUAudioInputStreamVerifyStereoRecording
) {
252 if (!CanRunAudioTests())
257 // Create an audio input stream which records in stereo.
258 AudioInputStream
* ais
= CreateAudioInputStream(CHANNEL_LAYOUT_STEREO
);
259 EXPECT_TRUE(ais
->Open());
261 MockAudioInputCallback sink
;
263 // We use 10ms packets and will run the test until ten packets are received.
264 // All should contain valid packets of the same size and a valid delay
266 // TODO(henrika): http://crbug.com/154352 forced us to run the capture side
267 // using a native buffer size of 128 audio frames and combine it with a FIFO
268 // to match the requested size by the client. This change might also have
269 // modified the delay estimates since the existing Ge(bytes_per_packet) for
270 // parameter #4 does no longer pass. I am removing this restriction here to
271 // ensure that we can land the patch but will revisit this test again when
272 // more analysis of the delay estimates are done.
273 base::RunLoop run_loop
;
274 EXPECT_CALL(sink
, OnData(ais
, NotNull(), _
, _
))
276 .WillRepeatedly(CheckCountAndPostQuitTask(
277 &count
, 10, &message_loop_
, run_loop
.QuitClosure()));
284 // This test is intended for manual tests and should only be enabled
285 // when it is required to store the captured data on a local file.
286 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
287 // To include disabled tests in test execution, just invoke the test program
288 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
289 // environment variable to a value greater than 0.
290 TEST_F(MacAudioInputTest
, DISABLED_AUAudioInputStreamRecordToFile
) {
291 if (!CanRunAudioTests())
293 const char* file_name
= "out_stereo_10sec.pcm";
295 int fs
= static_cast<int>(AUAudioInputStream::HardwareSampleRate());
296 AudioInputStream
* ais
= CreateDefaultAudioInputStream();
297 EXPECT_TRUE(ais
->Open());
299 fprintf(stderr
, " File name : %s\n", file_name
);
300 fprintf(stderr
, " Sample rate: %d\n", fs
);
301 WriteToFileAudioSink
file_sink(file_name
);
302 fprintf(stderr
, " >> Speak into the mic while recording...\n");
303 ais
->Start(&file_sink
);
304 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
306 fprintf(stderr
, " >> Recording has stopped.\n");