| // 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 <string> |
| |
| #include "base/command_line.h" |
| #include "base/message_loop.h" |
| #include "base/message_loop_proxy.h" |
| #include "media/audio/audio_output_dispatcher_impl.h" |
| #include "media/audio/audio_output_proxy.h" |
| #include "media/audio/audio_output_resampler.h" |
| #include "media/audio/audio_manager.h" |
| #include "media/audio/audio_manager_base.h" |
| #include "media/audio/fake_audio_output_stream.h" |
| #include "media/base/media_switches.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| |
| // TODO(dalecurtis): Temporarily disabled while switching pipeline to use float, |
| // http://crbug.com/114700 |
| #if defined(ENABLE_AUDIO_MIXER) |
| #include "media/audio/audio_output_mixer.h" |
| #endif |
| |
| using ::testing::_; |
| using ::testing::AllOf; |
| using ::testing::DoAll; |
| using ::testing::Field; |
| using ::testing::Mock; |
| using ::testing::NotNull; |
| using ::testing::Return; |
| using ::testing::SetArrayArgument; |
| using media::AudioBus; |
| using media::AudioBuffersState; |
| using media::AudioInputStream; |
| using media::AudioManager; |
| using media::AudioManagerBase; |
| using media::AudioOutputDispatcher; |
| using media::AudioOutputProxy; |
| using media::AudioOutputStream; |
| using media::AudioParameters; |
| using media::FakeAudioOutputStream; |
| |
| namespace { |
| |
| static const int kTestCloseDelayMs = 100; |
| |
| // Used in the test where we don't want a stream to be closed unexpectedly. |
| static const int kTestBigCloseDelaySeconds = 1000; |
| |
| // Delay between callbacks to AudioSourceCallback::OnMoreData. |
| static const int kOnMoreDataCallbackDelayMs = 10; |
| |
| // Let start run long enough for many OnMoreData callbacks to occur. |
| static const int kStartRunTimeMs = kOnMoreDataCallbackDelayMs * 10; |
| |
| class MockAudioOutputStream : public AudioOutputStream { |
| public: |
| MockAudioOutputStream(AudioManagerBase* manager, |
| const AudioParameters& params) |
| : start_called_(false), |
| stop_called_(false), |
| params_(params), |
| fake_output_stream_( |
| FakeAudioOutputStream::MakeFakeStream(manager, params_)) { |
| } |
| |
| void Start(AudioSourceCallback* callback) { |
| start_called_ = true; |
| fake_output_stream_->Start(callback); |
| } |
| |
| void Stop() { |
| stop_called_ = true; |
| fake_output_stream_->Stop(); |
| } |
| |
| ~MockAudioOutputStream() {} |
| |
| bool start_called() { return start_called_; } |
| bool stop_called() { return stop_called_; } |
| |
| MOCK_METHOD0(Open, bool()); |
| MOCK_METHOD1(SetVolume, void(double volume)); |
| MOCK_METHOD1(GetVolume, void(double* volume)); |
| MOCK_METHOD0(Close, void()); |
| |
| private: |
| bool start_called_; |
| bool stop_called_; |
| AudioParameters params_; |
| scoped_ptr<AudioOutputStream> fake_output_stream_; |
| }; |
| |
| class MockAudioManager : public AudioManagerBase { |
| public: |
| MockAudioManager() {} |
| virtual ~MockAudioManager() { |
| Shutdown(); |
| } |
| |
| MOCK_METHOD0(HasAudioOutputDevices, bool()); |
| MOCK_METHOD0(HasAudioInputDevices, bool()); |
| MOCK_METHOD0(GetAudioInputDeviceModel, string16()); |
| MOCK_METHOD1(MakeAudioOutputStream, AudioOutputStream*( |
| const AudioParameters& params)); |
| MOCK_METHOD1(MakeAudioOutputStreamProxy, AudioOutputStream*( |
| const AudioParameters& params)); |
| MOCK_METHOD2(MakeAudioInputStream, AudioInputStream*( |
| const AudioParameters& params, const std::string& device_id)); |
| MOCK_METHOD0(CanShowAudioInputSettings, bool()); |
| MOCK_METHOD0(ShowAudioInputSettings, void()); |
| MOCK_METHOD0(GetMessageLoop, scoped_refptr<base::MessageLoopProxy>()); |
| MOCK_METHOD1(GetAudioInputDeviceNames, void( |
| media::AudioDeviceNames* device_name)); |
| MOCK_METHOD0(IsRecordingInProcess, bool()); |
| |
| MOCK_METHOD1(MakeLinearOutputStream, AudioOutputStream*( |
| const AudioParameters& params)); |
| MOCK_METHOD1(MakeLowLatencyOutputStream, AudioOutputStream*( |
| const AudioParameters& params)); |
| MOCK_METHOD2(MakeLinearInputStream, AudioInputStream*( |
| const AudioParameters& params, const std::string& device_id)); |
| MOCK_METHOD2(MakeLowLatencyInputStream, AudioInputStream*( |
| const AudioParameters& params, const std::string& device_id)); |
| }; |
| |
| class MockAudioSourceCallback : public AudioOutputStream::AudioSourceCallback { |
| public: |
| int OnMoreData(AudioBus* audio_bus, AudioBuffersState buffers_state) { |
| audio_bus->Zero(); |
| return audio_bus->frames(); |
| } |
| int OnMoreIOData(AudioBus* source, AudioBus* dest, |
| AudioBuffersState buffers_state) { |
| return OnMoreData(dest, buffers_state); |
| } |
| MOCK_METHOD2(OnError, void(AudioOutputStream* stream, int code)); |
| }; |
| |
| } // namespace |
| |
| namespace media { |
| |
| class AudioOutputProxyTest : public testing::Test { |
| protected: |
| virtual void SetUp() { |
| EXPECT_CALL(manager_, GetMessageLoop()) |
| .WillRepeatedly(Return(message_loop_.message_loop_proxy())); |
| InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs)); |
| } |
| |
| virtual void TearDown() { |
| // All paused proxies should have been closed at this point. |
| EXPECT_EQ(0u, dispatcher_impl_->paused_proxies_); |
| |
| // This is necessary to free all proxy objects that have been |
| // closed by the test. |
| message_loop_.RunUntilIdle(); |
| } |
| |
| virtual void InitDispatcher(base::TimeDelta close_delay) { |
| // Use a low sample rate and large buffer size when testing otherwise the |
| // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e., |
| // RunUntilIdle() will never terminate. |
| params_ = AudioParameters(AudioParameters::AUDIO_PCM_LINEAR, |
| CHANNEL_LAYOUT_STEREO, 8000, 16, 2048); |
| dispatcher_impl_ = new AudioOutputDispatcherImpl(&manager(), |
| params_, |
| close_delay); |
| #if defined(ENABLE_AUDIO_MIXER) |
| mixer_ = new AudioOutputMixer(&manager(), params_, close_delay); |
| #endif |
| |
| // Necessary to know how long the dispatcher will wait before posting |
| // StopStreamTask. |
| pause_delay_ = dispatcher_impl_->pause_delay_; |
| } |
| |
| virtual void OnStart() {} |
| |
| MockAudioManager& manager() { |
| return manager_; |
| } |
| |
| // Wait for the close timer to fire. |
| void WaitForCloseTimer(const int timer_delay_ms) { |
| message_loop_.RunUntilIdle(); // OpenTask() may reset the timer. |
| base::PlatformThread::Sleep( |
| base::TimeDelta::FromMilliseconds(timer_delay_ms) * 2); |
| message_loop_.RunUntilIdle(); |
| } |
| |
| // Methods that do actual tests. |
| void OpenAndClose(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); |
| EXPECT_TRUE(proxy->Open()); |
| proxy->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| } |
| |
| // Create a stream, and then calls Start() and Stop(). |
| void StartAndStop(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, SetVolume(_)) |
| .Times(1); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); |
| EXPECT_TRUE(proxy->Open()); |
| |
| proxy->Start(&callback_); |
| OnStart(); |
| proxy->Stop(); |
| |
| proxy->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| EXPECT_TRUE(stream.stop_called()); |
| EXPECT_TRUE(stream.start_called()); |
| } |
| |
| // Verify that the stream is closed after Stop is called. |
| void CloseAfterStop(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, SetVolume(_)) |
| .Times(1); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); |
| EXPECT_TRUE(proxy->Open()); |
| |
| proxy->Start(&callback_); |
| OnStart(); |
| proxy->Stop(); |
| |
| // Wait for StopStream() to post StopStreamTask(). |
| base::PlatformThread::Sleep(pause_delay_ * 2); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| |
| // Verify expectation before calling Close(). |
| Mock::VerifyAndClear(&stream); |
| |
| proxy->Close(); |
| EXPECT_TRUE(stream.stop_called()); |
| EXPECT_TRUE(stream.start_called()); |
| } |
| |
| // Create two streams, but don't start them. Only one device must be open. |
| void TwoStreams(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher); |
| AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher); |
| EXPECT_TRUE(proxy1->Open()); |
| EXPECT_TRUE(proxy2->Open()); |
| proxy1->Close(); |
| proxy2->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| EXPECT_FALSE(stream.stop_called()); |
| EXPECT_FALSE(stream.start_called()); |
| } |
| |
| // Open() method failed. |
| void OpenFailed(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); |
| EXPECT_FALSE(proxy->Open()); |
| proxy->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| EXPECT_FALSE(stream.stop_called()); |
| EXPECT_FALSE(stream.start_called()); |
| } |
| |
| void CreateAndWait(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher); |
| EXPECT_TRUE(proxy->Open()); |
| |
| // Simulate a delay. |
| base::PlatformThread::Sleep( |
| base::TimeDelta::FromMilliseconds(kTestCloseDelayMs) * 2); |
| message_loop_.RunUntilIdle(); |
| |
| // Verify expectation before calling Close(). |
| Mock::VerifyAndClear(&stream); |
| |
| proxy->Close(); |
| EXPECT_FALSE(stream.stop_called()); |
| EXPECT_FALSE(stream.start_called()); |
| } |
| |
| void TwoStreams_OnePlaying(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream1(&manager_, params_); |
| MockAudioOutputStream stream2(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream1)) |
| .WillOnce(Return(&stream2)); |
| |
| EXPECT_CALL(stream1, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream1, SetVolume(_)) |
| .Times(1); |
| EXPECT_CALL(stream1, Close()) |
| .Times(1); |
| |
| EXPECT_CALL(stream2, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream2, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher); |
| AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher); |
| EXPECT_TRUE(proxy1->Open()); |
| EXPECT_TRUE(proxy2->Open()); |
| |
| proxy1->Start(&callback_); |
| message_loop_.RunUntilIdle(); |
| OnStart(); |
| proxy1->Stop(); |
| |
| proxy1->Close(); |
| proxy2->Close(); |
| EXPECT_TRUE(stream1.stop_called()); |
| EXPECT_TRUE(stream1.start_called()); |
| EXPECT_FALSE(stream2.stop_called()); |
| EXPECT_FALSE(stream2.start_called()); |
| } |
| |
| void TwoStreams_BothPlaying(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream1(&manager_, params_); |
| MockAudioOutputStream stream2(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream1)) |
| .WillOnce(Return(&stream2)); |
| |
| EXPECT_CALL(stream1, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream1, SetVolume(_)) |
| .Times(1); |
| EXPECT_CALL(stream1, Close()) |
| .Times(1); |
| |
| EXPECT_CALL(stream2, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream2, SetVolume(_)) |
| .Times(1); |
| EXPECT_CALL(stream2, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy1 = new AudioOutputProxy(dispatcher); |
| AudioOutputProxy* proxy2 = new AudioOutputProxy(dispatcher); |
| EXPECT_TRUE(proxy1->Open()); |
| EXPECT_TRUE(proxy2->Open()); |
| |
| proxy1->Start(&callback_); |
| proxy2->Start(&callback_); |
| OnStart(); |
| proxy1->Stop(); |
| proxy2->Stop(); |
| |
| proxy1->Close(); |
| proxy2->Close(); |
| EXPECT_TRUE(stream1.stop_called()); |
| EXPECT_TRUE(stream1.start_called()); |
| EXPECT_TRUE(stream2.stop_called()); |
| EXPECT_TRUE(stream2.start_called()); |
| } |
| |
| void StartFailed(AudioOutputDispatcher* dispatcher) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_); |
| EXPECT_TRUE(proxy->Open()); |
| |
| // Simulate a delay. |
| base::PlatformThread::Sleep( |
| base::TimeDelta::FromMilliseconds(kTestCloseDelayMs) * 2); |
| message_loop_.RunUntilIdle(); |
| |
| // Verify expectation before calling Close(). |
| Mock::VerifyAndClear(&stream); |
| |
| // |stream| is closed at this point. Start() should reopen it again. |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(reinterpret_cast<AudioOutputStream*>(NULL))); |
| |
| EXPECT_CALL(callback_, OnError(_, _)) |
| .Times(1); |
| |
| proxy->Start(&callback_); |
| |
| Mock::VerifyAndClear(&callback_); |
| |
| proxy->Close(); |
| } |
| |
| MessageLoop message_loop_; |
| scoped_refptr<AudioOutputDispatcherImpl> dispatcher_impl_; |
| #if defined(ENABLE_AUDIO_MIXER) |
| scoped_refptr<AudioOutputMixer> mixer_; |
| #endif |
| base::TimeDelta pause_delay_; |
| MockAudioManager manager_; |
| MockAudioSourceCallback callback_; |
| AudioParameters params_; |
| }; |
| |
| class AudioOutputResamplerTest : public AudioOutputProxyTest { |
| public: |
| virtual void TearDown() { |
| AudioOutputProxyTest::TearDown(); |
| } |
| |
| virtual void InitDispatcher(base::TimeDelta close_delay) { |
| AudioOutputProxyTest::InitDispatcher(close_delay); |
| // Use a low sample rate and large buffer size when testing otherwise the |
| // FakeAudioOutputStream will keep the message loop busy indefinitely; i.e., |
| // RunUntilIdle() will never terminate. |
| resampler_params_ = AudioParameters( |
| AudioParameters::AUDIO_PCM_LOW_LATENCY, CHANNEL_LAYOUT_STEREO, |
| 16000, 16, 1024); |
| resampler_ = new AudioOutputResampler( |
| &manager(), params_, resampler_params_, close_delay); |
| } |
| |
| virtual void OnStart() { |
| // Let start run for a bit. |
| message_loop_.RunUntilIdle(); |
| base::PlatformThread::Sleep( |
| base::TimeDelta::FromMilliseconds(kStartRunTimeMs)); |
| } |
| |
| protected: |
| AudioParameters resampler_params_; |
| scoped_refptr<AudioOutputResampler> resampler_; |
| }; |
| |
| TEST_F(AudioOutputProxyTest, CreateAndClose) { |
| AudioOutputProxy* proxy = new AudioOutputProxy(dispatcher_impl_); |
| proxy->Close(); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| TEST_F(AudioOutputProxyTest, CreateAndClose_Mixer) { |
| AudioOutputProxy* proxy = new AudioOutputProxy(mixer_); |
| proxy->Close(); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, CreateAndClose) { |
| AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); |
| proxy->Close(); |
| } |
| |
| TEST_F(AudioOutputProxyTest, OpenAndClose) { |
| OpenAndClose(dispatcher_impl_); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| TEST_F(AudioOutputProxyTest, OpenAndClose_Mixer) { |
| OpenAndClose(mixer_); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, OpenAndClose) { |
| OpenAndClose(resampler_); |
| } |
| |
| // Create a stream, and verify that it is closed after kTestCloseDelayMs. |
| // if it doesn't start playing. |
| TEST_F(AudioOutputProxyTest, CreateAndWait) { |
| CreateAndWait(dispatcher_impl_); |
| } |
| |
| // Create a stream, and verify that it is closed after kTestCloseDelayMs. |
| // if it doesn't start playing. |
| TEST_F(AudioOutputResamplerTest, CreateAndWait) { |
| CreateAndWait(resampler_); |
| } |
| |
| TEST_F(AudioOutputProxyTest, StartAndStop) { |
| StartAndStop(dispatcher_impl_); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| TEST_F(AudioOutputProxyTest, StartAndStop_Mixer) { |
| StartAndStop(mixer_); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, StartAndStop) { |
| StartAndStop(resampler_); |
| } |
| |
| TEST_F(AudioOutputProxyTest, CloseAfterStop) { |
| CloseAfterStop(dispatcher_impl_); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| TEST_F(AudioOutputProxyTest, CloseAfterStop_Mixer) { |
| CloseAfterStop(mixer_); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, CloseAfterStop) { |
| CloseAfterStop(resampler_); |
| } |
| |
| TEST_F(AudioOutputProxyTest, TwoStreams) { |
| TwoStreams(dispatcher_impl_); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| TEST_F(AudioOutputProxyTest, TwoStreams_Mixer) { |
| TwoStreams(mixer_); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, TwoStreams) { |
| TwoStreams(resampler_); |
| } |
| |
| // Two streams: verify that second stream is allocated when the first |
| // starts playing. |
| TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying) { |
| InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds)); |
| TwoStreams_OnePlaying(dispatcher_impl_); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| // Two streams: verify that only one device will be created. |
| TEST_F(AudioOutputProxyTest, TwoStreams_OnePlaying_Mixer) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs)); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, Start(_)) |
| .Times(1); |
| EXPECT_CALL(stream, SetVolume(_)) |
| .Times(1); |
| EXPECT_CALL(stream, Stop()) |
| .Times(1); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy1 = new AudioOutputProxy(mixer_); |
| AudioOutputProxy* proxy2 = new AudioOutputProxy(mixer_); |
| EXPECT_TRUE(proxy1->Open()); |
| EXPECT_TRUE(proxy2->Open()); |
| |
| proxy1->Start(&callback_); |
| proxy1->Stop(); |
| |
| proxy1->Close(); |
| proxy2->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, TwoStreams_OnePlaying) { |
| InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds)); |
| TwoStreams_OnePlaying(resampler_); |
| } |
| |
| // Two streams, both are playing. Dispatcher should not open a third stream. |
| TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying) { |
| InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds)); |
| TwoStreams_BothPlaying(dispatcher_impl_); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| // Two streams, both are playing. Still have to use single device. |
| // Also verifies that every proxy stream gets its own pending_bytes. |
| TEST_F(AudioOutputProxyTest, TwoStreams_BothPlaying_Mixer) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| InitDispatcher(base::TimeDelta::FromMilliseconds(kTestCloseDelayMs)); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, Start(_)) |
| .Times(1); |
| EXPECT_CALL(stream, SetVolume(_)) |
| .Times(1); |
| EXPECT_CALL(stream, Stop()) |
| .Times(1); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy1 = new AudioOutputProxy(mixer_); |
| AudioOutputProxy* proxy2 = new AudioOutputProxy(mixer_); |
| EXPECT_TRUE(proxy1->Open()); |
| EXPECT_TRUE(proxy2->Open()); |
| |
| proxy1->Start(&callback_); |
| |
| // Mute the proxy. Resulting stream should still have correct length. |
| proxy1->SetVolume(0.0); |
| |
| uint8 zeroes[4] = {0, 0, 0, 0}; |
| uint8 buf1[4] = {0}; |
| EXPECT_CALL(callback_, |
| OnMoreData(NotNull(), 4, |
| AllOf(Field(&AudioBuffersState::pending_bytes, 0), |
| Field(&AudioBuffersState::hardware_delay_bytes, 0)))) |
| .WillOnce(DoAll(SetArrayArgument<0>(zeroes, zeroes + sizeof(zeroes)), |
| Return(4))); |
| mixer_->OnMoreData(buf1, sizeof(buf1), AudioBuffersState(0, 0)); |
| proxy2->Start(&callback_); |
| uint8 buf2[4] = {0}; |
| EXPECT_CALL(callback_, |
| OnMoreData(NotNull(), 4, |
| AllOf(Field(&AudioBuffersState::pending_bytes, 4), |
| Field(&AudioBuffersState::hardware_delay_bytes, 0)))) |
| .WillOnce(DoAll(SetArrayArgument<0>(zeroes, zeroes + sizeof(zeroes)), |
| Return(4))); |
| EXPECT_CALL(callback_, |
| OnMoreData(NotNull(), 4, |
| AllOf(Field(&AudioBuffersState::pending_bytes, 0), |
| Field(&AudioBuffersState::hardware_delay_bytes, 0)))) |
| .WillOnce(DoAll(SetArrayArgument<0>(zeroes, zeroes + sizeof(zeroes)), |
| Return(4))); |
| mixer_->OnMoreData(buf2, sizeof(buf2), AudioBuffersState(4, 0)); |
| proxy1->Stop(); |
| proxy2->Stop(); |
| |
| proxy1->Close(); |
| proxy2->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, TwoStreams_BothPlaying) { |
| InitDispatcher(base::TimeDelta::FromSeconds(kTestBigCloseDelaySeconds)); |
| TwoStreams_BothPlaying(resampler_); |
| } |
| |
| TEST_F(AudioOutputProxyTest, OpenFailed) { |
| OpenFailed(dispatcher_impl_); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| TEST_F(AudioOutputProxyTest, OpenFailed_Mixer) { |
| OpenFailed(mixer_); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, OpenFailed) { |
| CommandLine::ForCurrentProcess()->AppendSwitch( |
| switches::kDisableAudioFallback); |
| OpenFailed(resampler_); |
| } |
| |
| // Start() method failed. |
| TEST_F(AudioOutputProxyTest, StartFailed) { |
| StartFailed(dispatcher_impl_); |
| } |
| |
| #if defined(ENABLE_AUDIO_MIXER) |
| // Start() method failed. |
| TEST_F(AudioOutputProxyTest, StartFailed_Mixer) { |
| MockAudioOutputStream stream(&manager_, params_); |
| |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| EXPECT_CALL(stream, Start(_)) |
| .Times(1); |
| EXPECT_CALL(stream, SetVolume(_)) |
| .Times(1); |
| EXPECT_CALL(stream, Stop()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy1 = new AudioOutputProxy(mixer_); |
| AudioOutputProxy* proxy2 = new AudioOutputProxy(mixer_); |
| EXPECT_TRUE(proxy1->Open()); |
| EXPECT_TRUE(proxy2->Open()); |
| proxy1->Start(&callback_); |
| proxy1->Stop(); |
| proxy1->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| |
| // Verify expectation before continueing. |
| Mock::VerifyAndClear(&stream); |
| |
| // |stream| is closed at this point. Start() should reopen it again. |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(reinterpret_cast<AudioOutputStream*>(NULL))); |
| |
| EXPECT_CALL(callback_, OnError(_, _)) |
| .Times(1); |
| |
| proxy2->Start(&callback_); |
| |
| Mock::VerifyAndClear(&callback_); |
| |
| proxy2->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| } |
| #endif |
| |
| TEST_F(AudioOutputResamplerTest, StartFailed) { |
| StartFailed(resampler_); |
| } |
| |
| // Simulate AudioOutputStream::Create() failure with a low latency stream and |
| // ensure AudioOutputResampler falls back to the high latency path. |
| TEST_F(AudioOutputResamplerTest, LowLatencyCreateFailedFallback) { |
| MockAudioOutputStream stream(&manager_, params_); |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .Times(2) |
| .WillOnce(Return(static_cast<AudioOutputStream*>(NULL))) |
| .WillRepeatedly(Return(&stream)); |
| EXPECT_CALL(stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); |
| EXPECT_TRUE(proxy->Open()); |
| proxy->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| } |
| |
| // Simulate AudioOutputStream::Open() failure with a low latency stream and |
| // ensure AudioOutputResampler falls back to the high latency path. |
| TEST_F(AudioOutputResamplerTest, LowLatencyOpenFailedFallback) { |
| MockAudioOutputStream failed_stream(&manager_, params_); |
| MockAudioOutputStream okay_stream(&manager_, params_); |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .Times(2) |
| .WillOnce(Return(&failed_stream)) |
| .WillRepeatedly(Return(&okay_stream)); |
| EXPECT_CALL(failed_stream, Open()) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(failed_stream, Close()) |
| .Times(1); |
| EXPECT_CALL(okay_stream, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(okay_stream, Close()) |
| .Times(1); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); |
| EXPECT_TRUE(proxy->Open()); |
| proxy->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| } |
| |
| // Simulate failures to open both the low latency and the fallback high latency |
| // stream and ensure AudioOutputResampler terminates normally. |
| TEST_F(AudioOutputResamplerTest, LowLatencyFallbackFailed) { |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .Times(2) |
| .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL))); |
| |
| AudioOutputProxy* proxy = new AudioOutputProxy(resampler_); |
| EXPECT_FALSE(proxy->Open()); |
| proxy->Close(); |
| WaitForCloseTimer(kTestCloseDelayMs); |
| } |
| |
| // Simulate an eventual OpenStream() failure; i.e. successful OpenStream() calls |
| // eventually followed by one which fails; root cause of http://crbug.com/150619 |
| TEST_F(AudioOutputResamplerTest, LowLatencyOpenEventuallyFails) { |
| MockAudioOutputStream stream1(&manager_, params_); |
| MockAudioOutputStream stream2(&manager_, params_); |
| MockAudioOutputStream stream3(&manager_, params_); |
| |
| // Setup the mock such that all three streams are successfully created. |
| EXPECT_CALL(manager(), MakeAudioOutputStream(_)) |
| .WillOnce(Return(&stream1)) |
| .WillOnce(Return(&stream2)) |
| .WillOnce(Return(&stream3)) |
| .WillRepeatedly(Return(static_cast<AudioOutputStream*>(NULL))); |
| |
| // Stream1 should be able to successfully open and start. |
| EXPECT_CALL(stream1, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream1, Close()) |
| .Times(1); |
| EXPECT_CALL(stream1, SetVolume(_)) |
| .Times(1); |
| |
| // Stream2 should also be able to successfully open and start. |
| EXPECT_CALL(stream2, Open()) |
| .WillOnce(Return(true)); |
| EXPECT_CALL(stream2, Close()) |
| .Times(1); |
| EXPECT_CALL(stream2, SetVolume(_)) |
| .Times(1); |
| |
| // Stream3 should fail on Open() (yet still be closed since |
| // MakeAudioOutputStream returned a valid AudioOutputStream object). |
| EXPECT_CALL(stream3, Open()) |
| .WillOnce(Return(false)); |
| EXPECT_CALL(stream3, Close()) |
| .Times(1); |
| |
| // Open and start the first proxy and stream. |
| AudioOutputProxy* proxy1 = new AudioOutputProxy(resampler_); |
| EXPECT_TRUE(proxy1->Open()); |
| proxy1->Start(&callback_); |
| OnStart(); |
| |
| // Open and start the second proxy and stream. |
| AudioOutputProxy* proxy2 = new AudioOutputProxy(resampler_); |
| EXPECT_TRUE(proxy2->Open()); |
| proxy2->Start(&callback_); |
| OnStart(); |
| |
| // Attempt to open the third stream which should fail. |
| AudioOutputProxy* proxy3 = new AudioOutputProxy(resampler_); |
| EXPECT_FALSE(proxy3->Open()); |
| |
| // Perform the required Stop()/Close() shutdown dance for each proxy. Under |
| // the hood each proxy should correctly call CloseStream() if OpenStream() |
| // succeeded or not. |
| proxy3->Stop(); |
| proxy3->Close(); |
| proxy2->Stop(); |
| proxy2->Close(); |
| proxy1->Stop(); |
| proxy1->Close(); |
| |
| // Wait for all of the messages to fly and then verify stream behavior. |
| WaitForCloseTimer(kTestCloseDelayMs); |
| EXPECT_TRUE(stream1.stop_called()); |
| EXPECT_TRUE(stream1.start_called()); |
| EXPECT_TRUE(stream2.stop_called()); |
| EXPECT_TRUE(stream2.start_called()); |
| EXPECT_FALSE(stream3.stop_called()); |
| EXPECT_FALSE(stream3.start_called()); |
| } |
| |
| } // namespace media |