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