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