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