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