Home | History | Annotate | Download | only in win
      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/files/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 
     24 using base::win::ScopedCOMInitializer;
     25 using ::testing::_;
     26 using ::testing::AnyNumber;
     27 using ::testing::AtLeast;
     28 using ::testing::Gt;
     29 using ::testing::NotNull;
     30 
     31 namespace media {
     32 
     33 ACTION_P3(CheckCountAndPostQuitTask, count, limit, loop) {
     34   if (++*count >= limit) {
     35     loop->PostTask(FROM_HERE, base::MessageLoop::QuitClosure());
     36   }
     37 }
     38 
     39 class 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 
     49 class 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.
     87 class 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.
    157 static 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.
    171 class 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                         sample_rate(), bits_per_sample(), frames_per_buffer_,
    212                         default_params_.effects()),
    213         AudioManagerBase::kDefaultDeviceId);
    214     EXPECT_TRUE(ais);
    215     return ais;
    216   }
    217 
    218   ScopedCOMInitializer com_init_;
    219   AudioManager* audio_man_;
    220   const AudioParameters default_params_;
    221   int frames_per_buffer_;
    222 };
    223 
    224 // Convenience method which creates a default AudioInputStream object.
    225 static AudioInputStream* CreateDefaultAudioInputStream(
    226     AudioManager* audio_manager) {
    227   AudioInputStreamWrapper aisw(audio_manager);
    228   AudioInputStream* ais = aisw.Create();
    229   return ais;
    230 }
    231 
    232 class ScopedAudioInputStream {
    233  public:
    234   explicit ScopedAudioInputStream(AudioInputStream* stream)
    235       : stream_(stream) {}
    236 
    237   ~ScopedAudioInputStream() {
    238     if (stream_)
    239       stream_->Close();
    240   }
    241 
    242   void Close() {
    243     if (stream_)
    244       stream_->Close();
    245     stream_ = NULL;
    246   }
    247 
    248   AudioInputStream* operator->() {
    249     return stream_;
    250   }
    251 
    252   AudioInputStream* get() const { return stream_; }
    253 
    254   void Reset(AudioInputStream* new_stream) {
    255     Close();
    256     stream_ = new_stream;
    257   }
    258 
    259  private:
    260   AudioInputStream* stream_;
    261 
    262   DISALLOW_COPY_AND_ASSIGN(ScopedAudioInputStream);
    263 };
    264 
    265 // Verify that we can retrieve the current hardware/mixing sample rate
    266 // for all available input devices.
    267 TEST(WinAudioInputTest, WASAPIAudioInputStreamHardwareSampleRate) {
    268   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    269   if (!CanRunAudioTests(audio_manager.get()))
    270     return;
    271 
    272   ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA);
    273 
    274   // Retrieve a list of all available input devices.
    275   media::AudioDeviceNames device_names;
    276   audio_manager->GetAudioInputDeviceNames(&device_names);
    277 
    278   // Scan all available input devices and repeat the same test for all of them.
    279   for (media::AudioDeviceNames::const_iterator it = device_names.begin();
    280        it != device_names.end(); ++it) {
    281     // Retrieve the hardware sample rate given a specified audio input device.
    282     AudioParameters params = WASAPIAudioInputStream::GetInputStreamParameters(
    283         it->unique_id);
    284     EXPECT_GE(params.sample_rate(), 0);
    285   }
    286 }
    287 
    288 // Test Create(), Close() calling sequence.
    289 TEST(WinAudioInputTest, WASAPIAudioInputStreamCreateAndClose) {
    290   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    291   if (!CanRunAudioTests(audio_manager.get()))
    292     return;
    293   ScopedAudioInputStream ais(
    294       CreateDefaultAudioInputStream(audio_manager.get()));
    295   ais.Close();
    296 }
    297 
    298 // Test Open(), Close() calling sequence.
    299 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenAndClose) {
    300   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    301   if (!CanRunAudioTests(audio_manager.get()))
    302     return;
    303   ScopedAudioInputStream ais(
    304       CreateDefaultAudioInputStream(audio_manager.get()));
    305   EXPECT_TRUE(ais->Open());
    306   ais.Close();
    307 }
    308 
    309 // Test Open(), Start(), Close() calling sequence.
    310 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartAndClose) {
    311   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    312   if (!CanRunAudioTests(audio_manager.get()))
    313     return;
    314   ScopedAudioInputStream ais(
    315       CreateDefaultAudioInputStream(audio_manager.get()));
    316   EXPECT_TRUE(ais->Open());
    317   MockAudioInputCallback sink;
    318   ais->Start(&sink);
    319   ais.Close();
    320 }
    321 
    322 // Test Open(), Start(), Stop(), Close() calling sequence.
    323 TEST(WinAudioInputTest, WASAPIAudioInputStreamOpenStartStopAndClose) {
    324   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    325   if (!CanRunAudioTests(audio_manager.get()))
    326     return;
    327   ScopedAudioInputStream ais(
    328       CreateDefaultAudioInputStream(audio_manager.get()));
    329   EXPECT_TRUE(ais->Open());
    330   MockAudioInputCallback sink;
    331   ais->Start(&sink);
    332   ais->Stop();
    333   ais.Close();
    334 }
    335 
    336 // Test some additional calling sequences.
    337 TEST(WinAudioInputTest, WASAPIAudioInputStreamMiscCallingSequences) {
    338   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    339   if (!CanRunAudioTests(audio_manager.get()))
    340     return;
    341   ScopedAudioInputStream ais(
    342       CreateDefaultAudioInputStream(audio_manager.get()));
    343   WASAPIAudioInputStream* wais =
    344       static_cast<WASAPIAudioInputStream*>(ais.get());
    345 
    346   // Open(), Open() should fail the second time.
    347   EXPECT_TRUE(ais->Open());
    348   EXPECT_FALSE(ais->Open());
    349 
    350   MockAudioInputCallback sink;
    351 
    352   // Start(), Start() is a valid calling sequence (second call does nothing).
    353   ais->Start(&sink);
    354   EXPECT_TRUE(wais->started());
    355   ais->Start(&sink);
    356   EXPECT_TRUE(wais->started());
    357 
    358   // Stop(), Stop() is a valid calling sequence (second call does nothing).
    359   ais->Stop();
    360   EXPECT_FALSE(wais->started());
    361   ais->Stop();
    362   EXPECT_FALSE(wais->started());
    363   ais.Close();
    364 }
    365 
    366 TEST(WinAudioInputTest, WASAPIAudioInputStreamTestPacketSizes) {
    367   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    368   if (!CanRunAudioTests(audio_manager.get()))
    369     return;
    370 
    371   int count = 0;
    372   base::MessageLoopForUI loop;
    373 
    374   // 10 ms packet size.
    375 
    376   // Create default WASAPI input stream which records in stereo using
    377   // the shared mixing rate. The default buffer size is 10ms.
    378   AudioInputStreamWrapper aisw(audio_manager.get());
    379   ScopedAudioInputStream ais(aisw.Create());
    380   EXPECT_TRUE(ais->Open());
    381 
    382   MockAudioInputCallback sink;
    383 
    384   // Derive the expected size in bytes of each recorded packet.
    385   uint32 bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
    386       (aisw.bits_per_sample() / 8);
    387 
    388   // We use 10ms packets and will run the test until ten packets are received.
    389   // All should contain valid packets of the same size and a valid delay
    390   // estimate.
    391   EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
    392       .Times(AtLeast(10))
    393       .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
    394   ais->Start(&sink);
    395   loop.Run();
    396   ais->Stop();
    397 
    398   // Store current packet size (to be used in the subsequent tests).
    399   int frames_per_buffer_10ms = aisw.frames_per_buffer();
    400 
    401   ais.Close();
    402 
    403   // 20 ms packet size.
    404 
    405   count = 0;
    406   ais.Reset(aisw.Create(2 * frames_per_buffer_10ms));
    407   EXPECT_TRUE(ais->Open());
    408   bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
    409       (aisw.bits_per_sample() / 8);
    410 
    411   EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
    412       .Times(AtLeast(10))
    413       .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
    414   ais->Start(&sink);
    415   loop.Run();
    416   ais->Stop();
    417   ais.Close();
    418 
    419   // 5 ms packet size.
    420 
    421   count = 0;
    422   ais.Reset(aisw.Create(frames_per_buffer_10ms / 2));
    423   EXPECT_TRUE(ais->Open());
    424   bytes_per_packet = aisw.channels() * aisw.frames_per_buffer() *
    425     (aisw.bits_per_sample() / 8);
    426 
    427   EXPECT_CALL(sink, OnData(ais.get(), NotNull(), Gt(bytes_per_packet), _))
    428       .Times(AtLeast(10))
    429       .WillRepeatedly(CheckCountAndPostQuitTask(&count, 10, &loop));
    430   ais->Start(&sink);
    431   loop.Run();
    432   ais->Stop();
    433   ais.Close();
    434 }
    435 
    436 // Test that we can capture a stream in loopback.
    437 TEST(WinAudioInputTest, WASAPIAudioInputStreamLoopback) {
    438   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    439   if (!audio_manager->HasAudioOutputDevices() || !CoreAudioUtil::IsSupported())
    440     return;
    441 
    442   AudioParameters params = audio_manager->GetInputStreamParameters(
    443       AudioManagerBase::kLoopbackInputDeviceId);
    444   EXPECT_EQ(params.effects(), 0);
    445 
    446   AudioParameters output_params =
    447       audio_manager->GetOutputStreamParameters(std::string());
    448   EXPECT_EQ(params.sample_rate(), output_params.sample_rate());
    449   EXPECT_EQ(params.channel_layout(), output_params.channel_layout());
    450 
    451   ScopedAudioInputStream stream(audio_manager->MakeAudioInputStream(
    452       params, AudioManagerBase::kLoopbackInputDeviceId));
    453   ASSERT_TRUE(stream->Open());
    454   FakeAudioInputCallback sink;
    455   stream->Start(&sink);
    456   ASSERT_FALSE(sink.error());
    457 
    458   sink.WaitForData();
    459   stream.Close();
    460 
    461   EXPECT_GT(sink.num_received_audio_frames(), 0);
    462   EXPECT_FALSE(sink.error());
    463 }
    464 
    465 // This test is intended for manual tests and should only be enabled
    466 // when it is required to store the captured data on a local file.
    467 // By default, GTest will print out YOU HAVE 1 DISABLED TEST.
    468 // To include disabled tests in test execution, just invoke the test program
    469 // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS
    470 // environment variable to a value greater than 0.
    471 TEST(WinAudioInputTest, DISABLED_WASAPIAudioInputStreamRecordToFile) {
    472   scoped_ptr<AudioManager> audio_manager(AudioManager::CreateForTesting());
    473   if (!CanRunAudioTests(audio_manager.get()))
    474     return;
    475 
    476   // Name of the output PCM file containing captured data. The output file
    477   // will be stored in the directory containing 'media_unittests.exe'.
    478   // Example of full name: \src\build\Debug\out_stereo_10sec.pcm.
    479   const char* file_name = "out_stereo_10sec.pcm";
    480 
    481   AudioInputStreamWrapper aisw(audio_manager.get());
    482   ScopedAudioInputStream ais(aisw.Create());
    483   EXPECT_TRUE(ais->Open());
    484 
    485   VLOG(0) << ">> Sample rate: " << aisw.sample_rate() << " [Hz]";
    486   WriteToFileAudioSink file_sink(file_name, aisw.bits_per_sample());
    487   VLOG(0) << ">> Speak into the default microphone while recording.";
    488   ais->Start(&file_sink);
    489   base::PlatformThread::Sleep(TestTimeouts::action_timeout());
    490   ais->Stop();
    491   VLOG(0) << ">> Recording has stopped.";
    492   ais.Close();
    493 }
    494 
    495 }  // namespace media
    496