Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(608)

Side by Side Diff: media/base/android/media_codec_loop_unittest.cc

Issue 2132653002: MediaCodecLoop unit tests (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: cl feedback Created 4 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch
« no previous file with comments | « media/base/android/media_codec_loop.cc ('k') | media/base/android/media_codec_util.h » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
OLDNEW
1 // Copyright 2016 The Chromium Authors. All rights reserved. 1 // Copyright 2016 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be 2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file. 3 // found in the LICENSE file.
4 4
5 #include "base/macros.h" 5 #include "base/macros.h"
6 #include "base/message_loop/message_loop.h" 6 #include "base/message_loop/message_loop.h"
7 #include "media/base/android/media_codec_bridge.h" 7 #include "media/base/android/media_codec_bridge.h"
8 #include "media/base/android/media_codec_loop.h" 8 #include "media/base/android/media_codec_loop.h"
9 #include "media/base/android/mock_media_codec_bridge.h"
10 #include "media/base/fake_single_thread_task_runner.h"
9 #include "testing/gmock/include/gmock/gmock.h" 11 #include "testing/gmock/include/gmock/gmock.h"
10 #include "testing/gtest/include/gtest/gtest.h" 12 #include "testing/gtest/include/gtest/gtest.h"
11 13
14 using ::testing::_;
15 using ::testing::AtLeast;
16 using ::testing::Eq;
17 using ::testing::Field;
18 using ::testing::InSequence;
19 using ::testing::Mock;
20 using ::testing::Return;
21 using ::testing::SetArgPointee;
22 using ::testing::StrictMock;
23
12 namespace media { 24 namespace media {
13 25
14 class MockMediaCodecLoopClient : public MediaCodecLoop::Client { 26 // These will come from mockable BuildInfo, once it exists.
27 enum TemporaryAndroidVersions {
28 kJellyBeanMR1 = 17,
29 kJellyBeanMR2 = 18,
30 kLollipop = 21,
31 };
32
33 // The client is a strict mock, since we don't want random calls into it. We
34 // want to be sure about the call sequence.
35 class MockMediaCodecLoopClient : public StrictMock<MediaCodecLoop::Client> {
15 public: 36 public:
16 MOCK_CONST_METHOD0(IsAnyInputPending, bool()); 37 MOCK_CONST_METHOD0(IsAnyInputPending, bool());
17 MOCK_METHOD0(ProvideInputData, MediaCodecLoop::InputData()); 38 MOCK_METHOD0(ProvideInputData, MediaCodecLoop::InputData());
18 MOCK_METHOD1(OnInputDataQueued, void(bool)); 39 MOCK_METHOD1(OnInputDataQueued, void(bool));
19 MOCK_METHOD1(OnDecodedEos, void(const MediaCodecLoop::OutputBuffer&)); 40 MOCK_METHOD1(OnDecodedEos, void(const MediaCodecLoop::OutputBuffer&));
20 MOCK_METHOD1(OnDecodedFrame, bool(const MediaCodecLoop::OutputBuffer&)); 41 MOCK_METHOD1(OnDecodedFrame, bool(const MediaCodecLoop::OutputBuffer&));
21 MOCK_METHOD0(OnOutputFormatChanged, bool()); 42 MOCK_METHOD0(OnOutputFormatChanged, bool());
22 MOCK_METHOD0(OnCodecLoopError, void()); 43 MOCK_METHOD0(OnCodecLoopError, void());
23 }; 44 };
24 45
25 class MediaCodecLoopTest : public testing::Test { 46 class MediaCodecLoopTest : public testing::Test {
26 public: 47 public:
27 MediaCodecLoopTest() : client_(new MockMediaCodecLoopClient) {} 48 MediaCodecLoopTest()
49 : client_(new StrictMock<MockMediaCodecLoopClient>()),
50 task_runner_(new FakeSingleThreadTaskRunner(&clock_)) {}
51
52 ~MediaCodecLoopTest() override {}
53
54 protected:
55 enum IdleExpectation {
56 ShouldBeIdle,
57 ShouldNotBeIdle,
58 };
59
60 // Wait until |codec_loop_| is idle.
61 // Do not call this in a sequence.
62 void WaitUntilIdle(IdleExpectation idleExpectation = ShouldBeIdle) {
63 switch (idleExpectation) {
64 case ShouldBeIdle:
65 EXPECT_CALL(*client_, IsAnyInputPending()).Times(0);
66 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)).Times(0);
67 break;
68 case ShouldNotBeIdle:
69 // Expect at least one call to see if more work is ready. We will
70 // return 'no'.
71 EXPECT_CALL(*client_, IsAnyInputPending())
72 .Times(AtLeast(1))
73 .WillRepeatedly(Return(false));
74 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
75 .Times(AtLeast(1))
76 .WillRepeatedly(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
77 break;
78 }
79
80 // Either way, we expect that MCL should not attempt to dequeue input
81 // buffers, either because it's idle or because we said that no input
82 // is pending.
83 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)).Times(0);
84
85 // TODO(liberato): assume that MCL doesn't retry for 30 seconds. Note
86 // that this doesn't actually wall-clock wait.
87 task_runner_->Sleep(base::TimeDelta::FromSeconds(30));
88 }
89
90 void ConstructCodecLoop(int sdk_int = kLollipop) {
91 std::unique_ptr<MediaCodecBridge> codec(new MockMediaCodecBridge());
92 // Since we're providing a codec, we do not expect an error.
93 EXPECT_CALL(*client_, OnCodecLoopError()).Times(0);
94 codec_loop_.reset(new MediaCodecLoop(sdk_int, client_.get(),
95 std::move(codec), task_runner_));
96 codec_loop_->SetTestTickClock(&clock_);
97 Mock::VerifyAndClearExpectations(client_.get());
98 }
99
100 // Set an expectation that MCL will try to get another input / output buffer,
101 // and not get one in DoPendingWork.
102 void ExpectEmptyIOLoop() {
103 ExpectIsAnyInputPending(false);
104 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
105 .Times(1)
106 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
107 }
108
109 void ExpectIsAnyInputPending(bool pending) {
110 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(pending));
111 }
112
113 void ExpectDequeueInputBuffer(int input_buffer_index,
114 MediaCodecStatus status = MEDIA_CODEC_OK) {
115 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _))
116 .WillOnce(DoAll(SetArgPointee<1>(input_buffer_index), Return(status)));
117 }
118
119 void ExpectInputDataQueued(bool success) {
120 EXPECT_CALL(*client_, OnInputDataQueued(success)).Times(1);
121 }
122
123 // Expect a call to queue |data| into MC buffer |input_buffer_index|.
124 void ExpectQueueInputBuffer(int input_buffer_index,
125 const MediaCodecLoop::InputData& data,
126 MediaCodecStatus status = MEDIA_CODEC_OK) {
127 EXPECT_CALL(Codec(), QueueInputBuffer(input_buffer_index, data.memory,
128 data.length, data.presentation_time))
129 .Times(1)
130 .WillOnce(Return(status));
131 }
132
133 void ExpectProvideInputData(const MediaCodecLoop::InputData& data) {
134 EXPECT_CALL(*client_, ProvideInputData()).WillOnce(Return(data));
135 }
136
137 MediaCodecLoop::InputData BigBuckBunny() {
138 MediaCodecLoop::InputData data;
139 data.memory = reinterpret_cast<const uint8_t*>("big buck bunny");
140 data.length = 14;
141 data.presentation_time = base::TimeDelta::FromSeconds(1);
142 return data;
143 }
144
145 struct OutputBuffer {
146 int index = 1;
147 size_t offset = 0;
148 size_t size = 1024;
149 base::TimeDelta pts = base::TimeDelta::FromSeconds(1);
150 bool eos = false;
151 bool key_frame = true;
152 };
153
154 struct EosOutputBuffer : public OutputBuffer {
155 EosOutputBuffer() { eos = true; }
156 };
157
158 void ExpectDequeueOutputBuffer(MediaCodecStatus status) {
159 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
160 .WillOnce(Return(status));
161 }
162
163 void ExpectDequeueOutputBuffer(const OutputBuffer& buffer) {
164 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
165 .WillOnce(DoAll(
166 SetArgPointee<1>(buffer.index), SetArgPointee<2>(buffer.offset),
167 SetArgPointee<3>(buffer.size), SetArgPointee<4>(buffer.pts),
168 SetArgPointee<5>(buffer.eos), SetArgPointee<6>(buffer.key_frame),
169 Return(MEDIA_CODEC_OK)));
170 }
171
172 void ExpectOnDecodedFrame(const OutputBuffer& buf) {
173 EXPECT_CALL(*client_,
174 OnDecodedFrame(
175 Field(&MediaCodecLoop::OutputBuffer::index, Eq(buf.index))))
176 .Times(1)
177 .WillOnce(Return(true));
178 }
179
180 MockMediaCodecBridge& Codec() {
181 return *static_cast<MockMediaCodecBridge*>(codec_loop_->GetCodec());
182 }
28 183
29 public: 184 public:
30 std::unique_ptr<MediaCodecLoop> codec_loop_; 185 std::unique_ptr<MediaCodecLoop> codec_loop_;
31 std::unique_ptr<MockMediaCodecLoopClient> client_; 186 std::unique_ptr<MockMediaCodecLoopClient> client_;
187 // TODO: how is the lifecycle of |clock_| handled? |task_runner_| can outlive
188 // us, since it's a refptr.
189 base::SimpleTestTickClock clock_;
190 scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
32 191
33 DISALLOW_COPY_AND_ASSIGN(MediaCodecLoopTest); 192 DISALLOW_COPY_AND_ASSIGN(MediaCodecLoopTest);
34 }; 193 };
35 194
36 TEST_F(MediaCodecLoopTest, TestConstructionWithNullCodec) { 195 TEST_F(MediaCodecLoopTest, TestConstructionWithNullCodec) {
37 std::unique_ptr<MediaCodecBridge> codec; 196 std::unique_ptr<MediaCodecBridge> codec;
38 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1); 197 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1);
39 codec_loop_.reset(new MediaCodecLoop(client_.get(), std::move(codec))); 198 const int sdk_int = kLollipop;
199 codec_loop_.reset(
200 new MediaCodecLoop(sdk_int, client_.get(), std::move(codec)));
201 // Do not WaitUntilIdle() here, since that assumes that we have a codec.
40 202
41 ASSERT_FALSE(codec_loop_->GetCodec()); 203 ASSERT_FALSE(codec_loop_->GetCodec());
42 } 204 }
43 205
206 TEST_F(MediaCodecLoopTest, TestConstructionWithCodec) {
207 ConstructCodecLoop();
208 ASSERT_EQ(codec_loop_->GetCodec(), &Codec());
209 WaitUntilIdle(ShouldBeIdle);
210 }
211
212 TEST_F(MediaCodecLoopTest, TestPendingWorkWithoutInput) {
213 ConstructCodecLoop();
214 // MCL should try ask if there is pending input, and try to dequeue output.
215 ExpectIsAnyInputPending(false);
216 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
217 .Times(1)
218 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
219 codec_loop_->DoPendingWork();
220 WaitUntilIdle(ShouldNotBeIdle);
221 }
222
223 TEST_F(MediaCodecLoopTest, TestPendingWorkWithInput) {
224 ConstructCodecLoop();
225 // MCL should try ask if there is pending input, and try to dequeue both an
226 // output and input buffer.
227 ExpectIsAnyInputPending(true);
228 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)).Times(1);
229 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)).Times(1);
230 codec_loop_->DoPendingWork();
231 WaitUntilIdle(ShouldNotBeIdle);
232 }
233
234 TEST_F(MediaCodecLoopTest, TestPendingWorkWithOutputBuffer) {
235 ConstructCodecLoop();
236 {
237 InSequence _s;
238
239 // MCL will first request input, then try to dequeue output.
240 ExpectIsAnyInputPending(false);
241 OutputBuffer buf;
242 ExpectDequeueOutputBuffer(buf);
243 ExpectOnDecodedFrame(buf);
244
245 // MCL will try again for another set of buffers before DoPendingWork()
246 // returns. This is why we don't just leave them for WaitUntilIdle().
247 ExpectEmptyIOLoop();
248 }
249 codec_loop_->DoPendingWork();
250 WaitUntilIdle(ShouldNotBeIdle);
251 }
252
253 TEST_F(MediaCodecLoopTest, TestQueueEos) {
254 // Test sending an EOS to MCL => MCB =dequeue output=> MCL .
255 ConstructCodecLoop();
256 {
257 InSequence _s;
258
259 ExpectIsAnyInputPending(true);
260 int input_buffer_index = 123;
261 ExpectDequeueInputBuffer(input_buffer_index);
262
263 MediaCodecLoop::InputData data;
264 data.is_eos = true;
265 ExpectProvideInputData(data);
266 EXPECT_CALL(Codec(), QueueEOS(input_buffer_index));
267 ExpectInputDataQueued(true);
268
269 // Now send the EOS back on the output queue.
270 EosOutputBuffer eos;
271 ExpectDequeueOutputBuffer(eos);
272 EXPECT_CALL(Codec(), ReleaseOutputBuffer(eos.index, false));
273 EXPECT_CALL(*client_, OnDecodedEos(_)).Times(1);
274
275 // See TestUnqueuedEos.
276 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
277 .Times(1)
278 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
279 }
280 codec_loop_->DoPendingWork();
281 // Don't WaitUntilIdle() here. See TestUnqueuedEos.
282 }
283
284 TEST_F(MediaCodecLoopTest, TestQueueInputData) {
285 // Send a buffer full of data into MCL and make sure that it gets queued with
286 // MediaCodecBridge correctly.
287 ConstructCodecLoop();
288 {
289 InSequence _s;
290
291 ExpectIsAnyInputPending(true);
292 int input_buffer_index = 123;
293 ExpectDequeueInputBuffer(input_buffer_index);
294
295 MediaCodecLoop::InputData data = BigBuckBunny();
296 ExpectProvideInputData(data);
297
298 // MCL should send the buffer into MediaCodec and notify the client.
299 ExpectQueueInputBuffer(input_buffer_index, data);
300 ExpectInputDataQueued(true);
301
302 // MCL will try to dequeue an output buffer too.
303 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
304 .Times(1)
305 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
306
307 // DoPendingWork will try again.
308 ExpectEmptyIOLoop();
309 }
310 codec_loop_->DoPendingWork();
311 WaitUntilIdle(ShouldNotBeIdle);
312 }
313
314 TEST_F(MediaCodecLoopTest, TestQueueInputDataFails) {
315 // Send a buffer full of data into MCL, but MediaCodecBridge fails to queue
316 // it successfully.
317 ConstructCodecLoop();
318 {
319 InSequence _s;
320
321 ExpectIsAnyInputPending(true);
322 int input_buffer_index = 123;
323 ExpectDequeueInputBuffer(input_buffer_index);
324
325 MediaCodecLoop::InputData data = BigBuckBunny();
326 ExpectProvideInputData(data);
327
328 // MCL should send the buffer into MediaCodec and notify the client.
329 ExpectQueueInputBuffer(input_buffer_index, data, MEDIA_CODEC_ERROR);
330 ExpectInputDataQueued(false);
331 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1);
332 }
333 codec_loop_->DoPendingWork();
334 // MCL is now in the error state.
335 }
336
337 TEST_F(MediaCodecLoopTest, TestQueueInputDataTryAgain) {
338 // Signal that there is input pending, but don't provide an input buffer.
339 ConstructCodecLoop();
340 {
341 InSequence _s;
342
343 ExpectIsAnyInputPending(true);
344 ExpectDequeueInputBuffer(-1, MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER);
345 // MCL will try for output too.
346 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
347 }
348 codec_loop_->DoPendingWork();
349 // Note that the client might not be allowed to change from "input pending"
350 // to "no input pending" without actually being asked for input. For now,
351 // MCL doesn't assume this.
352 WaitUntilIdle(ShouldNotBeIdle);
353 }
354
355 TEST_F(MediaCodecLoopTest, TestSeveralPendingIOBuffers) {
356 // Provide several input and output buffers to MCL.
357 ConstructCodecLoop();
358 int input_buffer_index = 123;
359 const int num_loops = 4;
360
361 InSequence _s;
362 for (int i = 0; i < num_loops; i++, input_buffer_index++) {
363 ExpectIsAnyInputPending(true);
364 ExpectDequeueInputBuffer(input_buffer_index);
365
366 MediaCodecLoop::InputData data = BigBuckBunny();
367 ExpectProvideInputData(data);
368
369 ExpectQueueInputBuffer(input_buffer_index, data);
370 ExpectInputDataQueued(true);
371
372 OutputBuffer buffer;
373 buffer.index = i;
374 buffer.size += i;
375 buffer.pts = base::TimeDelta::FromSeconds(i + 1);
376 ExpectDequeueOutputBuffer(buffer);
377 ExpectOnDecodedFrame(buffer);
378 }
379
380 ExpectEmptyIOLoop();
381
382 codec_loop_->DoPendingWork();
383 }
384
385 TEST_F(MediaCodecLoopTest, TestTryFlushOnJellyBeanMR2) {
386 // On JB MR2+ MCL should be willing to use MediaCodecBridge::Flush.
387 ConstructCodecLoop(kJellyBeanMR2);
388 EXPECT_CALL(Codec(), Flush()).Times(1).WillOnce(Return(MEDIA_CODEC_OK));
389 ASSERT_TRUE(codec_loop_->TryFlush());
390 }
391
392 TEST_F(MediaCodecLoopTest, TestTryFlushAfterJellyBeanMR2Fails) {
393 // On JB MR2+, MCL should be willing to use MediaCodecBridge::Flush. Try
394 // that, but make Flush fail.
395 ConstructCodecLoop(kJellyBeanMR2);
396 EXPECT_CALL(Codec(), Flush()).Times(1).WillOnce(Return(MEDIA_CODEC_ERROR));
397 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1);
398 ASSERT_FALSE(codec_loop_->TryFlush());
399 }
400
401 TEST_F(MediaCodecLoopTest, TestTryFlushOnJellyBeanMR1) {
402 // In JB MR1, MCL should not be willing to use MediaCodecBridge::Flush.
403 ConstructCodecLoop(kJellyBeanMR1);
404 ASSERT_FALSE(codec_loop_->TryFlush());
405 }
406
407 TEST_F(MediaCodecLoopTest, TestOnKeyAdded) {
408 ConstructCodecLoop();
409
410 int input_buffer_index = 123;
411 MediaCodecLoop::InputData data = BigBuckBunny();
412
413 // First provide input, but have MediaCodecBridge require a key.
414 {
415 InSequence _s;
416
417 // First DoPendingWork()
418 ExpectIsAnyInputPending(true);
419 ExpectDequeueInputBuffer(input_buffer_index);
420
421 ExpectProvideInputData(data);
422
423 // Notify MCL that it's missing the key.
424 ExpectQueueInputBuffer(input_buffer_index, data, MEDIA_CODEC_NO_KEY);
425
426 // MCL should now try for output buffers.
427 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
428
429 // MCL will try again, since trying to queue the input buffer is considered
430 // doing work, for some reason. It would be nice to make this optional.
431 // Note that it should not ask us for more input, since it has not yet sent
432 // the buffer we just provided.
433 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
434 }
435 codec_loop_->DoPendingWork();
436
437 // Try again, to be sure that MCL doesn't request more input. Note that this
438 // is also done in the above loop, but that one could be made optional. This
439 // forces MCL to try again as part of an entirely new DoPendingWork cycle.
440 {
441 InSequence _s;
442 // MCL should only try for output buffers, since it's still waiting for a
443 // key to be added.
444 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
445 }
446 codec_loop_->DoPendingWork();
447
448 // When we add the key, MCL will DoPending work again. This time, it should
449 // succeed since the key has been added.
450 {
451 InSequence _s;
452 // MCL should not retain the original pointer.
453 data.memory = nullptr;
454 ExpectQueueInputBuffer(input_buffer_index, data);
455 ExpectInputDataQueued(true);
456 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
457
458 // MCL did work, so it will try again.
459 ExpectEmptyIOLoop();
460 }
461
462 codec_loop_->OnKeyAdded();
463 WaitUntilIdle(ShouldNotBeIdle);
464 }
465
44 } // namespace media 466 } // namespace media
OLDNEW
« no previous file with comments | « media/base/android/media_codec_loop.cc ('k') | media/base/android/media_codec_util.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698