1 // Copyright 2017 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 "chromecast/media/audio/cast_audio_manager.h"
6
7 #include <memory>
8 #include <string>
9 #include <utility>
10
11 #include "base/bind.h"
12 #include "base/memory/ptr_util.h"
13 #include "base/test/task_environment.h"
14 #include "build/build_config.h"
15 #include "chromecast/chromecast_buildflags.h"
16 #include "chromecast/common/mojom/constants.mojom.h"
17 #include "chromecast/common/mojom/multiroom.mojom.h"
18 #include "chromecast/common/mojom/service_connector.mojom.h"
19 #include "chromecast/media/api/cma_backend.h"
20 #include "chromecast/media/api/test/mock_cma_backend.h"
21 #include "chromecast/media/cma/test/mock_cma_backend_factory.h"
22 #include "chromecast/media/cma/test/mock_multiroom_manager.h"
23 #include "media/audio/audio_device_info_accessor_for_tests.h"
24 #include "media/audio/fake_audio_log_factory.h"
25 #include "media/audio/mock_audio_source_callback.h"
26 #include "media/audio/test_audio_thread.h"
27 #include "mojo/public/cpp/bindings/pending_receiver.h"
28 #include "mojo/public/cpp/bindings/pending_remote.h"
29 #include "mojo/public/cpp/bindings/receiver_set.h"
30 #include "testing/gmock/include/gmock/gmock.h"
31 #include "testing/gtest/include/gtest/gtest.h"
32 #if defined(OS_ANDROID)
33 #include "media/audio/android/audio_track_output_stream.h"
34 #endif // defined(OS_ANDROID)
35
36 using testing::_;
37 using testing::AnyNumber;
38 using testing::Invoke;
39 using testing::NiceMock;
40 using testing::Return;
41 using testing::StrictMock;
42
43 namespace {
44
45 const ::media::AudioParameters kDefaultAudioParams(
46 ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
47 ::media::CHANNEL_LAYOUT_STEREO,
48 ::media::AudioParameters::kAudioCDSampleRate,
49 256);
50
51 const ::media::AudioParameters kAudioParamsInvalidLayout(
52 ::media::AudioParameters::AUDIO_PCM_LOW_LATENCY,
53 ::media::CHANNEL_LAYOUT_NONE,
54 ::media::AudioParameters::kAudioCDSampleRate,
55 256);
56
OnMoreData(base::TimeDelta delay,base::TimeTicks delay_timestamp,int prior_frames_skipped,::media::AudioBus * dest)57 int OnMoreData(base::TimeDelta delay,
58 base::TimeTicks delay_timestamp,
59 int prior_frames_skipped,
60 ::media::AudioBus* dest) {
61 dest->Zero();
62 return kDefaultAudioParams.frames_per_buffer();
63 }
64
DummyGetSessionId(const std::string &)65 std::string DummyGetSessionId(const std::string& /* audio_group_id */) {
66 return "";
67 }
68
69 } // namespace
70
71 namespace chromecast {
72 namespace media {
73
74 class CastAudioManagerTest : public testing::Test,
75 public chromecast::mojom::ServiceConnector {
76 public:
CastAudioManagerTest()77 CastAudioManagerTest() : audio_thread_("CastAudioThread") {}
78
SetUp()79 void SetUp() override { CreateAudioManagerForTesting(); }
80
TearDown()81 void TearDown() override {
82 RunThreadsUntilIdle();
83 audio_manager_->Shutdown();
84 RunThreadsUntilIdle();
85 audio_thread_.Stop();
86 }
87
88 // chromecast::mojom::ServiceConnector implementation:
Connect(const std::string & service_name,mojo::GenericPendingReceiver receiver)89 void Connect(const std::string& service_name,
90 mojo::GenericPendingReceiver receiver) override {
91 if (service_name != chromecast::mojom::kChromecastServiceName)
92 return;
93
94 if (auto r = receiver.As<mojom::MultiroomManager>())
95 multiroom_manager_.Bind(r.PassPipe());
96 }
97
98 protected:
FakeLogCallback(const std::string & string)99 void FakeLogCallback(const std::string& string) {}
GetCmaBackendFactory()100 CmaBackendFactory* GetCmaBackendFactory() {
101 return mock_backend_factory_.get();
102 }
103
CreateConnector()104 mojo::PendingRemote<chromecast::mojom::ServiceConnector> CreateConnector() {
105 mojo::PendingRemote<chromecast::mojom::ServiceConnector> connector;
106 connector_receivers_.Add(this, connector.InitWithNewPipeAndPassReceiver());
107 return connector;
108 }
109
CreateAudioManagerForTesting(bool use_mixer=false)110 void CreateAudioManagerForTesting(bool use_mixer = false) {
111 // Only one AudioManager may exist at a time, so destroy the one we're
112 // currently holding before creating a new one.
113 // Flush the message loop to run any shutdown tasks posted by AudioManager.
114 if (audio_manager_) {
115 audio_manager_->Shutdown();
116 audio_manager_.reset();
117 }
118 if (audio_thread_.IsRunning())
119 audio_thread_.Stop();
120 CHECK(audio_thread_.StartAndWaitForTesting());
121
122 mock_backend_factory_ = std::make_unique<MockCmaBackendFactory>();
123 audio_manager_ = base::WrapUnique(new CastAudioManager(
124 std::make_unique<::media::TestAudioThread>(), &fake_audio_log_factory_,
125 base::BindRepeating(&CastAudioManagerTest::GetCmaBackendFactory,
126 base::Unretained(this)),
127 base::BindRepeating(&DummyGetSessionId),
128 task_environment_.GetMainThreadTaskRunner(),
129 audio_thread_.task_runner(), CreateConnector(), use_mixer,
130 true /* force_use_cma_backend_for_output*/
131 ));
132 // A few AudioManager implementations post initialization tasks to
133 // audio thread. Flush the thread to ensure that |audio_manager_| is
134 // initialized and ready to use before returning from this function.
135 // TODO(alokp): We should perhaps do this in AudioManager::Create().
136 RunThreadsUntilIdle();
137 device_info_accessor_ =
138 std::make_unique<::media::AudioDeviceInfoAccessorForTests>(
139 audio_manager_.get());
140 }
141
SetUpBackendAndDecoder()142 void SetUpBackendAndDecoder() {
143 mock_audio_decoder_ =
144 std::make_unique<NiceMock<MockCmaBackend::AudioDecoder>>();
145 EXPECT_CALL(*mock_audio_decoder_, SetDelegate(_)).Times(1);
146 EXPECT_CALL(*mock_audio_decoder_, SetConfig(_)).WillOnce(Return(true));
147
148 mock_cma_backend_ = std::make_unique<NiceMock<MockCmaBackend>>();
149 EXPECT_CALL(*mock_cma_backend_, CreateAudioDecoder())
150 .WillOnce(Return(mock_audio_decoder_.get()));
151 EXPECT_CALL(*mock_cma_backend_, Initialize()).WillOnce(Return(true));
152
153 EXPECT_CALL(*mock_backend_factory_, CreateBackend(_))
154 .WillOnce(Invoke([this](const MediaPipelineDeviceParams&) {
155 return std::move(mock_cma_backend_);
156 }));
157 EXPECT_EQ(mock_backend_factory_.get(),
158 audio_manager_->helper_.GetCmaBackendFactory());
159 }
160
RunThreadsUntilIdle()161 void RunThreadsUntilIdle() {
162 task_environment_.RunUntilIdle();
163 audio_thread_.FlushForTesting();
164 }
165
GetDefaultOutputStreamParameters(::media::AudioParameters * params)166 void GetDefaultOutputStreamParameters(::media::AudioParameters* params) {
167 *params = device_info_accessor_->GetDefaultOutputStreamParameters();
168 }
169
170 base::Thread audio_thread_;
171 base::test::TaskEnvironment task_environment_;
172 ::media::FakeAudioLogFactory fake_audio_log_factory_;
173 std::unique_ptr<MockCmaBackendFactory> mock_backend_factory_;
174 ::media::MockAudioSourceCallback mock_source_callback_;
175 std::unique_ptr<MockCmaBackend> mock_cma_backend_;
176 std::unique_ptr<MockCmaBackend::AudioDecoder> mock_audio_decoder_;
177
178 std::unique_ptr<CastAudioManager> audio_manager_;
179 std::unique_ptr<::media::AudioDeviceInfoAccessorForTests>
180 device_info_accessor_;
181 mojo::ReceiverSet<chromecast::mojom::ServiceConnector> connector_receivers_;
182 MockMultiroomManager multiroom_manager_;
183 };
184
TEST_F(CastAudioManagerTest,HasValidOutputStreamParameters)185 TEST_F(CastAudioManagerTest, HasValidOutputStreamParameters) {
186 ::media::AudioParameters params;
187 GetDefaultOutputStreamParameters(¶ms);
188 EXPECT_TRUE(params.IsValid());
189 }
190
TEST_F(CastAudioManagerTest,CanMakeStream)191 TEST_F(CastAudioManagerTest, CanMakeStream) {
192 SetUpBackendAndDecoder();
193 ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
194 kDefaultAudioParams, "", ::media::AudioManager::LogCallback());
195 EXPECT_TRUE(stream->Open());
196
197 EXPECT_CALL(*mock_cma_backend_, Start(_)).WillOnce(Return(true));
198 EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
199 .WillRepeatedly(Invoke(OnMoreData));
200 EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
201 stream->Start(&mock_source_callback_);
202 RunThreadsUntilIdle();
203
204 stream->Stop();
205 RunThreadsUntilIdle();
206
207 stream->Close();
208 RunThreadsUntilIdle();
209 }
210
211 #if defined(OS_ANDROID)
TEST_F(CastAudioManagerTest,CanMakeAC3Stream)212 TEST_F(CastAudioManagerTest, CanMakeAC3Stream) {
213 const ::media::AudioParameters kAC3AudioParams(
214 ::media::AudioParameters::AUDIO_BITSTREAM_AC3,
215 ::media::CHANNEL_LAYOUT_5_1, ::media::AudioParameters::kAudioCDSampleRate,
216 256);
217 ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
218 kAC3AudioParams, "", ::media::AudioManager::LogCallback());
219 EXPECT_TRUE(stream);
220 // Only run the rest of the test if the device supports AC3.
221 if (stream->Open()) {
222 EXPECT_CALL(*mock_cma_backend_, Start(_)).WillOnce(Return(true));
223 EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
224 .WillRepeatedly(Invoke(OnMoreData));
225 EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
226 stream->Start(&mock_source_callback_);
227 RunThreadsUntilIdle();
228
229 stream->Stop();
230 RunThreadsUntilIdle();
231 }
232 stream->Close();
233 }
234 #endif // defined(OS_ANDROID)
235
TEST_F(CastAudioManagerTest,DISABLED_CanMakeStreamProxy)236 TEST_F(CastAudioManagerTest, DISABLED_CanMakeStreamProxy) {
237 SetUpBackendAndDecoder();
238 ::media::AudioOutputStream* stream =
239 audio_manager_->MakeAudioOutputStreamProxy(kDefaultAudioParams, "");
240 EXPECT_TRUE(stream->Open());
241 RunThreadsUntilIdle();
242 EXPECT_CALL(*mock_cma_backend_, Start(_)).WillOnce(Return(true));
243 EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
244 .WillRepeatedly(Invoke(OnMoreData));
245 EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
246 stream->Start(&mock_source_callback_);
247 RunThreadsUntilIdle();
248
249 stream->Stop();
250
251 stream->Close();
252 RunThreadsUntilIdle();
253 // TODO(steinbock) Figure out why stream is not unregistering itself from
254 // audio_manager_
255 }
256
TEST_F(CastAudioManagerTest,CanMakeMixerStream)257 TEST_F(CastAudioManagerTest, CanMakeMixerStream) {
258 CreateAudioManagerForTesting(true /* use_mixer */);
259 SetUpBackendAndDecoder();
260 ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
261 kDefaultAudioParams, "", ::media::AudioManager::LogCallback());
262 EXPECT_TRUE(stream->Open());
263
264 EXPECT_CALL(*mock_cma_backend_, Start(_)).WillOnce(Return(true));
265 EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
266 .WillRepeatedly(Invoke(OnMoreData));
267 EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
268 stream->Start(&mock_source_callback_);
269 RunThreadsUntilIdle();
270
271 stream->Stop();
272 RunThreadsUntilIdle();
273
274 stream->Close();
275 }
276
TEST_F(CastAudioManagerTest,CanMakeCommunicationsStream)277 TEST_F(CastAudioManagerTest, CanMakeCommunicationsStream) {
278 CreateAudioManagerForTesting();
279 SetUpBackendAndDecoder();
280
281 ::media::AudioOutputStream* stream = audio_manager_->MakeAudioOutputStream(
282 kDefaultAudioParams,
283 ::media::AudioDeviceDescription::kCommunicationsDeviceId,
284 ::media::AudioManager::LogCallback());
285 EXPECT_TRUE(stream->Open());
286
287 EXPECT_CALL(mock_source_callback_, OnMoreData(_, _, _, _))
288 .WillRepeatedly(Invoke(OnMoreData));
289 EXPECT_CALL(mock_source_callback_, OnError(_)).Times(0);
290 task_environment_.RunUntilIdle();
291
292 stream->Stop();
293 task_environment_.RunUntilIdle();
294
295 stream->Close();
296 }
297
298 } // namespace media
299 } // namespace chromecast
300