OLD | NEW |
---|---|
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 // The client is a strict mock, since we don't want random calls into it. We |
27 // want to be sure about the call sequence. | |
28 class MockMediaCodecLoopClient : public StrictMock<MediaCodecLoop::Client> { | |
15 public: | 29 public: |
16 MOCK_CONST_METHOD0(IsAnyInputPending, bool()); | 30 MOCK_CONST_METHOD0(IsAnyInputPending, bool()); |
17 MOCK_METHOD0(ProvideInputData, MediaCodecLoop::InputData()); | 31 MOCK_METHOD0(ProvideInputData, MediaCodecLoop::InputData()); |
18 MOCK_METHOD1(OnDecodedEos, void(const MediaCodecLoop::OutputBuffer&)); | 32 MOCK_METHOD1(OnDecodedEos, void(const MediaCodecLoop::OutputBuffer&)); |
19 MOCK_METHOD1(OnDecodedFrame, bool(const MediaCodecLoop::OutputBuffer&)); | 33 MOCK_METHOD1(OnDecodedFrame, bool(const MediaCodecLoop::OutputBuffer&)); |
20 MOCK_METHOD0(OnOutputFormatChanged, bool()); | 34 MOCK_METHOD0(OnOutputFormatChanged, bool()); |
21 MOCK_METHOD0(OnCodecLoopError, void()); | 35 MOCK_METHOD0(OnCodecLoopError, void()); |
22 }; | 36 }; |
23 | 37 |
24 class MediaCodecLoopTest : public testing::Test { | 38 class MediaCodecLoopTest : public testing::Test { |
25 public: | 39 public: |
26 MediaCodecLoopTest() : client_(new MockMediaCodecLoopClient) {} | 40 MediaCodecLoopTest() |
41 : client_(new StrictMock<MockMediaCodecLoopClient>()), | |
42 task_runner_(new FakeSingleThreadTaskRunner(&clock_)) {} | |
43 | |
44 ~MediaCodecLoopTest() override {} | |
45 | |
46 protected: | |
47 enum IdleExpectation { | |
48 ShouldBeIdle, | |
49 ShouldNotBeIdle, | |
50 }; | |
51 | |
52 // Wait until |codec_loop_| is idle. | |
53 void WaitUntilIdle(IdleExpectation idleExpectation = ShouldBeIdle) { | |
54 switch (idleExpectation) { | |
55 case ShouldBeIdle: | |
56 EXPECT_CALL(*client_, IsAnyInputPending()).Times(0); | |
57 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)).Times(0); | |
58 break; | |
59 case ShouldNotBeIdle: | |
60 // Expect at least one call to see if more work is ready. We will | |
61 // return 'no'. | |
62 EXPECT_CALL(*client_, IsAnyInputPending()) | |
63 .Times(AtLeast(1)) | |
64 .WillRepeatedly(Return(false)); | |
65 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
66 .Times(AtLeast(1)) | |
67 .WillRepeatedly(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
68 break; | |
69 } | |
70 | |
71 // Either way, we expect that MCL should not attempt to dequeue input | |
72 // buffers, either because it's idle or because we said that no input | |
73 // is pending. | |
74 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)).Times(0); | |
75 | |
76 // TODO(liberato): assume that MCL doesn't retry for 30 seconds. Note | |
77 // that this doesn't actually wall-clock wait. | |
78 task_runner_->Sleep(base::TimeDelta::FromSeconds(30)); | |
79 } | |
80 | |
81 void ConstructCodecLoop() { | |
82 std::unique_ptr<MediaCodecBridge> codec(new MockMediaCodecBridge()); | |
83 // Since we're providing a codec, we do not expect an error. | |
84 EXPECT_CALL(*client_, OnCodecLoopError()).Times(0); | |
85 codec_loop_.reset( | |
86 new MediaCodecLoop(client_.get(), std::move(codec), task_runner_)); | |
87 codec_loop_->SetTestTickClock(&clock_); | |
88 Mock::VerifyAndClearExpectations(client_.get()); | |
89 // We might want to WaitUntilIdle here. | |
90 } | |
91 | |
92 struct OutputBuffer { | |
93 int index = 1; | |
94 size_t offset = 0; | |
95 size_t size = 1024; | |
96 base::TimeDelta pts = base::TimeDelta::FromSeconds(1); | |
97 bool eos = false; | |
98 bool key_frame = true; | |
99 }; | |
100 | |
101 struct EosOutputBuffer : public OutputBuffer { | |
102 EosOutputBuffer() { eos = true; } | |
103 }; | |
104 | |
105 void QueueOutputBuffer(OutputBuffer buffer) { | |
106 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
107 .WillOnce(DoAll( | |
108 SetArgPointee<1>(buffer.index), SetArgPointee<2>(buffer.offset), | |
109 SetArgPointee<3>(buffer.size), SetArgPointee<4>(buffer.pts), | |
110 SetArgPointee<5>(buffer.eos), SetArgPointee<6>(buffer.key_frame), | |
111 Return(MEDIA_CODEC_OK))); | |
112 } | |
113 | |
114 MockMediaCodecBridge& Codec() { | |
115 return *static_cast<MockMediaCodecBridge*>(codec_loop_->GetCodec()); | |
116 } | |
27 | 117 |
28 public: | 118 public: |
29 std::unique_ptr<MediaCodecLoop> codec_loop_; | 119 std::unique_ptr<MediaCodecLoop> codec_loop_; |
30 std::unique_ptr<MockMediaCodecLoopClient> client_; | 120 std::unique_ptr<MockMediaCodecLoopClient> client_; |
121 // TODO: how is the lifecycle of |clock_| handled? |task_runner_| can outlive | |
122 // us, since it's a refptr. | |
123 base::SimpleTestTickClock clock_; | |
124 scoped_refptr<FakeSingleThreadTaskRunner> task_runner_; | |
31 | 125 |
32 DISALLOW_COPY_AND_ASSIGN(MediaCodecLoopTest); | 126 DISALLOW_COPY_AND_ASSIGN(MediaCodecLoopTest); |
33 }; | 127 }; |
34 | 128 |
35 TEST_F(MediaCodecLoopTest, TestConstructionWithNullCodec) { | 129 TEST_F(MediaCodecLoopTest, TestConstructionWithNullCodec) { |
36 std::unique_ptr<MediaCodecBridge> codec; | 130 std::unique_ptr<MediaCodecBridge> codec; |
37 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1); | 131 EXPECT_CALL(*client_, OnCodecLoopError()).Times(1); |
38 codec_loop_.reset(new MediaCodecLoop(client_.get(), std::move(codec))); | 132 codec_loop_.reset(new MediaCodecLoop(client_.get(), std::move(codec))); |
133 // Do not WaitUntilIdle() here, since that assumes that we have a codec. | |
39 | 134 |
40 ASSERT_FALSE(codec_loop_->GetCodec()); | 135 ASSERT_FALSE(codec_loop_->GetCodec()); |
41 } | 136 } |
42 | 137 |
138 TEST_F(MediaCodecLoopTest, TestConstructionWithCodec) { | |
139 ConstructCodecLoop(); | |
140 ASSERT_EQ(codec_loop_->GetCodec(), &Codec()); | |
141 WaitUntilIdle(ShouldBeIdle); | |
142 } | |
143 | |
144 TEST_F(MediaCodecLoopTest, TestPendingWorkWithoutInput) { | |
145 ConstructCodecLoop(); | |
146 // MCL should try ask if there is pending input, and try to dequeue output. | |
147 EXPECT_CALL(*client_, IsAnyInputPending()).Times(1).WillOnce(Return(false)); | |
148 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
149 .Times(1) | |
150 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
151 codec_loop_->DoPendingWork(); | |
152 WaitUntilIdle(ShouldNotBeIdle); | |
153 } | |
154 | |
155 TEST_F(MediaCodecLoopTest, TestPendingWorkWithInput) { | |
156 ConstructCodecLoop(); | |
157 // MCL should try ask if there is pending input, and try to dequeue both an | |
158 // output and input buffer. | |
159 EXPECT_CALL(*client_, IsAnyInputPending()).Times(1).WillOnce(Return(true)); | |
160 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)).Times(1); | |
161 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)).Times(1); | |
162 codec_loop_->DoPendingWork(); | |
163 WaitUntilIdle(ShouldNotBeIdle); | |
164 } | |
165 | |
166 TEST_F(MediaCodecLoopTest, TestPendingWorkWithOutputBuffer) { | |
167 ConstructCodecLoop(); | |
168 { | |
169 InSequence _s; | |
170 | |
171 // MCL will first request input, then try to dequeue output. | |
172 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(false)); | |
173 OutputBuffer buf; | |
174 QueueOutputBuffer(buf); | |
175 EXPECT_CALL(*client_, | |
liberato (no reviews please)
2016/07/08 17:49:46
gmock is really amazing. it lets me set an expect
| |
176 OnDecodedFrame( | |
177 Field(&MediaCodecLoop::OutputBuffer::index, Eq(buf.index)))) | |
178 .Times(1) | |
179 .WillOnce(Return(true)); | |
180 | |
181 // MCL will try again for another set of buffers before DoPendingWork() | |
182 // returns. This is why we don't just leave them for WaitUntilIdle(). | |
183 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(false)); | |
184 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
185 .Times(1) | |
186 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
187 } | |
188 codec_loop_->DoPendingWork(); | |
189 WaitUntilIdle(ShouldNotBeIdle); | |
190 } | |
191 | |
192 TEST_F(MediaCodecLoopTest, TestUnqueuedEos) { | |
193 // Test sending an unsolicited EOS from MCB to MCL. | |
194 ConstructCodecLoop(); | |
195 { | |
196 InSequence _s; | |
197 | |
198 // MCL will first request input, then try to dequeue output. | |
199 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(false)); | |
200 EosOutputBuffer eos; | |
201 QueueOutputBuffer(eos); | |
202 EXPECT_CALL(Codec(), ReleaseOutputBuffer(eos.index, false)); | |
203 EXPECT_CALL(*client_, OnDecodedEos(_)).Times(1); | |
204 | |
205 // MCL should not call back for another input buffer once it receives EOS, | |
206 // so don't expect a call to IsAnyInputPending. It will try for more | |
207 // output, however. It's unclear if it should. | |
208 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
209 .Times(1) | |
210 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
211 } | |
212 codec_loop_->DoPendingWork(); | |
213 // Don't WaitUntilIdle() here, since MCL is not requesting buffers. It would | |
214 // fail the IsAnyInputPending expectation. | |
215 } | |
216 | |
217 TEST_F(MediaCodecLoopTest, TestQueueEos) { | |
218 // Test sending an unsolicited EOS from MCB to MCL. | |
219 ConstructCodecLoop(); | |
220 { | |
221 InSequence _s; | |
222 | |
223 // MCL will first request input. We will provide an EOS from the client, | |
224 // and an input buffer to hold it. | |
225 EXPECT_CALL(*client_, IsAnyInputPending()).WillOnce(Return(true)); | |
226 int input_buffer_index = 123; | |
227 EXPECT_CALL(Codec(), DequeueInputBuffer(_, _)) | |
228 .WillOnce(DoAll(SetArgPointee<1>(input_buffer_index), | |
liberato (no reviews please)
2016/07/08 17:49:46
this sets the *index parameter of DequeueInputBuff
| |
229 Return(MEDIA_CODEC_OK))); | |
230 MediaCodecLoop::InputData data; | |
231 data.is_eos = true; | |
232 EXPECT_CALL(*client_, ProvideInputData()).WillOnce(Return(data)); | |
233 EXPECT_CALL(Codec(), QueueEOS(input_buffer_index)); | |
234 | |
235 // Now send the EOS back on the output queue. | |
236 EosOutputBuffer eos; | |
237 QueueOutputBuffer(eos); | |
238 EXPECT_CALL(Codec(), ReleaseOutputBuffer(eos.index, false)); | |
239 EXPECT_CALL(*client_, OnDecodedEos(_)).Times(1); | |
240 | |
241 // See TestUnqueuedEos. | |
242 EXPECT_CALL(Codec(), DequeueOutputBuffer(_, _, _, _, _, _, _)) | |
243 .Times(1) | |
244 .WillOnce(Return(MEDIA_CODEC_DEQUEUE_OUTPUT_AGAIN_LATER)); | |
245 } | |
246 codec_loop_->DoPendingWork(); | |
247 // Don't WaitUntilIdle() here. See TestUnqueuedEos. | |
248 } | |
249 | |
43 } // namespace media | 250 } // namespace media |
OLD | NEW |