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(&params);
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