• 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 && colorFormat != -1) {
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, (mWindow == nullptr) ? colorFormat : -1)) 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             if (mWindow != nullptr) {
394                 AMediaFormat* outFormat = AMediaCodec_getOutputFormat(mCodec);
395                 int outputColorFormat = -1;
396                 AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, &outputColorFormat);
397                 AMediaFormat_delete(outFormat);
398                 RETURN_IF_TRUE(outputColorFormat != COLOR_FormatSurface,
399                                std::string{"In surface mode, components MUST default to the color "
400                                            "format optimized for hardware display \n"}
401                                        .append(test->getErrorMsg()))
402             }
403             RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
404             RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
405             mCodec = nullptr;
406             RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
407                            std::string{"Decoder output is not consistent across runs \n"}.append(
408                                    test->getErrorMsg()))
409             if (validateFormat && !isOutputFormatOk(mInpDecFormat)) {
410                 return false;
411             }
412             RETURN_IF_TRUE(checksum != ref->getChecksum(),
413                            StringFormat("sdk output and ndk output for same configuration is not "
414                                         "identical. \n sdk buffer output checksum is %lu. \n ndk "
415                                         "buffer output checksum is %lu. \n",
416                                         checksum, ref->getChecksum()))
417             loopCounter++;
418         }
419     }
420     if (mSaveToMem && refFile && rmsError >= 0) {
421         setUpAudioReference(refFile);
422         float currError = ref->getRmsError(mRefData, mRefLength);
423         float errMargin = rmsError * kRmsErrorTolerance;
424         RETURN_IF_TRUE(currError > errMargin,
425                        StringFormat("rms error too high for file %s, ref/exp/got: %f/%f/%f",
426                                     testFile, rmsError, errMargin, currError))
427     }
428     return true;
429 }
430 
testFlush(const char * decoder,const char * testFile,int colorFormat)431 bool CodecDecoderTest::testFlush(const char* decoder, const char* testFile, int colorFormat) {
432     if (!setUpExtractor(testFile, colorFormat)) return false;
433     mCsdBuffers.clear();
434     for (int i = 0;; i++) {
435         char csdName[16];
436         void* csdBuffer;
437         size_t csdSize;
438         snprintf(csdName, sizeof(csdName), "csd-%d", i);
439         if (AMediaFormat_getBuffer(mInpDecFormat, csdName, &csdBuffer, &csdSize)) {
440             mCsdBuffers.emplace_back(std::make_pair(csdBuffer, csdSize));
441         } else break;
442     }
443     const int64_t pts = 500000;
444     const SeekMode mode = AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC;
445     auto ref = mRefBuff;
446     RETURN_IF_FALSE(decodeToMemory(decoder, mInpDecFormat, INT32_MAX, ref, pts, mode),
447                     StringFormat("decodeToMemory failed for file: %s codec: %s", testFile, decoder))
448     auto test = mTestBuff;
449     mOutputBuff = test;
450     const bool boolStates[]{true, false};
451     for (auto isAsync : boolStates) {
452         if (isAsync) continue;  // TODO(b/147576107)
453         /* TODO(b/149981033) */
454         /* Instead of create and delete codec at every iteration, we would like to create
455          * once and use it for all iterations and delete before exiting */
456         mCodec = AMediaCodec_createCodecByName(decoder);
457         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", decoder))
458         AMediaExtractor_seekTo(mExtractor, 0, mode);
459         if (!configureCodec(mInpDecFormat, isAsync, true, false)) return false;
460         AMediaFormat* defFormat = AMediaCodec_getOutputFormat(mCodec);
461         bool validateFormat = true;
462         if (isFormatSimilar(mInpDecFormat, defFormat)) {
463             ALOGD("Input format is same as default for format for %s", decoder);
464             validateFormat = false;
465         }
466         AMediaFormat_delete(defFormat);
467         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
468 
469         /* test flush in running state before queuing input */
470         if (!flushCodec()) return false;
471         if (mIsCodecInAsyncMode) {
472             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
473         }
474         if (!queueCodecConfig()) return false; /* flushed codec too soon, resubmit csd */
475         if (!doWork(1)) return false;
476 
477         if (!flushCodec()) return false;
478         if (mIsCodecInAsyncMode) {
479             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
480         }
481         if (!queueCodecConfig()) return false; /* flushed codec too soon, resubmit csd */
482         AMediaExtractor_seekTo(mExtractor, 0, mode);
483         test->reset();
484         if (!doWork(23)) return false;
485         RETURN_IF_TRUE(!mIsInterlaced && !test->isPtsStrictlyIncreasing(mPrevOutputPts),
486                        std::string{"Output timestamps are not strictly increasing \n"}.append(
487                                test->getErrorMsg()))
488 
489         /* test flush in running state */
490         if (!flushCodec()) return false;
491         if (mIsCodecInAsyncMode) {
492             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
493         }
494         mSaveToMem = (mWindow == nullptr);
495         test->reset();
496         AMediaExtractor_seekTo(mExtractor, pts, mode);
497         if (!doWork(INT32_MAX)) return false;
498         if (!queueEOS()) return false;
499         if (!waitForAllOutputs()) return false;
500         RETURN_IF_TRUE(isMediaTypeOutputUnAffectedBySeek(mMediaType) && !ref->equals(test),
501                        std::string{"Decoder output is not consistent across runs \n"}.append(
502                                test->getErrorMsg()))
503 
504         /* test flush in eos state */
505         if (!flushCodec()) return false;
506         if (mIsCodecInAsyncMode) {
507             RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
508         }
509         test->reset();
510         AMediaExtractor_seekTo(mExtractor, pts, mode);
511         if (!doWork(INT32_MAX)) return false;
512         if (!queueEOS()) return false;
513         if (!waitForAllOutputs()) return false;
514         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
515         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
516         mCodec = nullptr;
517         RETURN_IF_TRUE(isMediaTypeOutputUnAffectedBySeek(mMediaType) && !ref->equals(test),
518                        std::string{"Decoder output is not consistent across runs \n"}.append(
519                                test->getErrorMsg()))
520         if (validateFormat && !isOutputFormatOk(mInpDecFormat)) {
521             return false;
522         }
523         mSaveToMem = false;
524     }
525     return true;
526 }
527 
testOnlyEos(const char * decoder,const char * testFile,int colorFormat)528 bool CodecDecoderTest::testOnlyEos(const char* decoder, const char* testFile, int colorFormat) {
529     if (!setUpExtractor(testFile, colorFormat)) return false;
530     mSaveToMem = (mWindow == nullptr);
531     auto ref = mRefBuff;
532     auto test = mTestBuff;
533     const bool boolStates[]{true, false};
534     int loopCounter = 0;
535     for (auto isAsync : boolStates) {
536         mOutputBuff = loopCounter == 0 ? ref : test;
537         mOutputBuff->reset();
538         /* TODO(b/149981033) */
539         /* Instead of create and delete codec at every iteration, we would like to create
540          * once and use it for all iterations and delete before exiting */
541         mCodec = AMediaCodec_createCodecByName(decoder);
542         RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", decoder))
543         if (!configureCodec(mInpDecFormat, isAsync, false, false)) return false;
544         RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
545         if (!queueEOS()) return false;
546         if (!waitForAllOutputs()) return false;
547         RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
548         RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
549         mCodec = nullptr;
550         RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
551                        std::string{"Decoder output is not consistent across runs \n"}.append(
552                                test->getErrorMsg()))
553         loopCounter++;
554     }
555     return true;
556 }
557 
testSimpleDecodeQueueCSD(const char * decoder,const char * testFile,int colorFormat)558 bool CodecDecoderTest::testSimpleDecodeQueueCSD(const char* decoder, const char* testFile,
559                                                 int colorFormat) {
560     if (!setUpExtractor(testFile, colorFormat)) return false;
561     std::vector<AMediaFormat*> formats;
562     formats.push_back(mInpDecFormat);
563     mInpDecDupFormat = AMediaFormat_new();
564     AMediaFormat_copy(mInpDecDupFormat, mInpDecFormat);
565     formats.push_back(mInpDecDupFormat);
566     mCsdBuffers.clear();
567     for (int i = 0;; i++) {
568         char csdName[16];
569         void* csdBuffer;
570         size_t csdSize;
571         snprintf(csdName, sizeof(csdName), "csd-%d", i);
572         if (AMediaFormat_getBuffer(mInpDecDupFormat, csdName, &csdBuffer, &csdSize)) {
573             mCsdBuffers.emplace_back(std::make_pair(csdBuffer, csdSize));
574             AMediaFormat_setBuffer(mInpDecFormat, csdName, nullptr, 0);
575         } else break;
576     }
577 
578     const bool boolStates[]{true, false};
579     mSaveToMem = true;
580     auto ref = mRefBuff;
581     auto test = mTestBuff;
582     int loopCounter = 0;
583     for (int i = 0; i < formats.size(); i++) {
584         auto fmt = formats[i];
585         for (auto eosType : boolStates) {
586             for (auto isAsync : boolStates) {
587                 bool validateFormat = true;
588                 mOutputBuff = loopCounter == 0 ? ref : test;
589                 mOutputBuff->reset();
590                 /* TODO(b/149981033) */
591                 /* Instead of create and delete codec at every iteration, we would like to create
592                  * once and use it for all iterations and delete before exiting */
593                 mCodec = AMediaCodec_createCodecByName(decoder);
594                 RETURN_IF_NULL(mCodec, StringFormat("unable to create codec %s", decoder))
595                 AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
596                 if (!configureCodec(fmt, isAsync, eosType, false)) return false;
597                 AMediaFormat* defFormat = AMediaCodec_getOutputFormat(mCodec);
598                 if (isFormatSimilar(defFormat, mInpDecFormat)) {
599                     ALOGD("Input format is same as default for format for %s", decoder);
600                     validateFormat = false;
601                 }
602                 AMediaFormat_delete(defFormat);
603                 RETURN_IF_FAIL(AMediaCodec_start(mCodec), "AMediaCodec_start failed")
604                 /* formats[0] doesn't contain csd-data, so queuing csd separately, formats[1]
605                  * contain csd-data */
606                 if (i == 0 && !queueCodecConfig()) return false;
607                 if (!doWork(INT32_MAX)) return false;
608                 if (!queueEOS()) return false;
609                 if (!waitForAllOutputs()) return false;
610                 RETURN_IF_FAIL(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed")
611                 RETURN_IF_FAIL(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed")
612                 mCodec = nullptr;
613                 RETURN_IF_TRUE((loopCounter != 0 && !ref->equals(test)),
614                                std::string{"Decoder output is not consistent across runs \n"}
615                                        .append(test->getErrorMsg()))
616                 if (validateFormat && !isOutputFormatOk(mInpDecFormat)) {
617                     return false;
618                 }
619                 loopCounter++;
620             }
621         }
622     }
623     mSaveToMem = false;
624     return true;
625 }
626 
nativeTestSimpleDecode(JNIEnv * env,jobject,jstring jDecoder,jobject surface,jstring jMediaType,jstring jtestFile,jstring jrefFile,jint jColorFormat,jfloat jrmsError,jlong jChecksum,jobject jRetMsg)627 jboolean nativeTestSimpleDecode(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
628                                 jstring jMediaType, jstring jtestFile, jstring jrefFile,
629                                 jint jColorFormat, jfloat jrmsError, jlong jChecksum,
630                                 jobject jRetMsg) {
631     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
632     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
633     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
634     const char* cRefFile = env->GetStringUTFChars(jrefFile, nullptr);
635     float cRmsError = jrmsError;
636     uLong cChecksum = jChecksum;
637     ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
638     auto* codecDecoderTest = new CodecDecoderTest(cMediaType, window);
639     bool isPass = codecDecoderTest->testSimpleDecode(cDecoder, cTestFile, cRefFile, jColorFormat,
640                                                      cRmsError, cChecksum);
641     std::string msg = isPass ? std::string{} : codecDecoderTest->getErrorMsg();
642     delete codecDecoderTest;
643     jclass clazz = env->GetObjectClass(jRetMsg);
644     jmethodID mId =
645             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
646     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
647     if (window) {
648         ANativeWindow_release(window);
649         window = nullptr;
650     }
651     env->ReleaseStringUTFChars(jDecoder, cDecoder);
652     env->ReleaseStringUTFChars(jMediaType, cMediaType);
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 jMediaType,jstring jtestFile,jint jColorFormat,jobject jRetMsg)658 jboolean nativeTestOnlyEos(JNIEnv* env, jobject, jstring jDecoder, jstring jMediaType,
659                            jstring jtestFile, jint jColorFormat, jobject jRetMsg) {
660     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
661     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
662     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
663     auto* codecDecoderTest = new CodecDecoderTest(cMediaType, nullptr);
664     bool isPass = codecDecoderTest->testOnlyEos(cDecoder, cTestFile, jColorFormat);
665     std::string msg = isPass ? std::string{} : codecDecoderTest->getErrorMsg();
666     delete codecDecoderTest;
667     jclass clazz = env->GetObjectClass(jRetMsg);
668     jmethodID mId =
669             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
670     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
671     env->ReleaseStringUTFChars(jDecoder, cDecoder);
672     env->ReleaseStringUTFChars(jMediaType, cMediaType);
673     env->ReleaseStringUTFChars(jtestFile, cTestFile);
674     return static_cast<jboolean>(isPass);
675 }
676 
nativeTestFlush(JNIEnv * env,jobject,jstring jDecoder,jobject surface,jstring jMediaType,jstring jtestFile,jint jColorFormat,jobject jRetMsg)677 jboolean nativeTestFlush(JNIEnv* env, jobject, jstring jDecoder, jobject surface,
678                          jstring jMediaType, jstring jtestFile, jint jColorFormat,
679                          jobject jRetMsg) {
680     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
681     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
682     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
683     ANativeWindow* window = surface ? ANativeWindow_fromSurface(env, surface) : nullptr;
684     auto* codecDecoderTest = new CodecDecoderTest(cMediaType, window);
685     bool isPass = codecDecoderTest->testFlush(cDecoder, cTestFile, jColorFormat);
686     std::string msg = isPass ? std::string{} : codecDecoderTest->getErrorMsg();
687     delete codecDecoderTest;
688     jclass clazz = env->GetObjectClass(jRetMsg);
689     jmethodID mId =
690             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
691     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
692     if (window) {
693         ANativeWindow_release(window);
694         window = nullptr;
695     }
696     env->ReleaseStringUTFChars(jDecoder, cDecoder);
697     env->ReleaseStringUTFChars(jMediaType, cMediaType);
698     env->ReleaseStringUTFChars(jtestFile, cTestFile);
699     return static_cast<jboolean>(isPass);
700 }
701 
nativeTestSimpleDecodeQueueCSD(JNIEnv * env,jobject,jstring jDecoder,jstring jMediaType,jstring jtestFile,jint jColorFormat,jobject jRetMsg)702 jboolean nativeTestSimpleDecodeQueueCSD(JNIEnv* env, jobject, jstring jDecoder, jstring jMediaType,
703                                         jstring jtestFile, jint jColorFormat, jobject jRetMsg) {
704     const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr);
705     const char* cMediaType = env->GetStringUTFChars(jMediaType, nullptr);
706     const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr);
707     auto codecDecoderTest = new CodecDecoderTest(cMediaType, nullptr);
708     bool isPass = codecDecoderTest->testSimpleDecodeQueueCSD(cDecoder, cTestFile, jColorFormat);
709     std::string msg = isPass ? std::string{} : codecDecoderTest->getErrorMsg();
710     delete codecDecoderTest;
711     jclass clazz = env->GetObjectClass(jRetMsg);
712     jmethodID mId =
713             env->GetMethodID(clazz, "append", "(Ljava/lang/String;)Ljava/lang/StringBuilder;");
714     env->CallObjectMethod(jRetMsg, mId, env->NewStringUTF(msg.c_str()));
715     env->ReleaseStringUTFChars(jDecoder, cDecoder);
716     env->ReleaseStringUTFChars(jMediaType, cMediaType);
717     env->ReleaseStringUTFChars(jtestFile, cTestFile);
718     return static_cast<jboolean>(isPass);
719 }
720