1 /*
2 * Copyright (C) 2020 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17 #include <future>
18 #include <thread>
19
20 #include <gmock/gmock.h>
21 #include <gtest/gtest.h>
22
23 #include <gui/Surface.h>
24 #include <mediadrm/ICrypto.h>
25 #include <media/stagefright/CodecBase.h>
26 #include <media/stagefright/MediaCodec.h>
27 #include <media/stagefright/MediaCodecListWriter.h>
28 #include <media/MediaCodecInfo.h>
29
30 #include "MediaTestHelper.h"
31
32 namespace android {
33
34 class MockBufferChannel : public BufferChannelBase {
35 public:
36 ~MockBufferChannel() override = default;
37
38 MOCK_METHOD(void, setCrypto, (const sp<ICrypto> &crypto), (override));
39 MOCK_METHOD(void, setDescrambler, (const sp<IDescrambler> &descrambler), (override));
40 MOCK_METHOD(status_t, queueInputBuffer, (const sp<MediaCodecBuffer> &buffer), (override));
41 MOCK_METHOD(status_t, queueSecureInputBuffer,
42 (const sp<MediaCodecBuffer> &buffer,
43 bool secure,
44 const uint8_t *key,
45 const uint8_t *iv,
46 CryptoPlugin::Mode mode,
47 CryptoPlugin::Pattern pattern,
48 const CryptoPlugin::SubSample *subSamples,
49 size_t numSubSamples,
50 AString *errorDetailMsg),
51 (override));
52 MOCK_METHOD(status_t, attachBuffer,
53 (const std::shared_ptr<C2Buffer> &c2Buffer, const sp<MediaCodecBuffer> &buffer),
54 (override));
55 MOCK_METHOD(status_t, attachEncryptedBuffer,
56 (const sp<hardware::HidlMemory> &memory,
57 bool secure,
58 const uint8_t *key,
59 const uint8_t *iv,
60 CryptoPlugin::Mode mode,
61 CryptoPlugin::Pattern pattern,
62 size_t offset,
63 const CryptoPlugin::SubSample *subSamples,
64 size_t numSubSamples,
65 const sp<MediaCodecBuffer> &buffer),
66 (override));
67 MOCK_METHOD(status_t, renderOutputBuffer,
68 (const sp<MediaCodecBuffer> &buffer, int64_t timestampNs),
69 (override));
70 MOCK_METHOD(status_t, discardBuffer, (const sp<MediaCodecBuffer> &buffer), (override));
71 MOCK_METHOD(void, getInputBufferArray, (Vector<sp<MediaCodecBuffer>> *array), (override));
72 MOCK_METHOD(void, getOutputBufferArray, (Vector<sp<MediaCodecBuffer>> *array), (override));
73 };
74
75 class MockCodec : public CodecBase {
76 public:
MockCodec(std::function<void (const std::shared_ptr<MockBufferChannel> &)> mock)77 MockCodec(std::function<void(const std::shared_ptr<MockBufferChannel> &)> mock) {
78 mMockBufferChannel = std::make_shared<MockBufferChannel>();
79 mock(mMockBufferChannel);
80 }
81 ~MockCodec() override = default;
82
83 MOCK_METHOD(void, initiateAllocateComponent, (const sp<AMessage> &msg), (override));
84 MOCK_METHOD(void, initiateConfigureComponent, (const sp<AMessage> &msg), (override));
85 MOCK_METHOD(void, initiateCreateInputSurface, (), (override));
86 MOCK_METHOD(void, initiateSetInputSurface, (const sp<PersistentSurface> &surface), (override));
87 MOCK_METHOD(void, initiateStart, (), (override));
88 MOCK_METHOD(void, initiateShutdown, (bool keepComponentAllocated), (override));
89 MOCK_METHOD(void, onMessageReceived, (const sp<AMessage> &msg), (override));
90 MOCK_METHOD(status_t, setSurface, (const sp<Surface> &surface), (override));
91 MOCK_METHOD(void, signalFlush, (), (override));
92 MOCK_METHOD(void, signalResume, (), (override));
93 MOCK_METHOD(void, signalRequestIDRFrame, (), (override));
94 MOCK_METHOD(void, signalSetParameters, (const sp<AMessage> &msg), (override));
95 MOCK_METHOD(void, signalEndOfInputStream, (), (override));
96
getBufferChannel()97 std::shared_ptr<BufferChannelBase> getBufferChannel() override {
98 return mMockBufferChannel;
99 }
100
callback()101 const std::unique_ptr<CodecCallback> &callback() {
102 return mCallback;
103 }
104
105 std::shared_ptr<MockBufferChannel> mMockBufferChannel;
106 };
107
108 class Counter {
109 public:
110 Counter() = default;
Counter(int32_t initCount)111 explicit Counter(int32_t initCount) : mCount(initCount) {}
112 ~Counter() = default;
113
advance()114 int32_t advance() {
115 std::unique_lock<std::mutex> lock(mMutex);
116 ++mCount;
117 mCondition.notify_all();
118 return mCount;
119 }
120
121 template <typename Rep, typename Period, typename ...Args>
waitFor(const std::chrono::duration<Rep,Period> & duration,Args...values)122 int32_t waitFor(const std::chrono::duration<Rep, Period> &duration, Args... values) {
123 std::initializer_list<int32_t> list = {values...};
124 std::unique_lock<std::mutex> lock(mMutex);
125 mCondition.wait_for(
126 lock,
127 duration,
128 [&list, this]{
129 return std::find(list.begin(), list.end(), mCount) != list.end();
130 });
131 return mCount;
132 }
133
134 template <typename ...Args>
wait(Args...values)135 int32_t wait(Args... values) {
136 std::initializer_list<int32_t> list = {values...};
137 std::unique_lock<std::mutex> lock(mMutex);
138 mCondition.wait(
139 lock,
140 [&list, this]{
141 return std::find(list.begin(), list.end(), mCount) != list.end();
142 });
143 return mCount;
144 }
145
146 private:
147 std::mutex mMutex;
148 std::condition_variable mCondition;
149 int32_t mCount = 0;
150 };
151
152 } // namespace android
153
154 using namespace android;
155 using ::testing::_;
156
SetupMediaCodec(const AString & owner,const AString & codecName,const AString & mediaType,const sp<ALooper> & looper,std::function<sp<CodecBase> (const AString & name,const char * owner)> getCodecBase)157 static sp<MediaCodec> SetupMediaCodec(
158 const AString &owner,
159 const AString &codecName,
160 const AString &mediaType,
161 const sp<ALooper> &looper,
162 std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase) {
163 std::shared_ptr<MediaCodecListWriter> listWriter =
164 MediaTestHelper::CreateCodecListWriter();
165 std::unique_ptr<MediaCodecInfoWriter> infoWriter = listWriter->addMediaCodecInfo();
166 infoWriter->setName(codecName.c_str());
167 infoWriter->setOwner(owner.c_str());
168 infoWriter->addMediaType(mediaType.c_str());
169 std::vector<sp<MediaCodecInfo>> codecInfos;
170 MediaTestHelper::WriteCodecInfos(listWriter, &codecInfos);
171 std::function<status_t(const AString &, sp<MediaCodecInfo> *)> getCodecInfo =
172 [codecInfos](const AString &name, sp<MediaCodecInfo> *info) -> status_t {
173 auto it = std::find_if(
174 codecInfos.begin(), codecInfos.end(),
175 [&name](const sp<MediaCodecInfo> &info) {
176 return name.equalsIgnoreCase(info->getCodecName());
177 });
178
179 *info = (it == codecInfos.end()) ? nullptr : *it;
180 return (*info) ? OK : NAME_NOT_FOUND;
181 };
182
183 looper->start();
184 return MediaTestHelper::CreateCodec(
185 codecName, looper, getCodecBase, getCodecInfo);
186 }
187
TEST(MediaCodecTest,ReclaimReleaseRace)188 TEST(MediaCodecTest, ReclaimReleaseRace) {
189 // Test scenario:
190 //
191 // 1) ResourceManager thread calls reclaim(), message posted to
192 // MediaCodec looper thread.
193 // 2) MediaCodec looper thread calls initiateShutdown(), shutdown being
194 // handled at the component thread.
195 // 3) Client thread calls release(), message posted to & handle at
196 // MediaCodec looper thread.
197 // 4) MediaCodec looper thread may call initiateShutdown().
198 // 5) initiateShutdown() from 2) is handled at onReleaseComplete() event
199 // posted to MediaCodec looper thread.
200 // 6) If called, initiateShutdown() from 4) is handled and
201 // onReleaseComplete() event posted to MediaCodec looper thread.
202
203 static const AString kCodecName{"test.codec"};
204 static const AString kCodecOwner{"nobody"};
205 static const AString kMediaType{"video/x-test"};
206
207 enum {
208 kInit,
209 kShutdownFromReclaimReceived,
210 kReleaseCalled,
211 };
212 Counter counter{kInit};
213 sp<MockCodec> mockCodec;
214 std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase =
215 [&mockCodec, &counter](const AString &, const char *) {
216 mockCodec = new MockCodec([](const std::shared_ptr<MockBufferChannel> &) {
217 // No mock setup, as we don't expect any buffer operations
218 // in this scenario.
219 });
220 ON_CALL(*mockCodec, initiateAllocateComponent(_))
221 .WillByDefault([mockCodec](const sp<AMessage> &) {
222 mockCodec->callback()->onComponentAllocated(kCodecName.c_str());
223 });
224 ON_CALL(*mockCodec, initiateShutdown(_))
225 .WillByDefault([mockCodec, &counter](bool) {
226 int32_t stage = counter.wait(kInit, kReleaseCalled);
227 if (stage == kInit) {
228 // Mark that 2) happened, so test can proceed to 3)
229 counter.advance();
230 } else if (stage == kReleaseCalled) {
231 // Handle 6)
232 mockCodec->callback()->onReleaseCompleted();
233 }
234 });
235 return mockCodec;
236 };
237
238 sp<ALooper> looper{new ALooper};
239 sp<MediaCodec> codec = SetupMediaCodec(
240 kCodecOwner, kCodecName, kMediaType, looper, getCodecBase);
241 ASSERT_NE(nullptr, codec) << "Codec must not be null";
242 ASSERT_NE(nullptr, mockCodec) << "MockCodec must not be null";
243 std::promise<void> reclaimCompleted;
244 std::promise<void> releaseCompleted;
245 Counter threadExitCounter;
246 std::thread([codec, &reclaimCompleted]{
247 // Simulate ResourceManager thread. Proceed with 1)
248 MediaTestHelper::Reclaim(codec, true /* force */);
249 reclaimCompleted.set_value();
250 }).detach();
251 std::thread([codec, &counter, &releaseCompleted]{
252 // Simulate client thread. Wait until 2) is complete
253 (void)counter.wait(kShutdownFromReclaimReceived);
254 // Proceed to 3), and mark that 5) is ready to happen.
255 // NOTE: it's difficult to pinpoint when 4) happens, so we will sleep
256 // to meet the timing.
257 counter.advance();
258 codec->release();
259 releaseCompleted.set_value();
260 }).detach();
261 std::thread([mockCodec, &counter]{
262 // Simulate component thread. Wait until 3) is complete
263 (void)counter.wait(kReleaseCalled);
264 // We want 4) to complete before moving forward, but it is hard to
265 // wait for this exact event. Just sleep so that the other thread can
266 // proceed and complete 4).
267 std::this_thread::sleep_for(std::chrono::milliseconds(100));
268 // Proceed to 5).
269 mockCodec->callback()->onReleaseCompleted();
270 }).detach();
271 EXPECT_EQ(
272 std::future_status::ready,
273 reclaimCompleted.get_future().wait_for(std::chrono::seconds(5)))
274 << "reclaim timed out";
275 EXPECT_EQ(
276 std::future_status::ready,
277 releaseCompleted.get_future().wait_for(std::chrono::seconds(5)))
278 << "release timed out";
279 looper->stop();
280 }
281
TEST(MediaCodecTest,ErrorWhileStopping)282 TEST(MediaCodecTest, ErrorWhileStopping) {
283 // Test scenario:
284 //
285 // 1) Client thread calls stop(); MediaCodec looper thread calls
286 // initiateShutdown(); shutdown is being handled at the component thread.
287 // 2) Error occurred, but the shutdown operation is still being done.
288 // 3) Another error occurred during the shutdown operation.
289 // 4) MediaCodec looper thread handles the error.
290 // 5) Client releases the codec upon the error; previous shutdown is still
291 // going on.
292 // 6) Component thread completes shutdown and posts onStopCompleted();
293 // Shutdown from release also completes.
294
295 static const AString kCodecName{"test.codec"};
296 static const AString kCodecOwner{"nobody"};
297 static const AString kMediaType{"video/x-test"};
298
299 sp<MockCodec> mockCodec;
300 std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase =
301 [&mockCodec](const AString &, const char *) {
302 mockCodec = new MockCodec([](const std::shared_ptr<MockBufferChannel> &) {
303 // No mock setup, as we don't expect any buffer operations
304 // in this scenario.
305 });
306 ON_CALL(*mockCodec, initiateAllocateComponent(_))
307 .WillByDefault([mockCodec](const sp<AMessage> &) {
308 mockCodec->callback()->onComponentAllocated(kCodecName.c_str());
309 });
310 ON_CALL(*mockCodec, initiateConfigureComponent(_))
311 .WillByDefault([mockCodec](const sp<AMessage> &msg) {
312 mockCodec->callback()->onComponentConfigured(
313 msg->dup(), msg->dup());
314 });
315 ON_CALL(*mockCodec, initiateStart())
316 .WillByDefault([mockCodec]() {
317 mockCodec->callback()->onStartCompleted();
318 });
319 ON_CALL(*mockCodec, initiateShutdown(true))
320 .WillByDefault([mockCodec](bool) {
321 // 2)
322 mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
323 // 3)
324 mockCodec->callback()->onError(UNKNOWN_ERROR, ACTION_CODE_FATAL);
325 });
326 ON_CALL(*mockCodec, initiateShutdown(false))
327 .WillByDefault([mockCodec](bool) {
328 // Previous stop finished now.
329 mockCodec->callback()->onStopCompleted();
330 // Release also finished.
331 mockCodec->callback()->onReleaseCompleted();
332 });
333 return mockCodec;
334 };
335
336 sp<ALooper> looper{new ALooper};
337 sp<MediaCodec> codec = SetupMediaCodec(
338 kCodecOwner, kCodecName, kMediaType, looper, getCodecBase);
339 ASSERT_NE(nullptr, codec) << "Codec must not be null";
340 ASSERT_NE(nullptr, mockCodec) << "MockCodec must not be null";
341
342 codec->configure(new AMessage, nullptr, nullptr, 0);
343 codec->start();
344 // stop() will fail because of the error
345 EXPECT_NE(OK, codec->stop());
346 // sleep here so that the looper thread can handle all the errors.
347 std::this_thread::sleep_for(std::chrono::milliseconds(100));
348 // upon receiving the error, client tries to release the codec.
349 codec->release();
350 looper->stop();
351 }
352
TEST(MediaCodecTest,DeadWhileAsyncReleasing)353 TEST(MediaCodecTest, DeadWhileAsyncReleasing) {
354 // Test scenario:
355 //
356 // 1) Client thread calls release(); MediaCodec looper thread calls
357 // initiateShutdown(); shutdown is being handled at the component thread.
358 // 2) Codec service died during the shutdown operation.
359 // 3) MediaCodec looper thread handles the death.
360
361 static const AString kCodecName{"test.codec"};
362 static const AString kCodecOwner{"nobody"};
363 static const AString kMediaType{"video/x-test"};
364
365 sp<MockCodec> mockCodec;
366 std::function<sp<CodecBase>(const AString &name, const char *owner)> getCodecBase =
367 [&mockCodec](const AString &, const char *) {
368 mockCodec = new MockCodec([](const std::shared_ptr<MockBufferChannel> &) {
369 // No mock setup, as we don't expect any buffer operations
370 // in this scenario.
371 });
372 ON_CALL(*mockCodec, initiateAllocateComponent(_))
373 .WillByDefault([mockCodec](const sp<AMessage> &) {
374 mockCodec->callback()->onComponentAllocated(kCodecName.c_str());
375 });
376 ON_CALL(*mockCodec, initiateShutdown(_))
377 .WillByDefault([mockCodec](bool) {
378 // 2)
379 mockCodec->callback()->onError(DEAD_OBJECT, ACTION_CODE_FATAL);
380 // Codec service has died, no callback.
381 });
382 return mockCodec;
383 };
384
385 sp<ALooper> looper{new ALooper};
386 sp<MediaCodec> codec = SetupMediaCodec(
387 kCodecOwner, kCodecName, kMediaType, looper, getCodecBase);
388 ASSERT_NE(nullptr, codec) << "Codec must not be null";
389 ASSERT_NE(nullptr, mockCodec) << "MockCodec must not be null";
390
391 codec->releaseAsync(new AMessage);
392 // sleep here so that the looper thread can handle the error
393 std::this_thread::sleep_for(std::chrono::milliseconds(100));
394 looper->stop();
395 }
396