- add sources.
[platform/framework/web/crosswalk.git] / src / media / audio / mac / audio_low_latency_input_mac_unittest.cc
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.
4
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"
16
17 using ::testing::_;
18 using ::testing::AnyNumber;
19 using ::testing::AtLeast;
20 using ::testing::Ge;
21 using ::testing::NotNull;
22
23 namespace media {
24
25 ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) {
26   if (++*count >= limit) {
27     loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
28   }
29 }
30
31 class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
32  public:
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));
38 };
39
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
44 // analysis.
45 class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
46  public:
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;
50
51   explicit WriteToFileAudioSink(const char* file_name)
52       : buffer_(0, kMaxBufferSize),
53         file_(fopen(file_name, "wb")),
54         bytes_to_write_(0) {
55   }
56
57   virtual ~WriteToFileAudioSink() {
58     int bytes_written = 0;
59     while (bytes_written < bytes_to_write_) {
60       const uint8* chunk;
61       int chunk_size;
62
63       // Stop writing if no more data is available.
64       if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
65         break;
66
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;
71     }
72     fclose(file_);
73   }
74
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;
84     }
85   }
86
87   virtual void OnClose(AudioInputStream* stream) OVERRIDE {}
88   virtual void OnError(AudioInputStream* stream) OVERRIDE {}
89
90  private:
91   media::SeekableBuffer buffer_;
92   FILE* file_;
93   int bytes_to_write_;
94 };
95
96 class MacAudioInputTest : public testing::Test {
97  protected:
98   MacAudioInputTest() : audio_manager_(AudioManager::Create()) {}
99   virtual ~MacAudioInputTest() {}
100
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();
105     if (!has_input)
106       LOG(WARNING) << "No input devices detected";
107     return has_input;
108   }
109
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
112   // rate.
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);
120     EXPECT_TRUE(ais);
121     return ais;
122   }
123
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);
133     EXPECT_TRUE(ais);
134     return ais;
135   }
136
137   scoped_ptr<AudioManager> audio_manager_;
138 };
139
140 // Test Create(), Close().
141 TEST_F(MacAudioInputTest, AUAudioInputStreamCreateAndClose) {
142   if (!CanRunAudioTests())
143     return;
144   AudioInputStream* ais = CreateDefaultAudioInputStream();
145   ais->Close();
146 }
147
148 // Test Open(), Close().
149 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenAndClose) {
150   if (!CanRunAudioTests())
151     return;
152   AudioInputStream* ais = CreateDefaultAudioInputStream();
153   EXPECT_TRUE(ais->Open());
154   ais->Close();
155 }
156
157 // Test Open(), Start(), Close().
158 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartAndClose) {
159   if (!CanRunAudioTests())
160     return;
161   AudioInputStream* ais = CreateDefaultAudioInputStream();
162   EXPECT_TRUE(ais->Open());
163   MockAudioInputCallback sink;
164   ais->Start(&sink);
165   EXPECT_CALL(sink, OnClose(ais))
166       .Times(1);
167   ais->Close();
168 }
169
170 // Test Open(), Start(), Stop(), Close().
171 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartStopAndClose) {
172   if (!CanRunAudioTests())
173     return;
174   AudioInputStream* ais = CreateDefaultAudioInputStream();
175   EXPECT_TRUE(ais->Open());
176   MockAudioInputCallback sink;
177   ais->Start(&sink);
178   ais->Stop();
179   EXPECT_CALL(sink, OnClose(ais))
180       .Times(1);
181   ais->Close();
182 }
183
184 // Test some additional calling sequences.
185 TEST_F(MacAudioInputTest, AUAudioInputStreamMiscCallingSequences) {
186   if (!CanRunAudioTests())
187     return;
188   AudioInputStream* ais = CreateDefaultAudioInputStream();
189   AUAudioInputStream* auais = static_cast<AUAudioInputStream*>(ais);
190
191   // Open(), Open() should fail the second time.
192   EXPECT_TRUE(ais->Open());
193   EXPECT_FALSE(ais->Open());
194
195   MockAudioInputCallback sink;
196
197   // Start(), Start() is a valid calling sequence (second call does nothing).
198   ais->Start(&sink);
199   EXPECT_TRUE(auais->started());
200   ais->Start(&sink);
201   EXPECT_TRUE(auais->started());
202
203   // Stop(), Stop() is a valid calling sequence (second call does nothing).
204   ais->Stop();
205   EXPECT_FALSE(auais->started());
206   ais->Stop();
207   EXPECT_FALSE(auais->started());
208
209   EXPECT_CALL(sink, OnClose(ais))
210       .Times(1);
211   ais->Close();
212 }
213
214 // Verify that recording starts and stops correctly in mono using mocked sink.
215 TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyMonoRecording) {
216   if (!CanRunAudioTests())
217     return;
218
219   int count = 0;
220   base::MessageLoopForUI loop;
221
222   // Create an audio input stream which records in mono.
223   AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_MONO);
224   EXPECT_TRUE(ais->Open());
225
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);
230
231   MockAudioInputCallback sink;
232
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
235   // estimate.
236   EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _))
237       .Times(AtLeast(10))
238       .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
239   ais->Start(&sink);
240   loop.Run();
241   ais->Stop();
242
243   // Verify that the sink receieves OnClose() call when calling Close().
244   EXPECT_CALL(sink, OnClose(ais))
245       .Times(1);
246   ais->Close();
247 }
248
249 // Verify that recording starts and stops correctly in mono using mocked sink.
250 TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) {
251   if (!CanRunAudioTests())
252     return;
253
254   int count = 0;
255   base::MessageLoopForUI loop;
256
257   // Create an audio input stream which records in stereo.
258   AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_STEREO);
259   EXPECT_TRUE(ais->Open());
260
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);
265
266   MockAudioInputCallback sink;
267
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
270   // estimate.
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, _, _))
279       .Times(AtLeast(10))
280       .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
281   ais->Start(&sink);
282   loop.Run();
283   ais->Stop();
284
285   // Verify that the sink receieves OnClose() call when calling Close().
286   EXPECT_CALL(sink, OnClose(ais))
287       .Times(1);
288   ais->Close();
289 }
290
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())
299     return;
300   const char* file_name = "out_stereo_10sec.pcm";
301
302   int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
303   AudioInputStream* ais = CreateDefaultAudioInputStream();
304   EXPECT_TRUE(ais->Open());
305
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());
312   ais->Stop();
313   fprintf(stderr, "               >> Recording has stopped.\n");
314   ais->Close();
315 }
316
317 }  // namespace media