blob: 33429efff4ca4285289c6255f3f328a0451301e1 [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 "C2Encoder"
#include "C2Encoder.h"
int32_t C2Encoder::createCodec2Component(string compName, AMediaFormat *format) {
ALOGV("In %s", __func__);
mListener.reset(new CodecListener(
[this](std::list<std::unique_ptr<C2Work>> &workItems) { handleWorkDone(workItems); }));
if (!mListener) return -1;
const char *mime = nullptr;
AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
if (!mime) {
ALOGE("Error in AMediaFormat_getString");
return -1;
}
// Configure the plugin with Input properties
std::vector<C2Param *> configParam;
if (!strncmp(mime, "audio/", 6)) {
mIsAudioEncoder = true;
int32_t numChannels;
if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate)) {
ALOGE("AMEDIAFORMAT_KEY_SAMPLE_RATE not set");
return -1;
}
if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels)) {
ALOGE("AMEDIAFORMAT_KEY_CHANNEL_COUNT not set");
return -1;
}
C2StreamSampleRateInfo::input sampleRateInfo(0u, mSampleRate);
C2StreamChannelCountInfo::input channelCountInfo(0u, numChannels);
configParam.push_back(&sampleRateInfo);
configParam.push_back(&channelCountInfo);
} else {
mIsAudioEncoder = false;
if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth)) {
ALOGE("AMEDIAFORMAT_KEY_WIDTH not set");
return -1;
}
if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight)) {
ALOGE("AMEDIAFORMAT_KEY_HEIGHT not set");
return -1;
}
C2StreamPictureSizeInfo::input inputSize(0u, mWidth, mHeight);
configParam.push_back(&inputSize);
if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mFrameRate) ||
(mFrameRate <= 0)) {
mFrameRate = KDefaultFrameRate;
}
}
int64_t sTime = mStats->getCurTime();
mComponent = mClient->CreateComponentByName(compName.c_str(), mListener, &mClient);
if (mComponent == nullptr) {
ALOGE("Create component failed for %s", compName.c_str());
return -1;
}
std::vector<std::unique_ptr<C2SettingResult>> failures;
int32_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
if (failures.size() != 0) {
ALOGE("Invalid Configuration");
return -1;
}
status |= mComponent->start();
int64_t eTime = mStats->getCurTime();
int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
mStats->setInitTime(timeTaken);
return status;
}
// In encoder components, fetch the size of input buffer allocated
int32_t C2Encoder::getInputMaxBufSize() {
int32_t bitStreamInfo[1] = {0};
std::vector<std::unique_ptr<C2Param>> inParams;
c2_status_t status = mComponent->query({}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE},
C2_DONT_BLOCK, &inParams);
if (status != C2_OK && inParams.size() == 0) {
ALOGE("Query MaxBufferSizeInfo failed => %d", status);
return status;
} else {
size_t offset = sizeof(C2Param);
for (size_t i = 0; i < inParams.size(); ++i) {
C2Param *param = inParams[i].get();
bitStreamInfo[i] = *(int32_t *)((uint8_t *)param + offset);
}
}
mInputMaxBufSize = bitStreamInfo[0];
if (mInputMaxBufSize < 0) {
ALOGE("Invalid mInputMaxBufSize %d\n", mInputMaxBufSize);
return -1;
}
return status;
}
int32_t C2Encoder::encodeFrames(ifstream &eleStream, size_t inputBufferSize) {
ALOGV("In %s", __func__);
int32_t frameSize = 0;
if (!mIsAudioEncoder) {
frameSize = mWidth * mHeight * 3 / 2;
} else {
frameSize = DEFAULT_AUDIO_FRAME_SIZE;
if (getInputMaxBufSize() != 0) return -1;
if (frameSize > mInputMaxBufSize) {
frameSize = mInputMaxBufSize;
}
}
int32_t numFrames = (inputBufferSize + frameSize - 1) / frameSize;
// Temporary buffer to read data from the input file
char *data = (char *)malloc(frameSize);
if (!data) {
ALOGE("Insufficient memory to read from input file");
return -1;
}
typedef std::unique_lock<std::mutex> ULock;
uint64_t presentationTimeUs = 0;
size_t offset = 0;
c2_status_t status = C2_OK;
mStats->setStartTime();
while (numFrames > 0) {
std::unique_ptr<C2Work> work;
// Prepare C2Work
{
ULock l(mQueueLock);
if (mWorkQueue.empty()) mQueueCondition.wait_for(l, MAX_RETRY * TIME_OUT);
if (!mWorkQueue.empty()) {
mStats->addInputTime();
work.swap(mWorkQueue.front());
mWorkQueue.pop_front();
} else {
cout << "Wait for generating C2Work exceeded timeout" << endl;
return -1;
}
}
if (mIsAudioEncoder) {
presentationTimeUs = mNumInputFrame * frameSize * (1000000 / mSampleRate);
} else {
presentationTimeUs = mNumInputFrame * (1000000 / mFrameRate);
}
uint32_t flags = 0;
if (numFrames == 1) flags |= C2FrameData::FLAG_END_OF_STREAM;
work->input.flags = (C2FrameData::flags_t)flags;
work->input.ordinal.timestamp = presentationTimeUs;
work->input.ordinal.frameIndex = mNumInputFrame;
work->input.buffers.clear();
if (inputBufferSize - offset < frameSize) {
frameSize = inputBufferSize - offset;
}
eleStream.read(data, frameSize);
if (eleStream.gcount() != frameSize) {
ALOGE("read() from file failed. Incorrect bytes read");
return -1;
}
offset += frameSize;
if (frameSize) {
if (mIsAudioEncoder) {
std::shared_ptr<C2LinearBlock> block;
status = mLinearPool->fetchLinearBlock(
frameSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
if (status != C2_OK || !block) {
cout << "fetchLinearBlock failed : " << status << endl;
return status;
}
C2WriteView view = block->map().get();
if (view.error() != C2_OK) {
cout << "C2LinearBlock::map() failed : " << view.error() << endl;
return view.error();
}
memcpy(view.base(), data, frameSize);
work->input.buffers.emplace_back(new LinearBuffer(block));
} else {
std::shared_ptr<C2GraphicBlock> block;
status = mGraphicPool->fetchGraphicBlock(
mWidth, mHeight, HAL_PIXEL_FORMAT_YV12,
{C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
if (status != C2_OK || !block) {
cout << "fetchGraphicBlock failed : " << status << endl;
return status;
}
C2GraphicView view = block->map().get();
if (view.error() != C2_OK) {
cout << "C2GraphicBlock::map() failed : " << view.error() << endl;
return view.error();
}
uint8_t *pY = view.data()[C2PlanarLayout::PLANE_Y];
uint8_t *pU = view.data()[C2PlanarLayout::PLANE_U];
uint8_t *pV = view.data()[C2PlanarLayout::PLANE_V];
memcpy(pY, data, mWidth * mHeight);
memcpy(pU, data + mWidth * mHeight, (mWidth * mHeight >> 2));
memcpy(pV, data + (mWidth * mHeight * 5 >> 2), mWidth * mHeight >> 2);
work->input.buffers.emplace_back(new GraphicBuffer(block));
}
mStats->addFrameSize(frameSize);
}
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
std::list<std::unique_ptr<C2Work>> items;
items.push_back(std::move(work));
// queue() invokes process() function of C2 Plugin.
status = mComponent->queue(&items);
if (status != C2_OK) {
ALOGE("queue failed");
return status;
}
ALOGV("Frame #%d size = %d queued", mNumInputFrame, frameSize);
numFrames--;
mNumInputFrame++;
}
free(data);
return status;
}
void C2Encoder::deInitCodec() {
ALOGV("In %s", __func__);
if (!mComponent) return;
int64_t sTime = mStats->getCurTime();
mComponent->stop();
mComponent->release();
mComponent = nullptr;
int64_t eTime = mStats->getCurTime();
int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
mStats->setDeInitTime(timeTaken);
}
void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs) {
string operation = "c2encode";
mStats->dumpStatistics(operation, inputReference, durationUs);
}
void C2Encoder::resetEncoder() {
mIsAudioEncoder = false;
mNumInputFrame = 0;
mEos = false;
if (mStats) mStats->reset();
}