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