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

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: changed constexpr for windows compiler bug 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
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 // We might want to WaitUntilIdle here.
watk 2016/08/09 18:32:55 Not sure what you mean by this
liberato (no reviews please) 2016/08/09 21:20:47 doesn't mean much anymore. removed.
99 }
100
101 // Set an expectation that MCL will try to get another input / output buffer,
102 // and not get one in DoPendingWork.
103 void ExpectEmptyIOLoop() {
104 ExpectIsAnyInputPending(false);
105 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
106 .Times(1)
107 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
108 }
109
110 void ExpectIsAnyInputPending(bool pending) {
111 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(pending));
112 }
113
114 void ExpectDequeueInputBuffer(int input_buffer_index,
115 MediaCodecStatus status = MEDIA_CODEC_OK) {
116 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _))
117 .WillOnce(DoAll(SetArgPointee<1>(input_buffer_index), Return(status)));
118 }
119
120 void ExpectInputDataQueued(bool success) {
121 EXPECT_CALL(*client_, OnInputDataQueued(success)).Times(1);
122 }
123
124 // Expect a call to queue |data| into MC buffer |input_buffer_index|.
125 void ExpectQueueInputBuffer(int input_buffer_index,
126 const MediaCodecLoop::InputData& data,
127 MediaCodecStatus status = MEDIA_CODEC_OK) {
128 EXPECT_CALL(Codec(), QueueInputBuffer(input_buffer_index, data.memory,
129 data.length, data.presentation_time))
130 .Times(1)
131 .WillOnce(Return(status));
132 }
133
134 void ExpectProvideInputData(const MediaCodecLoop::InputData& data) {
135 EXPECT_CALL(*client_, ProvideInputData()).WillOnce(Return(data));
136 }
137
138 MediaCodecLoop::InputData BigBuckBunny() {
139 MediaCodecLoop::InputData data;
140 data.memory = reinterpret_cast<const uint8_t*>("big buck bunny");
141 data.length = 14;
142 data.presentation_time = base::TimeDelta::FromSeconds(1);
143 return data;
144 }
145
146 struct OutputBuffer {
147 int index = 1;
148 size_t offset = 0;
149 size_t size = 1024;
150 base::TimeDelta pts = base::TimeDelta::FromSeconds(1);
151 bool eos = false;
152 bool key_frame = true;
153 };
154
155 struct EosOutputBuffer : public OutputBuffer {
156 EosOutputBuffer() { eos = true; }
157 };
158
159 void ExpectDequeueOutputBuffer(MediaCodecStatus status) {
160 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
161 .WillOnce(Return(status));
162 }
163
164 void ExpectDequeueOutputBuffer(const OutputBuffer& buffer) {
165 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
166 .WillOnce(DoAll(
167 SetArgPointee<1>(buffer.index), SetArgPointee<2>(buffer.offset),
168 SetArgPointee<3>(buffer.size), SetArgPointee<4>(buffer.pts),
169 SetArgPointee<5>(buffer.eos), SetArgPointee<6>(buffer.key_frame),
170 Return(MEDIA_CODEC_OK)));
171 }
172
173 void ExpectOnDecodedFrame(const OutputBuffer& buf) {
174 EXPECT_CALL(*client_,
175 OnDecodedFrame(
176 Field(&MediaCodecLoop::OutputBuffer::index, Eq(buf.index))))
177 .Times(1)
178 .WillOnce(Return(true));
179 }
180
181 MockMediaCodecBridge& Codec() {
182 return *static_cast<MockMediaCodecBridge*>(codec_loop_->GetCodec());
183 }
28 184
29 public: 185 public:
30 std::unique_ptr<MediaCodecLoop> codec_loop_; 186 std::unique_ptr<MediaCodecLoop> codec_loop_;
31 std::unique_ptr<MockMediaCodecLoopClient> client_; 187 std::unique_ptr<MockMediaCodecLoopClient> client_;
188 // TODO: how is the lifecycle of |clock_| handled? |task_runner_| can outlive
189 // us, since it's a refptr.
190 base::SimpleTestTickClock clock_;
191 scoped_refptr<FakeSingleThreadTaskRunner> task_runner_;
32 192
33 DISALLOW_COPY_AND_ASSIGN(MediaCodecLoopTest); 193 DISALLOW_COPY_AND_ASSIGN(MediaCodecLoopTest);
34 }; 194 };
35 195
36 TEST_F(MediaCodecLoopTest, TestConstructionWithNullCodec) { 196 TEST_F(MediaCodecLoopTest, TestConstructionWithNullCodec) {
37 std::unique_ptr<MediaCodecBridge> codec; 197 std::unique_ptr<MediaCodecBridge> codec;
38 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1); 198 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1);
39 codec_loop_.reset(new MediaCodecLoop(client_.get(), std::move(codec))); 199 const int sdk_int = kLollipop;
200 codec_loop_.reset(
201 new MediaCodecLoop(sdk_int, client_.get(), std::move(codec)));
202 // Do not WaitUntilIdle() here, since that assumes that we have a codec.
40 203
41 ASSERT_FALSE(codec_loop_->GetCodec()); 204 ASSERT_FALSE(codec_loop_->GetCodec());
42 } 205 }
43 206
207 TEST_F(MediaCodecLoopTest, TestConstructionWithCodec) {
208 ConstructCodecLoop();
209 ASSERT_EQ(codec_loop_->GetCodec(), &Codec());
210 WaitUntilIdle(ShouldBeIdle);
211 }
212
213 TEST_F(MediaCodecLoopTest, TestPendingWorkWithoutInput) {
214 ConstructCodecLoop();
215 // MCL should try ask if there is pending input, and try to dequeue output.
216 ExpectIsAnyInputPending(false);
217 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
218 .Times(1)
219 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
220 codec_loop_->DoPendingWork();
221 WaitUntilIdle(ShouldNotBeIdle);
222 }
223
224 TEST_F(MediaCodecLoopTest, TestPendingWorkWithInput) {
225 ConstructCodecLoop();
226 // MCL should try ask if there is pending input, and try to dequeue both an
227 // output and input buffer.
228 ExpectIsAnyInputPending(true);
229 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)).Times(1);
230 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)).Times(1);
231 codec_loop_->DoPendingWork();
232 WaitUntilIdle(ShouldNotBeIdle);
233 }
234
235 TEST_F(MediaCodecLoopTest, TestPendingWorkWithOutputBuffer) {
236 ConstructCodecLoop();
237 {
238 InSequence _s;
239
240 // MCL will first request input, then try to dequeue output.
241 ExpectIsAnyInputPending(false);
242 OutputBuffer buf;
243 ExpectDequeueOutputBuffer(buf);
244 ExpectOnDecodedFrame(buf);
245
246 // MCL will try again for another set of buffers before DoPendingWork()
247 // returns. This is why we don't just leave them for WaitUntilIdle().
248 ExpectEmptyIOLoop();
249 }
250 codec_loop_->DoPendingWork();
251 WaitUntilIdle(ShouldNotBeIdle);
252 }
253
254 TEST_F(MediaCodecLoopTest, TestQueueEos) {
255 // Test sending an EOS to MCL => MCB =dequeue output=> MCL .
256 ConstructCodecLoop();
257 {
258 InSequence _s;
259
260 ExpectIsAnyInputPending(true);
261 int input_buffer_index = 123;
262 ExpectDequeueInputBuffer(input_buffer_index);
263
264 MediaCodecLoop::InputData data;
265 data.is_eos = true;
266 ExpectProvideInputData(data);
267 EXPECT_CALL(Codec(), QueueEOS(input_buffer_index));
268 ExpectInputDataQueued(true);
269
270 // Now send the EOS back on the output queue.
271 EosOutputBuffer eos;
272 ExpectDequeueOutputBuffer(eos);
273 EXPECT_CALL(Codec(), ReleaseOutputBuffer(eos.index, false));
274 EXPECT_CALL(*client_, OnDecodedEos(_)).Times(1);
275
276 // See TestUnqueuedEos.
277 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
278 .Times(1)
279 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
280 }
281 codec_loop_->DoPendingWork();
282 // Don't WaitUntilIdle() here. See TestUnqueuedEos.
283 }
284
285 TEST_F(MediaCodecLoopTest, TestQueueInputData) {
286 // Send a buffer full of data into MCL and make sure that it gets queued with
287 // MediaCodecBridge correctly.
288 ConstructCodecLoop();
289 {
290 InSequence _s;
291
292 ExpectIsAnyInputPending(true);
293 int input_buffer_index = 123;
294 ExpectDequeueInputBuffer(input_buffer_index);
295
296 MediaCodecLoop::InputData data = BigBuckBunny();
297 ExpectProvideInputData(data);
298
299 // MCL should send the buffer into MediaCodec and notify the client.
300 ExpectQueueInputBuffer(input_buffer_index, data);
301 ExpectInputDataQueued(true);
302
303 // MCL will try to dequeue an output buffer too.
304 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _))
305 .Times(1)
306 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER));
307
308 // DoPendingWork will try again.
309 ExpectEmptyIOLoop();
310 }
311 codec_loop_->DoPendingWork();
312 WaitUntilIdle(ShouldNotBeIdle);
313 }
314
315 TEST_F(MediaCodecLoopTest, TestQueueInputDataFails) {
316 // Send a buffer full of data into MCL, but MediaCodecBridge fails to queue
317 // it successfully.
318 ConstructCodecLoop();
319 {
320 InSequence _s;
321
322 ExpectIsAnyInputPending(true);
323 int input_buffer_index = 123;
324 ExpectDequeueInputBuffer(input_buffer_index);
325
326 MediaCodecLoop::InputData data = BigBuckBunny();
327 ExpectProvideInputData(data);
328
329 // MCL should send the buffer into MediaCodec and notify the client.
330 ExpectQueueInputBuffer(input_buffer_index, data, MEDIA_CODEC_ERROR);
331 ExpectInputDataQueued(false);
332 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1);
333 }
334 codec_loop_->DoPendingWork();
335 // MCL is now in the error state.
336 }
337
338 TEST_F(MediaCodecLoopTest, TestQueueInputDataTryAgain) {
339 // Signal that there is input pending, but don't provide an input buffer.
340 ConstructCodecLoop();
341 {
342 InSequence _s;
343
344 ExpectIsAnyInputPending(true);
345 ExpectDequeueInputBuffer(-1, MEDIA_CODEC_DEQUEUE_INPUT_AGAIN_LATER);
346 // MCL will try for output too.
347 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
348 }
349 codec_loop_->DoPendingWork();
350 // Note that the client might not be allowed to change from "input pending"
351 // to "no input pending" without actually being asked for input. For now,
352 // MCL doesn't assume this.
353 WaitUntilIdle(ShouldNotBeIdle);
354 }
355
356 TEST_F(MediaCodecLoopTest, TestSeveralPendingIOBuffers) {
357 // Provide several input and output buffers to MCL.
358 ConstructCodecLoop();
359 int input_buffer_index = 123;
360 const int num_loops = 4;
361
362 InSequence _s;
363 for (int i = 0; i < num_loops; i++, input_buffer_index++) {
364 ExpectIsAnyInputPending(true);
365 ExpectDequeueInputBuffer(input_buffer_index);
366
367 MediaCodecLoop::InputData data = BigBuckBunny();
368 ExpectProvideInputData(data);
369
370 ExpectQueueInputBuffer(input_buffer_index, data);
371 ExpectInputDataQueued(true);
372
373 OutputBuffer buffer;
374 buffer.index = i;
375 buffer.size += i;
376 buffer.pts = base::TimeDelta::FromSeconds(i + 1);
377 ExpectDequeueOutputBuffer(buffer);
378 ExpectOnDecodedFrame(buffer);
379 }
380
381 ExpectEmptyIOLoop();
382
383 codec_loop_->DoPendingWork();
384 }
385
386 TEST_F(MediaCodecLoopTest, TestTryFlushOnJellyBeanMR2) {
387 // On JB MR2+ MCL should be willing to use MediaCodecBridge::Flush.
388 ConstructCodecLoop(kJellyBeanMR2);
389 EXPECT_CALL(Codec(), Flush()).Times(1).WillOnce(Return(MEDIA_CODEC_OK));
390 ASSERT_TRUE(codec_loop_->TryFlush());
391 }
392
393 TEST_F(MediaCodecLoopTest, TestTryFlushAfterJellyBeanMR2Fails) {
394 // On JB MR2+, MCL should be willing to use MediaCodecBridge::Flush. Try
395 // that, but make Flush fail.
396 ConstructCodecLoop(kJellyBeanMR2);
397 EXPECT_CALL(Codec(), Flush()).Times(1).WillOnce(Return(MEDIA_CODEC_ERROR));
398 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1);
399 ASSERT_FALSE(codec_loop_->TryFlush());
400 }
401
402 TEST_F(MediaCodecLoopTest, TestTryFlushOnJellyBeanMR1) {
403 // In JB MR1, MCL should not be willing to use MediaCodecBridge::Flush.
404 ConstructCodecLoop(kJellyBeanMR1);
405 ASSERT_FALSE(codec_loop_->TryFlush());
406 }
407
408 TEST_F(MediaCodecLoopTest, TestOnKeyAdded) {
409 ConstructCodecLoop();
410
411 int input_buffer_index = 123;
412 MediaCodecLoop::InputData data = BigBuckBunny();
413
414 // First provide input, but have MediaCodecBridge require a key.
415 {
416 InSequence _s;
417
418 // First DoPendingWork()
419 ExpectIsAnyInputPending(true);
420 ExpectDequeueInputBuffer(input_buffer_index);
421
422 ExpectProvideInputData(data);
423
424 // Notify MCL that it's missing the key.
425 ExpectQueueInputBuffer(input_buffer_index, data, MEDIA_CODEC_NO_KEY);
426
427 // MCL should now try for output buffers.
428 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
429
430 // MCL will try again, since trying to queue the input buffer is considered
431 // doing work, for some reason. It would be nice to make this optional.
432 // Note that it should not ask us for more input, since it has not yet sent
433 // the buffer we just provided.
434 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
435 }
436 codec_loop_->DoPendingWork();
437
438 // Try again, to be sure that MCL doesn't request more input. Note that this
439 // is also done in the above loop, but that one could be made optional. This
440 // forces MCL to try again as part of an entirely new DoPendingWork cycle.
441 {
442 InSequence _s;
443 // MCL should only try for output buffers, since it's still waiting for a
444 // key to be added.
445 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
446 }
447 codec_loop_->DoPendingWork();
448
449 // When we add the key, MCL will DoPending work again. This time, it should
450 // succeed since the key has been added.
451 {
452 InSequence _s;
453 // MCL should not retain the original pointer.
454 data.memory = nullptr;
455 ExpectQueueInputBuffer(input_buffer_index, data);
456 ExpectInputDataQueued(true);
457 ExpectDequeueOutputBuffer(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER);
458
459 // MCL did work, so it will try again.
460 ExpectEmptyIOLoop();
461 }
462
463 codec_loop_->OnKeyAdded();
464 WaitUntilIdle(ShouldNotBeIdle);
465 }
466
44 } // namespace media 467 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698