| // Copyright 2017 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 "chromecast/media/audio/cast_audio_manager.h" |
| |
| #include <memory> |
| #include <string> |
| #include <utility> |
| |
| #include "base/bind.h" |
| #include "base/test/scoped_task_environment.h" |
| #include "build/build_config.h" |
| #include "chromecast/chromecast_buildflags.h" |
| #include "chromecast/common/mojom/constants.mojom.h" |
| #include "chromecast/common/mojom/multiroom.mojom.h" |
| #include "chromecast/media/cma/backend/cma_backend.h" |
| #include "chromecast/media/cma/test/mock_cma_backend.h" |
| #include "chromecast/media/cma/test/mock_cma_backend_factory.h" |
| #include "chromecast/media/cma/test/mock_multiroom_manager.h" |
| #include "media/audio/audio_device_info_accessor_for_tests.h" |
| #include "media/audio/fake_audio_log_factory.h" |
| #include "media/audio/mock_audio_source_callback.h" |
| #include "media/audio/test_audio_thread.h" |
| #include "services/service_manager/public/cpp/connector.h" |
| #include "testing/gmock/include/gmock/gmock.h" |
| #include "testing/gtest/include/gtest/gtest.h" |
| #if defined(OS_ANDROID) |
| #include "media/audio/android/audio_track_output_stream.h" |
| #endif // defined(OS_ANDROID) |
| |
| using testing::_; |
| using testing::AnyNumber; |
| using testing::Invoke; |
| using testing::Return; |
| using testing::NiceMock; |
| using testing::StrictMock; |
| |
| namespace { |
| |
| const ::media::AudioParameters kDefaultAudioParams( |
| ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| ::media::CHANNEL_LAYOUT_STEREO, |
| ::media::AudioParameters::kAudioCDSampleRate, |
| 256); |
| |
| const ::media::AudioParameters kAudioParamsInvalidLayout( |
| ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY, |
| ::media::CHANNEL_LAYOUT_NONE, |
| ::media::AudioParameters::kAudioCDSampleRate, |
| 256); |
| |
| std::unique_ptr<service_manager::Connector> CreateConnector() { |
| service_manager::mojom::ConnectorRequest request; |
| return service_manager::Connector::Create(&request); |
| } |
| |
| int OnMoreData(base::TimeDelta delay, |
| base::TimeTicks delay_timestamp, |
| int prior_frames_skipped, |
| ::media::AudioBus* dest) { |
| dest->Zero(); |
| return kDefaultAudioParams.frames_per_buffer(); |
| } |
| |
| std::string DummyGetSessionId(std::string /* audio_group_id */) { |
| return ""; |
| } |
| |
| } // namespace |
| |
| namespace chromecast { |
| namespace media { |
| |
| class CastAudioManagerTest : public testing::Test { |
| public: |
| CastAudioManagerTest() : audio_thread_("CastAudioThread") {} |
| |
| void SetUp() override { CreateAudioManagerForTesting(); } |
| |
| void TearDown() override { |
| RunThreadsUntilIdle(); |
| audio_manager_->Shutdown(); |
| RunThreadsUntilIdle(); |
| audio_thread_.Stop(); |
| } |
| |
| // Binds |multiroom_manager_| to the interface requested through the test |
| // connector. |
| void BindMultiroomManager(mojo::ScopedMessagePipeHandle handle) { |
| multiroom_manager_.Bind(std::move(handle)); |
| } |
| |
| protected: |
| void FakeLogCallback(const std::string& string) {} |
| CmaBackendFactory* GetCmaBackendFactory() { |
| return mock_backend_factory_.get(); |
| } |
| |
| void CreateConnectorForTesting() { |
| connector_ = CreateConnector(); |
| // Override the MultiroomManager interface for testing. |
| connector_->OverrideBinderForTesting( |
| service_manager::ServiceFilter::ByName( |
| chromecast::mojom::kChromecastServiceName), |
| mojom::MultiroomManager::Name_, |
| base::BindRepeating(&CastAudioManagerTest::BindMultiroomManager, |
| base::Unretained(this))); |
| } |
| |
| void CreateAudioManagerForTesting(bool use_mixer = false) { |
| if (!connector_) |
| CreateConnectorForTesting(); |
| |
| // Only one AudioManager may exist at a time, so destroy the one we're |
| // currently holding before creating a new one. |
| // Flush the message loop to run any shutdown tasks posted by AudioManager. |
| if (audio_manager_) { |
| audio_manager_->Shutdown(); |
| audio_manager_.reset(); |
| } |
| if (audio_thread_.IsRunning()) |
| audio_thread_.Stop(); |
| CHECK(audio_thread_.StartAndWaitForTesting()); |
| |
| mock_backend_factory_ = std::make_unique<MockCmaBackendFactory>(); |
| audio_manager_ = base::WrapUnique(new CastAudioManager( |
| std::make_unique<::media::TestAudioThread>(), &fake_audio_log_factory_, |
| base::BindRepeating(&CastAudioManagerTest::GetCmaBackendFactory, |
| base::Unretained(this)), |
| base::BindRepeating(&DummyGetSessionId), |
| scoped_task_environment_.GetMainThreadTaskRunner(), |
| audio_thread_.task_runner(), connector_.get(), use_mixer, |
| true /* force_use_cma_backend_for_output*/ |
| )); |
| // A few AudioManager implementations post initialization tasks to |
| // audio thread. Flush the thread to ensure that |audio_manager_| is |
| // initialized and ready to use before returning from this function. |
| // TODO(alokp): We should perhaps do this in AudioManager::Create(). |
| RunThreadsUntilIdle(); |
| device_info_accessor_ = |
| std::make_unique<::media::AudioDeviceInfoAccessorForTests>( |
| audio_manager_.get()); |
| } |
| |
| void SetUpBackendAndDecoder() { |
| mock_audio_decoder_ = |
| std::make_unique<NiceMock<MockCmaBackend::AudioDecoder>>(); |
| EXPECT_CALL(*mock_audio_decoder_, SetDelegate(_)).Times(1); |
| EXPECT_CALL(*mock_audio_decoder_, SetConfig(_)).WillOnce(Return(true)); |
| |
| mock_cma_backend_ = std::make_unique<NiceMock<MockCmaBackend>>(); |
| EXPECT_CALL(*mock_cma_backend_, CreateAudioDecoder()) |
| .WillOnce(Return(mock_audio_decoder_.get())); |
| EXPECT_CALL(*mock_cma_backend_, Initialize()).WillOnce(Return(true)); |
| |
| EXPECT_CALL(*mock_backend_factory_, CreateBackend(_)) |
| .WillOnce(Invoke([this](const MediaPipelineDeviceParams&) { |
| return std::move(mock_cma_backend_); |
| })); |
| EXPECT_EQ(mock_backend_factory_.get(), |
| audio_manager_->cma_backend_factory()); |
| } |
| |
| void RunThreadsUntilIdle() { |
| scoped_task_environment_.RunUntilIdle(); |
| audio_thread_.FlushForTesting(); |
| } |
| |
| void GetDefaultOutputStreamParameters(::media::AudioParameters* params) { |
| *params = device_info_accessor_->GetDefaultOutputStreamParameters(); |
| } |
| |
| base::Thread audio_thread_; |
| base::test::ScopedTaskEnvironment scoped_task_environment_; |
| ::media::FakeAudioLogFactory fake_audio_log_factory_; |
| std::unique_ptr<MockCmaBackendFactory> mock_backend_factory_; |
| ::media::MockAudioSourceCallback mock_source_callback_; |
| std::unique_ptr<MockCmaBackend> mock_cma_backend_; |
| std::unique_ptr<MockCmaBackend::AudioDecoder> mock_audio_decoder_; |
| |
| std::unique_ptr<CastAudioManager> audio_manager_; |
| std::unique_ptr<::media::AudioDeviceInfoAccessorForTests> |
| device_info_accessor_; |
| std::unique_ptr<service_manager::Connector> connector_; |
| MockMultiroomManager multiroom_manager_; |
| }; |
| |
| TEST_F(CastAudioManagerTest, HasValidOutputStreamParameters) { |
| ::media::AudioParameters params; |
| GetDefaultOutputStreamParameters(¶ms); |
| EXPECT_TRUE(params.IsValid()); |
| } |
| |
| TEST_F(CastAudioManagerTest, CanMakeStream) { |
| SetUpBackendAndDecoder(); |
| ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( |
| kDefaultAudioParams, "", ::media::AudioManager::LogCallback()); |
| EXPECT_TRUE(stream->Open()); |
| |
| EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _)) |
| .WillRepeatedly(Invoke(OnMoreData)); |
| EXPECT_CALL(mock_source_callback_, OnError()).Times(0); |
| stream->Start(&mock_source_callback_); |
| RunThreadsUntilIdle(); |
| |
| stream->Stop(); |
| RunThreadsUntilIdle(); |
| |
| stream->Close(); |
| RunThreadsUntilIdle(); |
| } |
| |
| #if defined(OS_ANDROID) && !BUILDFLAG(IS_ANDROID_THINGS) |
| // Android things emulators do not support AC3 codec |
| TEST_F(CastAudioManagerTest, CanMakeAC3Stream) { |
| const ::media::AudioParameters kAC3AudioParams( |
| ::media::AudioParameters::AUDIO_BITSTREAM_AC3, |
| ::media::CHANNEL_LAYOUT_5_1, ::media::AudioParameters::kAudioCDSampleRate, |
| 256); |
| ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( |
| kAC3AudioParams, "", ::media::AudioManager::LogCallback()); |
| EXPECT_TRUE(stream->Open()); |
| |
| EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _)) |
| .WillRepeatedly(Invoke(OnMoreData)); |
| EXPECT_CALL(mock_source_callback_, OnError()).Times(0); |
| stream->Start(&mock_source_callback_); |
| RunThreadsUntilIdle(); |
| |
| stream->Stop(); |
| RunThreadsUntilIdle(); |
| |
| stream->Close(); |
| } |
| #endif // defined(OS_ANDROID) && !BUILDFLAG(IS_ANDROID_THINGS) |
| |
| TEST_F(CastAudioManagerTest, DISABLED_CanMakeStreamProxy) { |
| SetUpBackendAndDecoder(); |
| ::media::AudioOutputStream* stream = |
| audio_manager_->MakeAudioOutputStreamProxy(kDefaultAudioParams, ""); |
| EXPECT_TRUE(stream->Open()); |
| RunThreadsUntilIdle(); |
| EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _)) |
| .WillRepeatedly(Invoke(OnMoreData)); |
| EXPECT_CALL(mock_source_callback_, OnError()).Times(0); |
| stream->Start(&mock_source_callback_); |
| RunThreadsUntilIdle(); |
| |
| stream->Stop(); |
| |
| stream->Close(); |
| RunThreadsUntilIdle(); |
| // TODO(steinbock) Figure out why stream is not unregistering itself from |
| // audio_manager_ |
| } |
| |
| TEST_F(CastAudioManagerTest, CanMakeMixerStream) { |
| CreateAudioManagerForTesting(true /* use_mixer */); |
| SetUpBackendAndDecoder(); |
| ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( |
| kDefaultAudioParams, "", ::media::AudioManager::LogCallback()); |
| EXPECT_TRUE(stream->Open()); |
| |
| EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _)) |
| .WillRepeatedly(Invoke(OnMoreData)); |
| EXPECT_CALL(mock_source_callback_, OnError()).Times(0); |
| stream->Start(&mock_source_callback_); |
| RunThreadsUntilIdle(); |
| |
| stream->Stop(); |
| RunThreadsUntilIdle(); |
| |
| stream->Close(); |
| } |
| |
| TEST_F(CastAudioManagerTest, CanMakeCommunicationsStream) { |
| CreateAudioManagerForTesting(); |
| SetUpBackendAndDecoder(); |
| |
| ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream( |
| kDefaultAudioParams, |
| ::media::AudioDeviceDescription::kCommunicationsDeviceId, |
| ::media::AudioManager::LogCallback()); |
| EXPECT_TRUE(stream->Open()); |
| |
| EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _)) |
| .WillRepeatedly(Invoke(OnMoreData)); |
| EXPECT_CALL(mock_source_callback_, OnError()).Times(0); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| stream->Stop(); |
| scoped_task_environment_.RunUntilIdle(); |
| |
| stream->Close(); |
| } |
| |
| } // namespace media |
| } // namespace chromecast |