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 //#define LOG_NDEBUG 0
18 #define LOG_TAG "NativeCodecDecoderTest"
19 #include <log/log.h>
20 #include <android/native_window_jni.h>
21 #include <NdkMediaExtractor.h>
22 #include <jni.h>
23 #include <sys/stat.h>
24
25 #include <array>
26 #include <fstream>
27
28 #include "NativeCodecTestBase.h"
29 #include "NativeMediaCommon.h"
30
31 class CodecDecoderTest final : public CodecTestBase {
32 private:
33 uint8_t* mRefData;
34 size_t mRefLength;
35 AMediaExtractor* mExtractor;
36 AMediaFormat* mInpDecFormat;
37 AMediaFormat* mInpDecDupFormat;
38 std::vector<std::pair<void*, size_t>> mCsdBuffers;
39 int mCurrCsdIdx;
40 ANativeWindow* mWindow;
41
42 void setUpAudioReference(const char* refFile);
43 void deleteReference();
44 bool setUpExtractor(const char* srcFile);
45 void deleteExtractor();
46 bool configureCodec(AMediaFormat* format, bool isAsync, bool signalEOSWithLastFrame,
47 bool isEncoder) override;
48 bool enqueueInput(size_t bufferIndex) override;
49 bool dequeueOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo) override;
50 bool queueCodecConfig();
51 bool enqueueCodecConfig(int32_t bufferIndex);
52 bool decodeToMemory(const char* decoder, AMediaFormat* format, int frameLimit,
53 OutputManager* ref, int64_t pts, SeekMode mode);
54
55 public:
56 explicit CodecDecoderTest(const char* mime, ANativeWindow* window);
57 ~CodecDecoderTest();
58
59 bool testSimpleDecode(const char* decoder, const char* testFile, const char* refFile,
60 float rmsError);
61 bool testFlush(const char* decoder, const char* testFile);
62 bool testOnlyEos(const char* decoder, const char* testFile);
63 bool testSimpleDecodeQueueCSD(const char* decoder, const char* testFile);
64 };
65
CodecDecoderTest(const char * mime,ANativeWindow * window)66 CodecDecoderTest::CodecDecoderTest(const char* mime, ANativeWindow* window)
67 : CodecTestBase(mime),
68 mRefData(nullptr),
69 mRefLength(0),
70 mExtractor(nullptr),
71 mInpDecFormat(nullptr),
72 mInpDecDupFormat(nullptr),
73 mCurrCsdIdx(0),
74 mWindow{window} {}
75
~CodecDecoderTest()76 CodecDecoderTest::~CodecDecoderTest() {
77 deleteReference();
78 deleteExtractor();
79 }
80
setUpAudioReference(const char * refFile)81 void CodecDecoderTest::setUpAudioReference(const char* refFile) {
82 FILE* fp = fopen(refFile, "rbe");
83 struct stat buf {};
84 if (fp && !fstat(fileno(fp), &buf)) {
85 deleteReference();
86 mRefLength = buf.st_size;
87 mRefData = new uint8_t[mRefLength];
88 fread(mRefData, sizeof(uint8_t), mRefLength, fp);
89 } else {
90 ALOGE("unable to open input file %s", refFile);
91 }
92 if (fp) fclose(fp);
93 }
94
deleteReference()95 void CodecDecoderTest::deleteReference() {
96 if (mRefData) {
97 delete[] mRefData;
98 mRefData = nullptr;
99 }
100 mRefLength = 0;
101 }
102
setUpExtractor(const char * srcFile)103 bool CodecDecoderTest::setUpExtractor(const char* srcFile) {
104 FILE* fp = fopen(srcFile, "rbe");
105 struct stat buf {};
106 if (fp && !fstat(fileno(fp), &buf)) {
107 deleteExtractor();
108 mExtractor = AMediaExtractor_new();
109 media_status_t res =
110 AMediaExtractor_setDataSourceFd(mExtractor, fileno(fp), 0, buf.st_size);
111 if (res != AMEDIA_OK) {
112 deleteExtractor();
113 } else {
114 for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(mExtractor);
115 trackID++) {
116 AMediaFormat* currFormat = AMediaExtractor_getTrackFormat(mExtractor, trackID);
117 const char* mime = nullptr;
118 AMediaFormat_getString(currFormat, AMEDIAFORMAT_KEY_MIME, &mime);
119 if (mime && strcmp(mMime, mime) == 0) {
120 AMediaExtractor_selectTrack(mExtractor, trackID);
121 if (!mIsAudio) {
122 AMediaFormat_setInt32(currFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT,
123 COLOR_FormatYUV420Flexible);
124 }
125 mInpDecFormat = currFormat;
126 break;
127 }
128 AMediaFormat_delete(currFormat);
129 }
130 }
131 }
132 if (fp) fclose(fp);
133 return mInpDecFormat != nullptr;
134 }
135
deleteExtractor()136 void CodecDecoderTest::deleteExtractor() {
137 if (mExtractor) {
138 AMediaExtractor_delete(mExtractor);
139 mExtractor = nullptr;
140 }
141 if (mInpDecFormat) {
142 AMediaFormat_delete(mInpDecFormat);
143 mInpDecFormat = nullptr;
144 }
145 if (mInpDecDupFormat) {
146 AMediaFormat_delete(mInpDecDupFormat);
147 mInpDecDupFormat = nullptr;
148 }
149 }
150
configureCodec(AMediaFormat * format,bool isAsync,bool signalEOSWithLastFrame,bool isEncoder)151 bool CodecDecoderTest::configureCodec(AMediaFormat* format, bool isAsync,
152 bool signalEOSWithLastFrame, bool isEncoder) {
153 resetContext(isAsync, signalEOSWithLastFrame);
154 CHECK_STATUS(mAsyncHandle.setCallBack(mCodec, isAsync),
155 "AMediaCodec_setAsyncNotifyCallback failed");
156 CHECK_STATUS(AMediaCodec_configure(mCodec, format, mWindow, nullptr,
157 isEncoder ? AMEDIACODEC_CONFIGURE_FLAG_ENCODE : 0),
158 "AMediaCodec_configure failed");
159 return true;
160 }
161
enqueueCodecConfig(int32_t bufferIndex)162 bool CodecDecoderTest::enqueueCodecConfig(int32_t bufferIndex) {
163 size_t bufSize;
164 uint8_t* buf = AMediaCodec_getInputBuffer(mCodec, bufferIndex, &bufSize);
165 if (buf == nullptr) {
166 ALOGE("AMediaCodec_getInputBuffer failed");
167 return false;
168 }
169 void* csdBuffer = mCsdBuffers[mCurrCsdIdx].first;
170 size_t csdSize = mCsdBuffers[mCurrCsdIdx].second;
171 if (bufSize < csdSize) {
172 ALOGE("csd exceeds input buffer size, csdSize: %zu bufSize: %zu", csdSize, bufSize);
173 return false;
174 }
175 memcpy((void*)buf, csdBuffer, csdSize);
176 uint32_t flags = AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG;
177 CHECK_STATUS(AMediaCodec_queueInputBuffer(mCodec, bufferIndex, 0, csdSize, 0, flags),
178 "AMediaCodec_queueInputBuffer failed");
179 return !hasSeenError();
180 }
181
enqueueInput(size_t bufferIndex)182 bool CodecDecoderTest::enqueueInput(size_t bufferIndex) {
183 if (AMediaExtractor_getSampleSize(mExtractor) < 0) {
184 return enqueueEOS(bufferIndex);
185 } else {
186 uint32_t flags = 0;
187 size_t bufSize;
188 uint8_t* buf = AMediaCodec_getInputBuffer(mCodec, bufferIndex, &bufSize);
189 if (buf == nullptr) {
190 ALOGE("AMediaCodec_getInputBuffer failed");
191 return false;
192 }
193 ssize_t size = AMediaExtractor_getSampleSize(mExtractor);
194 int64_t pts = AMediaExtractor_getSampleTime(mExtractor);
195 if (size > bufSize) {
196 ALOGE("extractor sample size exceeds codec input buffer size %zu %zu", size, bufSize);
197 return false;
198 }
199 if (size != AMediaExtractor_readSampleData(mExtractor, buf, bufSize)) {
200 ALOGE("AMediaExtractor_readSampleData failed");
201 return false;
202 }
203 if (!AMediaExtractor_advance(mExtractor) && mSignalEOSWithLastFrame) {
204 flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
205 mSawInputEOS = true;
206 }
207 CHECK_STATUS(AMediaCodec_queueInputBuffer(mCodec, bufferIndex, 0, size, pts, flags),
208 "AMediaCodec_queueInputBuffer failed");
209 ALOGV("input: id: %zu size: %zu pts: %d flags: %d", bufferIndex, size, (int)pts, flags);
210 if (size > 0) {
211 mOutputBuff->saveInPTS(pts);
212 mInputCount++;
213 }
214 }
215 return !hasSeenError();
216 }
217
dequeueOutput(size_t bufferIndex,AMediaCodecBufferInfo * info)218 bool CodecDecoderTest::dequeueOutput(size_t bufferIndex, AMediaCodecBufferInfo* info) {
219 if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) {
220 mSawOutputEOS = true;
221 }
222 if (info->size > 0 && (info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) {
223 if (mSaveToMem) {
224 size_t buffSize;
225 uint8_t* buf = AMediaCodec_getOutputBuffer(mCodec, bufferIndex, &buffSize);
226 if (mIsAudio) mOutputBuff->saveToMemory(buf, info);
227 else mOutputBuff->saveChecksum(buf, info);
228 }
229 mOutputBuff->saveOutPTS(info->presentationTimeUs);
230 mOutputCount++;
231 }
232 ALOGV("output: id: %zu size: %d pts: %d flags: %d", bufferIndex, info->size,
233 (int)info->presentationTimeUs, info->flags);
234 CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, mWindow != nullptr),
235 "AMediaCodec_releaseOutputBuffer failed");
236 return !hasSeenError();
237 }
238
queueCodecConfig()239 bool CodecDecoderTest::queueCodecConfig() {
240 bool isOk = true;
241 if (mIsCodecInAsyncMode) {
242 for (mCurrCsdIdx = 0; !hasSeenError() && isOk && mCurrCsdIdx < mCsdBuffers.size();
243 mCurrCsdIdx++) {
244 callbackObject element = mAsyncHandle.getInput();
245 if (element.bufferIndex >= 0) {
246 isOk = enqueueCodecConfig(element.bufferIndex);
247 }
248 }
249 } else {
250 int bufferIndex;
251 for (mCurrCsdIdx = 0; isOk && mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
252 bufferIndex = AMediaCodec_dequeueInputBuffer(mCodec, -1);
253 if (bufferIndex >= 0) {
254 isOk = enqueueCodecConfig(bufferIndex);
255 } else {
256 ALOGE("unexpected return value from *_dequeueInputBuffer: %d", (int)bufferIndex);
257 return false;
258 }
259 }
260 }
261 return !hasSeenError() && isOk;
262 }
263
decodeToMemory(const char * decoder,AMediaFormat * format,int frameLimit,OutputManager * ref,int64_t pts,SeekMode mode)264 bool CodecDecoderTest::decodeToMemory(const char* decoder, AMediaFormat* format, int frameLimit,
265 OutputManager* ref, int64_t pts, SeekMode mode) {
266 mSaveToMem = (mWindow == nullptr);
267 mOutputBuff = ref;
268 AMediaExtractor_seekTo(mExtractor, pts, mode);
269 mCodec = AMediaCodec_createCodecByName(decoder);
270 if (!mCodec) {
271 ALOGE("unable to create codec %s", decoder);
272 return false;
273 }
274 if (!configureCodec(format, false, true, false)) return false;
275 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
276 if (!doWork(frameLimit)) return false;
277 if (!queueEOS()) return false;
278 if (!waitForAllOutputs()) return false;
279 CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
280 CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed");
281 mCodec = nullptr;
282 mSaveToMem = false;
283 return !hasSeenError();
284 }
285
testSimpleDecode(const char * decoder,const char * testFile,const char * refFile,float rmsError)286 bool CodecDecoderTest::testSimpleDecode(const char* decoder, const char* testFile,
287 const char* refFile, float rmsError) {
288 bool isPass = true;
289 if (!setUpExtractor(testFile)) return false;
290 mSaveToMem = (mWindow == nullptr);
291 auto ref = &mRefBuff;
292 auto test = &mTestBuff;
293 const bool boolStates[]{true, false};
294 int loopCounter = 0;
295 for (auto eosType : boolStates) {
296 if (!isPass) break;
297 for (auto isAsync : boolStates) {
298 if (!isPass) break;
299 bool validateFormat = true;
300 char log[1000];
301 snprintf(log, sizeof(log), "codec: %s, file: %s, async mode: %s, eos type: %s:: \n",
302 decoder, testFile, (isAsync ? "async" : "sync"),
303 (eosType ? "eos with last frame" : "eos separate"));
304 mOutputBuff = loopCounter == 0 ? ref : test;
305 mOutputBuff->reset();
306 AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
307 /* TODO(b/147348711) */
308 /* Instead of create and delete codec at every iteration, we would like to create
309 * once and use it for all iterations and delete before exiting */
310 mCodec = AMediaCodec_createCodecByName(decoder);
311 if (!mCodec) {
312 ALOGE("unable to create codec %s", decoder);
313 return false;
314 }
315 char* name = nullptr;
316 if (AMEDIA_OK == AMediaCodec_getName(mCodec, &name)) {
317 if (!name || strcmp(name, decoder) != 0) {
318 ALOGE("%s error codec-name act/got: %s/%s", log, name, decoder);
319 if (name) AMediaCodec_releaseName(mCodec, name);
320 return false;
321 }
322 } else {
323 ALOGE("AMediaCodec_getName failed unexpectedly");
324 return false;
325 }
326 if (name) AMediaCodec_releaseName(mCodec, name);
327 if (!configureCodec(mInpDecFormat, isAsync, eosType, false)) return false;
328 AMediaFormat* decFormat = AMediaCodec_getOutputFormat(mCodec);
329 if (isFormatSimilar(mInpDecFormat, decFormat)) {
330 ALOGD("Input format is same as default for format for %s", decoder);
331 validateFormat = false;
332 }
333 AMediaFormat_delete(decFormat);
334 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
335 if (!doWork(INT32_MAX)) return false;
336 if (!queueEOS()) return false;
337 if (!waitForAllOutputs()) return false;
338 CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
339 CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed");
340 mCodec = nullptr;
341 CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
342 CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
343 CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
344 CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
345 CHECK_ERR(
346 loopCounter == 0 && mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
347 log, "pts is not strictly increasing", isPass);
348 CHECK_ERR(loopCounter == 0 && !mIsAudio &&
349 (!ref->isOutPtsListIdenticalToInpPtsList(false)),
350 log, "input pts list and output pts list are not identical", isPass);
351 if (validateFormat) {
352 if (mIsCodecInAsyncMode ? !mAsyncHandle.hasOutputFormatChanged()
353 : !mSignalledOutFormatChanged) {
354 ALOGE(log, "not received format change");
355 isPass = false;
356 } else if (!isFormatSimilar(mInpDecFormat, mIsCodecInAsyncMode
357 ? mAsyncHandle.getOutputFormat()
358 : mOutFormat)) {
359 ALOGE(log, "configured format and output format are not similar");
360 isPass = false;
361 }
362 }
363 loopCounter++;
364 }
365 }
366 if (mSaveToMem && refFile && rmsError >= 0) {
367 setUpAudioReference(refFile);
368 float error = ref->getRmsError(mRefData, mRefLength);
369 if (error > rmsError) {
370 isPass = false;
371 ALOGE("rms error too high for file %s, act/exp: %f/%f", testFile, error, rmsError);
372 }
373 }
374 return isPass;
375 }
376
testFlush(const char * decoder,const char * testFile)377 bool CodecDecoderTest::testFlush(const char* decoder, const char* testFile) {
378 bool isPass = true;
379 if (!setUpExtractor(testFile)) return false;
380 mCsdBuffers.clear();
381 for (int i = 0;; i++) {
382 char csdName[16];
383 void* csdBuffer;
384 size_t csdSize;
385 snprintf(csdName, sizeof(csdName), "csd-%d", i);
386 if (AMediaFormat_getBuffer(mInpDecFormat, csdName, &csdBuffer, &csdSize)) {
387 mCsdBuffers.push_back(std::make_pair(csdBuffer, csdSize));
388 } else break;
389 }
390 const int64_t pts = 500000;
391 const SeekMode mode = AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC;
392 auto ref = &mRefBuff;
393 if (!decodeToMemory(decoder, mInpDecFormat, INT32_MAX, ref, pts, mode)) {
394 ALOGE("decodeToMemory failed for file: %s codec: %s", testFile, decoder);
395 return false;
396 }
397 CHECK_ERR(mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)), "",
398 "pts is not strictly increasing", isPass);
399 CHECK_ERR(!mIsAudio && (!ref->isOutPtsListIdenticalToInpPtsList(false)), "",
400 "input pts list and output pts list are not identical", isPass);
401 if (!isPass) return false;
402
403 auto test = &mTestBuff;
404 mOutputBuff = test;
405 const bool boolStates[]{true, false};
406 for (auto isAsync : boolStates) {
407 if (!isPass) break;
408 char log[1000];
409 snprintf(log, sizeof(log), "codec: %s, file: %s, async mode: %s:: \n", decoder, testFile,
410 (isAsync ? "async" : "sync"));
411 /* TODO(b/147348711) */
412 /* Instead of create and delete codec at every iteration, we would like to create
413 * once and use it for all iterations and delete before exiting */
414 mCodec = AMediaCodec_createCodecByName(decoder);
415 if (!mCodec) {
416 ALOGE("unable to create codec %s", decoder);
417 return false;
418 }
419 AMediaExtractor_seekTo(mExtractor, 0, mode);
420 if (!configureCodec(mInpDecFormat, isAsync, true, false)) return false;
421 AMediaFormat* defFormat = AMediaCodec_getOutputFormat(mCodec);
422 bool validateFormat = true;
423 if (isFormatSimilar(mInpDecFormat, defFormat)) {
424 ALOGD("Input format is same as default for format for %s", decoder);
425 validateFormat = false;
426 }
427 AMediaFormat_delete(defFormat);
428 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
429
430 /* test flush in running state before queuing input */
431 if (!flushCodec()) return false;
432 if (mIsCodecInAsyncMode) {
433 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
434 }
435 if (!queueCodecConfig()) return false; /* flushed codec too soon, resubmit csd */
436 if (!doWork(1)) return false;
437
438 if (!flushCodec()) return false;
439 if (mIsCodecInAsyncMode) {
440 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
441 }
442 if (!queueCodecConfig()) return false; /* flushed codec too soon, resubmit csd */
443 AMediaExtractor_seekTo(mExtractor, 0, mode);
444 test->reset();
445 if (!doWork(23)) return false;
446 CHECK_ERR(!test->isPtsStrictlyIncreasing(mPrevOutputPts), "",
447 "pts is not strictly increasing", isPass);
448
449 /* test flush in running state */
450 if (!flushCodec()) return false;
451 if (mIsCodecInAsyncMode) {
452 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
453 }
454 mSaveToMem = (mWindow == nullptr);
455 test->reset();
456 AMediaExtractor_seekTo(mExtractor, pts, mode);
457 if (!doWork(INT32_MAX)) return false;
458 if (!queueEOS()) return false;
459 if (!waitForAllOutputs()) return false;
460 CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
461 CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
462 CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
463 CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass);
464 if (!isPass) continue;
465
466 /* test flush in eos state */
467 if (!flushCodec()) return false;
468 if (mIsCodecInAsyncMode) {
469 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
470 }
471 test->reset();
472 AMediaExtractor_seekTo(mExtractor, pts, mode);
473 if (!doWork(INT32_MAX)) return false;
474 if (!queueEOS()) return false;
475 if (!waitForAllOutputs()) return false;
476 CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
477 CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed");
478 mCodec = nullptr;
479 CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
480 CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
481 CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
482 CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass);
483 if (validateFormat) {
484 if (mIsCodecInAsyncMode ? !mAsyncHandle.hasOutputFormatChanged()
485 : !mSignalledOutFormatChanged) {
486 ALOGE(log, "not received format change");
487 isPass = false;
488 } else if (!isFormatSimilar(mInpDecFormat, mIsCodecInAsyncMode
489 ? mAsyncHandle.getOutputFormat()
490 : mOutFormat)) {
491 ALOGE(log, "configured format and output format are not similar");
492 isPass = false;
493 }
494 }
495 mSaveToMem = false;
496 }
497 return isPass;
498 }
499
testOnlyEos(const char * decoder,const char * testFile)500 bool CodecDecoderTest::testOnlyEos(const char* decoder, const char* testFile) {
501 bool isPass = true;
502 if (!setUpExtractor(testFile)) return false;
503 mSaveToMem = (mWindow == nullptr);
504 auto ref = &mRefBuff;
505 auto test = &mTestBuff;
506 const bool boolStates[]{true, false};
507 int loopCounter = 0;
508 for (auto isAsync : boolStates) {
509 if (!isPass) break;
510 char log[1000];
511 snprintf(log, sizeof(log), "codec: %s, file: %s, async mode: %s:: \n", decoder, testFile,
512 (isAsync ? "async" : "sync"));
513 mOutputBuff = loopCounter == 0 ? ref : test;
514 mOutputBuff->reset();
515 /* TODO(b/147348711) */
516 /* Instead of create and delete codec at every iteration, we would like to create
517 * once and use it for all iterations and delete before exiting */
518 mCodec = AMediaCodec_createCodecByName(decoder);
519 if (!mCodec) {
520 ALOGE("unable to create codec %s", decoder);
521 return false;
522 }
523 if (!configureCodec(mInpDecFormat, isAsync, false, false)) return false;
524 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
525 if (!queueEOS()) return false;
526 if (!waitForAllOutputs()) return false;
527 CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
528 CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed");
529 mCodec = nullptr;
530 CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
531 CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
532 CHECK_ERR(loopCounter == 0 && mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
533 log, "pts is not strictly increasing", isPass);
534 CHECK_ERR(loopCounter == 0 && !mIsAudio && (!ref->isOutPtsListIdenticalToInpPtsList(false)),
535 log, "input pts list and output pts list are not identical", isPass);
536 loopCounter++;
537 }
538 return isPass;
539 }
540
testSimpleDecodeQueueCSD(const char * decoder,const char * testFile)541 bool CodecDecoderTest::testSimpleDecodeQueueCSD(const char* decoder, const char* testFile) {
542 bool isPass = true;
543 if (!setUpExtractor(testFile)) return false;
544 std::vector<AMediaFormat*> formats;
545 formats.push_back(mInpDecFormat);
546 mInpDecDupFormat = AMediaFormat_new();
547 AMediaFormat_copy(mInpDecDupFormat, mInpDecFormat);
548 formats.push_back(mInpDecDupFormat);
549 mCsdBuffers.clear();
550 for (int i = 0;; i++) {
551 char csdName[16];
552 void* csdBuffer;
553 size_t csdSize;
554 snprintf(csdName, sizeof(csdName), "csd-%d", i);
555 if (AMediaFormat_getBuffer(mInpDecDupFormat, csdName, &csdBuffer, &csdSize)) {
556 mCsdBuffers.push_back(std::make_pair(csdBuffer, csdSize));
557 AMediaFormat_setBuffer(mInpDecFormat, csdName, nullptr, 0);
558 } else break;
559 }
560
561 const bool boolStates[]{true, false};
562 mSaveToMem = true;
563 auto ref = &mRefBuff;
564 auto test = &mTestBuff;
565 int loopCounter = 0;
566 for (int i = 0; i < formats.size() && isPass; i++) {
567 auto fmt = formats[i];
568 for (auto eosType : boolStates) {
569 if (!isPass) break;
570 for (auto isAsync : boolStates) {
571 if (!isPass) break;
572 bool validateFormat = true;
573 char log[1000];
574 snprintf(log, sizeof(log), "codec: %s, file: %s, async mode: %s, eos type: %s:: \n",
575 decoder, testFile, (isAsync ? "async" : "sync"),
576 (eosType ? "eos with last frame" : "eos separate"));
577 mOutputBuff = loopCounter == 0 ? ref : test;
578 mOutputBuff->reset();
579 /* TODO(b/147348711) */
580 /* Instead of create and delete codec at every iteration, we would like to create
581 * once and use it for all iterations and delete before exiting */
582 mCodec = AMediaCodec_createCodecByName(decoder);
583 if (!mCodec) {
584 ALOGE("unable to create codec");
585 return false;
586 }
587 AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
588 if (!configureCodec(fmt, isAsync, eosType, false)) return false;
589 AMediaFormat* defFormat = AMediaCodec_getOutputFormat(mCodec);
590 if (isFormatSimilar(defFormat, mInpDecFormat)) {
591 ALOGD("Input format is same as default for format for %s", decoder);
592 validateFormat = false;
593 }
594 AMediaFormat_delete(defFormat);
595 CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed");
596 /* formats[0] doesn't contain csd-data, so queuing csd separately, formats[1]
597 * contain csd-data */
598 if (i == 0 && !queueCodecConfig()) return false;
599 if (!doWork(INT32_MAX)) return false;
600 if (!queueEOS()) return false;
601 if (!waitForAllOutputs()) return false;
602 CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed");
603 CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed");
604 mCodec = nullptr;
605 CHECK_ERR(hasSeenError(), log, "has seen error", isPass);
606 CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass);
607 CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass);
608 CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass);
609 CHECK_ERR(loopCounter == 0 && mIsAudio &&
610 (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)),
611 log, "pts is not strictly increasing", isPass);
612 CHECK_ERR(loopCounter == 0 && !mIsAudio &&
613 (!ref->isOutPtsListIdenticalToInpPtsList(false)),
614 log, "input pts list and output pts list are not identical", isPass);
615 if (validateFormat) {
616 if (mIsCodecInAsyncMode ? !mAsyncHandle.hasOutputFormatChanged()
617 : !mSignalledOutFormatChanged) {
618 ALOGE(log, "not received format change");
619 isPass = false;
620 } else if (!isFormatSimilar(mInpDecFormat,
621 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat()
622 : mOutFormat)) {
623 ALOGE(log, "configured format and output format are not similar");
624 isPass = false;
625 }
626 }
627 loopCounter++;
628 }
629 }
630 }
631 mSaveToMem = false;
632 return isPass;
633 }
634
nativeTestSimpleDecode(JNIEnv * env,jobject,jstring jDecoder,jobject surface,jstring jMime,jstring jtestFile,jstring jrefFile,jfloat jrmsError)635 static jboolean nativeTestSimpleDecode(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
636 jstring jMime, jstring jtestFile, jstring jrefFile,
637 jfloat jrmsError) {
638 const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
639 const char* cMime = env->GetStringUTFChars(jMime, nullptr);
640 const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
641 const char* cRefFile = env->GetStringUTFChars(jrefFile, nullptr);
642 float cRmsError = jrmsError;
643 ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
644 auto* codecDecoderTest = new CodecDecoderTest(cMime, window);
645 bool isPass = codecDecoderTest->testSimpleDecode(cDecoder, cTestFile, cRefFile, cRmsError);
646 delete codecDecoderTest;
647 if (window) {
648 ANativeWindow_release(window);
649 window = nullptr;
650 }
651 env->ReleaseStringUTFChars(jDecoder, cDecoder);
652 env->ReleaseStringUTFChars(jMime, cMime);
653 env->ReleaseStringUTFChars(jtestFile, cTestFile);
654 env->ReleaseStringUTFChars(jrefFile, cRefFile);
655 return static_cast<jboolean>(isPass);
656 }
657
nativeTestOnlyEos(JNIEnv * env,jobject,jstring jDecoder,jstring jMime,jstring jtestFile)658 static jboolean nativeTestOnlyEos(JNIEnv* env, jobject, jstring jDecoder, jstring jMime,
659 jstring jtestFile) {
660 const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
661 const char* cMime = env->GetStringUTFChars(jMime, nullptr);
662 const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
663 auto* codecDecoderTest = new CodecDecoderTest(cMime, nullptr);
664 bool isPass = codecDecoderTest->testOnlyEos(cDecoder, cTestFile);
665 delete codecDecoderTest;
666 env->ReleaseStringUTFChars(jDecoder, cDecoder);
667 env->ReleaseStringUTFChars(jMime, cMime);
668 env->ReleaseStringUTFChars(jtestFile, cTestFile);
669 return static_cast<jboolean>(isPass);
670 }
671
nativeTestFlush(JNIEnv * env,jobject,jstring jDecoder,jobject surface,jstring jMime,jstring jtestFile)672 static jboolean nativeTestFlush(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
673 jstring jMime, jstring jtestFile) {
674 const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
675 const char* cMime = env->GetStringUTFChars(jMime, nullptr);
676 const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
677 ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
678 auto* codecDecoderTest = new CodecDecoderTest(cMime, window);
679 bool isPass = codecDecoderTest->testFlush(cDecoder, cTestFile);
680 delete codecDecoderTest;
681 if (window) {
682 ANativeWindow_release(window);
683 window = nullptr;
684 }
685 env->ReleaseStringUTFChars(jDecoder, cDecoder);
686 env->ReleaseStringUTFChars(jMime, cMime);
687 env->ReleaseStringUTFChars(jtestFile, cTestFile);
688 return static_cast<jboolean>(isPass);
689 }
690
nativeTestSimpleDecodeQueueCSD(JNIEnv * env,jobject,jstring jDecoder,jstring jMime,jstring jtestFile)691 static jboolean nativeTestSimpleDecodeQueueCSD(JNIEnv* env, jobject, jstring jDecoder,
692 jstring jMime, jstring jtestFile) {
693 const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
694 const char* cMime = env->GetStringUTFChars(jMime, nullptr);
695 const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
696 auto codecDecoderTest = new CodecDecoderTest(cMime, nullptr);
697 bool isPass = codecDecoderTest->testSimpleDecodeQueueCSD(cDecoder, cTestFile);
698 delete codecDecoderTest;
699 env->ReleaseStringUTFChars(jDecoder, cDecoder);
700 env->ReleaseStringUTFChars(jMime, cMime);
701 env->ReleaseStringUTFChars(jtestFile, cTestFile);
702 return static_cast<jboolean>(isPass);
703 }
704
registerAndroidMediaV2CtsDecoderTest(JNIEnv * env)705 int registerAndroidMediaV2CtsDecoderTest(JNIEnv* env) {
706 const JNINativeMethod methodTable[] = {
707 {"nativeTestSimpleDecode",
708 "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;Ljava/"
709 "lang/String;F)Z",
710 (void*)nativeTestSimpleDecode},
711 {"nativeTestOnlyEos", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
712 (void*)nativeTestOnlyEos},
713 {"nativeTestFlush",
714 "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;)Z",
715 (void*)nativeTestFlush},
716 {"nativeTestSimpleDecodeQueueCSD",
717 "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;)Z",
718 (void*)nativeTestSimpleDecodeQueueCSD},
719 };
720 jclass c = env->FindClass("android/mediav2/cts/CodecDecoderTest");
721 return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
722 }
723
registerAndroidMediaV2CtsDecoderSurfaceTest(JNIEnv * env)724 int registerAndroidMediaV2CtsDecoderSurfaceTest(JNIEnv* env) {
725 const JNINativeMethod methodTable[] = {
726 {"nativeTestSimpleDecode",
727 "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;Ljava/"
728 "lang/String;F)Z",
729 (void*)nativeTestSimpleDecode},
730 {"nativeTestFlush",
731 "(Ljava/lang/String;Landroid/view/Surface;Ljava/lang/String;Ljava/lang/String;)Z",
732 (void*)nativeTestFlush},
733 };
734 jclass c = env->FindClass("android/mediav2/cts/CodecDecoderSurfaceTest");
735 return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod));
736 }
737