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