1 // Copyright 2017 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.
5 #include "media/filters/offloading_video_decoder.h"
8 #include "base/callback_helpers.h"
9 #include "base/memory/raw_ptr.h"
10 #include "base/run_loop.h"
11 #include "base/test/gmock_callback_support.h"
12 #include "base/test/task_environment.h"
13 #include "media/base/decoder_buffer.h"
14 #include "media/base/mock_filters.h"
15 #include "media/base/test_data_util.h"
16 #include "media/base/test_helpers.h"
17 #include "media/base/video_frame.h"
18 #include "testing/gmock/include/gmock/gmock.h"
20 using base::test::RunClosure;
21 using base::test::RunOnceCallback;
22 using base::test::RunOnceClosure;
25 using testing::SaveArg;
29 ACTION_P(VerifyOn, task_runner) {
30 ASSERT_TRUE(task_runner->RunsTasksInCurrentSequence());
33 ACTION_P(VerifyNotOn, task_runner) {
34 ASSERT_FALSE(task_runner->RunsTasksInCurrentSequence());
37 class MockOffloadableVideoDecoder : public OffloadableVideoDecoder {
39 // OffloadableVideoDecoder implementation.
40 VideoDecoderType GetDecoderType() const override {
41 return VideoDecoderType::kTesting;
44 void Initialize(const VideoDecoderConfig& config,
46 CdmContext* cdm_context,
48 const OutputCB& output_cb,
49 const WaitingCB& waiting_cb) override {
50 Initialize_(config, low_delay, cdm_context, init_cb, output_cb, waiting_cb);
52 MOCK_METHOD6(Initialize_,
53 void(const VideoDecoderConfig& config,
55 CdmContext* cdm_context,
57 const OutputCB& output_cb,
58 const WaitingCB& waiting_cb));
59 void Decode(scoped_refptr<DecoderBuffer> buffer, DecodeCB cb) override {
60 Decode_(std::move(buffer), cb);
62 MOCK_METHOD2(Decode_, void(scoped_refptr<DecoderBuffer> buffer, DecodeCB&));
63 void Reset(base::OnceClosure cb) override { Reset_(cb); }
64 MOCK_METHOD1(Reset_, void(base::OnceClosure&));
65 MOCK_METHOD0(Detach, void(void));
68 class OffloadingVideoDecoderTest : public testing::Test {
70 OffloadingVideoDecoderTest()
72 base::test::TaskEnvironment::MainThreadType::DEFAULT,
73 base::test::TaskEnvironment::ThreadPoolExecutionMode::QUEUED) {}
75 OffloadingVideoDecoderTest(const OffloadingVideoDecoderTest&) = delete;
76 OffloadingVideoDecoderTest& operator=(const OffloadingVideoDecoderTest&) =
79 void CreateWrapper(int offload_width, VideoCodec codec) {
80 decoder_ = new testing::StrictMock<MockOffloadableVideoDecoder>();
81 offloading_decoder_ = std::make_unique<OffloadingVideoDecoder>(
82 offload_width, std::vector<VideoCodec>(1, codec),
83 std::unique_ptr<OffloadableVideoDecoder>(decoder_));
86 VideoDecoder::InitCB ExpectInitCB(bool success) {
87 EXPECT_CALL(*this, InitDone(success))
88 .WillOnce(VerifyOn(task_env_.GetMainThreadTaskRunner()));
89 return base::BindOnce(
90 [](base::OnceCallback<void(bool)> cb, DecoderStatus status) {
91 std::move(cb).Run(status.is_ok());
93 base::BindOnce(&OffloadingVideoDecoderTest::InitDone,
94 base::Unretained(this)));
97 VideoDecoder::OutputCB ExpectOutputCB() {
98 EXPECT_CALL(*this, OutputDone(_))
99 .WillOnce(VerifyOn(task_env_.GetMainThreadTaskRunner()));
100 return base::BindRepeating(&OffloadingVideoDecoderTest::OutputDone,
101 base::Unretained(this));
104 VideoDecoder::DecodeCB ExpectDecodeCB(DecoderStatus status) {
105 EXPECT_CALL(*this, DecodeDone(HasStatusCode(status)))
106 .WillOnce(VerifyOn(task_env_.GetMainThreadTaskRunner()));
107 return base::BindOnce(&OffloadingVideoDecoderTest::DecodeDone,
108 base::Unretained(this));
111 base::OnceClosure ExpectResetCB() {
112 EXPECT_CALL(*this, ResetDone())
113 .WillOnce(VerifyOn(task_env_.GetMainThreadTaskRunner()));
114 return base::BindOnce(&OffloadingVideoDecoderTest::ResetDone,
115 base::Unretained(this));
118 void TestNoOffloading(const VideoDecoderConfig& config) {
119 // Display name should be a simple passthrough.
120 EXPECT_EQ(offloading_decoder_->GetDecoderType(),
121 decoder_->GetDecoderType());
123 // When offloading decodes should not be parallelized.
124 EXPECT_EQ(offloading_decoder_->GetMaxDecodeRequests(), 1);
126 // Verify methods are called on the current thread since the offload codec
127 // requirement is not satisfied.
128 VideoDecoder::OutputCB output_cb;
129 EXPECT_CALL(*decoder_, Initialize_(_, false, nullptr, _, _, _))
130 .WillOnce(DoAll(VerifyOn(task_env_.GetMainThreadTaskRunner()),
131 RunOnceCallback<3>(DecoderStatus::Codes::kOk),
132 SaveArg<4>(&output_cb)));
133 offloading_decoder_->Initialize(config, false, nullptr, ExpectInitCB(true),
134 ExpectOutputCB(), base::NullCallback());
135 task_env_.RunUntilIdle();
137 // Verify decode works and is called on the right thread.
138 EXPECT_CALL(*decoder_, Decode_(_, _))
139 .WillOnce(DoAll(VerifyOn(task_env_.GetMainThreadTaskRunner()),
140 RunOnceClosure(base::BindOnce(output_cb, nullptr)),
141 RunOnceCallback<1>(DecoderStatus::Codes::kOk)));
142 offloading_decoder_->Decode(DecoderBuffer::CreateEOSBuffer(),
143 ExpectDecodeCB(DecoderStatus::Codes::kOk));
144 task_env_.RunUntilIdle();
146 // Reset so we can call Initialize() again.
147 EXPECT_CALL(*decoder_, Reset_(_))
148 .WillOnce(DoAll(VerifyOn(task_env_.GetMainThreadTaskRunner()),
149 RunOnceCallback<0>()));
150 offloading_decoder_->Reset(ExpectResetCB());
151 task_env_.RunUntilIdle();
154 void TestOffloading(const VideoDecoderConfig& config, bool detach = false) {
155 // Display name should be a simple passthrough.
156 EXPECT_EQ(offloading_decoder_->GetDecoderType(),
157 decoder_->GetDecoderType());
159 // Prior to Initialize() max decode requests is still 1.
160 EXPECT_EQ(offloading_decoder_->GetMaxDecodeRequests(), 1);
162 // Since this Initialize() should be happening on another thread, set the
163 // expectation after we make the call.
164 VideoDecoder::OutputCB output_cb;
166 EXPECT_CALL(*decoder_, Detach())
167 .WillOnce(VerifyOn(task_env_.GetMainThreadTaskRunner()));
169 offloading_decoder_->Initialize(config, false, nullptr, ExpectInitCB(true),
170 ExpectOutputCB(), base::NullCallback());
171 EXPECT_CALL(*decoder_, Initialize_(_, false, nullptr, _, _, _))
172 .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
173 RunOnceCallback<3>(DecoderStatus::Codes::kOk),
174 SaveArg<4>(&output_cb)));
175 task_env_.RunUntilIdle();
177 // When offloading decodes should be parallelized.
178 EXPECT_GT(offloading_decoder_->GetMaxDecodeRequests(), 1);
180 // Verify decode works and is called on the right thread.
181 offloading_decoder_->Decode(DecoderBuffer::CreateEOSBuffer(),
182 ExpectDecodeCB(DecoderStatus::Codes::kOk));
183 EXPECT_CALL(*decoder_, Decode_(_, _))
184 .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
185 RunOnceClosure(base::BindOnce(output_cb, nullptr)),
186 RunOnceCallback<1>(DecoderStatus::Codes::kOk)));
187 task_env_.RunUntilIdle();
189 // Reset so we can call Initialize() again.
190 offloading_decoder_->Reset(ExpectResetCB());
191 EXPECT_CALL(*decoder_, Reset_(_))
192 .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
193 RunOnceCallback<0>()));
194 task_env_.RunUntilIdle();
197 MOCK_METHOD1(InitDone, void(bool));
198 MOCK_METHOD1(OutputDone, void(scoped_refptr<VideoFrame>));
199 MOCK_METHOD1(DecodeDone, void(DecoderStatus));
200 MOCK_METHOD0(ResetDone, void(void));
202 base::test::TaskEnvironment task_env_;
203 std::unique_ptr<OffloadingVideoDecoder> offloading_decoder_;
204 raw_ptr<testing::StrictMock<MockOffloadableVideoDecoder>> decoder_ =
205 nullptr; // Owned by |offloading_decoder_|.
208 TEST_F(OffloadingVideoDecoderTest, NoOffloadingTooSmall) {
209 auto offload_config = TestVideoConfig::Large(VideoCodec::kVP9);
210 CreateWrapper(offload_config.coded_size().width(), VideoCodec::kVP9);
211 TestNoOffloading(TestVideoConfig::Normal(VideoCodec::kVP9));
214 TEST_F(OffloadingVideoDecoderTest, NoOffloadingDifferentCodec) {
215 auto offload_config = TestVideoConfig::Large(VideoCodec::kVP9);
216 CreateWrapper(offload_config.coded_size().width(), VideoCodec::kVP9);
217 TestNoOffloading(TestVideoConfig::Large(VideoCodec::kVP8));
220 TEST_F(OffloadingVideoDecoderTest, NoOffloadingHasEncryption) {
221 auto offload_config = TestVideoConfig::Large(VideoCodec::kVP9);
222 CreateWrapper(offload_config.coded_size().width(), VideoCodec::kVP9);
223 TestNoOffloading(TestVideoConfig::LargeEncrypted(VideoCodec::kVP9));
226 TEST_F(OffloadingVideoDecoderTest, Offloading) {
227 auto offload_config = TestVideoConfig::Large(VideoCodec::kVP9);
228 CreateWrapper(offload_config.coded_size().width(), VideoCodec::kVP9);
229 TestOffloading(offload_config);
232 TEST_F(OffloadingVideoDecoderTest, OffloadingAfterNoOffloading) {
233 auto offload_config = TestVideoConfig::Large(VideoCodec::kVP9);
234 CreateWrapper(offload_config.coded_size().width(), VideoCodec::kVP9);
236 // Setup and test the no offloading path first.
237 TestNoOffloading(TestVideoConfig::Normal(VideoCodec::kVP9));
239 // Test offloading now.
240 TestOffloading(offload_config, true);
242 // Reinitialize decoder with a stream which should not be offloaded. Detach()
243 // should be called on the right thread. Again since the first part of this
244 // should happen asynchronously, set expectation after the call.
245 VideoDecoder::OutputCB output_cb;
246 offloading_decoder_->Initialize(
247 TestVideoConfig::Normal(VideoCodec::kVP9), false, nullptr,
249 base::BindRepeating(&OffloadingVideoDecoderTest::OutputDone,
250 base::Unretained(this)),
251 base::NullCallback());
252 EXPECT_CALL(*decoder_, Detach())
253 .WillOnce(VerifyNotOn(task_env_.GetMainThreadTaskRunner()));
254 EXPECT_CALL(*decoder_, Initialize_(_, false, nullptr, _, _, _))
255 .WillOnce(DoAll(VerifyOn(task_env_.GetMainThreadTaskRunner()),
256 RunOnceCallback<3>(DecoderStatus::Codes::kOk),
257 SaveArg<4>(&output_cb)));
258 task_env_.RunUntilIdle();
261 TEST_F(OffloadingVideoDecoderTest, InitializeWithoutDetach) {
262 auto offload_config = TestVideoConfig::Large(VideoCodec::kVP9);
263 CreateWrapper(offload_config.coded_size().width(), VideoCodec::kVP9);
265 EXPECT_CALL(*decoder_, Detach()).Times(0);
266 TestNoOffloading(TestVideoConfig::Normal(VideoCodec::kVP9));
267 TestNoOffloading(TestVideoConfig::Normal(VideoCodec::kVP9));
270 TEST_F(OffloadingVideoDecoderTest, ParallelizedOffloading) {
271 auto offload_config = TestVideoConfig::Large(VideoCodec::kVP9);
272 CreateWrapper(offload_config.coded_size().width(), VideoCodec::kVP9);
274 // Since this Initialize() should be happening on another thread, set the
275 // expectation after we make the call.
276 VideoDecoder::OutputCB output_cb;
277 offloading_decoder_->Initialize(
278 offload_config, false, nullptr, ExpectInitCB(true),
279 base::BindRepeating(&OffloadingVideoDecoderTest::OutputDone,
280 base::Unretained(this)),
281 base::NullCallback());
282 EXPECT_CALL(*decoder_, Initialize_(_, false, nullptr, _, _, _))
283 .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
284 RunOnceCallback<3>(DecoderStatus::Codes::kOk),
285 SaveArg<4>(&output_cb)));
286 task_env_.RunUntilIdle();
288 // When offloading decodes should be parallelized.
289 EXPECT_GT(offloading_decoder_->GetMaxDecodeRequests(), 1);
291 // Verify decode works and is called on the right thread.
292 offloading_decoder_->Decode(
293 DecoderBuffer::CreateEOSBuffer(),
294 base::BindOnce(&OffloadingVideoDecoderTest::DecodeDone,
295 base::Unretained(this)));
296 offloading_decoder_->Decode(
297 DecoderBuffer::CreateEOSBuffer(),
298 base::BindOnce(&OffloadingVideoDecoderTest::DecodeDone,
299 base::Unretained(this)));
301 EXPECT_CALL(*decoder_, Decode_(_, _))
303 .WillRepeatedly(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
304 RunClosure(base::BindRepeating(output_cb, nullptr)),
305 RunOnceCallback<1>(DecoderStatus::Codes::kOk)));
306 EXPECT_CALL(*this, DecodeDone(IsOkStatus()))
308 .WillRepeatedly(VerifyOn(task_env_.GetMainThreadTaskRunner()));
309 EXPECT_CALL(*this, OutputDone(_))
311 .WillRepeatedly(VerifyOn(task_env_.GetMainThreadTaskRunner()));
312 task_env_.RunUntilIdle();
314 // Reset so we can call Initialize() again.
315 offloading_decoder_->Reset(ExpectResetCB());
316 EXPECT_CALL(*decoder_, Reset_(_))
317 .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
318 RunOnceCallback<0>()));
319 task_env_.RunUntilIdle();
322 TEST_F(OffloadingVideoDecoderTest, ParallelizedOffloadingResetAbortsDecodes) {
323 auto offload_config = TestVideoConfig::Large(VideoCodec::kVP9);
324 CreateWrapper(offload_config.coded_size().width(), VideoCodec::kVP9);
326 // Since this Initialize() should be happening on another thread, set the
327 // expectation after we make the call.
328 VideoDecoder::OutputCB output_cb;
329 offloading_decoder_->Initialize(
330 offload_config, false, nullptr, ExpectInitCB(true),
331 base::BindRepeating(&OffloadingVideoDecoderTest::OutputDone,
332 base::Unretained(this)),
333 base::NullCallback());
334 EXPECT_CALL(*decoder_, Initialize_(_, false, nullptr, _, _, _))
335 .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
336 RunOnceCallback<3>(DecoderStatus::Codes::kOk),
337 SaveArg<4>(&output_cb)));
338 task_env_.RunUntilIdle();
340 // When offloading decodes should be parallelized.
341 EXPECT_GT(offloading_decoder_->GetMaxDecodeRequests(), 1);
343 // Verify decode works and is called on the right thread.
344 offloading_decoder_->Decode(
345 DecoderBuffer::CreateEOSBuffer(),
346 base::BindOnce(&OffloadingVideoDecoderTest::DecodeDone,
347 base::Unretained(this)));
348 offloading_decoder_->Decode(
349 DecoderBuffer::CreateEOSBuffer(),
350 base::BindOnce(&OffloadingVideoDecoderTest::DecodeDone,
351 base::Unretained(this)));
353 EXPECT_CALL(*decoder_, Decode_(_, _)).Times(0);
354 EXPECT_CALL(*this, DecodeDone(HasStatusCode(DecoderStatus::Codes::kAborted)))
356 .WillRepeatedly(VerifyOn(task_env_.GetMainThreadTaskRunner()));
357 offloading_decoder_->Reset(ExpectResetCB());
358 EXPECT_CALL(*decoder_, Reset_(_))
359 .WillOnce(DoAll(VerifyNotOn(task_env_.GetMainThreadTaskRunner()),
360 RunOnceClosure<0>()));
361 task_env_.RunUntilIdle();