Separate Simple Backend creation from initialization.
[chromium-blink-merge.git] / media / audio / audio_low_latency_input_output_unittest.cc
blob27f4873173857afed28174c791e6556486a1f248
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/file_util.h"
8 #include "base/memory/scoped_ptr.h"
9 #include "base/message_loop.h"
10 #include "base/path_service.h"
11 #include "base/synchronization/lock.h"
12 #include "base/test/test_timeouts.h"
13 #include "base/time.h"
14 #include "build/build_config.h"
15 #include "media/audio/audio_io.h"
16 #include "media/audio/audio_manager_base.h"
17 #include "media/base/seekable_buffer.h"
18 #include "testing/gmock/include/gmock/gmock.h"
19 #include "testing/gtest/include/gtest/gtest.h"
21 #if defined(OS_LINUX) || defined(OS_OPENBSD)
22 #include "media/audio/linux/audio_manager_linux.h"
23 #elif defined(OS_MACOSX)
24 #include "media/audio/mac/audio_manager_mac.h"
25 #elif defined(OS_WIN)
26 #include "media/audio/win/audio_manager_win.h"
27 #include "media/audio/win/core_audio_util_win.h"
28 #elif defined(OS_ANDROID)
29 #include "media/audio/android/audio_manager_android.h"
30 #endif
32 namespace media {
34 #if defined(OS_LINUX) || defined(OS_OPENBSD)
35 typedef AudioManagerLinux AudioManagerAnyPlatform;
36 #elif defined(OS_MACOSX)
37 typedef AudioManagerMac AudioManagerAnyPlatform;
38 #elif defined(OS_WIN)
39 typedef AudioManagerWin AudioManagerAnyPlatform;
40 #elif defined(OS_ANDROID)
41 typedef AudioManagerAndroid AudioManagerAnyPlatform;
42 #endif
44 // Limits the number of delay measurements we can store in an array and
45 // then write to file at end of the WASAPIAudioInputOutputFullDuplex test.
46 static const size_t kMaxDelayMeasurements = 1000;
48 // Name of the output text file. The output file will be stored in the
49 // directory containing media_unittests.exe.
50 // Example: \src\build\Debug\audio_delay_values_ms.txt.
51 // See comments for the WASAPIAudioInputOutputFullDuplex test for more details
52 // about the file format.
53 static const char kDelayValuesFileName[] = "audio_delay_values_ms.txt";
55 // Contains delay values which are reported during the full-duplex test.
56 // Total delay = |buffer_delay_ms| + |input_delay_ms| + |output_delay_ms|.
57 struct AudioDelayState {
58 AudioDelayState()
59 : delta_time_ms(0),
60 buffer_delay_ms(0),
61 input_delay_ms(0),
62 output_delay_ms(0) {
65 // Time in milliseconds since last delay report. Typical value is ~10 [ms].
66 int delta_time_ms;
68 // Size of internal sync buffer. Typical value is ~0 [ms].
69 int buffer_delay_ms;
71 // Reported capture/input delay. Typical value is ~10 [ms].
72 int input_delay_ms;
74 // Reported render/output delay. Typical value is ~40 [ms].
75 int output_delay_ms;
78 // This class mocks the platform specific audio manager and overrides
79 // the GetMessageLoop() method to ensure that we can run our tests on
80 // the main thread instead of the audio thread.
81 class MockAudioManager : public AudioManagerAnyPlatform {
82 public:
83 MockAudioManager() {}
84 virtual ~MockAudioManager() {}
86 virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE {
87 return MessageLoop::current()->message_loop_proxy();
90 private:
91 DISALLOW_COPY_AND_ASSIGN(MockAudioManager);
94 // Test fixture class.
95 class AudioLowLatencyInputOutputTest : public testing::Test {
96 protected:
97 AudioLowLatencyInputOutputTest() {}
99 virtual ~AudioLowLatencyInputOutputTest() {}
101 AudioManager* audio_manager() { return &mock_audio_manager_; }
102 MessageLoopForUI* message_loop() { return &message_loop_; }
104 // Convenience method which ensures that we are not running on the build
105 // bots and that at least one valid input and output device can be found.
106 bool CanRunAudioTests() {
107 bool input = audio_manager()->HasAudioInputDevices();
108 bool output = audio_manager()->HasAudioOutputDevices();
109 LOG_IF(WARNING, !input) << "No input device detected.";
110 LOG_IF(WARNING, !output) << "No output device detected.";
111 return input && output;
114 private:
115 MessageLoopForUI message_loop_;
116 MockAudioManager mock_audio_manager_;
118 DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest);
121 // This audio source/sink implementation should be used for manual tests
122 // only since delay measurements are stored on an output text file.
123 // All incoming/recorded audio packets are stored in an intermediate media
124 // buffer which the renderer reads from when it needs audio for playout.
125 // The total effect is that recorded audio is played out in loop back using
126 // a sync buffer as temporary storage.
127 class FullDuplexAudioSinkSource
128 : public AudioInputStream::AudioInputCallback,
129 public AudioOutputStream::AudioSourceCallback {
130 public:
131 FullDuplexAudioSinkSource(int sample_rate,
132 int samples_per_packet,
133 int channels)
134 : sample_rate_(sample_rate),
135 samples_per_packet_(samples_per_packet),
136 channels_(channels),
137 input_elements_to_write_(0),
138 output_elements_to_write_(0),
139 previous_write_time_(base::Time::Now()) {
140 // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM).
141 frame_size_ = (16 / 8) * channels_;
143 // Start with the smallest possible buffer size. It will be increased
144 // dynamically during the test if required.
145 buffer_.reset(
146 new media::SeekableBuffer(0, samples_per_packet_ * frame_size_));
148 frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_);
149 delay_states_.reset(new AudioDelayState[kMaxDelayMeasurements]);
152 virtual ~FullDuplexAudioSinkSource() {
153 // Get complete file path to output file in the directory containing
154 // media_unittests.exe. Example: src/build/Debug/audio_delay_values_ms.txt.
155 base::FilePath file_name;
156 EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name));
157 file_name = file_name.AppendASCII(kDelayValuesFileName);
159 FILE* text_file = file_util::OpenFile(file_name, "wt");
160 DLOG_IF(ERROR, !text_file) << "Failed to open log file.";
161 LOG(INFO) << ">> Output file " << file_name.value() << " has been created.";
163 // Write the array which contains time-stamps, buffer size and
164 // audio delays values to a text file.
165 size_t elements_written = 0;
166 while (elements_written <
167 std::min(input_elements_to_write_, output_elements_to_write_)) {
168 const AudioDelayState state = delay_states_[elements_written];
169 fprintf(text_file, "%d %d %d %d\n",
170 state.delta_time_ms,
171 state.buffer_delay_ms,
172 state.input_delay_ms,
173 state.output_delay_ms);
174 ++elements_written;
177 file_util::CloseFile(text_file);
180 // AudioInputStream::AudioInputCallback.
181 virtual void OnData(AudioInputStream* stream,
182 const uint8* src, uint32 size,
183 uint32 hardware_delay_bytes,
184 double volume) OVERRIDE {
185 base::AutoLock lock(lock_);
187 // Update three components in the AudioDelayState for this recorded
188 // audio packet.
189 base::Time now_time = base::Time::Now();
190 int diff = (now_time - previous_write_time_).InMilliseconds();
191 previous_write_time_ = now_time;
192 if (input_elements_to_write_ < kMaxDelayMeasurements) {
193 delay_states_[input_elements_to_write_].delta_time_ms = diff;
194 delay_states_[input_elements_to_write_].buffer_delay_ms =
195 BytesToMilliseconds(buffer_->forward_bytes());
196 delay_states_[input_elements_to_write_].input_delay_ms =
197 BytesToMilliseconds(hardware_delay_bytes);
198 ++input_elements_to_write_;
201 // Store the captured audio packet in a seekable media buffer.
202 if (!buffer_->Append(src, size)) {
203 // An attempt to write outside the buffer limits has been made.
204 // Double the buffer capacity to ensure that we have a buffer large
205 // enough to handle the current sample test scenario.
206 buffer_->set_forward_capacity(2 * buffer_->forward_capacity());
207 buffer_->Clear();
211 virtual void OnClose(AudioInputStream* stream) OVERRIDE {}
212 virtual void OnError(AudioInputStream* stream) OVERRIDE {}
214 // AudioOutputStream::AudioSourceCallback.
215 virtual int OnMoreData(AudioBus* audio_bus,
216 AudioBuffersState buffers_state) OVERRIDE {
217 base::AutoLock lock(lock_);
219 // Update one component in the AudioDelayState for the packet
220 // which is about to be played out.
221 if (output_elements_to_write_ < kMaxDelayMeasurements) {
222 int output_delay_bytes = buffers_state.hardware_delay_bytes;
223 #if defined(OS_WIN)
224 // Special fix for Windows in combination with Wave where the
225 // pending bytes field of the audio buffer state is used to
226 // report the delay.
227 if (!CoreAudioUtil::IsSupported()) {
228 output_delay_bytes = buffers_state.pending_bytes;
230 #endif
231 delay_states_[output_elements_to_write_].output_delay_ms =
232 BytesToMilliseconds(output_delay_bytes);
233 ++output_elements_to_write_;
236 int size;
237 const uint8* source;
238 // Read the data from the seekable media buffer which contains
239 // captured data at the same size and sample rate as the output side.
240 if (buffer_->GetCurrentChunk(&source, &size) && size > 0) {
241 EXPECT_EQ(channels_, audio_bus->channels());
242 size = std::min(audio_bus->frames() * frame_size_, size);
243 EXPECT_EQ(static_cast<size_t>(size) % sizeof(*audio_bus->channel(0)), 0U);
244 audio_bus->FromInterleaved(
245 source, size / frame_size_, frame_size_ / channels_);
246 buffer_->Seek(size);
247 return size / frame_size_;
250 return 0;
253 virtual int OnMoreIOData(AudioBus* source,
254 AudioBus* dest,
255 AudioBuffersState buffers_state) OVERRIDE {
256 NOTREACHED();
257 return 0;
260 virtual void OnError(AudioOutputStream* stream) OVERRIDE {}
261 virtual void WaitTillDataReady() OVERRIDE {}
263 protected:
264 // Converts from bytes to milliseconds taking the sample rate and size
265 // of an audio frame into account.
266 int BytesToMilliseconds(uint32 delay_bytes) const {
267 return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5);
270 private:
271 base::Lock lock_;
272 scoped_ptr<media::SeekableBuffer> buffer_;
273 int sample_rate_;
274 int samples_per_packet_;
275 int channels_;
276 int frame_size_;
277 double frames_to_ms_;
278 scoped_ptr<AudioDelayState[]> delay_states_;
279 size_t input_elements_to_write_;
280 size_t output_elements_to_write_;
281 base::Time previous_write_time_;
284 class AudioInputStreamTraits {
285 public:
286 typedef AudioInputStream StreamType;
288 static AudioParameters GetDefaultAudioStreamParameters(
289 AudioManager* audio_manager) {
290 return audio_manager->GetInputStreamParameters(
291 AudioManagerBase::kDefaultDeviceId);
294 static StreamType* CreateStream(AudioManager* audio_manager,
295 const AudioParameters& params) {
296 return audio_manager->MakeAudioInputStream(params,
297 AudioManagerBase::kDefaultDeviceId);
301 class AudioOutputStreamTraits {
302 public:
303 typedef AudioOutputStream StreamType;
305 static AudioParameters GetDefaultAudioStreamParameters(
306 AudioManager* audio_manager) {
307 return audio_manager->GetDefaultOutputStreamParameters();
310 static StreamType* CreateStream(AudioManager* audio_manager,
311 const AudioParameters& params) {
312 return audio_manager->MakeAudioOutputStream(params);
316 // Traits template holding a trait of StreamType. It encapsulates
317 // AudioInputStream and AudioOutputStream stream types.
318 template <typename StreamTraits>
319 class StreamWrapper {
320 public:
321 typedef typename StreamTraits::StreamType StreamType;
323 explicit StreamWrapper(AudioManager* audio_manager)
325 audio_manager_(audio_manager),
326 format_(AudioParameters::AUDIO_PCM_LOW_LATENCY),
327 #if defined(OS_ANDROID)
328 channel_layout_(CHANNEL_LAYOUT_MONO),
329 #else
330 channel_layout_(CHANNEL_LAYOUT_STEREO),
331 #endif
332 bits_per_sample_(16) {
333 // Use the preferred sample rate.
334 const AudioParameters& params =
335 StreamTraits::GetDefaultAudioStreamParameters(audio_manager_);
336 sample_rate_ = params.sample_rate();
338 // Use the preferred buffer size. Note that the input side uses the same
339 // size as the output side in this implementation.
340 samples_per_packet_ = params.frames_per_buffer();
343 virtual ~StreamWrapper() {}
345 // Creates an Audio[Input|Output]Stream stream object using default
346 // parameters.
347 StreamType* Create() {
348 return CreateStream();
351 int channels() const {
352 return ChannelLayoutToChannelCount(channel_layout_);
354 int bits_per_sample() const { return bits_per_sample_; }
355 int sample_rate() const { return sample_rate_; }
356 int samples_per_packet() const { return samples_per_packet_; }
358 private:
359 StreamType* CreateStream() {
360 StreamType* stream = StreamTraits::CreateStream(audio_manager_,
361 AudioParameters(format_, channel_layout_, sample_rate_,
362 bits_per_sample_, samples_per_packet_));
363 EXPECT_TRUE(stream);
364 return stream;
367 AudioManager* audio_manager_;
368 AudioParameters::Format format_;
369 ChannelLayout channel_layout_;
370 int bits_per_sample_;
371 int sample_rate_;
372 int samples_per_packet_;
375 typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper;
376 typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper;
378 // This test is intended for manual tests and should only be enabled
379 // when it is required to make a real-time test of audio in full duplex and
380 // at the same time create a text file which contains measured delay values.
381 // The file can later be analyzed off line using e.g. MATLAB.
382 // MATLAB example:
383 // D=load('audio_delay_values_ms.txt');
384 // x=cumsum(D(:,1));
385 // plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4));
386 // axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]);
387 // legend('buffer delay','input delay','output delay','total delay');
388 // xlabel('time [msec]')
389 // ylabel('delay [msec]')
390 // title('Full-duplex audio delay measurement');
391 TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) {
392 if (!CanRunAudioTests())
393 return;
395 AudioInputStreamWrapper aisw(audio_manager());
396 AudioInputStream* ais = aisw.Create();
397 EXPECT_TRUE(ais);
399 AudioOutputStreamWrapper aosw(audio_manager());
400 AudioOutputStream* aos = aosw.Create();
401 EXPECT_TRUE(aos);
403 // This test only supports identical parameters in both directions.
404 // TODO(henrika): it is possible to cut delay here by using different
405 // buffer sizes for input and output.
406 if (aisw.sample_rate() != aosw.sample_rate() ||
407 aisw.samples_per_packet() != aosw.samples_per_packet() ||
408 aisw.channels()!= aosw.channels() ||
409 aisw.bits_per_sample() != aosw.bits_per_sample()) {
410 LOG(ERROR) << "This test requires symmetric input and output parameters. "
411 "Ensure that sample rate and number of channels are identical in "
412 "both directions";
413 aos->Close();
414 ais->Close();
415 return;
418 EXPECT_TRUE(ais->Open());
419 EXPECT_TRUE(aos->Open());
421 FullDuplexAudioSinkSource full_duplex(
422 aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels());
424 LOG(INFO) << ">> You should now be able to hear yourself in loopback...";
425 DLOG(INFO) << " sample_rate : " << aisw.sample_rate();
426 DLOG(INFO) << " samples_per_packet: " << aisw.samples_per_packet();
427 DLOG(INFO) << " channels : " << aisw.channels();
429 ais->Start(&full_duplex);
430 aos->Start(&full_duplex);
432 // Wait for approximately 10 seconds. The user shall hear his own voice
433 // in loop back during this time. At the same time, delay recordings are
434 // performed and stored in the output text file.
435 message_loop()->PostDelayedTask(FROM_HERE,
436 MessageLoop::QuitClosure(), TestTimeouts::action_timeout());
437 message_loop()->Run();
439 aos->Stop();
440 ais->Stop();
442 // All Close() operations that run on the mocked audio thread,
443 // should be synchronous and not post additional close tasks to
444 // mocked the audio thread. Hence, there is no need to call
445 // message_loop()->RunUntilIdle() after the Close() methods.
446 aos->Close();
447 ais->Close();
450 } // namespace media