| /* |
| * 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 "NativeCodecEncoderTest" |
| #include <log/log.h> |
| |
| #include <jni.h> |
| #include <sys/stat.h> |
| |
| #include "NativeCodecTestBase.h" |
| #include "NativeMediaCommon.h" |
| |
| class CodecEncoderTest final : CodecTestBase { |
| private: |
| uint8_t* mInputData; |
| size_t mInputLength; |
| int mNumBytesSubmitted; |
| int64_t mInputOffsetPts; |
| std::vector<AMediaFormat*> mFormats; |
| int mNumSyncFramesReceived; |
| std::vector<int> mSyncFramesPos; |
| |
| int32_t* mBitRates; |
| int mLen0; |
| int32_t* mEncParamList1; |
| int mLen1; |
| int32_t* mEncParamList2; |
| int mLen2; |
| |
| int mWidth, mHeight; |
| int mChannels; |
| int mSampleRate; |
| int mColorFormat; |
| int mMaxBFrames; |
| int mDefFrameRate; |
| const int kInpFrmWidth = 352; |
| const int kInpFrmHeight = 288; |
| |
| void convertyuv420ptoyuv420sp(); |
| void setUpSource(const char* srcPath); |
| void deleteSource(); |
| void setUpParams(int limit); |
| void deleteParams(); |
| bool flushCodec() override; |
| void resetContext(bool isAsync, bool signalEOSWithLastFrame) override; |
| bool enqueueInput(size_t bufferIndex) override; |
| bool dequeueOutput(size_t bufferIndex, AMediaCodecBufferInfo* bufferInfo) override; |
| void initFormat(AMediaFormat* format); |
| bool encodeToMemory(const char* file, const char* encoder, int frameLimit, AMediaFormat* format, |
| OutputManager* ref); |
| void fillByteBuffer(uint8_t* inputBuffer); |
| void forceSyncFrame(AMediaFormat* format); |
| void updateBitrate(AMediaFormat* format, int bitrate); |
| |
| public: |
| CodecEncoderTest(const char* mime, int32_t* list0, int len0, int32_t* list1, int len1, |
| int32_t* list2, int len2, int colorFormat); |
| ~CodecEncoderTest(); |
| |
| bool testSimpleEncode(const char* encoder, const char* srcPath); |
| bool testFlush(const char* encoder, const char* srcPath); |
| bool testReconfigure(const char* encoder, const char* srcPath); |
| bool testSetForceSyncFrame(const char* encoder, const char* srcPath); |
| bool testAdaptiveBitRate(const char* encoder, const char* srcPath); |
| bool testOnlyEos(const char* encoder); |
| }; |
| |
| CodecEncoderTest::CodecEncoderTest(const char* mime, int32_t* list0, int len0, int32_t* list1, |
| int len1, int32_t* list2, int len2, int colorFormat) |
| : CodecTestBase(mime), |
| mBitRates{list0}, |
| mLen0{len0}, |
| mEncParamList1{list1}, |
| mLen1{len1}, |
| mEncParamList2{list2}, |
| mLen2{len2}, |
| mColorFormat{colorFormat} { |
| mDefFrameRate = 30; |
| if (!strcmp(mime, AMEDIA_MIMETYPE_VIDEO_H263)) mDefFrameRate = 12; |
| else if (!strcmp(mime, AMEDIA_MIMETYPE_VIDEO_MPEG4)) mDefFrameRate = 12; |
| mMaxBFrames = 0; |
| mInputData = nullptr; |
| mInputLength = 0; |
| mNumBytesSubmitted = 0; |
| mInputOffsetPts = 0; |
| } |
| |
| CodecEncoderTest::~CodecEncoderTest() { |
| deleteSource(); |
| deleteParams(); |
| } |
| |
| void CodecEncoderTest::convertyuv420ptoyuv420sp() { |
| int ySize = kInpFrmWidth * kInpFrmHeight; |
| int uSize = kInpFrmWidth * kInpFrmHeight / 4; |
| int frameSize = ySize + uSize * 2; |
| int totalFrames = mInputLength / frameSize; |
| uint8_t* u = new uint8_t[uSize]; |
| uint8_t* v = new uint8_t[uSize]; |
| uint8_t* frameBase = mInputData; |
| for (int i = 0; i < totalFrames; i++) { |
| uint8_t* uvBase = frameBase + ySize; |
| memcpy(u, uvBase, uSize); |
| memcpy(v, uvBase + uSize, uSize); |
| for (int j = 0, idx = 0; j < uSize; j++, idx += 2) { |
| uvBase[idx] = u[j]; |
| uvBase[idx + 1] = v[j]; |
| } |
| frameBase += frameSize; |
| } |
| delete[] u; |
| delete[] v; |
| } |
| |
| void CodecEncoderTest::setUpSource(const char* srcPath) { |
| FILE* fp = fopen(srcPath, "rbe"); |
| struct stat buf {}; |
| if (fp && !fstat(fileno(fp), &buf)) { |
| deleteSource(); |
| mInputLength = buf.st_size; |
| mInputData = new uint8_t[mInputLength]; |
| fread(mInputData, sizeof(uint8_t), mInputLength, fp); |
| if (mColorFormat == COLOR_FormatYUV420SemiPlanar) { |
| convertyuv420ptoyuv420sp(); |
| } |
| } else { |
| ALOGE("unable to open input file %s", srcPath); |
| } |
| if (fp) fclose(fp); |
| } |
| |
| void CodecEncoderTest::deleteSource() { |
| if (mInputData) { |
| delete[] mInputData; |
| mInputData = nullptr; |
| } |
| mInputLength = 0; |
| } |
| |
| void CodecEncoderTest::setUpParams(int limit) { |
| int count = 0; |
| for (int k = 0; k < mLen0; k++) { |
| int bitrate = mBitRates[k]; |
| if (mIsAudio) { |
| for (int j = 0; j < mLen1; j++) { |
| int rate = mEncParamList1[j]; |
| for (int i = 0; i < mLen2; i++) { |
| int channels = mEncParamList2[i]; |
| AMediaFormat* format = AMediaFormat_new(); |
| AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mMime); |
| if (!strcmp(mMime, AMEDIA_MIMETYPE_AUDIO_FLAC)) { |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FLAC_COMPRESSION_LEVEL, |
| bitrate); |
| } else { |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, bitrate); |
| } |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, rate); |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, channels); |
| mFormats.push_back(format); |
| count++; |
| if (count >= limit) break; |
| } |
| } |
| } else { |
| for (int j = 0; j < mLen1; j++) { |
| int width = mEncParamList1[j]; |
| int height = mEncParamList2[j]; |
| AMediaFormat* format = AMediaFormat_new(); |
| AMediaFormat_setString(format, AMEDIAFORMAT_KEY_MIME, mMime); |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, bitrate); |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_WIDTH, width); |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_HEIGHT, height); |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, mDefFrameRate); |
| AMediaFormat_setInt32(format, TBD_AMEDIACODEC_PARAMETER_KEY_MAX_B_FRAMES, |
| mMaxBFrames); |
| AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1.0F); |
| AMediaFormat_setInt32(format, AMEDIAFORMAT_KEY_COLOR_FORMAT, mColorFormat); |
| mFormats.push_back(format); |
| count++; |
| if (count >= limit) break; |
| } |
| } |
| } |
| } |
| |
| void CodecEncoderTest::deleteParams() { |
| for (auto format : mFormats) AMediaFormat_delete(format); |
| mFormats.clear(); |
| } |
| |
| void CodecEncoderTest::resetContext(bool isAsync, bool signalEOSWithLastFrame) { |
| CodecTestBase::resetContext(isAsync, signalEOSWithLastFrame); |
| mNumBytesSubmitted = 0; |
| mInputOffsetPts = 0; |
| mNumSyncFramesReceived = 0; |
| mSyncFramesPos.clear(); |
| } |
| |
| bool CodecEncoderTest::flushCodec() { |
| bool isOk = CodecTestBase::flushCodec(); |
| if (mIsAudio) { |
| mInputOffsetPts = (mNumBytesSubmitted + 1024) * 1000000L / (2 * mChannels * mSampleRate); |
| } else { |
| mInputOffsetPts = (mInputCount + 5) * 1000000L / mDefFrameRate; |
| } |
| mPrevOutputPts = mInputOffsetPts - 1; |
| mNumBytesSubmitted = 0; |
| mNumSyncFramesReceived = 0; |
| mSyncFramesPos.clear(); |
| return isOk; |
| } |
| |
| void CodecEncoderTest::fillByteBuffer(uint8_t* inputBuffer) { |
| int width, height, tileWidth, tileHeight; |
| int offset = 0, frmOffset = mNumBytesSubmitted; |
| int numOfPlanes; |
| if (mColorFormat == COLOR_FormatYUV420SemiPlanar) { |
| numOfPlanes = 2; |
| } else { |
| numOfPlanes = 3; |
| } |
| for (int plane = 0; plane < numOfPlanes; plane++) { |
| if (plane == 0) { |
| width = mWidth; |
| height = mHeight; |
| tileWidth = kInpFrmWidth; |
| tileHeight = kInpFrmHeight; |
| } else { |
| if (mColorFormat == COLOR_FormatYUV420SemiPlanar) { |
| width = mWidth; |
| tileWidth = kInpFrmWidth; |
| } else { |
| width = mWidth / 2; |
| tileWidth = kInpFrmWidth / 2; |
| } |
| height = mHeight / 2; |
| tileHeight = kInpFrmHeight / 2; |
| } |
| for (int k = 0; k < height; k += tileHeight) { |
| int rowsToCopy = std::min(height - k, tileHeight); |
| for (int j = 0; j < rowsToCopy; j++) { |
| for (int i = 0; i < width; i += tileWidth) { |
| int colsToCopy = std::min(width - i, tileWidth); |
| memcpy(inputBuffer + (offset + (k + j) * width + i), |
| mInputData + (frmOffset + j * tileWidth), colsToCopy); |
| } |
| } |
| } |
| offset += width * height; |
| frmOffset += tileWidth * tileHeight; |
| } |
| } |
| |
| bool CodecEncoderTest::enqueueInput(size_t bufferIndex) { |
| if (mNumBytesSubmitted >= mInputLength) { |
| return enqueueEOS(bufferIndex); |
| } else { |
| int size = 0; |
| int flags = 0; |
| int64_t pts = mInputOffsetPts; |
| size_t buffSize; |
| uint8_t* inputBuffer = AMediaCodec_getInputBuffer(mCodec, bufferIndex, &buffSize); |
| if (mIsAudio) { |
| pts += mNumBytesSubmitted * 1000000L / (2 * mChannels * mSampleRate); |
| size = std::min(buffSize, mInputLength - mNumBytesSubmitted); |
| memcpy(inputBuffer, mInputData + mNumBytesSubmitted, size); |
| if (mNumBytesSubmitted + size >= mInputLength && mSignalEOSWithLastFrame) { |
| flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM; |
| mSawInputEOS = true; |
| } |
| mNumBytesSubmitted += size; |
| } else { |
| pts += mInputCount * 1000000L / mDefFrameRate; |
| size = mWidth * mHeight * 3 / 2; |
| int frmSize = kInpFrmWidth * kInpFrmHeight * 3 / 2; |
| if (mNumBytesSubmitted + frmSize > mInputLength) { |
| ALOGE("received partial frame to encode"); |
| return false; |
| } else if (size > buffSize) { |
| ALOGE("frame size exceeds buffer capacity of input buffer %d %zu", size, buffSize); |
| return false; |
| } else { |
| if (mWidth == kInpFrmWidth && mHeight == kInpFrmHeight) { |
| memcpy(inputBuffer, mInputData + mNumBytesSubmitted, size); |
| } else { |
| fillByteBuffer(inputBuffer); |
| } |
| } |
| if (mNumBytesSubmitted + frmSize >= mInputLength && mSignalEOSWithLastFrame) { |
| flags |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM; |
| mSawInputEOS = true; |
| } |
| mNumBytesSubmitted += frmSize; |
| } |
| CHECK_STATUS(AMediaCodec_queueInputBuffer(mCodec, bufferIndex, 0, size, pts, flags), |
| "AMediaCodec_queueInputBuffer failed"); |
| ALOGV("input: id: %zu size: %d pts: %" PRId64 " flags: %d", bufferIndex, size, pts, |
| flags); |
| mOutputBuff->saveInPTS(pts); |
| mInputCount++; |
| } |
| return !hasSeenError(); |
| } |
| |
| bool CodecEncoderTest::dequeueOutput(size_t bufferIndex, AMediaCodecBufferInfo* info) { |
| if ((info->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) != 0) { |
| mSawOutputEOS = true; |
| } |
| if (info->size > 0) { |
| if (mSaveToMem) { |
| size_t buffSize; |
| uint8_t* buf = AMediaCodec_getOutputBuffer(mCodec, bufferIndex, &buffSize); |
| mOutputBuff->saveToMemory(buf, info); |
| } |
| if ((info->flags & TBD_AMEDIACODEC_BUFFER_FLAG_KEY_FRAME) != 0) { |
| mNumSyncFramesReceived += 1; |
| mSyncFramesPos.push_back(mOutputCount); |
| } |
| if ((info->flags & AMEDIACODEC_BUFFER_FLAG_CODEC_CONFIG) == 0) { |
| mOutputBuff->saveOutPTS(info->presentationTimeUs); |
| mOutputCount++; |
| } |
| } |
| ALOGV("output: id: %zu size: %d pts: %" PRId64 " flags: %d", bufferIndex, info->size, |
| info->presentationTimeUs, info->flags); |
| CHECK_STATUS(AMediaCodec_releaseOutputBuffer(mCodec, bufferIndex, false), |
| "AMediaCodec_releaseOutputBuffer failed"); |
| return !hasSeenError(); |
| } |
| |
| void CodecEncoderTest::initFormat(AMediaFormat* format) { |
| if (mIsAudio) { |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate); |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &mChannels); |
| } else { |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth); |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight); |
| } |
| } |
| |
| bool CodecEncoderTest::encodeToMemory(const char* file, const char* encoder, int32_t frameLimit, |
| AMediaFormat* format, OutputManager* ref) { |
| /* TODO(b/149027258) */ |
| if (true) mSaveToMem = false; |
| else mSaveToMem = true; |
| mOutputBuff = ref; |
| mCodec = AMediaCodec_createCodecByName(encoder); |
| if (!mCodec) { |
| ALOGE("unable to create codec %s", encoder); |
| return false; |
| } |
| setUpSource(file); |
| if (!mInputData) return false; |
| if (!configureCodec(format, false, true, true)) return false; |
| initFormat(format); |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| if (!doWork(frameLimit)) return false; |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed"); |
| mCodec = nullptr; |
| mSaveToMem = false; |
| return !hasSeenError(); |
| } |
| |
| void CodecEncoderTest::forceSyncFrame(AMediaFormat* format) { |
| AMediaFormat_setInt32(format, TBD_AMEDIACODEC_PARAMETER_KEY_REQUEST_SYNC_FRAME, 0); |
| ALOGV("requesting key frame"); |
| AMediaCodec_setParameters(mCodec, format); |
| } |
| |
| void CodecEncoderTest::updateBitrate(AMediaFormat* format, int bitrate) { |
| AMediaFormat_setInt32(format, TBD_AMEDIACODEC_PARAMETER_KEY_VIDEO_BITRATE, bitrate); |
| ALOGV("requesting bitrate to be changed to %d", bitrate); |
| AMediaCodec_setParameters(mCodec, format); |
| } |
| |
| bool CodecEncoderTest::testSimpleEncode(const char* encoder, const char* srcPath) { |
| bool isPass = true; |
| setUpSource(srcPath); |
| if (!mInputData) return false; |
| setUpParams(INT32_MAX); |
| /* TODO(b/149027258) */ |
| if (true) mSaveToMem = false; |
| else mSaveToMem = true; |
| auto ref = &mRefBuff; |
| auto test = &mTestBuff; |
| const bool boolStates[]{true, false}; |
| for (int i = 0; i < mFormats.size() && isPass; i++) { |
| AMediaFormat* format = mFormats[i]; |
| initFormat(format); |
| int loopCounter = 0; |
| for (auto eosType : boolStates) { |
| if (!isPass) break; |
| for (auto isAsync : boolStates) { |
| if (!isPass) break; |
| char log[1000]; |
| snprintf(log, sizeof(log), |
| "format: %s \n codec: %s, file: %s, mode: %s, eos type: %s:: ", |
| AMediaFormat_toString(format), encoder, srcPath, |
| (isAsync ? "async" : "sync"), |
| (eosType ? "eos with last frame" : "eos separate")); |
| 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 */ |
| mCodec = AMediaCodec_createCodecByName(encoder); |
| if (!mCodec) { |
| ALOGE("%s unable to create media codec by name %s", log, encoder); |
| isPass = false; |
| continue; |
| } |
| char* name = nullptr; |
| if (AMEDIA_OK == AMediaCodec_getName(mCodec, &name)) { |
| if (!name || strcmp(name, encoder) != 0) { |
| ALOGE("%s error codec-name act/got: %s/%s", log, name, encoder); |
| if (name) AMediaCodec_releaseName(mCodec, name); |
| return false; |
| } |
| } else { |
| ALOGE("AMediaCodec_getName failed unexpectedly"); |
| return false; |
| } |
| if (name) AMediaCodec_releaseName(mCodec, name); |
| if (!configureCodec(format, isAsync, eosType, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| if (!doWork(INT32_MAX)) return false; |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed"); |
| mCodec = nullptr; |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass); |
| CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass); |
| CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, |
| "input cnt != output cnt", isPass); |
| CHECK_ERR((loopCounter != 0 && !ref->equals(test)), log, "output is flaky", isPass); |
| CHECK_ERR((loopCounter == 0 && mIsAudio && |
| !ref->isPtsStrictlyIncreasing(mPrevOutputPts)), |
| log, "pts is not strictly increasing", isPass); |
| CHECK_ERR((loopCounter == 0 && !mIsAudio && |
| !ref->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), |
| log, "input pts list and output pts list are not identical", isPass); |
| loopCounter++; |
| } |
| } |
| } |
| return isPass; |
| } |
| |
| bool CodecEncoderTest::testFlush(const char* encoder, const char* srcPath) { |
| bool isPass = true; |
| setUpSource(srcPath); |
| if (!mInputData) return false; |
| setUpParams(1); |
| mOutputBuff = &mTestBuff; |
| AMediaFormat* format = mFormats[0]; |
| initFormat(format); |
| const bool boolStates[]{true, false}; |
| for (auto isAsync : boolStates) { |
| if (!isPass) break; |
| char log[1000]; |
| snprintf(log, sizeof(log), |
| "format: %s \n codec: %s, file: %s, mode: %s:: ", AMediaFormat_toString(format), |
| encoder, srcPath, (isAsync ? "async" : "sync")); |
| /* 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 */ |
| mCodec = AMediaCodec_createCodecByName(encoder); |
| if (!mCodec) { |
| ALOGE("unable to create media codec by name %s", encoder); |
| isPass = false; |
| continue; |
| } |
| if (!configureCodec(format, isAsync, true, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| |
| /* test flush in running state before queuing input */ |
| if (!flushCodec()) return false; |
| mOutputBuff->reset(); |
| if (mIsCodecInAsyncMode) { |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| } |
| if (!doWork(23)) return false; |
| CHECK_ERR((!mOutputBuff->isPtsStrictlyIncreasing(mPrevOutputPts)), log, |
| "pts is not strictly increasing", isPass); |
| if (!isPass) continue; |
| |
| /* test flush in running state */ |
| if (!flushCodec()) return false; |
| mOutputBuff->reset(); |
| if (mIsCodecInAsyncMode) { |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| } |
| if (!doWork(INT32_MAX)) return false; |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass); |
| CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass); |
| CHECK_ERR((mIsAudio && !mOutputBuff->isPtsStrictlyIncreasing(mPrevOutputPts)), log, |
| "pts is not strictly increasing", isPass); |
| CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt", |
| isPass); |
| CHECK_ERR((!mIsAudio && !mOutputBuff->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), |
| log, "input pts list and output pts list are not identical", isPass); |
| if (!isPass) continue; |
| |
| /* test flush in eos state */ |
| if (!flushCodec()) return false; |
| mOutputBuff->reset(); |
| if (mIsCodecInAsyncMode) { |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| } |
| if (!doWork(INT32_MAX)) return false; |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass); |
| CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass); |
| CHECK_ERR((mIsAudio && !mOutputBuff->isPtsStrictlyIncreasing(mPrevOutputPts)), log, |
| "pts is not strictly increasing", isPass); |
| CHECK_ERR(!mIsAudio && (mInputCount != mOutputCount), log, "input cnt != output cnt", |
| isPass); |
| CHECK_ERR(!mIsAudio && (!mOutputBuff->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), |
| log, "input pts list and output pts list are not identical", isPass); |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed"); |
| mCodec = nullptr; |
| } |
| return isPass; |
| } |
| |
| bool CodecEncoderTest::testReconfigure(const char* encoder, const char* srcPath) { |
| bool isPass = true; |
| setUpSource(srcPath); |
| if (!mInputData) return false; |
| setUpParams(2); |
| auto configRef = &mReconfBuff; |
| if (mFormats.size() > 1) { |
| auto format = mFormats[1]; |
| if (!encodeToMemory(srcPath, encoder, INT32_MAX, format, configRef)) { |
| ALOGE("encodeToMemory failed for file: %s codec: %s \n format: %s", srcPath, encoder, |
| AMediaFormat_toString(format)); |
| return false; |
| } |
| CHECK_ERR(mIsAudio && (!configRef->isPtsStrictlyIncreasing(mPrevOutputPts)), "", |
| "pts is not strictly increasing", isPass); |
| CHECK_ERR(!mIsAudio && (!configRef->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), |
| "", "input pts list and output pts list are not identical", isPass); |
| if (!isPass) return false; |
| } |
| auto format = mFormats[0]; |
| auto ref = &mRefBuff; |
| if (!encodeToMemory(srcPath, encoder, INT32_MAX, format, ref)) { |
| ALOGE("encodeToMemory failed for file: %s codec: %s \n format: %s", srcPath, encoder, |
| AMediaFormat_toString(format)); |
| return false; |
| } |
| CHECK_ERR(mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)), "", |
| "pts is not strictly increasing", isPass); |
| CHECK_ERR(!mIsAudio && (!ref->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), "", |
| "input pts list and output pts list are not identical", isPass); |
| if (!isPass) return false; |
| |
| auto test = &mTestBuff; |
| mOutputBuff = test; |
| const bool boolStates[]{true, false}; |
| for (auto isAsync : boolStates) { |
| if (!isPass) break; |
| char log[1000]; |
| snprintf(log, sizeof(log), |
| "format: %s \n codec: %s, file: %s, mode: %s:: ", AMediaFormat_toString(format), |
| encoder, srcPath, (isAsync ? "async" : "sync")); |
| /* 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 */ |
| mCodec = AMediaCodec_createCodecByName(encoder); |
| if (!mCodec) { |
| ALOGE("%s unable to create media codec by name %s", log, encoder); |
| isPass = false; |
| continue; |
| } |
| if (!configureCodec(format, isAsync, true, true)) return false; |
| /* test reconfigure in init state */ |
| if (!reConfigureCodec(format, !isAsync, false, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| |
| /* test reconfigure in running state before queuing input */ |
| if (!reConfigureCodec(format, !isAsync, false, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| if (!doWork(23)) return false; |
| |
| /* test reconfigure codec in running state */ |
| if (!reConfigureCodec(format, isAsync, true, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| |
| /* TODO(b/149027258) */ |
| if (true) mSaveToMem = false; |
| else mSaveToMem = true; |
| test->reset(); |
| if (!doWork(INT32_MAX)) return false; |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass); |
| CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass); |
| CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt", |
| isPass); |
| CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass); |
| if (!isPass) continue; |
| |
| /* test reconfigure codec at eos state */ |
| if (!reConfigureCodec(format, !isAsync, false, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| test->reset(); |
| if (!doWork(INT32_MAX)) return false; |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass); |
| CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass); |
| CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt", |
| isPass); |
| CHECK_ERR((!ref->equals(test)), log, "output is flaky", isPass); |
| |
| /* test reconfigure codec for new format */ |
| if (mFormats.size() > 1) { |
| if (!reConfigureCodec(mFormats[1], isAsync, false, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| test->reset(); |
| if (!doWork(INT32_MAX)) return false; |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass); |
| CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass); |
| CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt", |
| isPass); |
| CHECK_ERR((!configRef->equals(test)), log, "output is flaky", isPass); |
| } |
| mSaveToMem = false; |
| CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed"); |
| mCodec = nullptr; |
| } |
| return isPass; |
| } |
| |
| bool CodecEncoderTest::testOnlyEos(const char* encoder) { |
| bool isPass = true; |
| setUpParams(1); |
| /* TODO(b/149027258) */ |
| if (true) mSaveToMem = false; |
| else mSaveToMem = true; |
| auto ref = &mRefBuff; |
| auto test = &mTestBuff; |
| const bool boolStates[]{true, false}; |
| AMediaFormat* format = mFormats[0]; |
| int loopCounter = 0; |
| for (int k = 0; (k < (sizeof(boolStates) / sizeof(boolStates[0]))) && isPass; k++) { |
| bool isAsync = boolStates[k]; |
| char log[1000]; |
| snprintf(log, sizeof(log), |
| "format: %s \n codec: %s, mode: %s:: ", AMediaFormat_toString(format), encoder, |
| (isAsync ? "async" : "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 */ |
| mCodec = AMediaCodec_createCodecByName(encoder); |
| if (!mCodec) { |
| ALOGE("unable to create media codec by name %s", encoder); |
| isPass = false; |
| continue; |
| } |
| if (!configureCodec(format, isAsync, false, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed"); |
| mCodec = nullptr; |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR(loopCounter != 0 && (!ref->equals(test)), log, "output is flaky", isPass); |
| CHECK_ERR(loopCounter == 0 && mIsAudio && (!ref->isPtsStrictlyIncreasing(mPrevOutputPts)), |
| log, "pts is not strictly increasing", isPass); |
| CHECK_ERR(loopCounter == 0 && !mIsAudio && |
| (!ref->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), |
| log, "input pts list and output pts list are not identical", isPass); |
| loopCounter++; |
| } |
| return isPass; |
| } |
| |
| bool CodecEncoderTest::testSetForceSyncFrame(const char* encoder, const char* srcPath) { |
| bool isPass = true; |
| setUpSource(srcPath); |
| if (!mInputData) return false; |
| setUpParams(1); |
| AMediaFormat* format = mFormats[0]; |
| AMediaFormat_setFloat(format, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 500.f); |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth); |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight); |
| // Maximum allowed key frame interval variation from the target value. |
| int kMaxKeyFrameIntervalVariation = 3; |
| int kKeyFrameInterval = 2; // force key frame every 2 seconds. |
| int kKeyFramePos = mDefFrameRate * kKeyFrameInterval; |
| int kNumKeyFrameRequests = 7; |
| AMediaFormat* params = AMediaFormat_new(); |
| mFormats.push_back(params); |
| mOutputBuff = &mTestBuff; |
| const bool boolStates[]{true, false}; |
| for (auto isAsync : boolStates) { |
| if (!isPass) break; |
| char log[1000]; |
| snprintf(log, sizeof(log), |
| "format: %s \n codec: %s, file: %s, mode: %s:: ", AMediaFormat_toString(format), |
| encoder, srcPath, (isAsync ? "async" : "sync")); |
| 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 */ |
| mCodec = AMediaCodec_createCodecByName(encoder); |
| if (!mCodec) { |
| ALOGE("%s unable to create media codec by name %s", log, encoder); |
| isPass = false; |
| continue; |
| } |
| if (!configureCodec(format, isAsync, false, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| for (int i = 0; i < kNumKeyFrameRequests; i++) { |
| if (!doWork(kKeyFramePos)) return false; |
| assert(!mSawInputEOS); |
| forceSyncFrame(params); |
| mNumBytesSubmitted = 0; |
| } |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed"); |
| mCodec = nullptr; |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass); |
| CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass); |
| CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt", |
| isPass); |
| CHECK_ERR((!mOutputBuff->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), log, |
| "input pts list and output pts list are not identical", isPass); |
| CHECK_ERR((mNumSyncFramesReceived < kNumKeyFrameRequests), log, |
| "Num Sync Frames Received != Num Key Frame Requested", isPass); |
| ALOGD("mNumSyncFramesReceived %d", mNumSyncFramesReceived); |
| for (int i = 0, expPos = 0, index = 0; i < kNumKeyFrameRequests; i++) { |
| int j = index; |
| for (; j < mSyncFramesPos.size(); j++) { |
| // Check key frame intervals: |
| // key frame position should not be greater than target value + 3 |
| // key frame position should not be less than target value - 3 |
| if (abs(expPos - mSyncFramesPos.at(j)) <= kMaxKeyFrameIntervalVariation) { |
| index = j; |
| break; |
| } |
| } |
| if (j == mSyncFramesPos.size()) { |
| ALOGW("requested key frame at frame index %d none found near by", expPos); |
| } |
| expPos += kKeyFramePos; |
| } |
| } |
| return isPass; |
| } |
| |
| bool CodecEncoderTest::testAdaptiveBitRate(const char* encoder, const char* srcPath) { |
| bool isPass = true; |
| setUpSource(srcPath); |
| if (!mInputData) return false; |
| setUpParams(1); |
| AMediaFormat* format = mFormats[0]; |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth); |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight); |
| int kAdaptiveBitrateInterval = 3; // change bitrate every 3 seconds. |
| int kAdaptiveBitrateDurationFrame = mDefFrameRate * kAdaptiveBitrateInterval; |
| int kBitrateChangeRequests = 7; |
| AMediaFormat* params = AMediaFormat_new(); |
| mFormats.push_back(params); |
| // Setting in CBR Mode |
| AMediaFormat_setInt32(format, TBD_AMEDIAFORMAT_KEY_BIT_RATE_MODE, kBitrateModeConstant); |
| mOutputBuff = &mTestBuff; |
| mSaveToMem = true; |
| const bool boolStates[]{true, false}; |
| for (auto isAsync : boolStates) { |
| if (!isPass) break; |
| char log[1000]; |
| snprintf(log, sizeof(log), |
| "format: %s \n codec: %s, file: %s, mode: %s:: ", AMediaFormat_toString(format), |
| encoder, srcPath, (isAsync ? "async" : "sync")); |
| 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 */ |
| mCodec = AMediaCodec_createCodecByName(encoder); |
| if (!mCodec) { |
| ALOGE("%s unable to create media codec by name %s", log, encoder); |
| isPass = false; |
| continue; |
| } |
| if (!configureCodec(format, isAsync, false, true)) return false; |
| CHECK_STATUS(AMediaCodec_start(mCodec), "AMediaCodec_start failed"); |
| int expOutSize = 0; |
| int bitrate; |
| AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_BIT_RATE, &bitrate); |
| for (int i = 0; i < kBitrateChangeRequests; i++) { |
| if (!doWork(kAdaptiveBitrateDurationFrame)) return false; |
| assert(!mSawInputEOS); |
| expOutSize += kAdaptiveBitrateInterval * bitrate; |
| if ((i & 1) == 1) bitrate *= 2; |
| else bitrate /= 2; |
| updateBitrate(params, bitrate); |
| mNumBytesSubmitted = 0; |
| } |
| if (!queueEOS()) return false; |
| if (!waitForAllOutputs()) return false; |
| CHECK_STATUS(AMediaCodec_stop(mCodec), "AMediaCodec_stop failed"); |
| CHECK_STATUS(AMediaCodec_delete(mCodec), "AMediaCodec_delete failed"); |
| mCodec = nullptr; |
| CHECK_ERR((hasSeenError()), log, "has seen error", isPass); |
| CHECK_ERR((0 == mInputCount), log, "queued 0 inputs", isPass); |
| CHECK_ERR((0 == mOutputCount), log, "received 0 outputs", isPass); |
| CHECK_ERR((!mIsAudio && mInputCount != mOutputCount), log, "input cnt != output cnt", |
| isPass); |
| CHECK_ERR((!mOutputBuff->isOutPtsListIdenticalToInpPtsList(mMaxBFrames != 0)), log, |
| "input pts list and output pts list are not identical", isPass); |
| /* TODO: validate output br with sliding window constraints Sec 5.2 cdd */ |
| int outSize = mOutputBuff->getOutStreamSize() * 8; |
| float brDev = abs(expOutSize - outSize) * 100.0f / expOutSize; |
| ALOGD("%s relative bitrate error is %f %%", log, brDev); |
| if (brDev > 50) { |
| ALOGE("%s relative bitrate error is is too large %f %%", log, brDev); |
| return false; |
| } |
| } |
| return isPass; |
| } |
| |
| static jboolean nativeTestSimpleEncode(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath, |
| jstring jMime, jintArray jList0, jintArray jList1, |
| jintArray jList2, jint colorFormat) { |
| const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr); |
| const char* cmime = env->GetStringUTFChars(jMime, nullptr); |
| const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr); |
| jsize cLen0 = env->GetArrayLength(jList0); |
| jint* cList0 = env->GetIntArrayElements(jList0, nullptr); |
| jsize cLen1 = env->GetArrayLength(jList1); |
| jint* cList1 = env->GetIntArrayElements(jList1, nullptr); |
| jsize cLen2 = env->GetArrayLength(jList2); |
| jint* cList2 = env->GetIntArrayElements(jList2, nullptr); |
| auto codecEncoderTest = new CodecEncoderTest(cmime, cList0, cLen0, cList1, cLen1, cList2, cLen2, |
| (int)colorFormat); |
| bool isPass = codecEncoderTest->testSimpleEncode(cEncoder, csrcPath); |
| delete codecEncoderTest; |
| env->ReleaseIntArrayElements(jList0, cList0, 0); |
| env->ReleaseIntArrayElements(jList1, cList1, 0); |
| env->ReleaseIntArrayElements(jList2, cList2, 0); |
| env->ReleaseStringUTFChars(jEncoder, cEncoder); |
| env->ReleaseStringUTFChars(jMime, cmime); |
| env->ReleaseStringUTFChars(jsrcPath, csrcPath); |
| return static_cast<jboolean>(isPass); |
| } |
| |
| static jboolean nativeTestFlush(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath, |
| jstring jMime, jintArray jList0, jintArray jList1, jintArray jList2, |
| jint colorFormat) { |
| const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr); |
| const char* cmime = env->GetStringUTFChars(jMime, nullptr); |
| const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr); |
| jsize cLen0 = env->GetArrayLength(jList0); |
| jint* cList0 = env->GetIntArrayElements(jList0, nullptr); |
| jsize cLen1 = env->GetArrayLength(jList1); |
| jint* cList1 = env->GetIntArrayElements(jList1, nullptr); |
| jsize cLen2 = env->GetArrayLength(jList2); |
| jint* cList2 = env->GetIntArrayElements(jList2, nullptr); |
| auto codecEncoderTest = new CodecEncoderTest(cmime, cList0, cLen0, cList1, cLen1, cList2, cLen2, |
| (int)colorFormat); |
| bool isPass = codecEncoderTest->testFlush(cEncoder, csrcPath); |
| delete codecEncoderTest; |
| env->ReleaseIntArrayElements(jList0, cList0, 0); |
| env->ReleaseIntArrayElements(jList1, cList1, 0); |
| env->ReleaseIntArrayElements(jList2, cList2, 0); |
| env->ReleaseStringUTFChars(jEncoder, cEncoder); |
| env->ReleaseStringUTFChars(jMime, cmime); |
| env->ReleaseStringUTFChars(jsrcPath, csrcPath); |
| return static_cast<jboolean>(isPass); |
| } |
| |
| static jboolean nativeTestReconfigure(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath, |
| jstring jMime, jintArray jList0, jintArray jList1, |
| jintArray jList2, jint colorFormat) { |
| bool isPass; |
| const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr); |
| const char* cmime = env->GetStringUTFChars(jMime, nullptr); |
| const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr); |
| jsize cLen0 = env->GetArrayLength(jList0); |
| jint* cList0 = env->GetIntArrayElements(jList0, nullptr); |
| jsize cLen1 = env->GetArrayLength(jList1); |
| jint* cList1 = env->GetIntArrayElements(jList1, nullptr); |
| jsize cLen2 = env->GetArrayLength(jList2); |
| jint* cList2 = env->GetIntArrayElements(jList2, nullptr); |
| auto codecEncoderTest = new CodecEncoderTest(cmime, cList0, cLen0, cList1, cLen1, cList2, cLen2, |
| (int)colorFormat); |
| isPass = codecEncoderTest->testReconfigure(cEncoder, csrcPath); |
| delete codecEncoderTest; |
| env->ReleaseIntArrayElements(jList0, cList0, 0); |
| env->ReleaseIntArrayElements(jList1, cList1, 0); |
| env->ReleaseIntArrayElements(jList2, cList2, 0); |
| env->ReleaseStringUTFChars(jEncoder, cEncoder); |
| env->ReleaseStringUTFChars(jMime, cmime); |
| env->ReleaseStringUTFChars(jsrcPath, csrcPath); |
| return static_cast<jboolean>(isPass); |
| } |
| |
| static jboolean nativeTestSetForceSyncFrame(JNIEnv* env, jobject, jstring jEncoder, |
| jstring jsrcPath, jstring jMime, jintArray jList0, |
| jintArray jList1, jintArray jList2, jint colorFormat) { |
| const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr); |
| const char* cmime = env->GetStringUTFChars(jMime, nullptr); |
| const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr); |
| jsize cLen0 = env->GetArrayLength(jList0); |
| jint* cList0 = env->GetIntArrayElements(jList0, nullptr); |
| jsize cLen1 = env->GetArrayLength(jList1); |
| jint* cList1 = env->GetIntArrayElements(jList1, nullptr); |
| jsize cLen2 = env->GetArrayLength(jList2); |
| jint* cList2 = env->GetIntArrayElements(jList2, nullptr); |
| auto codecEncoderTest = new CodecEncoderTest(cmime, cList0, cLen0, cList1, cLen1, cList2, cLen2, |
| (int)colorFormat); |
| bool isPass = codecEncoderTest->testSetForceSyncFrame(cEncoder, csrcPath); |
| delete codecEncoderTest; |
| env->ReleaseIntArrayElements(jList0, cList0, 0); |
| env->ReleaseIntArrayElements(jList1, cList1, 0); |
| env->ReleaseIntArrayElements(jList2, cList2, 0); |
| env->ReleaseStringUTFChars(jEncoder, cEncoder); |
| env->ReleaseStringUTFChars(jMime, cmime); |
| env->ReleaseStringUTFChars(jsrcPath, csrcPath); |
| return static_cast<jboolean>(isPass); |
| } |
| |
| static jboolean nativeTestAdaptiveBitRate(JNIEnv* env, jobject, jstring jEncoder, jstring jsrcPath, |
| jstring jMime, jintArray jList0, jintArray jList1, |
| jintArray jList2, jint colorFormat) { |
| const char* csrcPath = env->GetStringUTFChars(jsrcPath, nullptr); |
| const char* cmime = env->GetStringUTFChars(jMime, nullptr); |
| const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr); |
| jsize cLen0 = env->GetArrayLength(jList0); |
| jint* cList0 = env->GetIntArrayElements(jList0, nullptr); |
| jsize cLen1 = env->GetArrayLength(jList1); |
| jint* cList1 = env->GetIntArrayElements(jList1, nullptr); |
| jsize cLen2 = env->GetArrayLength(jList2); |
| jint* cList2 = env->GetIntArrayElements(jList2, nullptr); |
| auto codecEncoderTest = new CodecEncoderTest(cmime, cList0, cLen0, cList1, cLen1, cList2, cLen2, |
| (int)colorFormat); |
| bool isPass = codecEncoderTest->testAdaptiveBitRate(cEncoder, csrcPath); |
| delete codecEncoderTest; |
| env->ReleaseIntArrayElements(jList0, cList0, 0); |
| env->ReleaseIntArrayElements(jList1, cList1, 0); |
| env->ReleaseIntArrayElements(jList2, cList2, 0); |
| env->ReleaseStringUTFChars(jEncoder, cEncoder); |
| env->ReleaseStringUTFChars(jMime, cmime); |
| env->ReleaseStringUTFChars(jsrcPath, csrcPath); |
| return static_cast<jboolean>(isPass); |
| } |
| |
| static jboolean nativeTestOnlyEos(JNIEnv* env, jobject, jstring jEncoder, jstring jMime, |
| jintArray jList0, jintArray jList1, jintArray jList2, |
| jint colorFormat) { |
| const char* cmime = env->GetStringUTFChars(jMime, nullptr); |
| const char* cEncoder = env->GetStringUTFChars(jEncoder, nullptr); |
| jsize cLen0 = env->GetArrayLength(jList0); |
| jint* cList0 = env->GetIntArrayElements(jList0, nullptr); |
| jsize cLen1 = env->GetArrayLength(jList1); |
| jint* cList1 = env->GetIntArrayElements(jList1, nullptr); |
| jsize cLen2 = env->GetArrayLength(jList2); |
| jint* cList2 = env->GetIntArrayElements(jList2, nullptr); |
| auto codecEncoderTest = new CodecEncoderTest(cmime, cList0, cLen0, cList1, cLen1, cList2, cLen2, |
| (int)colorFormat); |
| bool isPass = codecEncoderTest->testOnlyEos(cEncoder); |
| delete codecEncoderTest; |
| env->ReleaseIntArrayElements(jList0, cList0, 0); |
| env->ReleaseIntArrayElements(jList1, cList1, 0); |
| env->ReleaseIntArrayElements(jList2, cList2, 0); |
| env->ReleaseStringUTFChars(jEncoder, cEncoder); |
| env->ReleaseStringUTFChars(jMime, cmime); |
| return static_cast<jboolean>(isPass); |
| } |
| |
| int registerAndroidMediaV2CtsEncoderTest(JNIEnv* env) { |
| const JNINativeMethod methodTable[] = { |
| {"nativeTestSimpleEncode", |
| "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I[II)Z", |
| (void*)nativeTestSimpleEncode}, |
| {"nativeTestFlush", "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I[II)Z", |
| (void*)nativeTestFlush}, |
| {"nativeTestReconfigure", |
| "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I[II)Z", |
| (void*)nativeTestReconfigure}, |
| {"nativeTestSetForceSyncFrame", |
| "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I[II)Z", |
| (void*)nativeTestSetForceSyncFrame}, |
| {"nativeTestAdaptiveBitRate", |
| "(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;[I[I[II)Z", |
| (void*)nativeTestAdaptiveBitRate}, |
| {"nativeTestOnlyEos", "(Ljava/lang/String;Ljava/lang/String;[I[I[II)Z", |
| (void*)nativeTestOnlyEos}, |
| }; |
| jclass c = env->FindClass("android/mediav2/cts/CodecEncoderTest"); |
| return env->RegisterNatives(c, methodTable, sizeof(methodTable) / sizeof(JNINativeMethod)); |
| } |
| |
| extern int registerAndroidMediaV2CtsCodecUnitTest(JNIEnv* env); |
| extern int registerAndroidMediaV2CtsDecoderTest(JNIEnv* env); |
| extern int registerAndroidMediaV2CtsDecoderSurfaceTest(JNIEnv* env); |
| extern int registerAndroidMediaV2CtsEncoderSurfaceTest(JNIEnv* env); |
| |
| extern "C" JNIEXPORT jint JNI_OnLoad(JavaVM* vm, void*) { |
| JNIEnv* env; |
| if (vm->GetEnv(reinterpret_cast<void**>(&env), JNI_VERSION_1_6) != JNI_OK) return JNI_ERR; |
| if (registerAndroidMediaV2CtsCodecUnitTest(env) != JNI_OK) return JNI_ERR; |
| if (registerAndroidMediaV2CtsEncoderTest(env) != JNI_OK) return JNI_ERR; |
| if (registerAndroidMediaV2CtsDecoderTest(env) != JNI_OK) return JNI_ERR; |
| if (registerAndroidMediaV2CtsDecoderSurfaceTest(env) != JNI_OK) return JNI_ERR; |
| if (registerAndroidMediaV2CtsEncoderSurfaceTest(env) != JNI_OK) return JNI_ERR; |
| return JNI_VERSION_1_6; |
| } |