1 // Copyright 2015 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
11 #include "base/functional/bind.h"
12 #include "base/functional/callback.h"
13 #include "base/run_loop.h"
14 #include "base/task/single_thread_task_runner.h"
15 #include "base/test/task_environment.h"
16 #include "build/build_config.h"
17 #include "media/base/cdm_config.h"
18 #include "media/base/mock_filters.h"
19 #include "media/base/test_helpers.h"
20 #include "media/cdm/clear_key_cdm_common.h"
21 #include "media/media_buildflags.h"
22 #include "media/mojo/buildflags.h"
23 #include "media/mojo/clients/mojo_decryptor.h"
24 #include "media/mojo/clients/mojo_demuxer_stream_impl.h"
25 #include "media/mojo/common/media_type_converters.h"
26 #include "media/mojo/mojom/content_decryption_module.mojom.h"
27 #include "media/mojo/mojom/decryptor.mojom.h"
28 #include "media/mojo/mojom/interface_factory.mojom.h"
29 #include "media/mojo/mojom/media_service.mojom.h"
30 #include "media/mojo/mojom/renderer.mojom.h"
31 #include "media/mojo/services/media_service_factory.h"
32 #include "mojo/public/cpp/bindings/associated_receiver.h"
33 #include "mojo/public/cpp/bindings/pending_associated_receiver.h"
34 #include "mojo/public/cpp/bindings/pending_remote.h"
35 #include "mojo/public/cpp/bindings/remote.h"
36 #include "testing/gmock/include/gmock/gmock.h"
37 #include "testing/gtest/include/gtest/gtest.h"
46 using testing::Invoke;
47 using testing::InvokeWithoutArgs;
48 using testing::NiceMock;
49 using testing::SaveArg;
50 using testing::StrictMock;
51 using testing::WithArg;
53 MATCHER_P(MatchesResult, success, "") {
54 return arg->success == success;
57 #if BUILDFLAG(ENABLE_MOJO_CDM) && !BUILDFLAG(IS_ANDROID)
58 const char kInvalidKeySystem[] = "invalid.key.system";
61 class MockRendererClient : public mojom::RendererClient {
63 MockRendererClient() = default;
65 MockRendererClient(const MockRendererClient&) = delete;
66 MockRendererClient& operator=(const MockRendererClient&) = delete;
68 ~MockRendererClient() override = default;
70 // mojom::RendererClient implementation.
71 MOCK_METHOD3(OnTimeUpdate,
72 void(base::TimeDelta time,
73 base::TimeDelta max_time,
74 base::TimeTicks capture_time));
75 MOCK_METHOD2(OnBufferingStateChange,
76 void(BufferingState state, BufferingStateChangeReason reason));
77 MOCK_METHOD0(OnEnded, void());
78 MOCK_METHOD1(OnError, void(const PipelineStatus& status));
79 MOCK_METHOD1(OnVideoOpacityChange, void(bool opaque));
80 MOCK_METHOD1(OnAudioConfigChange, void(const AudioDecoderConfig&));
81 MOCK_METHOD1(OnVideoConfigChange, void(const VideoDecoderConfig&));
82 MOCK_METHOD1(OnVideoNaturalSizeChange, void(const gfx::Size& size));
83 MOCK_METHOD1(OnStatisticsUpdate, void(const PipelineStatistics& stats));
84 MOCK_METHOD1(OnWaiting, void(WaitingReason));
85 MOCK_METHOD1(OnDurationChange, void(base::TimeDelta duration));
86 MOCK_METHOD1(OnRemotePlayStateChange, void(MediaStatus::State state));
89 ACTION_P(QuitLoop, run_loop) {
90 base::SingleThreadTaskRunner::GetCurrentDefault()->PostTask(
91 FROM_HERE, run_loop->QuitClosure());
94 // Tests MediaService using TestMojoMediaClient, which supports CDM creation
95 // using DefaultCdmFactory (only supports Clear Key key system), and Renderer
96 // creation using RendererImplFactory that always create RendererImpl.
97 class MediaServiceTest : public testing::Test {
100 : renderer_client_receiver_(&renderer_client_),
101 video_stream_(DemuxerStream::VIDEO) {}
103 MediaServiceTest(const MediaServiceTest&) = delete;
104 MediaServiceTest& operator=(const MediaServiceTest&) = delete;
106 ~MediaServiceTest() override = default;
108 void SetUp() override {
109 mojo::PendingRemote<mojom::FrameInterfaceFactory> frame_interfaces;
110 std::ignore = frame_interfaces.InitWithNewPipeAndPassReceiver();
112 media_service_impl_ = CreateMediaServiceForTesting(
113 media_service_.BindNewPipeAndPassReceiver());
114 media_service_.set_idle_handler(
116 base::BindRepeating(&MediaServiceTest::OnMediaServiceIdle,
117 base::Unretained(this)));
118 media_service_->CreateInterfaceFactory(
119 interface_factory_.BindNewPipeAndPassReceiver(),
120 std::move(frame_interfaces));
123 MOCK_METHOD0(OnCdmConnectionError, void());
125 void InitializeCdm(const std::string& key_system, bool expected_result) {
126 interface_factory_->CreateCdm(
127 {key_system, false, false, false},
128 base::BindOnce(&MediaServiceTest::OnCdmCreated, base::Unretained(this),
130 // Run this to idle to complete the CreateCdm call.
131 task_environment_.RunUntilIdle();
134 MOCK_METHOD1(OnRendererInitialized, void(bool));
136 void InitializeRenderer(const VideoDecoderConfig& video_config,
137 bool expected_result) {
138 base::RunLoop run_loop;
139 interface_factory_->CreateDefaultRenderer(
140 std::string(), renderer_.BindNewPipeAndPassReceiver());
142 video_stream_.set_video_decoder_config(video_config);
144 mojo::PendingRemote<mojom::DemuxerStream> video_stream_proxy;
145 mojo_video_stream_ = std::make_unique<MojoDemuxerStreamImpl>(
146 &video_stream_, video_stream_proxy.InitWithNewPipeAndPassReceiver());
148 mojo::PendingAssociatedRemote<mojom::RendererClient> client_remote;
149 renderer_client_receiver_.Bind(
150 client_remote.InitWithNewEndpointAndPassReceiver());
152 std::vector<mojo::PendingRemote<mojom::DemuxerStream>> streams;
153 streams.push_back(std::move(video_stream_proxy));
155 EXPECT_CALL(*this, OnRendererInitialized(expected_result))
156 .WillOnce(QuitLoop(&run_loop));
157 renderer_->Initialize(
158 std::move(client_remote), std::move(streams), nullptr,
159 base::BindOnce(&MediaServiceTest::OnRendererInitialized,
160 base::Unretained(this)));
164 MOCK_METHOD0(OnMediaServiceIdle, void());
167 void OnCdmCreated(bool expected_result,
168 mojo::PendingRemote<mojom::ContentDecryptionModule> remote,
169 mojom::CdmContextPtr cdm_context,
170 const std::string& error_message) {
171 if (!expected_result) {
172 EXPECT_FALSE(remote);
173 EXPECT_FALSE(cdm_context);
174 EXPECT_TRUE(!error_message.empty());
178 EXPECT_TRUE(error_message.empty());
179 cdm_.Bind(std::move(remote));
180 cdm_.set_disconnect_handler(base::BindOnce(
181 &MediaServiceTest::OnCdmConnectionError, base::Unretained(this)));
183 base::test::TaskEnvironment task_environment_;
185 mojo::Remote<mojom::MediaService> media_service_;
186 mojo::Remote<mojom::InterfaceFactory> interface_factory_;
187 mojo::Remote<mojom::ContentDecryptionModule> cdm_;
188 mojo::Remote<mojom::Renderer> renderer_;
190 std::unique_ptr<MediaService> media_service_impl_;
192 NiceMock<MockRendererClient> renderer_client_;
193 mojo::AssociatedReceiver<mojom::RendererClient> renderer_client_receiver_;
195 StrictMock<MockDemuxerStream> video_stream_;
196 std::unique_ptr<MojoDemuxerStreamImpl> mojo_video_stream_;
201 // Note: base::RunLoop::RunUntilIdle() does not work well in these tests because
202 // even when the loop is idle, we may still have pending events in the pipe.
203 // - If you have an InterfacePtr hosted by the service in the service process,
204 // you can use InterfacePtr::FlushForTesting(). Note that this doesn't drain
205 // the task runner in the test process and doesn't cover all negative cases.
206 // - If you expect a callback on an InterfacePtr call or connection error, use
207 // base::RunLoop::Run() and QuitLoop().
209 // TODO(crbug.com/829233): Enable these tests on Android.
210 #if BUILDFLAG(ENABLE_MOJO_CDM) && !BUILDFLAG(IS_ANDROID)
211 TEST_F(MediaServiceTest, InitializeCdm_Success) {
212 InitializeCdm(kClearKeyKeySystem, true);
215 TEST_F(MediaServiceTest, InitializeCdm_InvalidKeySystem) {
216 InitializeCdm(kInvalidKeySystem, false);
218 #endif // BUILDFLAG(ENABLE_MOJO_CDM) && !BUILDFLAG(IS_ANDROID)
220 #if BUILDFLAG(ENABLE_MOJO_RENDERER)
221 TEST_F(MediaServiceTest, InitializeRenderer) {
222 InitializeRenderer(TestVideoConfig::Normal(), true);
224 #endif // BUILDFLAG(ENABLE_MOJO_RENDERER)
226 TEST_F(MediaServiceTest, InterfaceFactoryPreventsIdling) {
227 // The service should not idle during this operation.
228 interface_factory_.FlushForTesting();
230 // Disconnecting InterfaceFactory will cause the service to idle since there
231 // are no media components hosted and so no other interfaces should be
233 base::RunLoop run_loop;
234 EXPECT_CALL(*this, OnMediaServiceIdle()).WillOnce(QuitLoop(&run_loop));
235 interface_factory_.reset();
239 #if (BUILDFLAG(ENABLE_MOJO_CDM) && !BUILDFLAG(IS_ANDROID)) || \
240 BUILDFLAG(ENABLE_MOJO_RENDERER)
241 // MediaService stays alive as long as there are InterfaceFactory impls, which
242 // are then deferred destroyed until no media components (e.g. CDM or Renderer)
244 TEST_F(MediaServiceTest, Idling) {
245 #if BUILDFLAG(ENABLE_MOJO_CDM) && !BUILDFLAG(IS_ANDROID)
246 InitializeCdm(kClearKeyKeySystem, true);
249 #if BUILDFLAG(ENABLE_MOJO_RENDERER)
250 InitializeRenderer(TestVideoConfig::Normal(), true);
253 // Disconnecting CDM and Renderer services doesn't terminate MediaService
254 // since |interface_factory_| is still alive.
257 interface_factory_.FlushForTesting();
259 // Disconnecting InterfaceFactory will cause the service to idle since no
260 // other interfaces are connected.
261 base::RunLoop run_loop;
262 EXPECT_CALL(*this, OnMediaServiceIdle()).WillOnce(QuitLoop(&run_loop));
263 interface_factory_.reset();
267 TEST_F(MediaServiceTest, MoreIdling) {
268 #if BUILDFLAG(ENABLE_MOJO_CDM) && !BUILDFLAG(IS_ANDROID)
269 InitializeCdm(kClearKeyKeySystem, true);
272 #if BUILDFLAG(ENABLE_MOJO_RENDERER)
273 InitializeRenderer(TestVideoConfig::Normal(), true);
276 ASSERT_TRUE(cdm_ || renderer_);
278 // Disconnecting InterfaceFactory should not terminate the MediaService since
279 // there are still media components (CDM or Renderer) hosted.
280 interface_factory_.reset();
282 cdm_.FlushForTesting();
284 renderer_.FlushForTesting();
288 // Disconnecting CDM and Renderer will cause the service to idle since no
289 // other interfaces are connected.
290 base::RunLoop run_loop;
291 EXPECT_CALL(*this, OnMediaServiceIdle()).WillOnce(QuitLoop(&run_loop));
296 #endif // (BUILDFLAG(ENABLE_MOJO_CDM) && !BUILDFLAG(IS_ANDROID)) ||
297 // BUILDFLAG(ENABLE_MOJO_RENDERER)