Donate to e Foundation | Murena handsets with /e/OS | Own a part of Murena! Learn more

Commit 8d92a02d authored by Rakesh Kumar's avatar Rakesh Kumar Committed by android-build-merger
Browse files

Merge "Benchmark: add Encoder" am: 0647d8c5

am: 8c8ce72c

Change-Id: I83b38de79a51d9a1008d6c1c2a67929c950f9989
parents 25cab90e 8c8ce72c
Loading
Loading
Loading
Loading
+10 −0
Original line number Diff line number Diff line
@@ -48,3 +48,13 @@ Setup steps are same as extractor.
```
adb shell /data/local/tmp/muxerTest -P /sdcard/res/
```

## Encoder

The test encodes input stream and benchmarks the encoders available in NDK.

Setup steps are same as extractor.

```
adb shell /data/local/tmp/encoderTest -P /sdcard/res/
```
+11 −1
Original line number Diff line number Diff line
@@ -96,6 +96,15 @@ void Decoder::onOutputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx,
            return;
        }

        if (mOutFp != nullptr) {
            size_t bufSize;
            uint8_t *buf = AMediaCodec_getOutputBuffer(mCodec, bufIdx, &bufSize);
            if (buf) {
                fwrite(buf, sizeof(char), bufferInfo->size, mOutFp);
                ALOGV("bytes written into file  %d\n", bufferInfo->size);
            }
        }

        AMediaCodec_releaseOutputBuffer(mCodec, bufIdx, false);
        mSawOutputEOS = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM));
        mNumOutputFrame++;
@@ -123,11 +132,12 @@ void Decoder::setupDecoder() {
}

int32_t Decoder::decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
                        string &codecName, bool asyncMode) {
                        string &codecName, bool asyncMode, FILE *outFp) {
    ALOGV("In %s", __func__);
    mInputBuffer = inputBuffer;
    mFrameMetaData = frameInfo;
    mOffset = 0;
    mOutFp = outFp;

    const char *mime = nullptr;
    AMediaFormat_getString(mFormat, AMEDIAFORMAT_KEY_MIME, &mime);
+4 −2
Original line number Diff line number Diff line
@@ -39,7 +39,8 @@ class Decoder : public CallBackHandle {
          mSawInputEOS(false),
          mSawOutputEOS(false),
          mSignalledError(false),
          mInputBuffer(nullptr) {
          mInputBuffer(nullptr),
          mOutFp(nullptr) {
        mExtractor = new Extractor();
    }

@@ -69,7 +70,7 @@ class Decoder : public CallBackHandle {

    // Process the frames and give decoded output
    int32_t decode(uint8_t *inputBuffer, vector<AMediaCodecBufferInfo> &frameInfo,
                   string &codecName, bool asyncMode);
                   string &codecName, bool asyncMode, FILE *outFp = nullptr);

    void dumpStatistics(string inputReference);

@@ -91,6 +92,7 @@ class Decoder : public CallBackHandle {
    int32_t mOffset;
    uint8_t *mInputBuffer;
    vector<AMediaCodecBufferInfo> mFrameMetaData;
    FILE *mOutFp;

    /* Asynchronous locks */
    mutex mMutex;
+33 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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.
 */

cc_library_static {
    name: "libbenchmark_encoder",
    defaults: [
        "libbenchmark_common-defaults",
        "libbenchmark_soft_sanitize_all-defaults",
    ],

    srcs: ["Encoder.cpp"],

    static_libs: ["libbenchmark_extractor",
                  "libbenchmark_decoder",
    ],

    export_include_dirs: ["."],

    ldflags: ["-Wl,-Bsymbolic"]
}
+274 −0
Original line number Diff line number Diff line
/*
 * Copyright (C) 2019 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 "encoder"

#include <fstream>

#include "Encoder.h"

void Encoder::onInputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx) {
    ALOGV("In %s", __func__);
    if (mediaCodec == mCodec && mediaCodec) {
        if (mSawInputEOS || bufIdx < 0) return;
        if (mSignalledError) {
            CallBackHandle::mSawError = true;
            mEncoderDoneCondition.notify_one();
            return;
        }

        size_t bufSize = 0;
        char *buf = (char *)AMediaCodec_getInputBuffer(mCodec, bufIdx, &bufSize);
        if (!buf) {
            mSignalledError = true;
            mEncoderDoneCondition.notify_one();
            return;
        }

        if (mInputBufferSize < mOffset) {
            ALOGE("Out of bound access of input buffer\n");
            mSignalledError = true;
            mEncoderDoneCondition.notify_one();
            return;
        }
        size_t bytesRead = mParams.frameSize;
        if (mInputBufferSize - mOffset < mParams.frameSize) {
            bytesRead = mInputBufferSize - mOffset;
        }
        if (bufSize < bytesRead) {
            ALOGE("bytes to read %zu bufSize %zu \n", bytesRead, bufSize);
            mSignalledError = true;
            mEncoderDoneCondition.notify_one();
            return;
        }
        if (bytesRead < mParams.frameSize && mNumInputFrame < mParams.numFrames - 1) {
            ALOGE("Partial frame at frameID %d bytesRead %zu frameSize %d total numFrames %d\n",
                  mNumInputFrame, bytesRead, mParams.frameSize, mParams.numFrames);
            mSignalledError = true;
            mEncoderDoneCondition.notify_one();
            return;
        }
        mEleStream->read(buf, bytesRead);
        size_t bytesgcount = mEleStream->gcount();
        if (bytesgcount != bytesRead) {
            ALOGE("bytes to read %zu actual bytes read %zu \n", bytesRead, bytesgcount);
            mSignalledError = true;
            mEncoderDoneCondition.notify_one();
            return;
        }

        uint32_t flag = 0;
        if (mNumInputFrame == mParams.numFrames - 1 || bytesRead == 0) {
            ALOGD("Sending EOS on input Last frame\n");
            flag |= AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
        }

        uint64_t presentationTimeUs;
        if (!strncmp(mMime, "video/", 6)) {
            presentationTimeUs = mNumInputFrame * (1000000 / mParams.frameRate);
        } else {
            presentationTimeUs =
                    (uint64_t)mNumInputFrame * mParams.frameSize * 1000000 / mParams.sampleRate;
        }

        if (flag == AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) mSawInputEOS = true;
        ALOGV("%s bytesRead : %zd presentationTimeUs : %" PRIu64 " mSawInputEOS : %s", __FUNCTION__,
              bytesRead, presentationTimeUs, mSawInputEOS ? "TRUE" : "FALSE");

        int status = AMediaCodec_queueInputBuffer(mCodec, bufIdx, 0 /* offset */, bytesRead,
                                                  presentationTimeUs, flag);
        if (AMEDIA_OK != status) {
            mSignalledError = true;
            mEncoderDoneCondition.notify_one();
            return;
        }
        mNumInputFrame++;
        mOffset += bytesRead;
    }
}

void Encoder::onOutputAvailable(AMediaCodec *mediaCodec, int32_t bufIdx,
                                AMediaCodecBufferInfo *bufferInfo) {
    ALOGV("In %s", __func__);
    if (mediaCodec == mCodec && mediaCodec) {
        if (mSawOutputEOS || bufIdx < 0) return;
        if (mSignalledError) {
            CallBackHandle::mSawError = true;
            mEncoderDoneCondition.notify_one();
            return;
        }

        AMediaCodec_releaseOutputBuffer(mCodec, bufIdx, false);
        mSawOutputEOS = (0 != (bufferInfo->flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM));
        mNumOutputFrame++;
        ALOGV("%s index : %d  mSawOutputEOS : %s count : %u", __FUNCTION__, bufIdx,
              mSawOutputEOS ? "TRUE" : "FALSE", mNumOutputFrame);
        if (mSawOutputEOS) {
            CallBackHandle::mIsDone = true;
            mEncoderDoneCondition.notify_one();
        }
    }
}

void Encoder::onFormatChanged(AMediaCodec *mediaCodec, AMediaFormat *format) {
    ALOGV("In %s", __func__);
    if (mediaCodec == mCodec && mediaCodec) {
        ALOGV("%s { %s }", __FUNCTION__, AMediaFormat_toString(format));
        mFormat = format;
    }
}

void Encoder::setupEncoder() {
    if (!mFormat) mFormat = AMediaFormat_new();
    if (!mTimer) mTimer = new Timer();
}

void Encoder::deInitCodec() {
    int64_t sTime = mTimer->getCurTime();
    if (mFormat) {
        AMediaFormat_delete(mFormat);
        mFormat = nullptr;
    }
    AMediaCodec_stop(mCodec);
    AMediaCodec_delete(mCodec);
    int64_t eTime = mTimer->getCurTime();
    int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);
    mTimer->setDeInitTime(timeTaken);
}

void Encoder::resetEncoder() {
    if (mTimer) mTimer->resetTimers();
    if (mEleStream) mEleStream = nullptr;
    if (mMime) mMime = nullptr;
    mInputBufferSize = 0;
    memset(&mParams, 0, sizeof mParams);
}

void Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
    string operation = "encode";
    mTimer->dumpStatistics(operation, inputReference, durationUs);
}

int32_t Encoder::encode(string &codecName, ifstream &eleStream, size_t eleSize,
                        bool asyncMode, encParameter encParams, char *mime) {
    ALOGV("In %s", __func__);
    mEleStream = &eleStream;
    mInputBufferSize = eleSize;
    mParams = encParams;
    mOffset = 0;
    mMime = mime;
    AMediaFormat_setString(mFormat, AMEDIAFORMAT_KEY_MIME, mMime);

    // Set Format
    if (!strncmp(mMime, "video/", 6)) {
        AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_WIDTH, mParams.width);
        AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_HEIGHT, mParams.height);
        AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_FRAME_RATE, mParams.frameRate);
        AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, mParams.bitrate);
        AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, 1);
        if (mParams.profile && mParams.level) {
            AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_PROFILE, mParams.profile);
            AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_LEVEL, mParams.level);
        }
    } else {
        AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_SAMPLE_RATE, mParams.sampleRate);
        AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_CHANNEL_COUNT, mParams.numChannels);
        AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_BIT_RATE, mParams.bitrate);
    }
    const char *s = AMediaFormat_toString(mFormat);
    ALOGV("Input format: %s\n", s);

    int64_t sTime = mTimer->getCurTime();
    mCodec = createMediaCodec(mFormat, mMime, codecName, true /*isEncoder*/);
    if (!mCodec) return AMEDIA_ERROR_INVALID_OBJECT;
    int64_t eTime = mTimer->getCurTime();
    int64_t timeTaken = mTimer->getTimeDiff(sTime, eTime);

    if (!strncmp(mMime, "video/", 6)) {
        mParams.frameSize = mParams.width * mParams.height * 3 / 2;
    } else {
        mParams.frameSize = 4096;
        // Get mInputMaxBufSize
        AMediaFormat *inputFormat = AMediaCodec_getInputFormat(mCodec);
        AMediaFormat_getInt32(inputFormat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, &mParams.maxFrameSize);
        if (mParams.maxFrameSize < 0) {
            ALOGE("Invalid mParams.maxFrameSize %d\n", mParams.maxFrameSize);
            return AMEDIA_ERROR_INVALID_PARAMETER;
        }
        if (mParams.frameSize > mParams.maxFrameSize) {
            mParams.frameSize = mParams.maxFrameSize;
        }
    }
    mParams.numFrames = (mInputBufferSize + mParams.frameSize - 1) / mParams.frameSize;

    sTime = mTimer->getCurTime();
    if (asyncMode) {
        AMediaCodecOnAsyncNotifyCallback aCB = {OnInputAvailableCB, OnOutputAvailableCB,
                                                OnFormatChangedCB, OnErrorCB};
        AMediaCodec_setAsyncNotifyCallback(mCodec, aCB, this);
        CallBackHandle *callbackHandle = new CallBackHandle();
        callbackHandle->mIOThread = thread(&CallBackHandle::ioThread, this);
    }
    AMediaCodec_start(mCodec);
    eTime = mTimer->getCurTime();
    timeTaken += mTimer->getTimeDiff(sTime, eTime);
    mTimer->setInitTime(timeTaken);

    mTimer->setStartTime();
    if (!asyncMode) {
        while (!mSawOutputEOS && !mSignalledError) {
            // Queue input data
            if (!mSawInputEOS) {
                ssize_t inIdx = AMediaCodec_dequeueInputBuffer(mCodec, kQueueDequeueTimeoutUs);
                if (inIdx < 0 && inIdx != AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
                    ALOGE("AMediaCodec_dequeueInputBuffer returned invalid index %zd\n", inIdx);
                    return AMEDIA_ERROR_IO;
                } else if (inIdx >= 0) {
                    mTimer->addInputTime();
                    onInputAvailable(mCodec, inIdx);
                }
            }

            // Dequeue output data
            AMediaCodecBufferInfo info;
            ssize_t outIdx = AMediaCodec_dequeueOutputBuffer(mCodec, &info, kQueueDequeueTimeoutUs);
            if (outIdx == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
                mFormat = AMediaCodec_getOutputFormat(mCodec);
                const char *s = AMediaFormat_toString(mFormat);
                ALOGI("Output format: %s\n", s);
            } else if (outIdx >= 0) {
                mTimer->addOutputTime();
                onOutputAvailable(mCodec, outIdx, &info);
            } else if (!(outIdx == AMEDIACODEC_INFO_TRY_AGAIN_LATER ||
                         outIdx == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED)) {
                ALOGE("AMediaCodec_dequeueOutputBuffer returned invalid index %zd\n", outIdx);
                return AMEDIA_ERROR_IO;
            }
        }
    } else {
        unique_lock<mutex> lock(mMutex);
        mEncoderDoneCondition.wait(lock, [this]() { return (mSawOutputEOS || mSignalledError); });
    }

    if (codecName.empty()) {
        char *encName;
        AMediaCodec_getName(mCodec, &encName);
        codecName.assign(encName);
        AMediaCodec_releaseName(mCodec, encName);
    }
    return AMEDIA_OK;
}
Loading