/* * Copyright (C) 2020 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "NativeCodecEncoderSurfaceTest" #include #include #include #include #include #include #include "NativeCodecTestBase.h" #include "NativeMediaCommon.h" class CodecEncoderSurfaceTest { private: const char* mMime; ANativeWindow* mWindow; AMediaExtractor* mExtractor; AMediaFormat* mDecFormat; AMediaFormat* mEncFormat; AMediaMuxer* mMuxer; AMediaCodec* mDecoder; AMediaCodec* mEncoder; CodecAsyncHandler mAsyncHandleDecoder; CodecAsyncHandler mAsyncHandleEncoder; bool mIsCodecInAsyncMode; bool mSawDecInputEOS; bool mSawDecOutputEOS; bool mSawEncOutputEOS; bool mSignalEOSWithLastFrame; int mDecInputCount; int mDecOutputCount; int mEncOutputCount; int mEncBitrate; int mEncFramerate; int mMaxBFrames; int mLatency; bool mReviseLatency; int mMuxTrackID; OutputManager* mOutputBuff; OutputManager mRefBuff; OutputManager mTestBuff; bool mSaveToMem; bool setUpExtractor(const char* srcPath); void deleteExtractor(); bool configureCodec(bool isAsync, bool signalEOSWithLastFrame); void resetContext(bool isAsync, bool signalEOSWithLastFrame); void setUpEncoderFormat(); bool enqueueDecoderInput(size_t bufferIndex); bool dequeueDecoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo); bool dequeueEncoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* info); bool tryEncoderOutput(long timeOutUs); bool waitForAllEncoderOutputs(); bool queueEOS(); bool enqueueDecoderEOS(size_t bufferIndex); bool doWork(int frameLimit); bool hasSeenError() { return mAsyncHandleDecoder.getError() || mAsyncHandleEncoder.getError(); } public: CodecEncoderSurfaceTest(const char* mime, int bitrate, int framerate); ~CodecEncoderSurfaceTest(); bool testSimpleEncode(const char* encoder, const char* decoder, const char* srcPath, const char* muxOutPath); }; CodecEncoderSurfaceTest::CodecEncoderSurfaceTest(const char* mime, int bitrate, int framerate) : mMime{mime}, mEncBitrate{bitrate}, mEncFramerate{framerate} { mWindow = nullptr; mExtractor = nullptr; mDecFormat = nullptr; mEncFormat = nullptr; mMuxer = nullptr; mDecoder = nullptr; mEncoder = nullptr; resetContext(false, false); mMaxBFrames = 0; mLatency = mMaxBFrames; mReviseLatency = false; mMuxTrackID = -1; } CodecEncoderSurfaceTest::~CodecEncoderSurfaceTest() { deleteExtractor(); if (mWindow) { ANativeWindow_release(mWindow); mWindow = nullptr; } if (mEncFormat) { AMediaFormat_delete(mEncFormat); mEncFormat = nullptr; } if (mMuxer) { AMediaMuxer_delete(mMuxer); mMuxer = nullptr; } if (mDecoder) { AMediaCodec_delete(mDecoder); mDecoder = nullptr; } if (mEncoder) { AMediaCodec_delete(mEncoder); mEncoder = nullptr; } } bool CodecEncoderSurfaceTest::setUpExtractor(const char* srcFile) { FILE* fp = fopen(srcFile, "rbe"); struct stat buf {}; if (fp && !fstat(fileno(fp), &buf)) { deleteExtractor(); mExtractor = AMediaExtractor_new(); media_status_t res = AMediaExtractor_setDataSourceFd(mExtractor, fileno(fp), 0, buf.st_size); if (res != AMEDIA_OK) { deleteExtractor(); } else { for (size_t trackID = 0; trackID < AMediaExtractor_getTrackCount(mExtractor); trackID++) { AMediaFormat* currFormat = AMediaExtractor_getTrackFormat(mExtractor, trackID); const char* mime = nullptr; AMediaFormat_getString(currFormat, AMEDIAFORMAT_KEY_MIME, &mime); if (mime && strncmp(mime, "video/", strlen("video/")) == 0) { AMediaExtractor_selectTrack(mExtractor, trackID); AMediaFormat_setInt32(currFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatYUV420Flexible); mDecFormat = currFormat; break; } AMediaFormat_delete(currFormat); } } } if (fp) fclose(fp); return mDecFormat != nullptr; } void CodecEncoderSurfaceTest::deleteExtractor() { if (mExtractor) { AMediaExtractor_delete(mExtractor); mExtractor = nullptr; } if (mDecFormat) { AMediaFormat_delete(mDecFormat); mDecFormat = nullptr; } } bool CodecEncoderSurfaceTest::configureCodec(bool isAsync, bool signalEOSWithLastFrame) { resetContext(isAsync, signalEOSWithLastFrame); CHECK_STATUS(mAsyncHandleEncoder.setCallBack(mEncoder, isAsync), "AMediaCodec_setAsyncNotifyCallback failed"); CHECK_STATUS(AMediaCodec_configure(mEncoder, mEncFormat, nullptr, nullptr, AMEDIACODEC_CONFIGURE_FLAG_ENCODE), "AMediaCodec_configure failed"); AMediaFormat* inpFormat = AMediaCodec_getInputFormat(mEncoder); mReviseLatency = AMediaFormat_getInt32(inpFormat, AMEDIAFORMAT_KEY_LATENCY, &mLatency); AMediaFormat_delete(inpFormat); CHECK_STATUS(AMediaCodec_createInputSurface(mEncoder, &mWindow), "AMediaCodec_createInputSurface failed"); CHECK_STATUS(mAsyncHandleDecoder.setCallBack(mDecoder, isAsync), "AMediaCodec_setAsyncNotifyCallback failed"); CHECK_STATUS(AMediaCodec_configure(mDecoder, mDecFormat, mWindow, nullptr, 0), "AMediaCodec_configure failed"); return !hasSeenError(); } void CodecEncoderSurfaceTest::resetContext(bool isAsync, bool signalEOSWithLastFrame) { mAsyncHandleDecoder.resetContext(); mAsyncHandleEncoder.resetContext(); mIsCodecInAsyncMode = isAsync; mSawDecInputEOS = false; mSawDecOutputEOS = false; mSawEncOutputEOS = false; mSignalEOSWithLastFrame = signalEOSWithLastFrame; mDecInputCount = 0; mDecOutputCount = 0; mEncOutputCount = 0; } void CodecEncoderSurfaceTest::setUpEncoderFormat() { if (mEncFormat) AMediaFormat_delete(mEncFormat); mEncFormat = AMediaFormat_new(); int width, height; AMediaFormat_getInt32(mDecFormat, AMEDIAFORMAT_KEY_WIDTH, &width); AMediaFormat_getInt32(mDecFormat, AMEDIAFORMAT_KEY_HEIGHT, &height); AMediaFormat_setString(mEncFormat, AMEDIAFORMAT_KEY_MIME, mMime); AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_WIDTH, width); AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_HEIGHT, height); AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_BIT_RATE, mEncBitrate); AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_FRAME_RATE, mEncFramerate); AMediaFormat_setInt32(mEncFormat, TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES, mMaxBFrames); AMediaFormat_setInt32(mEncFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, COLOR_FormatSurface); AMediaFormat_setFloat(mEncFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1.0F); } bool CodecEncoderSurfaceTest::enqueueDecoderEOS(size_t bufferIndex) { if (!hasSeenError() && !mSawDecInputEOS) { CHECK_STATUS(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, 0, 0, AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM), "Queued Decoder End of Stream Failed"); mSawDecInputEOS = true; ALOGV("Queued Decoder End of Stream"); } return !hasSeenError(); } bool CodecEncoderSurfaceTest::enqueueDecoderInput(size_t bufferIndex) { if (AMediaExtractor_getSampleSize(mExtractor) < 0) { return enqueueDecoderEOS(bufferIndex); } else { uint32_t flags = 0; size_t bufSize = 0; uint8_t* buf = AMediaCodec_getInputBuffer(mDecoder, bufferIndex, &bufSize); if (buf == nullptr) { ALOGE("AMediaCodec_getInputBuffer failed"); return false; } ssize_t size = AMediaExtractor_getSampleSize(mExtractor); int64_t pts = AMediaExtractor_getSampleTime(mExtractor); if (size > bufSize) { ALOGE("extractor sample size exceeds codec input buffer size %zu %zu", size, bufSize); return false; } if (size != AMediaExtractor_readSampleData(mExtractor, buf, bufSize)) { ALOGE("AMediaExtractor_readSampleData failed"); return false; } if (!AMediaExtractor_advance(mExtractor) && mSignalEOSWithLastFrame) { flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM; mSawDecInputEOS = true; } CHECK_STATUS(AMediaCodec_queueInputBuffer(mDecoder, bufferIndex, 0, size, pts, flags), "AMediaCodec_queueInputBuffer failed"); ALOGV("input: id: %zu size: %zu pts: %" PRId64 " flags: %d", bufferIndex, size, pts, flags); if (size > 0) { mOutputBuff->saveInPTS(pts); mDecInputCount++; } } return !hasSeenError(); } bool CodecEncoderSurfaceTest::dequeueDecoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo) { if ((bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) { mSawDecOutputEOS = true; } if (bufferInfo->size > 0 && (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) { mDecOutputCount++; } ALOGV("output: id: %zu size: %d pts: %" PRId64 " flags: %d", bufferIndex, bufferInfo->size, bufferInfo->presentationTimeUs, bufferInfo->flags); CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mDecoder, bufferIndex, mWindow != nullptr), "AMediaCodec_releaseOutputBuffer failed"); return !hasSeenError(); } bool CodecEncoderSurfaceTest::dequeueEncoderOutput(size_t bufferIndex, AMediaCodecBufferInfo* info) { if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) { mSawEncOutputEOS = true; } if (info->size > 0) { size_t buffSize; uint8_t* buf = AMediaCodec_getOutputBuffer(mEncoder, bufferIndex, &buffSize); if (mSaveToMem) { mOutputBuff->saveToMemory(buf, info); } if (mMuxer != nullptr) { if (mMuxTrackID == -1) { mMuxTrackID = AMediaMuxer_addTrack(mMuxer, AMediaCodec_getOutputFormat(mEncoder)); CHECK_STATUS(AMediaMuxer_start(mMuxer), "AMediaMuxer_start failed"); } CHECK_STATUS(AMediaMuxer_writeSampleData(mMuxer, mMuxTrackID, buf, info), "AMediaMuxer_writeSampleData failed"); } if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) { mOutputBuff->saveOutPTS(info->presentationTimeUs); mEncOutputCount++; } } ALOGV("output: id: %zu size: %d pts: %" PRId64 " flags: %d", bufferIndex, info->size, info->presentationTimeUs, info->flags); CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mEncoder, bufferIndex, false), "AMediaCodec_releaseOutputBuffer failed"); return !hasSeenError(); } bool CodecEncoderSurfaceTest::tryEncoderOutput(long timeOutUs) { if (mIsCodecInAsyncMode) { if (!hasSeenError() && !mSawEncOutputEOS) { int retry = 0; while (mReviseLatency) { if (mAsyncHandleEncoder.hasOutputFormatChanged()) { int actualLatency; mReviseLatency = false; if (AMediaFormat_getInt32(mAsyncHandleEncoder.getOutputFormat(), AMEDIAFORMAT_KEY_LATENCY, &actualLatency)) { if (mLatency < actualLatency) { mLatency = actualLatency; return !hasSeenError(); } } } else { if (retry > kRetryLimit) return false; usleep(kQDeQTimeOutUs); retry ++; } } callbackObject element = mAsyncHandleEncoder.getOutput(); if (element.bufferIndex >= 0) { if (!dequeueEncoderOutput(element.bufferIndex, &element.bufferInfo)) return false; } } } else { AMediaCodecBufferInfo outInfo; if (!mSawEncOutputEOS) { int bufferID = AMediaCodec_dequeueOutputBuffer(mEncoder, &outInfo, timeOutUs); if (bufferID >= 0) { if (!dequeueEncoderOutput(bufferID, &outInfo)) return false; } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { AMediaFormat* outFormat = AMediaCodec_getOutputFormat(mEncoder); AMediaFormat_getInt32(outFormat, AMEDIAFORMAT_KEY_LATENCY, &mLatency); AMediaFormat_delete(outFormat); } else if (bufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { } else if (bufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { } else { ALOGE("unexpected return value from *_dequeueOutputBuffer: %d", bufferID); return false; } } } return !hasSeenError(); } bool CodecEncoderSurfaceTest::waitForAllEncoderOutputs() { if (mIsCodecInAsyncMode) { while (!hasSeenError() && !mSawEncOutputEOS) { if (!tryEncoderOutput(kQDeQTimeOutUs)) return false; } } else { while (!mSawEncOutputEOS) { if (!tryEncoderOutput(kQDeQTimeOutUs)) return false; } } return !hasSeenError(); } bool CodecEncoderSurfaceTest::queueEOS() { if (mIsCodecInAsyncMode) { while (!hasSeenError() && !mSawDecInputEOS) { callbackObject element = mAsyncHandleDecoder.getWork(); if (element.bufferIndex >= 0) { if (element.isInput) { if (!enqueueDecoderEOS(element.bufferIndex)) return false; } else { if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) { return false; } } } } } else { AMediaCodecBufferInfo outInfo; while (!mSawDecInputEOS) { ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs); if (oBufferID >= 0) { if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false; } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { } else { ALOGE("unexpected return value from *_dequeueOutputBuffer: %zd", oBufferID); return false; } ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs); if (iBufferId >= 0) { if (!enqueueDecoderEOS(iBufferId)) return false; } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { } else { ALOGE("unexpected return value from *_dequeueInputBuffer: %zd", iBufferId); return false; } } } if (mIsCodecInAsyncMode) { // output processing after queuing EOS is done in waitForAllOutputs() while (!hasSeenError() && !mSawDecOutputEOS) { callbackObject element = mAsyncHandleDecoder.getOutput(); if (element.bufferIndex >= 0) { if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) return false; } if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder); if (mDecOutputCount - mEncOutputCount > mLatency) { if (!tryEncoderOutput(-1)) return false; } } } else { AMediaCodecBufferInfo outInfo; // output processing after queuing EOS is done in waitForAllOutputs() while (!mSawDecOutputEOS) { if (!mSawDecOutputEOS) { ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs); if (oBufferID >= 0) { if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false; } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { } else { ALOGE("unexpected return value from *_dequeueOutputBuffer: %zd", oBufferID); return false; } } if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder); if (mDecOutputCount - mEncOutputCount > mLatency) { if (!tryEncoderOutput(-1)) return false; } } } return !hasSeenError(); } bool CodecEncoderSurfaceTest::doWork(int frameLimit) { int frameCnt = 0; if (mIsCodecInAsyncMode) { // output processing after queuing EOS is done in waitForAllOutputs() while (!hasSeenError() && !mSawDecInputEOS && frameCnt < frameLimit) { callbackObject element = mAsyncHandleDecoder.getWork(); if (element.bufferIndex >= 0) { if (element.isInput) { if (!enqueueDecoderInput(element.bufferIndex)) return false; frameCnt++; } else { if (!dequeueDecoderOutput(element.bufferIndex, &element.bufferInfo)) { return false; } } } // check decoder EOS if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder); // encoder output if (mDecOutputCount - mEncOutputCount > mLatency) { if (!tryEncoderOutput(-1)) return false; } } } else { AMediaCodecBufferInfo outInfo; // output processing after queuing EOS is done in waitForAllOutputs() while (!mSawDecInputEOS && frameCnt < frameLimit) { ssize_t oBufferID = AMediaCodec_dequeueOutputBuffer(mDecoder, &outInfo, kQDeQTimeOutUs); if (oBufferID >= 0) { if (!dequeueDecoderOutput(oBufferID, &outInfo)) return false; } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) { } else if (oBufferID == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { } else if (oBufferID == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) { } else { ALOGE("unexpected return value from *_dequeueOutputBuffer: %zd", oBufferID); return false; } ssize_t iBufferId = AMediaCodec_dequeueInputBuffer(mDecoder, kQDeQTimeOutUs); if (iBufferId >= 0) { if (!enqueueDecoderInput(iBufferId)) return false; frameCnt++; } else if (iBufferId == AMEDIACODEC_INFO_TRY_AGAIN_LATER) { } else { ALOGE("unexpected return value from *_dequeueInputBuffer: %zd", iBufferId); return false; } if (mSawDecOutputEOS) AMediaCodec_signalEndOfInputStream(mEncoder); if (mDecOutputCount - mEncOutputCount > mLatency) { if (!tryEncoderOutput(-1)) return false; } } } return !hasSeenError(); } bool CodecEncoderSurfaceTest::testSimpleEncode(const char* encoder, const char* decoder, const char* srcPath, const char* muxOutPath) { bool isPass = true; if (!setUpExtractor(srcPath)) { ALOGE("setUpExtractor failed"); return false; } setUpEncoderFormat(); bool muxOutput = true; /* TODO(b/149027258) */ if (true) mSaveToMem = false; else mSaveToMem = true; auto ref = &mRefBuff; auto test = &mTestBuff; int loopCounter = 0; const bool boolStates[]{true, false}; for (bool isAsync : boolStates) { if (!isPass) break; AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC); mOutputBuff = loopCounter == 0 ? ref : test; mOutputBuff->reset(); /* TODO(b/147348711) */ /* Instead of create and delete codec at every iteration, we would like to create * once and use it for all iterations and delete before exiting */ mEncoder = AMediaCodec_createCodecByName(encoder); mDecoder = AMediaCodec_createCodecByName(decoder); if (!mDecoder || !mEncoder) { ALOGE("unable to create media codec by name %s or %s", encoder, decoder); isPass = false; continue; } FILE* ofp = nullptr; if (muxOutput && loopCounter == 0) { int muxerFormat = 0; if (!strcmp(mMime, AMEDIA_MIMETYPE_VIDEO_VP8) || !strcmp(mMime, AMEDIA_MIMETYPE_VIDEO_VP9)) { muxerFormat = OUTPUT_FORMAT_WEBM; } else { muxerFormat = OUTPUT_FORMAT_MPEG_4; } ofp = fopen(muxOutPath, "wbe+"); if (ofp) { mMuxer = AMediaMuxer_new(fileno(ofp), (OutputFormat)muxerFormat); } } if (!configureCodec(mIsCodecInAsyncMode, mSignalEOSWithLastFrame)) return false; CHECK_STATUS(AMediaCodec_start(mEncoder), "AMediaCodec_start failed"); CHECK_STATUS(AMediaCodec_start(mDecoder), "AMediaCodec_start failed"); if (!doWork(INT32_MAX)) return false; if (!queueEOS()) return false; if (!waitForAllEncoderOutputs()) return false; if (muxOutput) { if (mMuxer != nullptr) { CHECK_STATUS(AMediaMuxer_stop(mMuxer), "AMediaMuxer_stop failed"); mMuxTrackID = -1; CHECK_STATUS(AMediaMuxer_delete(mMuxer), "AMediaMuxer_delete failed"); mMuxer = nullptr; } if (ofp) fclose(ofp); } CHECK_STATUS(AMediaCodec_stop(mDecoder), "AMediaCodec_stop failed"); CHECK_STATUS(AMediaCodec_stop(mEncoder), "AMediaCodec_stop failed"); char log[1000]; snprintf(log, sizeof(log), "format: %s \n codec: %s, file: %s, mode: %s:: ", AMediaFormat_toString(mEncFormat), encoder, srcPath, (isAsync ? "async" : "sync")); CHECK_ERR((hasSeenError()), log, "has seen error", isPass); CHECK_ERR((0 == mDecInputCount), log, "no input sent", isPass); CHECK_ERR((0 == mDecOutputCount), log, "no decoder output received", isPass); CHECK_ERR((0 == mEncOutputCount), log, "no encoder output received", isPass); CHECK_ERR((mDecInputCount != mDecOutputCount), log, "decoder input count != output count", isPass); /* TODO(b/153127506) * Currently disabling all encoder output checks. Added checks only for encoder timeStamp * is in increasing order or not. * Once issue is fixed remove increasing timestamp check and enable encoder checks. */ /*CHECK_ERR((mEncOutputCount != mDecOutputCount), log, "encoder output count != decoder output count", isPass); CHECK_ERR((loopCounter != 0 && !ref->equals(test)), log, "output is flaky", isPass); CHECK_ERR((loopCounter == 0 && !ref->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), log, "input pts list and output pts list are not identical", isPass);*/ CHECK_ERR(loopCounter == 0 && (!ref->isPtsStrictlyIncreasing(INT32_MIN)), log, "Ref pts is not strictly increasing", isPass); CHECK_ERR(loopCounter != 0 && (!test->isPtsStrictlyIncreasing(INT32_MIN)), log, "Test pts is not strictly increasing", isPass); loopCounter++; ANativeWindow_release(mWindow); mWindow = nullptr; CHECK_STATUS(AMediaCodec_delete(mEncoder), "AMediaCodec_delete failed"); mEncoder = nullptr; CHECK_STATUS(AMediaCodec_delete(mDecoder), "AMediaCodec_delete failed"); mDecoder = nullptr; } return isPass; } static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jDecoder, jstring jMime, jstring jtestFile, jstring jmuxFile, jint jBitrate, jint jFramerate) { const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr); const char* cDecoder = env->GetStringUTFChars(jDecoder, nullptr); const char* cMime = env->GetStringUTFChars(jMime, nullptr); const char* cTestFile = env->GetStringUTFChars(jtestFile, nullptr); const char* cMuxFile = env->GetStringUTFChars(jmuxFile, nullptr); auto codecEncoderSurfaceTest = new CodecEncoderSurfaceTest(cMime, (int)jBitrate, (int)jFramerate); bool isPass = codecEncoderSurfaceTest->testSimpleEncode(cEncoder, cDecoder, cTestFile, cMuxFile); delete codecEncoderSurfaceTest; env->ReleaseStringUTFChars(jEncoder, cEncoder); env->ReleaseStringUTFChars(jDecoder, cDecoder); env->ReleaseStringUTFChars(jMime, cMime); env->ReleaseStringUTFChars(jtestFile, cTestFile); env->ReleaseStringUTFChars(jmuxFile, cMuxFile); return static_cast(isPass); } int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env) { const JNINativeMethod methodTable[] = { {"nativeTestSimpleEncode", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/" "String;II)Z", (void*)nativeTestSimpleEncode}, }; jclass c = env->FindClass("android/mediav2/cts/CodecEncoderSurfaceTest"); return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod)); }