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.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
, 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_METHOD2(OnError
, void(AudioInputStream
* stream
, int code
));
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
) {
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
) {}
88 virtual void OnError(AudioInputStream
* stream
, int code
) {}
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 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 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");