Upstream version 7.36.149.0
[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/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"
17
18 using ::testing::_;
19 using ::testing::AnyNumber;
20 using ::testing::AtLeast;
21 using ::testing::Ge;
22 using ::testing::NotNull;
23
24 namespace media {
25
26 ACTION_P4(CheckCountAndPostQuitTask, count, limit, loop, closure) {
27   if (++*count >= limit) {
28     loop->PostTask(FROM_HERE, closure);
29   }
30 }
31
32 class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
33  public:
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));
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 OnError(AudioInputStream* stream) OVERRIDE {}
88
89  private:
90   media::SeekableBuffer buffer_;
91   FILE* file_;
92   int bytes_to_write_;
93 };
94
95 class MacAudioInputTest : public testing::Test {
96  protected:
97   MacAudioInputTest()
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();
102   }
103
104   virtual ~MacAudioInputTest() {
105     base::RunLoop().RunUntilIdle();
106   }
107
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();
112     if (!has_input)
113       LOG(WARNING) << "No input devices detected";
114     return has_input;
115   }
116
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
119   // rate.
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);
127     EXPECT_TRUE(ais);
128     return ais;
129   }
130
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);
140     EXPECT_TRUE(ais);
141     return ais;
142   }
143
144   base::MessageLoop message_loop_;
145   scoped_ptr<AudioManager> audio_manager_;
146 };
147
148 // Test Create(), Close().
149 TEST_F(MacAudioInputTest, AUAudioInputStreamCreateAndClose) {
150   if (!CanRunAudioTests())
151     return;
152   AudioInputStream* ais = CreateDefaultAudioInputStream();
153   ais->Close();
154 }
155
156 // Test Open(), Close().
157 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenAndClose) {
158   if (!CanRunAudioTests())
159     return;
160   AudioInputStream* ais = CreateDefaultAudioInputStream();
161   EXPECT_TRUE(ais->Open());
162   ais->Close();
163 }
164
165 // Test Open(), Start(), Close().
166 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartAndClose) {
167   if (!CanRunAudioTests())
168     return;
169   AudioInputStream* ais = CreateDefaultAudioInputStream();
170   EXPECT_TRUE(ais->Open());
171   MockAudioInputCallback sink;
172   ais->Start(&sink);
173   ais->Close();
174 }
175
176 // Test Open(), Start(), Stop(), Close().
177 TEST_F(MacAudioInputTest, AUAudioInputStreamOpenStartStopAndClose) {
178   if (!CanRunAudioTests())
179     return;
180   AudioInputStream* ais = CreateDefaultAudioInputStream();
181   EXPECT_TRUE(ais->Open());
182   MockAudioInputCallback sink;
183   ais->Start(&sink);
184   ais->Stop();
185   ais->Close();
186 }
187
188 // Test some additional calling sequences.
189 TEST_F(MacAudioInputTest, AUAudioInputStreamMiscCallingSequences) {
190   if (!CanRunAudioTests())
191     return;
192   AudioInputStream* ais = CreateDefaultAudioInputStream();
193   AUAudioInputStream* auais = static_cast<AUAudioInputStream*>(ais);
194
195   // Open(), Open() should fail the second time.
196   EXPECT_TRUE(ais->Open());
197   EXPECT_FALSE(ais->Open());
198
199   MockAudioInputCallback sink;
200
201   // Start(), Start() is a valid calling sequence (second call does nothing).
202   ais->Start(&sink);
203   EXPECT_TRUE(auais->started());
204   ais->Start(&sink);
205   EXPECT_TRUE(auais->started());
206
207   // Stop(), Stop() is a valid calling sequence (second call does nothing).
208   ais->Stop();
209   EXPECT_FALSE(auais->started());
210   ais->Stop();
211   EXPECT_FALSE(auais->started());
212
213   ais->Close();
214 }
215
216 // Verify that recording starts and stops correctly in mono using mocked sink.
217 TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyMonoRecording) {
218   if (!CanRunAudioTests())
219     return;
220
221   int count = 0;
222
223   // Create an audio input stream which records in mono.
224   AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_MONO);
225   EXPECT_TRUE(ais->Open());
226
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);
231
232   MockAudioInputCallback sink;
233
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
236   // estimate.
237   base::RunLoop run_loop;
238   EXPECT_CALL(sink, OnData(ais, NotNull(), bytes_per_packet, _, _))
239       .Times(AtLeast(10))
240       .WillRepeatedly(CheckCountAndPostQuitTask(
241           &count, 10, &message_loop_, run_loop.QuitClosure()));
242   ais->Start(&sink);
243   run_loop.Run();
244   ais->Stop();
245   ais->Close();
246 }
247
248 // Verify that recording starts and stops correctly in mono using mocked sink.
249 TEST_F(MacAudioInputTest, AUAudioInputStreamVerifyStereoRecording) {
250   if (!CanRunAudioTests())
251     return;
252
253   int count = 0;
254
255   // Create an audio input stream which records in stereo.
256   AudioInputStream* ais = CreateAudioInputStream(CHANNEL_LAYOUT_STEREO);
257   EXPECT_TRUE(ais->Open());
258
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);
263
264   MockAudioInputCallback sink;
265
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
268   // estimate.
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, _, _))
278       .Times(AtLeast(10))
279       .WillRepeatedly(CheckCountAndPostQuitTask(
280           &count, 10, &message_loop_, run_loop.QuitClosure()));
281   ais->Start(&sink);
282   run_loop.Run();
283   ais->Stop();
284   ais->Close();
285 }
286
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())
295     return;
296   const char* file_name = "out_stereo_10sec.pcm";
297
298   int fs = static_cast<int>(AUAudioInputStream::HardwareSampleRate());
299   AudioInputStream* ais = CreateDefaultAudioInputStream();
300   EXPECT_TRUE(ais->Open());
301
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());
308   ais->Stop();
309   fprintf(stderr, "               >> Recording has stopped.\n");
310   ais->Close();
311 }
312
313 }  // namespace media