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/base/seekable_buffer.h" 18 #include "testing/gmock/include/gmock/gmock.h" 19 #include "testing/gtest/include/gtest/gtest.h" 20 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 31 32 namespace media { 33 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 43 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; 47 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"; 54 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) { 63 } 64 65 // Time in milliseconds since last delay report. Typical value is ~10 [ms]. 66 int delta_time_ms; 67 68 // Size of internal sync buffer. Typical value is ~0 [ms]. 69 int buffer_delay_ms; 70 71 // Reported capture/input delay. Typical value is ~10 [ms]. 72 int input_delay_ms; 73 74 // Reported render/output delay. Typical value is ~40 [ms]. 75 int output_delay_ms; 76 }; 77 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() {} 85 86 virtual scoped_refptr<base::MessageLoopProxy> GetMessageLoop() OVERRIDE { 87 return base::MessageLoop::current()->message_loop_proxy(); 88 } 89 90 private: 91 DISALLOW_COPY_AND_ASSIGN(MockAudioManager); 92 }; 93 94 // Test fixture class. 95 class AudioLowLatencyInputOutputTest : public testing::Test { 96 protected: 97 AudioLowLatencyInputOutputTest() {} 98 99 virtual ~AudioLowLatencyInputOutputTest() {} 100 101 AudioManager* audio_manager() { return &mock_audio_manager_; } 102 base::MessageLoopForUI* message_loop() { return &message_loop_; } 103 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; 112 } 113 114 private: 115 base::MessageLoopForUI message_loop_; 116 MockAudioManager mock_audio_manager_; 117 118 DISALLOW_COPY_AND_ASSIGN(AudioLowLatencyInputOutputTest); 119 }; 120 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::TimeTicks::Now()) { 140 // Size in bytes of each audio frame (4 bytes for 16-bit stereo PCM). 141 frame_size_ = (16 / 8) * channels_; 142 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_)); 147 148 frames_to_ms_ = static_cast<double>(1000.0 / sample_rate_); 149 delay_states_.reset(new AudioDelayState[kMaxDelayMeasurements]); 150 } 151 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); 158 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."; 162 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; 175 } 176 177 file_util::CloseFile(text_file); 178 } 179 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_); 186 187 // Update three components in the AudioDelayState for this recorded 188 // audio packet. 189 const base::TimeTicks now_time = base::TimeTicks::Now(); 190 const 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_; 199 } 200 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(); 208 } 209 } 210 211 virtual void OnClose(AudioInputStream* stream) OVERRIDE {} 212 virtual void OnError(AudioInputStream* stream) OVERRIDE {} 213 214 // AudioOutputStream::AudioSourceCallback. 215 virtual int OnMoreData(AudioBus* audio_bus, 216 AudioBuffersState buffers_state) OVERRIDE { 217 base::AutoLock lock(lock_); 218 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; 229 } 230 #endif 231 delay_states_[output_elements_to_write_].output_delay_ms = 232 BytesToMilliseconds(output_delay_bytes); 233 ++output_elements_to_write_; 234 } 235 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_; 248 } 249 250 return 0; 251 } 252 253 virtual int OnMoreIOData(AudioBus* source, 254 AudioBus* dest, 255 AudioBuffersState buffers_state) OVERRIDE { 256 NOTREACHED(); 257 return 0; 258 } 259 260 virtual void OnError(AudioOutputStream* stream) OVERRIDE {} 261 262 protected: 263 // Converts from bytes to milliseconds taking the sample rate and size 264 // of an audio frame into account. 265 int BytesToMilliseconds(uint32 delay_bytes) const { 266 return static_cast<int>((delay_bytes / frame_size_) * frames_to_ms_ + 0.5); 267 } 268 269 private: 270 base::Lock lock_; 271 scoped_ptr<media::SeekableBuffer> buffer_; 272 int sample_rate_; 273 int samples_per_packet_; 274 int channels_; 275 int frame_size_; 276 double frames_to_ms_; 277 scoped_ptr<AudioDelayState[]> delay_states_; 278 size_t input_elements_to_write_; 279 size_t output_elements_to_write_; 280 base::TimeTicks previous_write_time_; 281 }; 282 283 class AudioInputStreamTraits { 284 public: 285 typedef AudioInputStream StreamType; 286 287 static AudioParameters GetDefaultAudioStreamParameters( 288 AudioManager* audio_manager) { 289 return audio_manager->GetInputStreamParameters( 290 AudioManagerBase::kDefaultDeviceId); 291 } 292 293 static StreamType* CreateStream(AudioManager* audio_manager, 294 const AudioParameters& params) { 295 return audio_manager->MakeAudioInputStream(params, 296 AudioManagerBase::kDefaultDeviceId); 297 } 298 }; 299 300 class AudioOutputStreamTraits { 301 public: 302 typedef AudioOutputStream StreamType; 303 304 static AudioParameters GetDefaultAudioStreamParameters( 305 AudioManager* audio_manager) { 306 return audio_manager->GetDefaultOutputStreamParameters(); 307 } 308 309 static StreamType* CreateStream(AudioManager* audio_manager, 310 const AudioParameters& params) { 311 return audio_manager->MakeAudioOutputStream(params, std::string()); 312 } 313 }; 314 315 // Traits template holding a trait of StreamType. It encapsulates 316 // AudioInputStream and AudioOutputStream stream types. 317 template <typename StreamTraits> 318 class StreamWrapper { 319 public: 320 typedef typename StreamTraits::StreamType StreamType; 321 322 explicit StreamWrapper(AudioManager* audio_manager) 323 : 324 audio_manager_(audio_manager), 325 format_(AudioParameters::AUDIO_PCM_LOW_LATENCY), 326 #if defined(OS_ANDROID) 327 channel_layout_(CHANNEL_LAYOUT_MONO), 328 #else 329 channel_layout_(CHANNEL_LAYOUT_STEREO), 330 #endif 331 bits_per_sample_(16) { 332 // Use the preferred sample rate. 333 const AudioParameters& params = 334 StreamTraits::GetDefaultAudioStreamParameters(audio_manager_); 335 sample_rate_ = params.sample_rate(); 336 337 // Use the preferred buffer size. Note that the input side uses the same 338 // size as the output side in this implementation. 339 samples_per_packet_ = params.frames_per_buffer(); 340 } 341 342 virtual ~StreamWrapper() {} 343 344 // Creates an Audio[Input|Output]Stream stream object using default 345 // parameters. 346 StreamType* Create() { 347 return CreateStream(); 348 } 349 350 int channels() const { 351 return ChannelLayoutToChannelCount(channel_layout_); 352 } 353 int bits_per_sample() const { return bits_per_sample_; } 354 int sample_rate() const { return sample_rate_; } 355 int samples_per_packet() const { return samples_per_packet_; } 356 357 private: 358 StreamType* CreateStream() { 359 StreamType* stream = StreamTraits::CreateStream(audio_manager_, 360 AudioParameters(format_, channel_layout_, sample_rate_, 361 bits_per_sample_, samples_per_packet_)); 362 EXPECT_TRUE(stream); 363 return stream; 364 } 365 366 AudioManager* audio_manager_; 367 AudioParameters::Format format_; 368 ChannelLayout channel_layout_; 369 int bits_per_sample_; 370 int sample_rate_; 371 int samples_per_packet_; 372 }; 373 374 typedef StreamWrapper<AudioInputStreamTraits> AudioInputStreamWrapper; 375 typedef StreamWrapper<AudioOutputStreamTraits> AudioOutputStreamWrapper; 376 377 // This test is intended for manual tests and should only be enabled 378 // when it is required to make a real-time test of audio in full duplex and 379 // at the same time create a text file which contains measured delay values. 380 // The file can later be analyzed off line using e.g. MATLAB. 381 // MATLAB example: 382 // D=load('audio_delay_values_ms.txt'); 383 // x=cumsum(D(:,1)); 384 // plot(x, D(:,2), x, D(:,3), x, D(:,4), x, D(:,2)+D(:,3)+D(:,4)); 385 // axis([0, max(x), 0, max(D(:,2)+D(:,3)+D(:,4))+10]); 386 // legend('buffer delay','input delay','output delay','total delay'); 387 // xlabel('time [msec]') 388 // ylabel('delay [msec]') 389 // title('Full-duplex audio delay measurement'); 390 TEST_F(AudioLowLatencyInputOutputTest, DISABLED_FullDuplexDelayMeasurement) { 391 if (!CanRunAudioTests()) 392 return; 393 394 AudioInputStreamWrapper aisw(audio_manager()); 395 AudioInputStream* ais = aisw.Create(); 396 EXPECT_TRUE(ais); 397 398 AudioOutputStreamWrapper aosw(audio_manager()); 399 AudioOutputStream* aos = aosw.Create(); 400 EXPECT_TRUE(aos); 401 402 // This test only supports identical parameters in both directions. 403 // TODO(henrika): it is possible to cut delay here by using different 404 // buffer sizes for input and output. 405 if (aisw.sample_rate() != aosw.sample_rate() || 406 aisw.samples_per_packet() != aosw.samples_per_packet() || 407 aisw.channels()!= aosw.channels() || 408 aisw.bits_per_sample() != aosw.bits_per_sample()) { 409 LOG(ERROR) << "This test requires symmetric input and output parameters. " 410 "Ensure that sample rate and number of channels are identical in " 411 "both directions"; 412 aos->Close(); 413 ais->Close(); 414 return; 415 } 416 417 EXPECT_TRUE(ais->Open()); 418 EXPECT_TRUE(aos->Open()); 419 420 FullDuplexAudioSinkSource full_duplex( 421 aisw.sample_rate(), aisw.samples_per_packet(), aisw.channels()); 422 423 LOG(INFO) << ">> You should now be able to hear yourself in loopback..."; 424 DLOG(INFO) << " sample_rate : " << aisw.sample_rate(); 425 DLOG(INFO) << " samples_per_packet: " << aisw.samples_per_packet(); 426 DLOG(INFO) << " channels : " << aisw.channels(); 427 428 ais->Start(&full_duplex); 429 aos->Start(&full_duplex); 430 431 // Wait for approximately 10 seconds. The user shall hear his own voice 432 // in loop back during this time. At the same time, delay recordings are 433 // performed and stored in the output text file. 434 message_loop()->PostDelayedTask(FROM_HERE, 435 base::MessageLoop::QuitClosure(), TestTimeouts::action_timeout()); 436 message_loop()->Run(); 437 438 aos->Stop(); 439 ais->Stop(); 440 441 // All Close() operations that run on the mocked audio thread, 442 // should be synchronous and not post additional close tasks to 443 // mocked the audio thread. Hence, there is no need to call 444 // message_loop()->RunUntilIdle() after the Close() methods. 445 aos->Close(); 446 ais->Close(); 447 } 448 449 } // namespace media 450