blob: 26fb1b92ce0caddd418427d3e3565e48d92da542 [file] [log] [blame]
/*
* 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) {
mErrorCode = AMEDIA_ERROR_IO;
mSignalledError = true;
mEncoderDoneCondition.notify_one();
return;
}
if (mInputBufferSize < mOffset) {
ALOGE("Out of bound access of input buffer\n");
mErrorCode = AMEDIA_ERROR_MALFORMED;
mSignalledError = true;
mEncoderDoneCondition.notify_one();
return;
}
size_t bytesToRead = mParams.frameSize;
if (mInputBufferSize - mOffset < mParams.frameSize) {
bytesToRead = mInputBufferSize - mOffset;
}
//b/148655275 - Update Frame size, as Format value may not be valid
if (bufSize < bytesToRead) {
if(mNumInputFrame == 0) {
mParams.frameSize = bufSize;
bytesToRead = bufSize;
mParams.numFrames = (mInputBufferSize + mParams.frameSize - 1) / mParams.frameSize;
} else {
ALOGE("bytes to read %zu bufSize %zu \n", bytesToRead, bufSize);
mErrorCode = AMEDIA_ERROR_MALFORMED;
mSignalledError = true;
mEncoderDoneCondition.notify_one();
return;
}
}
if (bytesToRead < mParams.frameSize && mNumInputFrame < mParams.numFrames - 1) {
ALOGE("Partial frame at frameID %d bytesToRead %zu frameSize %d total numFrames %d\n",
mNumInputFrame, bytesToRead, mParams.frameSize, mParams.numFrames);
mErrorCode = AMEDIA_ERROR_MALFORMED;
mSignalledError = true;
mEncoderDoneCondition.notify_one();
return;
}
mEleStream->read(buf, bytesToRead);
size_t bytesgcount = mEleStream->gcount();
if (bytesgcount != bytesToRead) {
ALOGE("bytes to read %zu actual bytes read %zu \n", bytesToRead, bytesgcount);
mErrorCode = AMEDIA_ERROR_MALFORMED;
mSignalledError = true;
mEncoderDoneCondition.notify_one();
return;
}
uint32_t flag = 0;
if (mNumInputFrame == mParams.numFrames - 1 || bytesToRead == 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__,
bytesToRead, presentationTimeUs, mSawInputEOS ? "TRUE" : "FALSE");
media_status_t status = AMediaCodec_queueInputBuffer(mCodec, bufIdx, 0 /* offset */,
bytesToRead, presentationTimeUs, flag);
if (AMEDIA_OK != status) {
mErrorCode = status;
mSignalledError = true;
mEncoderDoneCondition.notify_one();
return;
}
mNumInputFrame++;
mOffset += bytesToRead;
}
}
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;
}
mStats->addFrameSize(bufferInfo->size);
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::onError(AMediaCodec *mediaCodec, media_status_t err) {
ALOGV("In %s", __func__);
if (mediaCodec == mCodec && mediaCodec) {
ALOGE("Received Error %d", err);
mErrorCode = err;
mSignalledError = true;
mEncoderDoneCondition.notify_one();
}
}
void Encoder::setupEncoder() {
if (!mFormat) mFormat = AMediaFormat_new();
}
void Encoder::deInitCodec() {
if (mFormat) {
AMediaFormat_delete(mFormat);
mFormat = nullptr;
}
if (!mCodec) return;
int64_t sTime = mStats->getCurTime();
AMediaCodec_stop(mCodec);
AMediaCodec_delete(mCodec);
int64_t eTime = mStats->getCurTime();
int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
mStats->setDeInitTime(timeTaken);
}
void Encoder::resetEncoder() {
if (mStats) mStats->reset();
if (mEleStream) mEleStream = nullptr;
if (mMime) mMime = nullptr;
mInputBufferSize = 0;
memset(&mParams, 0, sizeof mParams);
}
void Encoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
string mode, string statsFile) {
string operation = "encode";
mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
}
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);
}
AMediaFormat_setInt32(mFormat, AMEDIAFORMAT_KEY_COLOR_FORMAT, mParams.colorFormat);
} 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);
ALOGI("Input format: %s\n", s);
int64_t sTime = mStats->getCurTime();
mCodec = createMediaCodec(mFormat, mMime, codecName, true /*isEncoder*/);
if (!mCodec) return AMEDIA_ERROR_INVALID_OBJECT;
int64_t eTime = mStats->getCurTime();
int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
if (!strncmp(mMime, "video/", 6)) {
mParams.frameSize = mParams.width * mParams.height * 3 / 2;
} else {
mParams.frameSize = kDefaultAudioEncodeFrameSize;
// Get mInputMaxBufSize
AMediaFormat *inputFormat = AMediaCodec_getInputFormat(mCodec);
AMediaFormat_getInt32(inputFormat, AMEDIAFORMAT_KEY_MAX_INPUT_SIZE, &mParams.maxFrameSize);
if (mParams.maxFrameSize < 0) {
mParams.maxFrameSize = kDefaultAudioEncodeFrameSize;
}
if (mParams.frameSize > mParams.maxFrameSize) {
mParams.frameSize = mParams.maxFrameSize;
}
}
mParams.numFrames = (mInputBufferSize + mParams.frameSize - 1) / mParams.frameSize;
sTime = mStats->getCurTime();
if (asyncMode) {
AMediaCodecOnAsyncNotifyCallback aCB = {OnInputAvailableCB, OnOutputAvailableCB,
OnFormatChangedCB, OnErrorCB};
AMediaCodec_setAsyncNotifyCallback(mCodec, aCB, this);
mIOThread = thread(&CallBackHandle::ioThread, this);
}
AMediaCodec_start(mCodec);
eTime = mStats->getCurTime();
timeTaken += mStats->getTimeDiff(sTime, eTime);
mStats->setInitTime(timeTaken);
mStats->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);
mErrorCode = (media_status_t)inIdx;
return mErrorCode;
} else if (inIdx >= 0) {
mStats->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) {
mStats->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);
mErrorCode = (media_status_t)outIdx;
return mErrorCode;
}
}
} else {
unique_lock<mutex> lock(mMutex);
mEncoderDoneCondition.wait(lock, [this]() { return (mSawOutputEOS || mSignalledError); });
}
if (mSignalledError) {
ALOGE("Received Error while Encoding");
return mErrorCode;
}
if (codecName.empty()) {
char *encName;
AMediaCodec_getName(mCodec, &encName);
codecName.assign(encName);
AMediaCodec_releaseName(mCodec, encName);
}
return AMEDIA_OK;
}