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 <windows.h> 6#include <mmsystem.h> 7 8#include "base/basictypes.h" 9#include "base/environment.h" 10#include "base/file_util.h" 11#include "base/memory/scoped_ptr.h" 12#include "base/message_loop/message_loop.h" 13#include "base/path_service.h" 14#include "base/test/test_timeouts.h" 15#include "base/win/scoped_com_initializer.h" 16#include "media/audio/audio_io.h" 17#include "media/audio/audio_manager_base.h" 18#include "media/audio/win/audio_low_latency_input_win.h" 19#include "media/audio/win/core_audio_util_win.h" 20#include "media/base/seekable_buffer.h" 21#include "testing/gmock/include/gmock/gmock.h" 22#include "testing/gtest/include/gtest/gtest.h" 23 24using base::win::ScopedCOMInitializer; 25using ::testing::_; 26using ::testing::AnyNumber; 27using ::testing::AtLeast; 28using ::testing::Gt; 29using ::testing::NotNull; 30 31namespace media { 32 33ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) { 34 if (++*count >= limit) { 35 loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure()); 36 } 37} 38 39class MockAudioInputCallback : public AudioInputStream::AudioInputCallback { 40 public: 41 MOCK_METHOD4(OnData, 42 void(AudioInputStream* stream, 43 const AudioBus* src, 44 uint32 hardware_delay_bytes, 45 double volume)); 46 MOCK_METHOD1(OnError, void(AudioInputStream* stream)); 47}; 48 49class FakeAudioInputCallback : public AudioInputStream::AudioInputCallback { 50 public: 51 FakeAudioInputCallback() 52 : error_(false), 53 data_event_(false, false), 54 num_received_audio_frames_(0) {} 55 56 bool error() const { return error_; } 57 int num_received_audio_frames() const { return num_received_audio_frames_; } 58 59 // Waits until OnData() is called on another thread. 60 void WaitForData() { 61 data_event_.Wait(); 62 } 63 64 virtual void OnData(AudioInputStream* stream, 65 const AudioBus* src, 66 uint32 hardware_delay_bytes, 67 double volume) OVERRIDE { 68 EXPECT_NE(hardware_delay_bytes, 0u); 69 num_received_audio_frames_ += src->frames(); 70 data_event_.Signal(); 71 } 72 73 virtual void OnError(AudioInputStream* stream) OVERRIDE { 74 error_ = true; 75 } 76 77 private: 78 int num_received_audio_frames_; 79 base::WaitableEvent data_event_; 80 bool error_; 81 82 DISALLOW_COPY_AND_ASSIGN(FakeAudioInputCallback); 83}; 84 85// This audio sink implementation should be used for manual tests only since 86// the recorded data is stored on a raw binary data file. 87class WriteToFileAudioSink : public AudioInputStream::AudioInputCallback { 88 public: 89 // Allocate space for ~10 seconds of data @ 48kHz in stereo: 90 // 2 bytes per sample, 2 channels, 10ms @ 48kHz, 10 seconds <=> 1920000 bytes. 91 static const size_t kMaxBufferSize = 2 * 2 * 480 * 100 * 10; 92 93 explicit WriteToFileAudioSink(const char* file_name, int bits_per_sample) 94 : bits_per_sample_(bits_per_sample), 95 buffer_(0, kMaxBufferSize), 96 bytes_to_write_(0) { 97 base::FilePath file_path; 98 EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_path)); 99 file_path = file_path.AppendASCII(file_name); 100 binary_file_ = base::OpenFile(file_path, "wb"); 101 DLOG_IF(ERROR, !binary_file_) << "Failed to open binary PCM data file."; 102 VLOG(0) << ">> Output file: " << file_path.value() << " has been created."; 103 VLOG(0) << "bits_per_sample_:" << bits_per_sample_; 104 } 105 106 virtual ~WriteToFileAudioSink() { 107 size_t bytes_written = 0; 108 while (bytes_written < bytes_to_write_) { 109 const uint8* chunk; 110 int chunk_size; 111 112 // Stop writing if no more data is available. 113 if (!buffer_.GetCurrentChunk(&chunk, &chunk_size)) 114 break; 115 116 // Write recorded data chunk to the file and prepare for next chunk. 117 fwrite(chunk, 1, chunk_size, binary_file_); 118 buffer_.Seek(chunk_size); 119 bytes_written += chunk_size; 120 } 121 base::CloseFile(binary_file_); 122 } 123 124 // AudioInputStream::AudioInputCallback implementation. 125 virtual void OnData(AudioInputStream* stream, 126 const AudioBus* src, 127 uint32 hardware_delay_bytes, 128 double volume) { 129 EXPECT_EQ(bits_per_sample_, 16); 130 const int num_samples = src->frames() * src->channels(); 131 scoped_ptr<int16> interleaved(new int16[num_samples]); 132 const int bytes_per_sample = sizeof(*interleaved); 133 src->ToInterleaved(src->frames(), bytes_per_sample, interleaved.get()); 134 135 // Store data data in a temporary buffer to avoid making blocking 136 // fwrite() calls in the audio callback. The complete buffer will be 137 // written to file in the destructor. 138 const int size = bytes_per_sample * num_samples; 139 if (buffer_.Append((const uint8*)interleaved.get(), size)) { 140 bytes_to_write_ += size; 141 } 142 } 143 144 virtual void OnError(AudioInputStream* stream) {} 145 146 private: 147 int bits_per_sample_; 148 media::SeekableBuffer buffer_; 149 FILE* binary_file_; 150 size_t bytes_to_write_; 151}; 152 153// Convenience method which ensures that we are not running on the build 154// bots and that at least one valid input device can be found. We also 155// verify that we are not running on XP since the low-latency (WASAPI- 156// based) version requires Windows Vista or higher. 157static bool CanRunAudioTests(AudioManager* audio_man) { 158 if (!CoreAudioUtil::IsSupported()) { 159 LOG(WARNING) << "This tests requires Windows Vista or higher."; 160 return false; 161 } 162 // TODO(henrika): note that we use Wave today to query the number of 163 // existing input devices. 164 bool input = audio_man->HasAudioInputDevices(); 165 LOG_IF(WARNING, !input) << "No input device detected."; 166 return input; 167} 168 169// Convenience method which creates a default AudioInputStream object but 170// also allows the user to modify the default settings. 171class AudioInputStreamWrapper { 172 public: 173 explicit AudioInputStreamWrapper(AudioManager* audio_manager) 174 : com_init_(ScopedCOMInitializer::kMTA), 175 audio_man_(audio_manager), 176 default_params_( 177 audio_manager->GetInputStreamParameters( 178 AudioManagerBase::kDefaultDeviceId)) { 179 EXPECT_EQ(format(), AudioParameters::AUDIO_PCM_LOW_LATENCY); 180 frames_per_buffer_ = default_params_.frames_per_buffer(); 181 // We expect the default buffer size to be a 10ms buffer. 182 EXPECT_EQ(frames_per_buffer_, sample_rate() / 100); 183 } 184 185 ~AudioInputStreamWrapper() {} 186 187 // Creates AudioInputStream object using default parameters. 188 AudioInputStream* Create() { 189 return CreateInputStream(); 190 } 191 192 // Creates AudioInputStream object using non-default parameters where the 193 // frame size is modified. 194 AudioInputStream* Create(int frames_per_buffer) { 195 frames_per_buffer_ = frames_per_buffer; 196 return CreateInputStream(); 197 } 198 199 AudioParameters::Format format() const { return default_params_.format(); } 200 int channels() const { 201 return ChannelLayoutToChannelCount(default_params_.channel_layout()); 202 } 203 int bits_per_sample() const { return default_params_.bits_per_sample(); } 204 int sample_rate() const { return default_params_.sample_rate(); } 205 int frames_per_buffer() const { return frames_per_buffer_; } 206 207 private: 208 AudioInputStream* CreateInputStream() { 209 AudioInputStream* ais = audio_man_->MakeAudioInputStream( 210 AudioParameters(format(), default_params_.channel_layout(), 211 default_params_.input_channels(), 212 sample_rate(), bits_per_sample(), frames_per_buffer_, 213 default_params_.effects()), 214 AudioManagerBase::kDefaultDeviceId); 215 EXPECT_TRUE(ais); 216 return ais; 217 } 218 219 ScopedCOMInitializer com_init_; 220 AudioManager* audio_man_; 221 const AudioParameters default_params_; 222 int frames_per_buffer_; 223}; 224 225// Convenience method which creates a default AudioInputStream object. 226static AudioInputStream* CreateDefaultAudioInputStream( 227 AudioManager* audio_manager) { 228 AudioInputStreamWrapper aisw(audio_manager); 229 AudioInputStream* ais = aisw.Create(); 230 return ais; 231} 232 233class ScopedAudioInputStream { 234 public: 235 explicit ScopedAudioInputStream(AudioInputStream* stream) 236 : stream_(stream) {} 237 238 ~ScopedAudioInputStream() { 239 if (stream_) 240 stream_->Close(); 241 } 242 243 void Close() { 244 if (stream_) 245 stream_->Close(); 246 stream_ = NULL; 247 } 248 249 AudioInputStream* operator->() { 250 return stream_; 251 } 252 253 AudioInputStream* get() const { return stream_; } 254 255 void Reset(AudioInputStream* new_stream) { 256 Close(); 257 stream_ = new_stream; 258 } 259 260 private: 261 AudioInputStream* stream_; 262 263 DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream); 264}; 265 266// Verify that we can retrieve the current hardware/mixing sample rate 267// for all available input devices. 268TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) { 269 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 270 if (!CanRunAudioTests(audio_manager.get())) 271 return; 272 273 ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); 274 275 // Retrieve a list of all available input devices. 276 media::AudioDeviceNames device_names; 277 audio_manager->GetAudioInputDeviceNames(&device_names); 278 279 // Scan all available input devices and repeat the same test for all of them. 280 for (media::AudioDeviceNames::const_iterator it = device_names.begin(); 281 it != device_names.end(); ++it) { 282 // Retrieve the hardware sample rate given a specified audio input device. 283 AudioParameters params = WASAPIAudioInputStream::GetInputStreamParameters( 284 it->unique_id); 285 EXPECT_GE(params.sample_rate(), 0); 286 } 287} 288 289// Test Create(), Close() calling sequence. 290TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) { 291 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 292 if (!CanRunAudioTests(audio_manager.get())) 293 return; 294 ScopedAudioInputStream ais( 295 CreateDefaultAudioInputStream(audio_manager.get())); 296 ais.Close(); 297} 298 299// Test Open(), Close() calling sequence. 300TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) { 301 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 302 if (!CanRunAudioTests(audio_manager.get())) 303 return; 304 ScopedAudioInputStream ais( 305 CreateDefaultAudioInputStream(audio_manager.get())); 306 EXPECT_TRUE(ais->Open()); 307 ais.Close(); 308} 309 310// Test Open(), Start(), Close() calling sequence. 311TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) { 312 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 313 if (!CanRunAudioTests(audio_manager.get())) 314 return; 315 ScopedAudioInputStream ais( 316 CreateDefaultAudioInputStream(audio_manager.get())); 317 EXPECT_TRUE(ais->Open()); 318 MockAudioInputCallback sink; 319 ais->Start(&sink); 320 ais.Close(); 321} 322 323// Test Open(), Start(), Stop(), Close() calling sequence. 324TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) { 325 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 326 if (!CanRunAudioTests(audio_manager.get())) 327 return; 328 ScopedAudioInputStream ais( 329 CreateDefaultAudioInputStream(audio_manager.get())); 330 EXPECT_TRUE(ais->Open()); 331 MockAudioInputCallback sink; 332 ais->Start(&sink); 333 ais->Stop(); 334 ais.Close(); 335} 336 337// Test some additional calling sequences. 338TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) { 339 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 340 if (!CanRunAudioTests(audio_manager.get())) 341 return; 342 ScopedAudioInputStream ais( 343 CreateDefaultAudioInputStream(audio_manager.get())); 344 WASAPIAudioInputStream* wais = 345 static_cast<WASAPIAudioInputStream*>(ais.get()); 346 347 // Open(), Open() should fail the second time. 348 EXPECT_TRUE(ais->Open()); 349 EXPECT_FALSE(ais->Open()); 350 351 MockAudioInputCallback sink; 352 353 // Start(), Start() is a valid calling sequence (second call does nothing). 354 ais->Start(&sink); 355 EXPECT_TRUE(wais->started()); 356 ais->Start(&sink); 357 EXPECT_TRUE(wais->started()); 358 359 // Stop(), Stop() is a valid calling sequence (second call does nothing). 360 ais->Stop(); 361 EXPECT_FALSE(wais->started()); 362 ais->Stop(); 363 EXPECT_FALSE(wais->started()); 364 ais.Close(); 365} 366 367TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) { 368 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 369 if (!CanRunAudioTests(audio_manager.get())) 370 return; 371 372 int count = 0; 373 base::MessageLoopForUI loop; 374 375 // 10 ms packet size. 376 377 // Create default WASAPI input stream which records in stereo using 378 // the shared mixing rate. The default buffer size is 10ms. 379 AudioInputStreamWrapper aisw(audio_manager.get()); 380 ScopedAudioInputStream ais(aisw.Create()); 381 EXPECT_TRUE(ais->Open()); 382 383 MockAudioInputCallback sink; 384 385 // Derive the expected size in bytes of each recorded packet. 386 uint32 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * 387 (aisw.bits_per_sample() / 8); 388 389 // We use 10ms packets and will run the test until ten packets are received. 390 // All should contain valid packets of the same size and a valid delay 391 // estimate. 392 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _)) 393 .Times(AtLeast(10)) 394 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); 395 ais->Start(&sink); 396 loop.Run(); 397 ais->Stop(); 398 399 // Store current packet size (to be used in the subsequent tests). 400 int frames_per_buffer_10ms = aisw.frames_per_buffer(); 401 402 ais.Close(); 403 404 // 20 ms packet size. 405 406 count = 0; 407 ais.Reset(aisw.Create(2 * frames_per_buffer_10ms)); 408 EXPECT_TRUE(ais->Open()); 409 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * 410 (aisw.bits_per_sample() / 8); 411 412 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _)) 413 .Times(AtLeast(10)) 414 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); 415 ais->Start(&sink); 416 loop.Run(); 417 ais->Stop(); 418 ais.Close(); 419 420 // 5 ms packet size. 421 422 count = 0; 423 ais.Reset(aisw.Create(frames_per_buffer_10ms / 2)); 424 EXPECT_TRUE(ais->Open()); 425 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() * 426 (aisw.bits_per_sample() / 8); 427 428 EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _)) 429 .Times(AtLeast(10)) 430 .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop)); 431 ais->Start(&sink); 432 loop.Run(); 433 ais->Stop(); 434 ais.Close(); 435} 436 437// Test that we can capture a stream in loopback. 438TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) { 439 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 440 if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported()) 441 return; 442 443 AudioParameters params = audio_manager->GetInputStreamParameters( 444 AudioManagerBase::kLoopbackInputDeviceId); 445 EXPECT_EQ(params.effects(), 0); 446 447 AudioParameters output_params = 448 audio_manager->GetOutputStreamParameters(std::string()); 449 EXPECT_EQ(params.sample_rate(), output_params.sample_rate()); 450 EXPECT_EQ(params.channel_layout(), output_params.channel_layout()); 451 452 ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream( 453 params, AudioManagerBase::kLoopbackInputDeviceId)); 454 ASSERT_TRUE(stream->Open()); 455 FakeAudioInputCallback sink; 456 stream->Start(&sink); 457 ASSERT_FALSE(sink.error()); 458 459 sink.WaitForData(); 460 stream.Close(); 461 462 EXPECT_GT(sink.num_received_audio_frames(), 0); 463 EXPECT_FALSE(sink.error()); 464} 465 466// This test is intended for manual tests and should only be enabled 467// when it is required to store the captured data on a local file. 468// By default, GTest will print out YOU HAVE 1 DISABLED TEST. 469// To include disabled tests in test execution, just invoke the test program 470// with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS 471// environment variable to a value greater than 0. 472TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) { 473 scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting()); 474 if (!CanRunAudioTests(audio_manager.get())) 475 return; 476 477 // Name of the output PCM file containing captured data. The output file 478 // will be stored in the directory containing 'media_unittests.exe'. 479 // Example of full name: \src\build\Debug\out_stereo_10sec.pcm. 480 const char* file_name = "out_stereo_10sec.pcm"; 481 482 AudioInputStreamWrapper aisw(audio_manager.get()); 483 ScopedAudioInputStream ais(aisw.Create()); 484 EXPECT_TRUE(ais->Open()); 485 486 VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]"; 487 WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample()); 488 VLOG(0) << ">> Speak into the default microphone while recording."; 489 ais->Start(&file_sink); 490 base::PlatformThread::Sleep(TestTimeouts::action_timeout()); 491 ais->Stop(); 492 VLOG(0) << ">> Recording has stopped."; 493 ais.Close(); 494} 495 496} // namespace media 497