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/test/test_timeouts.h"
9 #include "base/threading/platform_thread.h"
10 #include "media/audio/audio_io.h"
11 #include "media/audio/audio_manager_base.h"
12 #include "media/audio/mac/audio_low_latency_input_mac.h"
13 #include "media/base/seekable_buffer.h"
14 #include "testing/gmock/include/gmock/gmock.h"
15 #include "testing/gtest/include/gtest/gtest.h"
18 using ::testing::AnyNumber;
19 using ::testing::AtLeast;
21 using ::testing::NotNull;
25 ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) {
26 if (++*count >= limit) {
27 loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
31 class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
33 MOCK_METHOD5(OnData, void(AudioInputStream* stream,
34 const uint8* src, uint32 size,
35 uint32 hardware_delay_bytes, double volume));
36 MOCK_METHOD1(OnClose, void(AudioInputStream* stream));
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 OnClose(AudioInputStream* stream) OVERRIDE {}
88 virtual void OnError(AudioInputStream* stream) OVERRIDE {}
91 media::SeekableBuffer buffer_;
96 class MacAudioInputTest : public testing::Test {
98 MacAudioInputTest() : audio_manager_(AudioManager::Create()) {}
99 virtual ~MacAudioInputTest() {}
101 // Convenience method which ensures that we are not running on the build
102 // bots and that at least one valid input device can be found.
103 bool CanRunAudioTests() {
104 bool has_input = audio_manager_->HasAudioInputDevices();
106 LOG(WARNING) << "No input devices detected";
110 // Convenience method which creates a default AudioInputStream object using
111 // a 10ms frame size and a sample rate which is set to the hardware sample
113 AudioInputStream* CreateDefaultAudioInputStream() {
114 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
115 int samples_per_packet = fs / 100;
116 AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
117 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
118 CHANNEL_LAYOUT_STEREO, fs, 16, samples_per_packet),
119 AudioManagerBase::kDefaultDeviceId);
124 // Convenience method which creates an AudioInputStream object with a
125 // specified channel layout.
126 AudioInputStream* CreateAudioInputStream(ChannelLayout channel_layout) {
127 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
128 int samples_per_packet = fs / 100;
129 AudioInputStream* ais = audio_manager_->MakeAudioInputStream(
130 AudioParameters(AudioParameters::AUDIO_PCM_LOW_LATENCY,
131 channel_layout, fs, 16, samples_per_packet),
132 AudioManagerBase::kDefaultDeviceId);
137 scoped_ptr<AudioManager> audio_manager_;
140 // Test Create(), Close().
141 TEST_F(MacAudioInputTest, AUAudioInputStreamCreateAndClose) {
142 if (!CanRunAudioTests())
144 AudioInputStream* ais = CreateDefaultAudioInputStream();
148 // Test Open(), Close().
149 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenAndClose) {
150 if (!CanRunAudioTests())
152 AudioInputStream* ais = CreateDefaultAudioInputStream();
153 EXPECT_TRUE(ais->Open());
157 // Test Open(), Start(), Close().
158 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartAndClose) {
159 if (!CanRunAudioTests())
161 AudioInputStream* ais = CreateDefaultAudioInputStream();
162 EXPECT_TRUE(ais->Open());
163 MockAudioInputCallback sink;
165 EXPECT_CALL(sink, OnClose(ais))
170 // Test Open(), Start(), Stop(), Close().
171 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartStopAndClose) {
172 if (!CanRunAudioTests())
174 AudioInputStream* ais = CreateDefaultAudioInputStream();
175 EXPECT_TRUE(ais->Open());
176 MockAudioInputCallback sink;
179 EXPECT_CALL(sink, OnClose(ais))
184 // Test some additional calling sequences.
185 TEST_F(MacAudioInputTest, AUAudioInputStreamMiscCallingSequences) {
186 if (!CanRunAudioTests())
188 AudioInputStream* ais = CreateDefaultAudioInputStream();
189 AUAudioInputStream* auais = static_cast<AUAudioInputStream*>(ais);
191 // Open(), Open() should fail the second time.
192 EXPECT_TRUE(ais->Open());
193 EXPECT_FALSE(ais->Open());
195 MockAudioInputCallback sink;
197 // Start(), Start() is a valid calling sequence (second call does nothing).
199 EXPECT_TRUE(auais->started());
201 EXPECT_TRUE(auais->started());
203 // Stop(), Stop() is a valid calling sequence (second call does nothing).
205 EXPECT_FALSE(auais->started());
207 EXPECT_FALSE(auais->started());
209 EXPECT_CALL(sink, OnClose(ais))
214 // Verify that recording starts and stops correctly in mono using mocked sink.
215 TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyMonoRecording) {
216 if (!CanRunAudioTests())
220 base::MessageLoopForUI loop;
222 // Create an audio input stream which records in mono.
223 AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_MONO);
224 EXPECT_TRUE(ais->Open());
226 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
227 int samples_per_packet = fs / 100;
228 int bits_per_sample = 16;
229 uint32 bytes_per_packet = samples_per_packet * (bits_per_sample / 8);
231 MockAudioInputCallback sink;
233 // We use 10ms packets and will run the test until ten packets are received.
234 // All should contain valid packets of the same size and a valid delay
236 EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _))
238 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
243 // Verify that the sink receieves OnClose() call when calling Close().
244 EXPECT_CALL(sink, OnClose(ais))
249 // Verify that recording starts and stops correctly in mono using mocked sink.
250 TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) {
251 if (!CanRunAudioTests())
255 base::MessageLoopForUI loop;
257 // Create an audio input stream which records in stereo.
258 AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_STEREO);
259 EXPECT_TRUE(ais->Open());
261 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
262 int samples_per_packet = fs / 100;
263 int bits_per_sample = 16;
264 uint32 bytes_per_packet = 2 * samples_per_packet * (bits_per_sample / 8);
266 MockAudioInputCallback sink;
268 // We use 10ms packets and will run the test until ten packets are received.
269 // All should contain valid packets of the same size and a valid delay
271 // TODO(henrika): http://crbug.com/154352 forced us to run the capture side
272 // using a native buffer size of 128 audio frames and combine it with a FIFO
273 // to match the requested size by the client. This change might also have
274 // modified the delay estimates since the existing Ge(bytes_per_packet) for
275 // parameter #4 does no longer pass. I am removing this restriction here to
276 // ensure that we can land the patch but will revisit this test again when
277 // more analysis of the delay estimates are done.
278 EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _))
280 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
285 // Verify that the sink receieves OnClose() call when calling Close().
286 EXPECT_CALL(sink, OnClose(ais))
291 // This test is intended for manual tests and should only be enabled
292 // when it is required to store the captured data on a local file.
293 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
294 // To include disabled tests in test execution, just invoke the test program
295 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
296 // environment variable to a value greater than 0.
297 TEST_F(MacAudioInputTest, DISABLED_AUAudioInputStreamRecordToFile) {
298 if (!CanRunAudioTests())
300 const char* file_name = "out_stereo_10sec.pcm";
302 int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
303 AudioInputStream* ais = CreateDefaultAudioInputStream();
304 EXPECT_TRUE(ais->Open());
306 fprintf(stderr, " File name : %s\n", file_name);
307 fprintf(stderr, " Sample rate: %d\n", fs);
308 WriteToFileAudioSink file_sink(file_name);
309 fprintf(stderr, " >> Speak into the mic while recording...\n");
310 ais->Start(&file_sink);
311 base::PlatformThread::Sleep(TestTimeouts::action_timeout());
313 fprintf(stderr, " >> Recording has stopped.\n");