• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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             if (mIsAudio) {
256                 mOutputBuff->saveToMemory(buf, info);
257                 mOutputBuff->updateChecksum(buf, info);
258             } else {
259                 AMediaFormat* format =
260                         mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : mOutFormat;
261                 int32_t width, height, stride;
262                 AMediaFormat_getInt32(format, "width", &width);
263                 AMediaFormat_getInt32(format, "height", &height);
264                 AMediaFormat_getInt32(format, "stride", &stride);
265                 mOutputBuff->updateChecksum(buf, info, width, height, stride, mBytesPerSample);
266             }
267         }
268         mOutputBuff->saveOutPTS(info->presentationTimeUs);
269         mOutputCount++;
270     }
271     ALOGV("output: id: %zu  size: %d  pts: %" PRId64 "  flags: %d", bufferIndex, info->size,
272           info->presentationTimeUs, info->flags);
273     RETURN_IF_FAIL(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, mWindow != nullptr),
274                    "AMediaCodec_releaseOutputBuffer failed")
275     return !hasSeenError();
276 }
277 
isTestStateValid()278 bool CodecDecoderTest::isTestStateValid() {
279     if (!CodecTestBase::isTestStateValid()) return false;
280     RETURN_IF_FALSE(mOutputBuff->isPtsStrictlyIncreasing(mPrevOutputPts),
281                     std::string{"Output timestamps are not strictly increasing \n"}.append(
282                             mOutputBuff->getErrorMsg()))
283     RETURN_IF_TRUE(mIsVideo && !mIsInterlaced &&
284                    !mOutputBuff->isOutPtsListIdenticalToInpPtsList(false),
285                    std::string{"Input pts list and Output pts list are not identical \n"}.append(
286                            mOutputBuff->getErrorMsg()))
287     return true;
288 }
289 
isOutputFormatOk(AMediaFormat * configFormat)290 bool CodecDecoderTest::isOutputFormatOk(AMediaFormat* configFormat) {
291     RETURN_IF_TRUE(mIsCodecInAsyncMode ? !mAsyncHandle.hasOutputFormatChanged()
292                                        : !mSignalledOutFormatChanged,
293                    std::string{"Input test file format is not same as default format of component, "
294                                "but test did not receive INFO_OUTPUT_FORMAT_CHANGED signal.\n"})
295     RETURN_IF_TRUE(!isFormatSimilar(configFormat,
296                                     mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat()
297                                                         : mOutFormat),
298                    StringFormat("Configured input format and received output format are not "
299                                 "similar. \nConfigured Input format is :- %s \nReceived Output "
300                                 "format is :- %s \n",
301                                 AMediaFormat_toString(configFormat),
302                                 mIsCodecInAsyncMode ? mAsyncHandle.getOutputFormat() : mOutFormat))
303     return true;
304 }
305 
queueCodecConfig()306 bool CodecDecoderTest::queueCodecConfig() {
307     bool isOk = true;
308     if (mIsCodecInAsyncMode) {
309         for (mCurrCsdIdx = 0; !hasSeenError() && isOk && mCurrCsdIdx < mCsdBuffers.size();
310              mCurrCsdIdx++) {
311             callbackObject element = mAsyncHandle.getInput();
312             if (element.bufferIndex >= 0) {
313                 isOk = enqueueCodecConfig(element.bufferIndex);
314             }
315         }
316     } else {
317         int bufferIndex;
318         for (mCurrCsdIdx = 0; isOk && mCurrCsdIdx < mCsdBuffers.size(); mCurrCsdIdx++) {
319             bufferIndex = AMediaCodec_dequeueInputBuffer(mCodec, -1);
320             if (bufferIndex >= 0) {
321                 isOk = enqueueCodecConfig(bufferIndex);
322             } else {
323                 auto msg = StringFormat("unexpected return value from "
324                                         "AMediaCodec_dequeueInputBuffer: %d \n",
325                                         bufferIndex);
326                 mErrorLogs.append(msg);
327                 ALOGE("%s", msg.c_str());
328                 return false;
329             }
330         }
331     }
332     return !hasSeenError() && isOk;
333 }
334 
decodeToMemory(const char * decoder,AMediaFormat * format,int frameLimit,OutputManager * ref,int64_t pts,SeekMode mode)335 bool CodecDecoderTest::decodeToMemory(const char* decoder, AMediaFormat* format, int frameLimit,
336                                       OutputManager* ref, int64_t pts, SeekMode mode) {
337     mSaveToMem = (mWindow == nullptr);
338     mOutputBuff = ref;
339     AMediaExtractor_seekTo(mExtractor, pts, mode);
340     mCodec = AMediaCodec_createCodecByName(decoder);
341     RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", decoder))
342     if (!configureCodec(format, false, true, false)) return false;
343     RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
344     if (!doWork(frameLimit)) return false;
345     if (!queueEOS()) return false;
346     if (!waitForAllOutputs()) return false;
347     RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
348     RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
349     mCodec = nullptr;
350     mSaveToMem = false;
351     return !hasSeenError();
352 }
353 
testSimpleDecode(const char * decoder,const char * testFile,const char * refFile,int colorFormat,float rmsError,uLong checksum)354 bool CodecDecoderTest::testSimpleDecode(const char* decoder, const char* testFile,
355                                         const char* refFile, int colorFormat, float rmsError,
356                                         uLong checksum) {
357     if (!setUpExtractor(testFile, colorFormat)) return false;
358     mSaveToMem = (mWindow == nullptr);
359     auto ref = mRefBuff;
360     auto test = mTestBuff;
361     const bool boolStates[]{true, false};
362     int loopCounter = 0;
363     for (auto eosType : boolStates) {
364         for (auto isAsync : boolStates) {
365             bool validateFormat = true;
366             mOutputBuff = loopCounter == 0 ? ref : test;
367             mOutputBuff->reset();
368             AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
369             /* TODO(b/149981033) */
370             /* Instead of create and delete codec at every iteration, we would like to create
371              * once and use it for all iterations and delete before exiting */
372             mCodec = AMediaCodec_createCodecByName(decoder);
373             RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", decoder))
374             char* name = nullptr;
375             RETURN_IF_FAIL(AMediaCodec_getName(mCodec, &name), "AMediaCodec_getName failed")
376             RETURN_IF_NULL(name, std::string{"AMediaCodec_getName returned null"})
377             auto res = strcmp(name, decoder) != 0;
378             AMediaCodec_releaseName(mCodec, name);
379             RETURN_IF_TRUE(res, StringFormat("Codec name mismatch act/got: %s/%s", decoder, name))
380             if (!configureCodec(mInpDecFormat, isAsync, eosType, false)) return false;
381             AMediaFormat* decFormat = AMediaCodec_getOutputFormat(mCodec);
382             if (isFormatSimilar(mInpDecFormat, decFormat)) {
383                 ALOGD("Input format is same as default for format for %s", decoder);
384                 validateFormat = false;
385             }
386             AMediaFormat_delete(decFormat);
387             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
388             if (!doWork(INT32_MAX)) return false;
389             if (!queueEOS()) return false;
390             if (!waitForAllOutputs()) return false;
391             RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
392             RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
393             mCodec = nullptr;
394             RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
395                            std::string{"Decoder output is not consistent across runs \n"}.append(
396                                    test->getErrorMsg()))
397             if (validateFormat && !isOutputFormatOk(mInpDecFormat)) {
398                 return false;
399             }
400             RETURN_IF_TRUE(checksum != ref->getChecksum(),
401                            StringFormat("sdk output and ndk output for same configuration is not "
402                                         "identical. \n sdk buffer output checksum is %lu. \n ndk "
403                                         "buffer output checksum is %lu. \n",
404                                         checksum, ref->getChecksum()))
405             loopCounter++;
406         }
407     }
408     if (mSaveToMem && refFile && rmsError >= 0) {
409         setUpAudioReference(refFile);
410         float currError = ref->getRmsError(mRefData, mRefLength);
411         float errMargin = rmsError * kRmsErrorTolerance;
412         RETURN_IF_TRUE(currError > errMargin,
413                        StringFormat("rms error too high for file %s, ref/exp/got: %f/%f/%f",
414                                     testFile, rmsError, errMargin, currError))
415     }
416     return true;
417 }
418 
testFlush(const char * decoder,const char * testFile,int colorFormat)419 bool CodecDecoderTest::testFlush(const char* decoder, const char* testFile, int colorFormat) {
420     if (!setUpExtractor(testFile, colorFormat)) return false;
421     mCsdBuffers.clear();
422     for (int i = 0;; i++) {
423         char csdName[16];
424         void* csdBuffer;
425         size_t csdSize;
426         snprintf(csdName, sizeof(csdName), "csd-%d", i);
427         if (AMediaFormat_getBuffer(mInpDecFormat, csdName, &csdBuffer, &csdSize)) {
428             mCsdBuffers.emplace_back(std::make_pair(csdBuffer, csdSize));
429         } else break;
430     }
431     const int64_t pts = 500000;
432     const SeekMode mode = AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC;
433     auto ref = mRefBuff;
434     RETURN_IF_FALSE(decodeToMemory(decoder, mInpDecFormat, INT32_MAX, ref, pts, mode),
435                     StringFormat("decodeToMemory failed for file: %s codec: %s", testFile, decoder))
436     auto test = mTestBuff;
437     mOutputBuff = test;
438     const bool boolStates[]{true, false};
439     for (auto isAsync : boolStates) {
440         if (isAsync) continue;  // TODO(b/147576107)
441         /* TODO(b/149981033) */
442         /* Instead of create and delete codec at every iteration, we would like to create
443          * once and use it for all iterations and delete before exiting */
444         mCodec = AMediaCodec_createCodecByName(decoder);
445         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", decoder))
446         AMediaExtractor_seekTo(mExtractor, 0, mode);
447         if (!configureCodec(mInpDecFormat, isAsync, true, false)) return false;
448         AMediaFormat* defFormat = AMediaCodec_getOutputFormat(mCodec);
449         bool validateFormat = true;
450         if (isFormatSimilar(mInpDecFormat, defFormat)) {
451             ALOGD("Input format is same as default for format for %s", decoder);
452             validateFormat = false;
453         }
454         AMediaFormat_delete(defFormat);
455         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
456 
457         /* test flush in running state before queuing input */
458         if (!flushCodec()) return false;
459         if (mIsCodecInAsyncMode) {
460             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
461         }
462         if (!queueCodecConfig()) return false; /* flushed codec too soon, resubmit csd */
463         if (!doWork(1)) return false;
464 
465         if (!flushCodec()) return false;
466         if (mIsCodecInAsyncMode) {
467             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
468         }
469         if (!queueCodecConfig()) return false; /* flushed codec too soon, resubmit csd */
470         AMediaExtractor_seekTo(mExtractor, 0, mode);
471         test->reset();
472         if (!doWork(23)) return false;
473         RETURN_IF_TRUE(!mIsInterlaced && !test->isPtsStrictlyIncreasing(mPrevOutputPts),
474                        std::string{"Output timestamps are not strictly increasing \n"}.append(
475                                test->getErrorMsg()))
476 
477         /* test flush in running state */
478         if (!flushCodec()) return false;
479         if (mIsCodecInAsyncMode) {
480             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
481         }
482         mSaveToMem = (mWindow == nullptr);
483         test->reset();
484         AMediaExtractor_seekTo(mExtractor, pts, mode);
485         if (!doWork(INT32_MAX)) return false;
486         if (!queueEOS()) return false;
487         if (!waitForAllOutputs()) return false;
488         RETURN_IF_TRUE(isMediaTypeOutputUnAffectedBySeek(mMediaType) && !ref->equals(test),
489                        std::string{"Decoder output is not consistent across runs \n"}.append(
490                                test->getErrorMsg()))
491 
492         /* test flush in eos state */
493         if (!flushCodec()) return false;
494         if (mIsCodecInAsyncMode) {
495             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
496         }
497         test->reset();
498         AMediaExtractor_seekTo(mExtractor, pts, mode);
499         if (!doWork(INT32_MAX)) return false;
500         if (!queueEOS()) return false;
501         if (!waitForAllOutputs()) return false;
502         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
503         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
504         mCodec = nullptr;
505         RETURN_IF_TRUE(isMediaTypeOutputUnAffectedBySeek(mMediaType) && !ref->equals(test),
506                        std::string{"Decoder output is not consistent across runs \n"}.append(
507                                test->getErrorMsg()))
508         if (validateFormat && !isOutputFormatOk(mInpDecFormat)) {
509             return false;
510         }
511         mSaveToMem = false;
512     }
513     return true;
514 }
515 
testOnlyEos(const char * decoder,const char * testFile,int colorFormat)516 bool CodecDecoderTest::testOnlyEos(const char* decoder, const char* testFile, int colorFormat) {
517     if (!setUpExtractor(testFile, colorFormat)) return false;
518     mSaveToMem = (mWindow == nullptr);
519     auto ref = mRefBuff;
520     auto test = mTestBuff;
521     const bool boolStates[]{true, false};
522     int loopCounter = 0;
523     for (auto isAsync : boolStates) {
524         mOutputBuff = loopCounter == 0 ? ref : test;
525         mOutputBuff->reset();
526         /* TODO(b/149981033) */
527         /* Instead of create and delete codec at every iteration, we would like to create
528          * once and use it for all iterations and delete before exiting */
529         mCodec = AMediaCodec_createCodecByName(decoder);
530         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", decoder))
531         if (!configureCodec(mInpDecFormat, isAsync, false, false)) return false;
532         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
533         if (!queueEOS()) return false;
534         if (!waitForAllOutputs()) return false;
535         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
536         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
537         mCodec = nullptr;
538         RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
539                        std::string{"Decoder output is not consistent across runs \n"}.append(
540                                test->getErrorMsg()))
541         loopCounter++;
542     }
543     return true;
544 }
545 
testSimpleDecodeQueueCSD(const char * decoder,const char * testFile,int colorFormat)546 bool CodecDecoderTest::testSimpleDecodeQueueCSD(const char* decoder, const char* testFile,
547                                                 int colorFormat) {
548     if (!setUpExtractor(testFile, colorFormat)) return false;
549     std::vector<AMediaFormat*> formats;
550     formats.push_back(mInpDecFormat);
551     mInpDecDupFormat = AMediaFormat_new();
552     AMediaFormat_copy(mInpDecDupFormat, mInpDecFormat);
553     formats.push_back(mInpDecDupFormat);
554     mCsdBuffers.clear();
555     for (int i = 0;; i++) {
556         char csdName[16];
557         void* csdBuffer;
558         size_t csdSize;
559         snprintf(csdName, sizeof(csdName), "csd-%d", i);
560         if (AMediaFormat_getBuffer(mInpDecDupFormat, csdName, &csdBuffer, &csdSize)) {
561             mCsdBuffers.emplace_back(std::make_pair(csdBuffer, csdSize));
562             AMediaFormat_setBuffer(mInpDecFormat, csdName, nullptr, 0);
563         } else break;
564     }
565 
566     const bool boolStates[]{true, false};
567     mSaveToMem = true;
568     auto ref = mRefBuff;
569     auto test = mTestBuff;
570     int loopCounter = 0;
571     for (int i = 0; i < formats.size(); i++) {
572         auto fmt = formats[i];
573         for (auto eosType : boolStates) {
574             for (auto isAsync : boolStates) {
575                 bool validateFormat = true;
576                 mOutputBuff = loopCounter == 0 ? ref : test;
577                 mOutputBuff->reset();
578                 /* TODO(b/149981033) */
579                 /* Instead of create and delete codec at every iteration, we would like to create
580                  * once and use it for all iterations and delete before exiting */
581                 mCodec = AMediaCodec_createCodecByName(decoder);
582                 RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", decoder))
583                 AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
584                 if (!configureCodec(fmt, isAsync, eosType, false)) return false;
585                 AMediaFormat* defFormat = AMediaCodec_getOutputFormat(mCodec);
586                 if (isFormatSimilar(defFormat, mInpDecFormat)) {
587                     ALOGD("Input format is same as default for format for %s", decoder);
588                     validateFormat = false;
589                 }
590                 AMediaFormat_delete(defFormat);
591                 RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
592                 /* formats[0] doesn't contain csd-data, so queuing csd separately, formats[1]
593                  * contain csd-data */
594                 if (i == 0 && !queueCodecConfig()) return false;
595                 if (!doWork(INT32_MAX)) return false;
596                 if (!queueEOS()) return false;
597                 if (!waitForAllOutputs()) return false;
598                 RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
599                 RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
600                 mCodec = nullptr;
601                 RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
602                                std::string{"Decoder output is not consistent across runs \n"}
603                                        .append(test->getErrorMsg()))
604                 if (validateFormat && !isOutputFormatOk(mInpDecFormat)) {
605                     return false;
606                 }
607                 loopCounter++;
608             }
609         }
610     }
611     mSaveToMem = false;
612     return true;
613 }
614 
nativeTestSimpleDecode(JNIEnv * env,jobject,jstring jDecoder,jobject surface,jstring jMediaType,jstring jtestFile,jstring jrefFile,jint jColorFormat,jfloat jrmsError,jlong jChecksum,jobject jRetMsg)615 jboolean nativeTestSimpleDecode(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
616                                 jstring jMediaType, jstring jtestFile, jstring jrefFile,
617                                 jint jColorFormat, jfloat jrmsError, jlong jChecksum,
618                                 jobject jRetMsg) {
619     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
620     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
621     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
622     const char* cRefFile = env->GetStringUTFChars(jrefFile, nullptr);
623     float cRmsError = jrmsError;
624     uLong cChecksum = jChecksum;
625     ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
626     auto* codecDecoderTest = new CodecDecoderTest(cMediaType, window);
627     bool isPass = codecDecoderTest->testSimpleDecode(cDecoder, cTestFile, cRefFile, jColorFormat,
628                                                      cRmsError, cChecksum);
629     std::string msg = isPass ? std::string{} : codecDecoderTest->getErrorMsg();
630     delete codecDecoderTest;
631     jclass clazz = env->GetObjectClass(jRetMsg);
632     jmethodID mId =
633             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
634     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
635     if (window) {
636         ANativeWindow_release(window);
637         window = nullptr;
638     }
639     env->ReleaseStringUTFChars(jDecoder, cDecoder);
640     env->ReleaseStringUTFChars(jMediaType, cMediaType);
641     env->ReleaseStringUTFChars(jtestFile, cTestFile);
642     env->ReleaseStringUTFChars(jrefFile, cRefFile);
643     return static_cast<jboolean>(isPass);
644 }
645 
nativeTestOnlyEos(JNIEnv * env,jobject,jstring jDecoder,jstring jMediaType,jstring jtestFile,jint jColorFormat,jobject jRetMsg)646 jboolean nativeTestOnlyEos(JNIEnv* env, jobject, jstring jDecoder, jstring jMediaType,
647                            jstring jtestFile, jint jColorFormat, jobject jRetMsg) {
648     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
649     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
650     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
651     auto* codecDecoderTest = new CodecDecoderTest(cMediaType, nullptr);
652     bool isPass = codecDecoderTest->testOnlyEos(cDecoder, cTestFile, jColorFormat);
653     std::string msg = isPass ? std::string{} : codecDecoderTest->getErrorMsg();
654     delete codecDecoderTest;
655     jclass clazz = env->GetObjectClass(jRetMsg);
656     jmethodID mId =
657             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
658     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
659     env->ReleaseStringUTFChars(jDecoder, cDecoder);
660     env->ReleaseStringUTFChars(jMediaType, cMediaType);
661     env->ReleaseStringUTFChars(jtestFile, cTestFile);
662     return static_cast<jboolean>(isPass);
663 }
664 
nativeTestFlush(JNIEnv * env,jobject,jstring jDecoder,jobject surface,jstring jMediaType,jstring jtestFile,jint jColorFormat,jobject jRetMsg)665 jboolean nativeTestFlush(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
666                          jstring jMediaType, jstring jtestFile, jint jColorFormat,
667                          jobject jRetMsg) {
668     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
669     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
670     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
671     ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
672     auto* codecDecoderTest = new CodecDecoderTest(cMediaType, window);
673     bool isPass = codecDecoderTest->testFlush(cDecoder, cTestFile, jColorFormat);
674     std::string msg = isPass ? std::string{} : codecDecoderTest->getErrorMsg();
675     delete codecDecoderTest;
676     jclass clazz = env->GetObjectClass(jRetMsg);
677     jmethodID mId =
678             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
679     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
680     if (window) {
681         ANativeWindow_release(window);
682         window = nullptr;
683     }
684     env->ReleaseStringUTFChars(jDecoder, cDecoder);
685     env->ReleaseStringUTFChars(jMediaType, cMediaType);
686     env->ReleaseStringUTFChars(jtestFile, cTestFile);
687     return static_cast<jboolean>(isPass);
688 }
689 
nativeTestSimpleDecodeQueueCSD(JNIEnv * env,jobject,jstring jDecoder,jstring jMediaType,jstring jtestFile,jint jColorFormat,jobject jRetMsg)690 jboolean nativeTestSimpleDecodeQueueCSD(JNIEnv* env, jobject, jstring jDecoder, jstring jMediaType,
691                                         jstring jtestFile, jint jColorFormat, jobject jRetMsg) {
692     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
693     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
694     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
695     auto codecDecoderTest = new CodecDecoderTest(cMediaType, nullptr);
696     bool isPass = codecDecoderTest->testSimpleDecodeQueueCSD(cDecoder, cTestFile, jColorFormat);
697     std::string msg = isPass ? std::string{} : codecDecoderTest->getErrorMsg();
698     delete codecDecoderTest;
699     jclass clazz = env->GetObjectClass(jRetMsg);
700     jmethodID mId =
701             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
702     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
703     env->ReleaseStringUTFChars(jDecoder, cDecoder);
704     env->ReleaseStringUTFChars(jMediaType, cMediaType);
705     env->ReleaseStringUTFChars(jtestFile, cTestFile);
706     return static_cast<jboolean>(isPass);
707 }
708