| // Copyright (c) 2012 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include <windows.h> |
| #include <mmsystem.h> |
| |
| #include "base/basictypes.h" |
| #include "base/environment.h" |
| #include "base/file_util.h" |
| #include "base/memory/scoped_ptr.h" |
| #include "base/message_loop.h" |
| #include "base/test/test_timeouts.h" |
| #include "base/time.h" |
| #include "base/path_service.h" |
| #include "base/win/scoped_com_initializer.h" |
| #include "media/audio/audio_io.h" |
| #include "media/audio/audio_manager.h" |
| #include "media/audio/audio_util.h" |
| #include "media/audio/win/audio_low_latency_output_win.h" |
| #include "media/audio/win/core_audio_util_win.h" |
| #include "media/base/decoder_buffer.h" |
| #include "media/base/seekable_buffer.h" |
| #include "media/base/test_data_util.h" |
| #include "testing/gmock_mutant.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| using ::testing::_; |
| using ::testing::AnyNumber; |
| using ::testing::Between; |
| using ::testing::CreateFunctor; |
| using ::testing::DoAll; |
| using ::testing::Gt; |
| using ::testing::InvokeWithoutArgs; |
| using ::testing::NotNull; |
| using ::testing::Return; |
| using base::win::ScopedCOMInitializer; |
| |
| namespace media { |
| |
| static const char kSpeechFile_16b_s_48k[] = "speech_16b_stereo_48kHz.raw"; |
| static const char kSpeechFile_16b_s_44k[] = "speech_16b_stereo_44kHz.raw"; |
| static const size_t kFileDurationMs = 20000; |
| static const size_t kNumFileSegments = 2; |
| static const int kBitsPerSample = 16; |
| static const ChannelLayout kChannelLayout = CHANNEL_LAYOUT_STEREO; |
| static const size_t kMaxDeltaSamples = 1000; |
| static const char* kDeltaTimeMsFileName = "delta_times_ms.txt"; |
| |
| MATCHER_P(HasValidDelay, value, "") { |
| // It is difficult to come up with a perfect test condition for the delay |
| // estimation. For now, verify that the produced output delay is always |
| // larger than the selected buffer size. |
| return arg.hardware_delay_bytes > value.hardware_delay_bytes; |
| } |
| |
| // Used to terminate a loop from a different thread than the loop belongs to. |
| // |loop| should be a MessageLoopProxy. |
| ACTION_P(QuitLoop, loop) { |
| loop->PostTask(FROM_HERE, MessageLoop::QuitClosure()); |
| } |
| |
| class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { |
| public: |
| MOCK_METHOD2(OnMoreData, int(AudioBus* audio_bus, |
| AudioBuffersState buffers_state)); |
| MOCK_METHOD3(OnMoreIOData, int(AudioBus* source, |
| AudioBus* dest, |
| AudioBuffersState buffers_state)); |
| MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); |
| }; |
| |
| // This audio source implementation should be used for manual tests only since |
| // it takes about 20 seconds to play out a file. |
| class ReadFromFileAudioSource : public AudioOutputStream::AudioSourceCallback { |
| public: |
| explicit ReadFromFileAudioSource(const std::string& name) |
| : pos_(0), |
| previous_call_time_(base::Time::Now()), |
| text_file_(NULL), |
| elements_to_write_(0) { |
| // Reads a test file from media/test/data directory. |
| file_ = ReadTestDataFile(name); |
| |
| // Creates an array that will store delta times between callbacks. |
| // The content of this array will be written to a text file at |
| // destruction and can then be used for off-line analysis of the exact |
| // timing of callbacks. The text file will be stored in media/test/data. |
| delta_times_.reset(new int[kMaxDeltaSamples]); |
| } |
| |
| virtual ~ReadFromFileAudioSource() { |
| // Get complete file path to output file in directory containing |
| // media_unittests.exe. |
| FilePath file_name; |
| EXPECT_TRUE(PathService::Get(base::DIR_EXE, &file_name)); |
| file_name = file_name.AppendASCII(kDeltaTimeMsFileName); |
| |
| EXPECT_TRUE(!text_file_); |
| text_file_ = file_util::OpenFile(file_name, "wt"); |
| DLOG_IF(ERROR, !text_file_) << "Failed to open log file."; |
| |
| // Write the array which contains delta times to a text file. |
| size_t elements_written = 0; |
| while (elements_written < elements_to_write_) { |
| fprintf(text_file_, "%d\n", delta_times_[elements_written]); |
| ++elements_written; |
| } |
| |
| file_util::CloseFile(text_file_); |
| } |
| |
| // AudioOutputStream::AudioSourceCallback implementation. |
| virtual int OnMoreData(AudioBus* audio_bus, |
| AudioBuffersState buffers_state) { |
| // Store time difference between two successive callbacks in an array. |
| // These values will be written to a file in the destructor. |
| int diff = (base::Time::Now() - previous_call_time_).InMilliseconds(); |
| previous_call_time_ = base::Time::Now(); |
| if (elements_to_write_ < kMaxDeltaSamples) { |
| delta_times_[elements_to_write_] = diff; |
| ++elements_to_write_; |
| } |
| |
| int max_size = |
| audio_bus->frames() * audio_bus->channels() * kBitsPerSample / 8; |
| |
| // Use samples read from a data file and fill up the audio buffer |
| // provided to us in the callback. |
| if (pos_ + static_cast<int>(max_size) > file_size()) |
| max_size = file_size() - pos_; |
| int frames = max_size / (audio_bus->channels() * kBitsPerSample / 8); |
| if (max_size) { |
| audio_bus->FromInterleaved( |
| file_->GetData() + pos_, frames, kBitsPerSample / 8); |
| pos_ += max_size; |
| } |
| return frames; |
| } |
| |
| virtual int OnMoreIOData(AudioBus* source, |
| AudioBus* dest, |
| AudioBuffersState buffers_state) OVERRIDE { |
| NOTREACHED(); |
| return 0; |
| } |
| |
| virtual void OnError(AudioOutputStream* stream, int code) {} |
| |
| int file_size() { return file_->GetDataSize(); } |
| |
| private: |
| scoped_refptr<DecoderBuffer> file_; |
| scoped_array<int> delta_times_; |
| int pos_; |
| base::Time previous_call_time_; |
| FILE* text_file_; |
| size_t elements_to_write_; |
| }; |
| |
| static bool ExclusiveModeIsEnabled() { |
| return (WASAPIAudioOutputStream::GetShareMode() == |
| AUDCLNT_SHAREMODE_EXCLUSIVE); |
| } |
| |
| // Convenience method which ensures that we are not running on the build |
| // bots and that at least one valid output device can be found. We also |
| // verify that we are not running on XP since the low-latency (WASAPI- |
| // based) version requires Windows Vista or higher. |
| static bool CanRunAudioTests(AudioManager* audio_man) { |
| if (!CoreAudioUtil::IsSupported()) { |
| LOG(WARNING) << "This test requires Windows Vista or higher."; |
| return false; |
| } |
| |
| // TODO(henrika): note that we use Wave today to query the number of |
| // existing output devices. |
| if (!audio_man->HasAudioOutputDevices()) { |
| LOG(WARNING) << "No output devices detected."; |
| return false; |
| } |
| |
| if (WASAPIAudioOutputStream::HardwareChannelLayout() != kChannelLayout) { |
| LOG(WARNING) << "This test requires stereo audio output."; |
| return false; |
| } |
| |
| return true; |
| } |
| |
| // Convenience method which creates a default AudioOutputStream object but |
| // also allows the user to modify the default settings. |
| class AudioOutputStreamWrapper { |
| public: |
| explicit AudioOutputStreamWrapper(AudioManager* audio_manager) |
| : com_init_(ScopedCOMInitializer::kMTA), |
| audio_man_(audio_manager), |
| format_(AudioParameters::AUDIO_PCM_LOW_LATENCY), |
| channel_layout_(kChannelLayout), |
| bits_per_sample_(kBitsPerSample) { |
| // Use native/mixing sample rate and 10ms frame size as default. |
| sample_rate_ = static_cast<int>( |
| WASAPIAudioOutputStream::HardwareSampleRate(eConsole)); |
| samples_per_packet_ = sample_rate_ / 100; |
| DCHECK(sample_rate_); |
| } |
| |
| ~AudioOutputStreamWrapper() {} |
| |
| // Creates AudioOutputStream object using default parameters. |
| AudioOutputStream* Create() { |
| return CreateOutputStream(); |
| } |
| |
| // Creates AudioOutputStream object using non-default parameters where the |
| // frame size is modified. |
| AudioOutputStream* Create(int samples_per_packet) { |
| samples_per_packet_ = samples_per_packet; |
| return CreateOutputStream(); |
| } |
| |
| // Creates AudioOutputStream object using non-default parameters where the |
| // sample rate and frame size are modified. |
| AudioOutputStream* Create(int sample_rate, int samples_per_packet) { |
| sample_rate_ = sample_rate; |
| samples_per_packet_ = samples_per_packet; |
| return CreateOutputStream(); |
| } |
| |
| AudioParameters::Format format() const { return format_; } |
| int channels() const { return ChannelLayoutToChannelCount(channel_layout_); } |
| int bits_per_sample() const { return bits_per_sample_; } |
| int sample_rate() const { return sample_rate_; } |
| int samples_per_packet() const { return samples_per_packet_; } |
| |
| private: |
| AudioOutputStream* CreateOutputStream() { |
| AudioOutputStream* aos = audio_man_->MakeAudioOutputStream( |
| AudioParameters(format_, channel_layout_, sample_rate_, |
| bits_per_sample_, samples_per_packet_)); |
| EXPECT_TRUE(aos); |
| return aos; |
| } |
| |
| ScopedCOMInitializer com_init_; |
| AudioManager* audio_man_; |
| AudioParameters::Format format_; |
| ChannelLayout channel_layout_; |
| int bits_per_sample_; |
| int sample_rate_; |
| int samples_per_packet_; |
| }; |
| |
| // Convenience method which creates a default AudioOutputStream object. |
| static AudioOutputStream* CreateDefaultAudioOutputStream( |
| AudioManager* audio_manager) { |
| AudioOutputStreamWrapper aosw(audio_manager); |
| AudioOutputStream* aos = aosw.Create(); |
| return aos; |
| } |
| |
| // Verify that we can retrieve the current hardware/mixing sample rate |
| // for all supported device roles. The ERole enumeration defines constants |
| // that indicate the role that the system/user has assigned to an audio |
| // endpoint device. |
| // TODO(henrika): modify this test when we support full device enumeration. |
| TEST(WASAPIAudioOutputStreamTest, HardwareSampleRate) { |
| // Skip this test in exclusive mode since the resulting rate is only utilized |
| // for shared mode streams. |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get()) || ExclusiveModeIsEnabled()) |
| return; |
| |
| ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); |
| |
| // Default device intended for games, system notification sounds, |
| // and voice commands. |
| int fs = static_cast<int>( |
| WASAPIAudioOutputStream::HardwareSampleRate(eConsole)); |
| EXPECT_GE(fs, 0); |
| |
| // Default communication device intended for e.g. VoIP communication. |
| fs = static_cast<int>( |
| WASAPIAudioOutputStream::HardwareSampleRate(eCommunications)); |
| EXPECT_GE(fs, 0); |
| |
| // Multimedia device for music, movies and live music recording. |
| fs = static_cast<int>( |
| WASAPIAudioOutputStream::HardwareSampleRate(eMultimedia)); |
| EXPECT_GE(fs, 0); |
| } |
| |
| // Test Create(), Close() calling sequence. |
| TEST(WASAPIAudioOutputStreamTest, CreateAndClose) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); |
| aos->Close(); |
| } |
| |
| // Verify that the created object is configured to use the same number of |
| // audio channels as is reported by the static HardwareChannelCount() method. |
| TEST(WASAPIAudioOutputStreamTest, HardwareChannelCount) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| ScopedCOMInitializer com_init(ScopedCOMInitializer::kMTA); |
| |
| // First, verify that we can read a valid native/hardware channel-count. |
| int hardware_channel_count = WASAPIAudioOutputStream::HardwareChannelCount(); |
| EXPECT_GE(hardware_channel_count, 1); |
| |
| AudioOutputStreamWrapper aosw(audio_manager.get()); |
| WASAPIAudioOutputStream* aos = |
| static_cast<WASAPIAudioOutputStream*>(aosw.Create()); |
| |
| // Next, ensure that the created output stream object is really using the |
| // hardware channel-count. |
| EXPECT_EQ(hardware_channel_count, aos->GetEndpointChannelCountForTesting()); |
| aos->Close(); |
| } |
| |
| // Test Open(), Close() calling sequence. |
| TEST(WASAPIAudioOutputStreamTest, OpenAndClose) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); |
| EXPECT_TRUE(aos->Open()); |
| aos->Close(); |
| } |
| |
| // Test Open(), Start(), Close() calling sequence. |
| TEST(WASAPIAudioOutputStreamTest, OpenStartAndClose) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); |
| EXPECT_TRUE(aos->Open()); |
| MockAudioSourceCallback source; |
| EXPECT_CALL(source, OnError(aos, _)) |
| .Times(0); |
| aos->Start(&source); |
| aos->Close(); |
| } |
| |
| // Test Open(), Start(), Stop(), Close() calling sequence. |
| TEST(WASAPIAudioOutputStreamTest, OpenStartStopAndClose) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); |
| EXPECT_TRUE(aos->Open()); |
| MockAudioSourceCallback source; |
| EXPECT_CALL(source, OnError(aos, _)) |
| .Times(0); |
| aos->Start(&source); |
| aos->Stop(); |
| aos->Close(); |
| } |
| |
| // Test SetVolume(), GetVolume() |
| TEST(WASAPIAudioOutputStreamTest, Volume) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); |
| |
| // Initial volume should be full volume (1.0). |
| double volume = 0.0; |
| aos->GetVolume(&volume); |
| EXPECT_EQ(1.0, volume); |
| |
| // Verify some valid volume settings. |
| aos->SetVolume(0.0); |
| aos->GetVolume(&volume); |
| EXPECT_EQ(0.0, volume); |
| |
| aos->SetVolume(0.5); |
| aos->GetVolume(&volume); |
| EXPECT_EQ(0.5, volume); |
| |
| aos->SetVolume(1.0); |
| aos->GetVolume(&volume); |
| EXPECT_EQ(1.0, volume); |
| |
| // Ensure that invalid volume setting have no effect. |
| aos->SetVolume(1.5); |
| aos->GetVolume(&volume); |
| EXPECT_EQ(1.0, volume); |
| |
| aos->SetVolume(-0.5); |
| aos->GetVolume(&volume); |
| EXPECT_EQ(1.0, volume); |
| |
| aos->Close(); |
| } |
| |
| // Test some additional calling sequences. |
| TEST(WASAPIAudioOutputStreamTest, MiscCallingSequences) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| AudioOutputStream* aos = CreateDefaultAudioOutputStream(audio_manager.get()); |
| WASAPIAudioOutputStream* waos = static_cast<WASAPIAudioOutputStream*>(aos); |
| |
| // Open(), Open() is a valid calling sequence (second call does nothing). |
| EXPECT_TRUE(aos->Open()); |
| EXPECT_TRUE(aos->Open()); |
| |
| MockAudioSourceCallback source; |
| |
| // Start(), Start() is a valid calling sequence (second call does nothing). |
| aos->Start(&source); |
| EXPECT_TRUE(waos->started()); |
| aos->Start(&source); |
| EXPECT_TRUE(waos->started()); |
| |
| // Stop(), Stop() is a valid calling sequence (second call does nothing). |
| aos->Stop(); |
| EXPECT_FALSE(waos->started()); |
| aos->Stop(); |
| EXPECT_FALSE(waos->started()); |
| |
| // Start(), Stop(), Start(), Stop(). |
| aos->Start(&source); |
| EXPECT_TRUE(waos->started()); |
| aos->Stop(); |
| EXPECT_FALSE(waos->started()); |
| aos->Start(&source); |
| EXPECT_TRUE(waos->started()); |
| aos->Stop(); |
| EXPECT_FALSE(waos->started()); |
| |
| aos->Close(); |
| } |
| |
| // Use default packet size (10ms) and verify that rendering starts. |
| TEST(WASAPIAudioOutputStreamTest, PacketSizeInMilliseconds) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| MessageLoopForUI loop; |
| MockAudioSourceCallback source; |
| |
| // Create default WASAPI output stream which plays out in stereo using |
| // the shared mixing rate. The default buffer size is 10ms. |
| AudioOutputStreamWrapper aosw(audio_manager.get()); |
| AudioOutputStream* aos = aosw.Create(); |
| EXPECT_TRUE(aos->Open()); |
| |
| // Derive the expected size in bytes of each packet. |
| uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() * |
| (aosw.bits_per_sample() / 8); |
| |
| // Set up expected minimum delay estimation. |
| AudioBuffersState state(0, bytes_per_packet); |
| |
| // Wait for the first callback and verify its parameters. |
| EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) |
| .WillOnce(DoAll( |
| QuitLoop(loop.message_loop_proxy()), |
| Return(aosw.samples_per_packet()))); |
| |
| aos->Start(&source); |
| loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), |
| TestTimeouts::action_timeout()); |
| loop.Run(); |
| aos->Stop(); |
| aos->Close(); |
| } |
| |
| // Use a fixed packets size (independent of sample rate) and verify |
| // that rendering starts. |
| TEST(WASAPIAudioOutputStreamTest, PacketSizeInSamples) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| MessageLoopForUI loop; |
| MockAudioSourceCallback source; |
| |
| // Create default WASAPI output stream which reads data in stereo using |
| // the native mixing rate and channel count. The buffer size is set to |
| // 1024 samples. |
| AudioOutputStreamWrapper aosw(audio_manager.get()); |
| AudioOutputStream* aos = aosw.Create(1024); |
| EXPECT_TRUE(aos->Open()); |
| |
| // Derive the expected size in bytes of each packet. |
| uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() * |
| (aosw.bits_per_sample() / 8); |
| |
| // Set up expected minimum delay estimation. |
| AudioBuffersState state(0, bytes_per_packet); |
| |
| // Ensure that callbacks start correctly. |
| EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) |
| .WillOnce(DoAll( |
| QuitLoop(loop.message_loop_proxy()), |
| Return(aosw.samples_per_packet()))) |
| .WillRepeatedly(Return(aosw.samples_per_packet())); |
| |
| aos->Start(&source); |
| loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), |
| TestTimeouts::action_timeout()); |
| loop.Run(); |
| aos->Stop(); |
| aos->Close(); |
| } |
| |
| // This test is intended for manual tests and should only be enabled |
| // when it is required to play out data from a local PCM file. |
| // By default, GTest will print out YOU HAVE 1 DISABLED TEST. |
| // To include disabled tests in test execution, just invoke the test program |
| // with --gtest_also_run_disabled_tests or set the GTEST_ALSO_RUN_DISABLED_TESTS |
| // environment variable to a value greater than 0. |
| // The test files are approximately 20 seconds long. |
| TEST(WASAPIAudioOutputStreamTest, DISABLED_ReadFromStereoFile) { |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| AudioOutputStreamWrapper aosw(audio_manager.get()); |
| AudioOutputStream* aos = aosw.Create(); |
| EXPECT_TRUE(aos->Open()); |
| |
| std::string file_name; |
| if (aosw.sample_rate() == 48000) { |
| file_name = kSpeechFile_16b_s_48k; |
| } else if (aosw.sample_rate() == 44100) { |
| file_name = kSpeechFile_16b_s_44k; |
| } else if (aosw.sample_rate() == 96000) { |
| // Use 48kHz file at 96kHz as well. Will sound like Donald Duck. |
| file_name = kSpeechFile_16b_s_48k; |
| } else { |
| FAIL() << "This test supports 44.1, 48kHz and 96kHz only."; |
| return; |
| } |
| ReadFromFileAudioSource file_source(file_name); |
| |
| LOG(INFO) << "File name : " << file_name.c_str(); |
| LOG(INFO) << "Sample rate : " << aosw.sample_rate(); |
| LOG(INFO) << "Bits per sample: " << aosw.bits_per_sample(); |
| LOG(INFO) << "#channels : " << aosw.channels(); |
| LOG(INFO) << "File size : " << file_source.file_size(); |
| LOG(INFO) << "#file segments : " << kNumFileSegments; |
| LOG(INFO) << ">> Listen to the stereo file while playing..."; |
| |
| for (int i = 0; i < kNumFileSegments; i++) { |
| // Each segment will start with a short (~20ms) block of zeros, hence |
| // some short glitches might be heard in this test if kNumFileSegments |
| // is larger than one. The exact length of the silence period depends on |
| // the selected sample rate. |
| aos->Start(&file_source); |
| base::PlatformThread::Sleep( |
| base::TimeDelta::FromMilliseconds(kFileDurationMs / kNumFileSegments)); |
| aos->Stop(); |
| } |
| |
| LOG(INFO) << ">> Stereo file playout has stopped."; |
| aos->Close(); |
| } |
| |
| // Verify that we can open the output stream in exclusive mode using a |
| // certain set of audio parameters and a sample rate of 48kHz. |
| // The expected outcomes of each setting in this test has been derived |
| // manually using log outputs (--v=1). |
| TEST(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt48kHz) { |
| if (!ExclusiveModeIsEnabled()) |
| return; |
| |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| AudioOutputStreamWrapper aosw(audio_manager.get()); |
| |
| // 10ms @ 48kHz shall work. |
| // Note that, this is the same size as we can use for shared-mode streaming |
| // but here the endpoint buffer delay is only 10ms instead of 20ms. |
| AudioOutputStream* aos = aosw.Create(48000, 480); |
| EXPECT_TRUE(aos->Open()); |
| aos->Close(); |
| |
| // 5ms @ 48kHz does not work due to misalignment. |
| // This test will propose an aligned buffer size of 5.3333ms. |
| // Note that we must call Close() even is Open() fails since Close() also |
| // deletes the object and we want to create a new object in the next test. |
| aos = aosw.Create(48000, 240); |
| EXPECT_FALSE(aos->Open()); |
| aos->Close(); |
| |
| // 5.3333ms @ 48kHz should work (see test above). |
| aos = aosw.Create(48000, 256); |
| EXPECT_TRUE(aos->Open()); |
| aos->Close(); |
| |
| // 2.6667ms is smaller than the minimum supported size (=3ms). |
| aos = aosw.Create(48000, 128); |
| EXPECT_FALSE(aos->Open()); |
| aos->Close(); |
| |
| // 3ms does not correspond to an aligned buffer size. |
| // This test will propose an aligned buffer size of 3.3333ms. |
| aos = aosw.Create(48000, 144); |
| EXPECT_FALSE(aos->Open()); |
| aos->Close(); |
| |
| // 3.3333ms @ 48kHz <=> smallest possible buffer size we can use. |
| aos = aosw.Create(48000, 160); |
| EXPECT_TRUE(aos->Open()); |
| aos->Close(); |
| } |
| |
| // Verify that we can open the output stream in exclusive mode using a |
| // certain set of audio parameters and a sample rate of 44.1kHz. |
| // The expected outcomes of each setting in this test has been derived |
| // manually using log outputs (--v=1). |
| TEST(WASAPIAudioOutputStreamTest, ExclusiveModeBufferSizesAt44kHz) { |
| if (!ExclusiveModeIsEnabled()) |
| return; |
| |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| AudioOutputStreamWrapper aosw(audio_manager.get()); |
| |
| // 10ms @ 44.1kHz does not work due to misalignment. |
| // This test will propose an aligned buffer size of 10.1587ms. |
| AudioOutputStream* aos = aosw.Create(44100, 441); |
| EXPECT_FALSE(aos->Open()); |
| aos->Close(); |
| |
| // 10.1587ms @ 44.1kHz shall work (see test above). |
| aos = aosw.Create(44100, 448); |
| EXPECT_TRUE(aos->Open()); |
| aos->Close(); |
| |
| // 5.8050ms @ 44.1 should work. |
| aos = aosw.Create(44100, 256); |
| EXPECT_TRUE(aos->Open()); |
| aos->Close(); |
| |
| // 4.9887ms @ 44.1kHz does not work to misalignment. |
| // This test will propose an aligned buffer size of 5.0794ms. |
| // Note that we must call Close() even is Open() fails since Close() also |
| // deletes the object and we want to create a new object in the next test. |
| aos = aosw.Create(44100, 220); |
| EXPECT_FALSE(aos->Open()); |
| aos->Close(); |
| |
| // 5.0794ms @ 44.1kHz shall work (see test above). |
| aos = aosw.Create(44100, 224); |
| EXPECT_TRUE(aos->Open()); |
| aos->Close(); |
| |
| // 2.9025ms is smaller than the minimum supported size (=3ms). |
| aos = aosw.Create(44100, 132); |
| EXPECT_FALSE(aos->Open()); |
| aos->Close(); |
| |
| // 3.01587ms is larger than the minimum size but is not aligned. |
| // This test will propose an aligned buffer size of 3.6281ms. |
| aos = aosw.Create(44100, 133); |
| EXPECT_FALSE(aos->Open()); |
| aos->Close(); |
| |
| // 3.6281ms @ 44.1kHz <=> smallest possible buffer size we can use. |
| aos = aosw.Create(44100, 160); |
| EXPECT_TRUE(aos->Open()); |
| aos->Close(); |
| } |
| |
| // Verify that we can open and start the output stream in exclusive mode at |
| // the lowest possible delay at 48kHz. |
| TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt48kHz) { |
| if (!ExclusiveModeIsEnabled()) |
| return; |
| |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| MessageLoopForUI loop; |
| MockAudioSourceCallback source; |
| |
| // Create exclusive-mode WASAPI output stream which plays out in stereo |
| // using the minimum buffer size at 48kHz sample rate. |
| AudioOutputStreamWrapper aosw(audio_manager.get()); |
| AudioOutputStream* aos = aosw.Create(48000, 160); |
| EXPECT_TRUE(aos->Open()); |
| |
| // Derive the expected size in bytes of each packet. |
| uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() * |
| (aosw.bits_per_sample() / 8); |
| |
| // Set up expected minimum delay estimation. |
| AudioBuffersState state(0, bytes_per_packet); |
| |
| // Wait for the first callback and verify its parameters. |
| EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) |
| .WillOnce(DoAll( |
| QuitLoop(loop.message_loop_proxy()), |
| Return(aosw.samples_per_packet()))) |
| .WillRepeatedly(Return(aosw.samples_per_packet())); |
| |
| aos->Start(&source); |
| loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), |
| TestTimeouts::action_timeout()); |
| loop.Run(); |
| aos->Stop(); |
| aos->Close(); |
| } |
| |
| // Verify that we can open and start the output stream in exclusive mode at |
| // the lowest possible delay at 44.1kHz. |
| TEST(WASAPIAudioOutputStreamTest, ExclusiveModeMinBufferSizeAt44kHz) { |
| if (!ExclusiveModeIsEnabled()) |
| return; |
| |
| scoped_ptr<AudioManager> audio_manager(AudioManager::Create()); |
| if (!CanRunAudioTests(audio_manager.get())) |
| return; |
| |
| MessageLoopForUI loop; |
| MockAudioSourceCallback source; |
| |
| // Create exclusive-mode WASAPI output stream which plays out in stereo |
| // using the minimum buffer size at 44.1kHz sample rate. |
| AudioOutputStreamWrapper aosw(audio_manager.get()); |
| AudioOutputStream* aos = aosw.Create(44100, 160); |
| EXPECT_TRUE(aos->Open()); |
| |
| // Derive the expected size in bytes of each packet. |
| uint32 bytes_per_packet = aosw.channels() * aosw.samples_per_packet() * |
| (aosw.bits_per_sample() / 8); |
| |
| // Set up expected minimum delay estimation. |
| AudioBuffersState state(0, bytes_per_packet); |
| |
| // Wait for the first callback and verify its parameters. |
| EXPECT_CALL(source, OnMoreData(NotNull(), HasValidDelay(state))) |
| .WillOnce(DoAll( |
| QuitLoop(loop.message_loop_proxy()), |
| Return(aosw.samples_per_packet()))) |
| .WillRepeatedly(Return(aosw.samples_per_packet())); |
| |
| aos->Start(&source); |
| loop.PostDelayedTask(FROM_HERE, MessageLoop::QuitClosure(), |
| TestTimeouts::action_timeout()); |
| loop.Run(); |
| aos->Stop(); |
| aos->Close(); |
| } |
| |
| } // namespace media |