Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / media / audio / win / audio_low_latency_input_win_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 <windows.h>
6 #include <mmsystem.h>
7
8 #include "base/basictypes.h"
9 #include "base/environment.h"
10 #include "base/files/file_util.h"
11 #include "base/memory/scoped_ptr.h"
12 #include "base/message_loop/message_loop.h"
13 #include "base/path_service.h"
14 #include "base/test/test_timeouts.h"
15 #include "base/win/scoped_com_initializer.h"
16 #include "media/audio/audio_io.h"
17 #include "media/audio/audio_manager_base.h"
18 #include "media/audio/win/audio_low_latency_input_win.h"
19 #include "media/audio/win/core_audio_util_win.h"
20 #include "media/base/seekable_buffer.h"
21 #include "testing/gmock/include/gmock/gmock.h"
22 #include "testing/gtest/include/gtest/gtest.h"
23
24 using base::win::ScopedCOMInitializer;
25 using ::testing::_;
26 using ::testing::AnyNumber;
27 using ::testing::AtLeast;
28 using ::testing::Gt;
29 using ::testing::NotNull;
30
31 namespace media {
32
33 ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) {
34   if (++*count >= limit) {
35     loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
36   }
37 }
38
39 class MockAudioInputCallback : public AudioInputStream::AudioInputCallback {
40  public:
41   MOCK_METHOD4(OnData,
42                void(AudioInputStream* stream,
43                     const AudioBus* src,
44                     uint32 hardware_delay_bytes,
45                     double volume));
46   MOCK_METHOD1(OnError, void(AudioInputStream* stream));
47 };
48
49 class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback {
50  public:
51   FakeAudioInputCallback()
52       : error_(false),
53         data_event_(false, false),
54         num_received_audio_frames_(0) {}
55
56   bool error() const { return error_; }
57   int num_received_audio_frames() const { return num_received_audio_frames_; }
58
59   // Waits until OnData() is called on another thread.
60   void WaitForData() {
61     data_event_.Wait();
62   }
63
64   virtual void OnData(AudioInputStream* stream,
65                       const AudioBus* src,
66                       uint32 hardware_delay_bytes,
67                       double volume) OVERRIDE {
68     EXPECT_NE(hardware_delay_bytes, 0u);
69     num_received_audio_frames_ += src->frames();
70     data_event_.Signal();
71   }
72
73   virtual void OnError(AudioInputStream* stream) OVERRIDE {
74     error_ = true;
75   }
76
77  private:
78   int num_received_audio_frames_;
79   base::WaitableEvent data_event_;
80   bool error_;
81
82   DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback);
83 };
84
85 // This audio sink implementation should be used for manual tests only since
86 // the recorded data is stored on a raw binary data file.
87 class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback {
88  public:
89   // Allocate space for ~10 seconds of data @ 48kHz in stereo:
90   // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes.
91   static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10;
92
93   explicit WriteToFileAudioSink(const char* file_name, int bits_per_sample)
94       : bits_per_sample_(bits_per_sample),
95         buffer_(0, kMaxBufferSize),
96         bytes_to_write_(0) {
97     base::FilePath file_path;
98     EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path));
99     file_path = file_path.AppendASCII(file_name);
100     binary_file_ = base::OpenFile(file_path, "wb");
101     DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file.";
102     VLOG(0) << ">> Output file: " << file_path.value() << " has been created.";
103     VLOG(0) << "bits_per_sample_:" << bits_per_sample_;
104   }
105
106   virtual ~WriteToFileAudioSink() {
107     size_t bytes_written = 0;
108     while (bytes_written < bytes_to_write_) {
109       const uint8* chunk;
110       int chunk_size;
111
112       // Stop writing if no more data is available.
113       if (!buffer_.GetCurrentChunk(&chunk, &chunk_size))
114         break;
115
116       // Write recorded data chunk to the file and prepare for next chunk.
117       fwrite(chunk, 1, chunk_size, binary_file_);
118       buffer_.Seek(chunk_size);
119       bytes_written += chunk_size;
120     }
121     base::CloseFile(binary_file_);
122   }
123
124   // AudioInputStream::AudioInputCallback implementation.
125   virtual void OnData(AudioInputStream* stream,
126                       const AudioBus* src,
127                       uint32 hardware_delay_bytes,
128                       double volume) {
129     EXPECT_EQ(bits_per_sample_, 16);
130     const int num_samples = src->frames() * src->channels();
131     scoped_ptr<int16> interleaved(new int16[num_samples]);
132     const int bytes_per_sample = sizeof(*interleaved);
133     src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get());
134
135     // Store data data in a temporary buffer to avoid making blocking
136     // fwrite() calls in the audio callback. The complete buffer will be
137     // written to file in the destructor.
138     const int size = bytes_per_sample * num_samples;
139     if (buffer_.Append((const uint8*)interleaved.get(), size)) {
140       bytes_to_write_ += size;
141     }
142   }
143
144   virtual void OnError(AudioInputStream* stream) {}
145
146  private:
147   int bits_per_sample_;
148   media::SeekableBuffer buffer_;
149   FILE* binary_file_;
150   size_t bytes_to_write_;
151 };
152
153 // Convenience method which ensures that we are not running on the build
154 // bots and that at least one valid input device can be found. We also
155 // verify that we are not running on XP since the low-latency (WASAPI-
156 // based) version requires Windows Vista or higher.
157 static bool CanRunAudioTests(AudioManager* audio_man) {
158   if (!CoreAudioUtil::IsSupported()) {
159     LOG(WARNING) << "This tests requires Windows Vista or higher.";
160     return false;
161   }
162   // TODO(henrika): note that we use Wave today to query the number of
163   // existing input devices.
164   bool input = audio_man->HasAudioInputDevices();
165   LOG_IF(WARNING, !input) << "No input device detected.";
166   return input;
167 }
168
169 // Convenience method which creates a default AudioInputStream object but
170 // also allows the user to modify the default settings.
171 class AudioInputStreamWrapper {
172  public:
173   explicit AudioInputStreamWrapper(AudioManager* audio_manager)
174       : com_init_(ScopedCOMInitializer::kMTA),
175         audio_man_(audio_manager),
176         default_params_(
177             audio_manager->GetInputStreamParameters(
178                   AudioManagerBase::kDefaultDeviceId)) {
179     EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY);
180     frames_per_buffer_ = default_params_.frames_per_buffer();
181     // We expect the default buffer size to be a 10ms buffer.
182     EXPECT_EQ(frames_per_buffer_, sample_rate() / 100);
183   }
184
185   ~AudioInputStreamWrapper() {}
186
187   // Creates AudioInputStream object using default parameters.
188   AudioInputStream* Create() {
189     return CreateInputStream();
190   }
191
192   // Creates AudioInputStream object using non-default parameters where the
193   // frame size is modified.
194   AudioInputStream* Create(int frames_per_buffer) {
195     frames_per_buffer_ = frames_per_buffer;
196     return CreateInputStream();
197   }
198
199   AudioParameters::Format format() const { return default_params_.format(); }
200   int channels() const {
201     return ChannelLayoutToChannelCount(default_params_.channel_layout());
202   }
203   int bits_per_sample() const { return default_params_.bits_per_sample(); }
204   int sample_rate() const { return default_params_.sample_rate(); }
205   int frames_per_buffer() const { return frames_per_buffer_; }
206
207  private:
208   AudioInputStream* CreateInputStream() {
209     AudioInputStream* ais = audio_man_->MakeAudioInputStream(
210         AudioParameters(format(), default_params_.channel_layout(),
211                         sample_rate(), bits_per_sample(), frames_per_buffer_,
212                         default_params_.effects()),
213         AudioManagerBase::kDefaultDeviceId);
214     EXPECT_TRUE(ais);
215     return ais;
216   }
217
218   ScopedCOMInitializer com_init_;
219   AudioManager* audio_man_;
220   const AudioParameters default_params_;
221   int frames_per_buffer_;
222 };
223
224 // Convenience method which creates a default AudioInputStream object.
225 static AudioInputStream* CreateDefaultAudioInputStream(
226     AudioManager* audio_manager) {
227   AudioInputStreamWrapper aisw(audio_manager);
228   AudioInputStream* ais = aisw.Create();
229   return ais;
230 }
231
232 class ScopedAudioInputStream {
233  public:
234   explicit ScopedAudioInputStream(AudioInputStream* stream)
235       : stream_(stream) {}
236
237   ~ScopedAudioInputStream() {
238     if (stream_)
239       stream_->Close();
240   }
241
242   void Close() {
243     if (stream_)
244       stream_->Close();
245     stream_ = NULL;
246   }
247
248   AudioInputStream* operator->() {
249     return stream_;
250   }
251
252   AudioInputStream* get() const { return stream_; }
253
254   void Reset(AudioInputStream* new_stream) {
255     Close();
256     stream_ = new_stream;
257   }
258
259  private:
260   AudioInputStream* stream_;
261
262   DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
263 };
264
265 // Verify that we can retrieve the current hardware/mixing sample rate
266 // for all available input devices.
267 TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
268   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
269   if (!CanRunAudioTests(audio_manager.get()))
270     return;
271
272   ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
273
274   // Retrieve a list of all available input devices.
275   media::AudioDeviceNames device_names;
276   audio_manager->GetAudioInputDeviceNames(&device_names);
277
278   // Scan all available input devices and repeat the same test for all of them.
279   for (media::AudioDeviceNames::const_iterator it = device_names.begin();
280        it != device_names.end(); ++it) {
281     // Retrieve the hardware sample rate given a specified audio input device.
282     AudioParameters params = WASAPIAudioInputStream::GetInputStreamParameters(
283         it->unique_id);
284     EXPECT_GE(params.sample_rate(), 0);
285   }
286 }
287
288 // Test Create(), Close() calling sequence.
289 TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
290   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
291   if (!CanRunAudioTests(audio_manager.get()))
292     return;
293   ScopedAudioInputStream ais(
294       CreateDefaultAudioInputStream(audio_manager.get()));
295   ais.Close();
296 }
297
298 // Test Open(), Close() calling sequence.
299 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
300   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
301   if (!CanRunAudioTests(audio_manager.get()))
302     return;
303   ScopedAudioInputStream ais(
304       CreateDefaultAudioInputStream(audio_manager.get()));
305   EXPECT_TRUE(ais->Open());
306   ais.Close();
307 }
308
309 // Test Open(), Start(), Close() calling sequence.
310 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
311   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
312   if (!CanRunAudioTests(audio_manager.get()))
313     return;
314   ScopedAudioInputStream ais(
315       CreateDefaultAudioInputStream(audio_manager.get()));
316   EXPECT_TRUE(ais->Open());
317   MockAudioInputCallback sink;
318   ais->Start(&sink);
319   ais.Close();
320 }
321
322 // Test Open(), Start(), Stop(), Close() calling sequence.
323 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
324   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
325   if (!CanRunAudioTests(audio_manager.get()))
326     return;
327   ScopedAudioInputStream ais(
328       CreateDefaultAudioInputStream(audio_manager.get()));
329   EXPECT_TRUE(ais->Open());
330   MockAudioInputCallback sink;
331   ais->Start(&sink);
332   ais->Stop();
333   ais.Close();
334 }
335
336 // Test some additional calling sequences.
337 TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
338   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
339   if (!CanRunAudioTests(audio_manager.get()))
340     return;
341   ScopedAudioInputStream ais(
342       CreateDefaultAudioInputStream(audio_manager.get()));
343   WASAPIAudioInputStream* wais =
344       static_cast<WASAPIAudioInputStream*>(ais.get());
345
346   // Open(), Open() should fail the second time.
347   EXPECT_TRUE(ais->Open());
348   EXPECT_FALSE(ais->Open());
349
350   MockAudioInputCallback sink;
351
352   // Start(), Start() is a valid calling sequence (second call does nothing).
353   ais->Start(&sink);
354   EXPECT_TRUE(wais->started());
355   ais->Start(&sink);
356   EXPECT_TRUE(wais->started());
357
358   // Stop(), Stop() is a valid calling sequence (second call does nothing).
359   ais->Stop();
360   EXPECT_FALSE(wais->started());
361   ais->Stop();
362   EXPECT_FALSE(wais->started());
363   ais.Close();
364 }
365
366 TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
367   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
368   if (!CanRunAudioTests(audio_manager.get()))
369     return;
370
371   int count = 0;
372   base::MessageLoopForUI loop;
373
374   // 10 ms packet size.
375
376   // Create default WASAPI input stream which records in stereo using
377   // the shared mixing rate. The default buffer size is 10ms.
378   AudioInputStreamWrapper aisw(audio_manager.get());
379   ScopedAudioInputStream ais(aisw.Create());
380   EXPECT_TRUE(ais->Open());
381
382   MockAudioInputCallback sink;
383
384   // Derive the expected size in bytes of each recorded packet.
385   uint32 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
386       (aisw.bits_per_sample() / 8);
387
388   // We use 10ms packets and will run the test until ten packets are received.
389   // All should contain valid packets of the same size and a valid delay
390   // estimate.
391   EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
392       .Times(AtLeast(10))
393       .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
394   ais->Start(&sink);
395   loop.Run();
396   ais->Stop();
397
398   // Store current packet size (to be used in the subsequent tests).
399   int frames_per_buffer_10ms = aisw.frames_per_buffer();
400
401   ais.Close();
402
403   // 20 ms packet size.
404
405   count = 0;
406   ais.Reset(aisw.Create(2 * frames_per_buffer_10ms));
407   EXPECT_TRUE(ais->Open());
408   bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
409       (aisw.bits_per_sample() / 8);
410
411   EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
412       .Times(AtLeast(10))
413       .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
414   ais->Start(&sink);
415   loop.Run();
416   ais->Stop();
417   ais.Close();
418
419   // 5 ms packet size.
420
421   count = 0;
422   ais.Reset(aisw.Create(frames_per_buffer_10ms / 2));
423   EXPECT_TRUE(ais->Open());
424   bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
425     (aisw.bits_per_sample() / 8);
426
427   EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
428       .Times(AtLeast(10))
429       .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
430   ais->Start(&sink);
431   loop.Run();
432   ais->Stop();
433   ais.Close();
434 }
435
436 // Test that we can capture a stream in loopback.
437 TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
438   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
439   if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported())
440     return;
441
442   AudioParameters params = audio_manager->GetInputStreamParameters(
443       AudioManagerBase::kLoopbackInputDeviceId);
444   EXPECT_EQ(params.effects(), 0);
445
446   AudioParameters output_params =
447       audio_manager->GetOutputStreamParameters(std::string());
448   EXPECT_EQ(params.sample_rate(), output_params.sample_rate());
449   EXPECT_EQ(params.channel_layout(), output_params.channel_layout());
450
451   ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream(
452       params, AudioManagerBase::kLoopbackInputDeviceId));
453   ASSERT_TRUE(stream->Open());
454   FakeAudioInputCallback sink;
455   stream->Start(&sink);
456   ASSERT_FALSE(sink.error());
457
458   sink.WaitForData();
459   stream.Close();
460
461   EXPECT_GT(sink.num_received_audio_frames(), 0);
462   EXPECT_FALSE(sink.error());
463 }
464
465 // This test is intended for manual tests and should only be enabled
466 // when it is required to store the captured data on a local file.
467 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
468 // To include disabled tests in test execution, just invoke the test program
469 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
470 // environment variable to a value greater than 0.
471 TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
472   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
473   if (!CanRunAudioTests(audio_manager.get()))
474     return;
475
476   // Name of the output PCM file containing captured data. The output file
477   // will be stored in the directory containing 'media_unittests.exe'.
478   // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
479   const char* file_name = "out_stereo_10sec.pcm";
480
481   AudioInputStreamWrapper aisw(audio_manager.get());
482   ScopedAudioInputStream ais(aisw.Create());
483   EXPECT_TRUE(ais->Open());
484
485   VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
486   WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample());
487   VLOG(0) << ">> Speak into the default microphone while recording.";
488   ais->Start(&file_sink);
489   base::PlatformThread::Sleep(TestTimeouts::action_timeout());
490   ais->Stop();
491   VLOG(0) << ">> Recording has stopped.";
492   ais.Close();
493 }
494
495 }  // namespace media