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