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 {
34 MOCK_METHOD5(OnData, void(AudioInputStream* stream,
35 const uint8* src, uint32 size,
36 uint32 hardware_delay_bytes, double volume));
37 MOCK_METHOD1(OnError, void(AudioInputStream* stream));
40 // This audio sink implementation should be used for manual tests only since
41 // the recorded data is stored on a raw binary data file.
42 // The last test (WriteToFileAudioSink) - which is disabled by default -
43 // can use this audio sink to store the captured data on a file for offline
45 class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
47 // Allocate space for ~10 seconds of data @ 48kHz in stereo:
48 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
49 static const int kMaxBufferSize = 2 * 2 * 480 * 100 * 10;
51 explicit WriteToFileAudioSink(const char* file_name)
52 : buffer_(0, kMaxBufferSize),
53 file_(fopen(file_name, "wb")),
57 virtual ~WriteToFileAudioSink() {
58 int bytes_written = 0;
59 while (bytes_written < bytes_to_write_) {
63 // Stop writing if no more data is available.
64 if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
67 // Write recorded data chunk to the file and prepare for next chunk.
68 fwrite(chunk, 1, chunk_size, file_);
69 buffer_.Seek(chunk_size);
70 bytes_written += chunk_size;
75 // AudioInputStream::AudioInputCallback implementation.
76 virtual void OnData(AudioInputStream* stream,
77 const uint8* src, uint32 size,
78 uint32 hardware_delay_bytes, double volume) OVERRIDE {
79 // Store data data in a temporary buffer to avoid making blocking
80 // fwrite() calls in the audio callback. The complete buffer will be
81 // written to file in the destructor.
82 if (buffer_.Append(src, size)) {
83 bytes_to_write_ += size;
87 virtual void OnError(AudioInputStream* stream) OVERRIDE {}
90 media::SeekableBuffer buffer_;
95 class MacAudioInputTest : public testing::Test {
98 : message_loop_(base::MessageLoop::TYPE_UI),
99 audio_manager_(AudioManager::CreateForTesting()) {
100 // Wait for the AudioManager to finish any initialization on the audio loop.
101 base::RunLoop().RunUntilIdle();
104 virtual ~MacAudioInputTest() {
105 base::RunLoop().RunUntilIdle();
108 // Convenience method which ensures that we are not running on the build
109 // bots and that at least one valid input device can be found.
110 bool CanRunAudioTests() {
111 bool has_input = audio_manager_->HasAudioInputDevices();
113 LOG(WARNING) << "No input devices detected";
117 // Convenience method which creates a default AudioInputStream object using
118 // a 10ms frame size and a sample rate which is set to the hardware sample
120 AudioInputStream* CreateDefaultAudioInputStream() {
121 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
122 int samples_per_packet = fs / 100;
123 AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
124 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
125 CHANNEL_LAYOUT_STEREO, fs, 16, samples_per_packet),
126 AudioManagerBase::kDefaultDeviceId);
131 // Convenience method which creates an AudioInputStream object with a
132 // specified channel layout.
133 AudioInputStream* CreateAudioInputStream(ChannelLayout channel_layout) {
134 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
135 int samples_per_packet = fs / 100;
136 AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
137 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
138 channel_layout, fs, 16, samples_per_packet),
139 AudioManagerBase::kDefaultDeviceId);
144 base::MessageLoop message_loop_;
145 scoped_ptr<AudioManager> audio_manager_;
148 // Test Create(), Close().
149 TEST_F(MacAudioInputTest, AUAudioInputStreamCreateAndClose) {
150 if (!CanRunAudioTests())
152 AudioInputStream* ais = CreateDefaultAudioInputStream();
156 // Test Open(), Close().
157 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenAndClose) {
158 if (!CanRunAudioTests())
160 AudioInputStream* ais = CreateDefaultAudioInputStream();
161 EXPECT_TRUE(ais->Open());
165 // Test Open(), Start(), Close().
166 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartAndClose) {
167 if (!CanRunAudioTests())
169 AudioInputStream* ais = CreateDefaultAudioInputStream();
170 EXPECT_TRUE(ais->Open());
171 MockAudioInputCallback sink;
176 // Test Open(), Start(), Stop(), Close().
177 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartStopAndClose) {
178 if (!CanRunAudioTests())
180 AudioInputStream* ais = CreateDefaultAudioInputStream();
181 EXPECT_TRUE(ais->Open());
182 MockAudioInputCallback sink;
188 // Test some additional calling sequences.
189 TEST_F(MacAudioInputTest, AUAudioInputStreamMiscCallingSequences) {
190 if (!CanRunAudioTests())
192 AudioInputStream* ais = CreateDefaultAudioInputStream();
193 AUAudioInputStream* auais = static_cast<AUAudioInputStream*>(ais);
195 // Open(), Open() should fail the second time.
196 EXPECT_TRUE(ais->Open());
197 EXPECT_FALSE(ais->Open());
199 MockAudioInputCallback sink;
201 // Start(), Start() is a valid calling sequence (second call does nothing).
203 EXPECT_TRUE(auais->started());
205 EXPECT_TRUE(auais->started());
207 // Stop(), Stop() is a valid calling sequence (second call does nothing).
209 EXPECT_FALSE(auais->started());
211 EXPECT_FALSE(auais->started());
216 // Verify that recording starts and stops correctly in mono using mocked sink.
217 TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyMonoRecording) {
218 if (!CanRunAudioTests())
223 // Create an audio input stream which records in mono.
224 AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_MONO);
225 EXPECT_TRUE(ais->Open());
227 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
228 int samples_per_packet = fs / 100;
229 int bits_per_sample = 16;
230 uint32 bytes_per_packet = samples_per_packet * (bits_per_sample / 8);
232 MockAudioInputCallback sink;
234 // We use 10ms packets and will run the test until ten packets are received.
235 // All should contain valid packets of the same size and a valid delay
237 base::RunLoop run_loop;
238 EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _))
240 .WillRepeatedly(CheckCountAndPostQuitTask(
241 &count, 10, &message_loop_, run_loop.QuitClosure()));
248 // Verify that recording starts and stops correctly in mono using mocked sink.
249 TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) {
250 if (!CanRunAudioTests())
255 // Create an audio input stream which records in stereo.
256 AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_STEREO);
257 EXPECT_TRUE(ais->Open());
259 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
260 int samples_per_packet = fs / 100;
261 int bits_per_sample = 16;
262 uint32 bytes_per_packet = 2 * samples_per_packet * (bits_per_sample / 8);
264 MockAudioInputCallback sink;
266 // We use 10ms packets and will run the test until ten packets are received.
267 // All should contain valid packets of the same size and a valid delay
269 // TODO(henrika): http://crbug.com/154352 forced us to run the capture side
270 // using a native buffer size of 128 audio frames and combine it with a FIFO
271 // to match the requested size by the client. This change might also have
272 // modified the delay estimates since the existing Ge(bytes_per_packet) for
273 // parameter #4 does no longer pass. I am removing this restriction here to
274 // ensure that we can land the patch but will revisit this test again when
275 // more analysis of the delay estimates are done.
276 base::RunLoop run_loop;
277 EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _))
279 .WillRepeatedly(CheckCountAndPostQuitTask(
280 &count, 10, &message_loop_, run_loop.QuitClosure()));
287 // This test is intended for manual tests and should only be enabled
288 // when it is required to store the captured data on a local file.
289 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
290 // To include disabled tests in test execution, just invoke the test program
291 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
292 // environment variable to a value greater than 0.
293 TEST_F(MacAudioInputTest, DISABLED_AUAudioInputStreamRecordToFile) {
294 if (!CanRunAudioTests())
296 const char* file_name = "out_stereo_10sec.pcm";
298 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
299 AudioInputStream* ais = CreateDefaultAudioInputStream();
300 EXPECT_TRUE(ais->Open());
302 fprintf(stderr, " File name : %s\n", file_name);
303 fprintf(stderr, " Sample rate: %d\n", fs);
304 WriteToFileAudioSink file_sink(file_name);
305 fprintf(stderr, " >> Speak into the mic while recording...\n");
306 ais->Start(&file_sink);
307 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
309 fprintf(stderr, " >> Recording has stopped.\n");