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