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