blob: b287b91c14e4b8649d1c12d9b05d31fc72f98095 [file] [log] [blame]
/*
* Copyright 2023 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 "Codec2-MultiAccessUnitHelper"
#include <android-base/logging.h>
#include <com_android_media_codec_flags.h>
#include <codec2/common/MultiAccessUnitHelper.h>
#include <android-base/properties.h>
#include <C2BufferPriv.h>
#include <C2Debug.h>
#include <C2PlatformSupport.h>
static inline constexpr uint32_t MAX_SUPPORTED_SIZE = ( 10 * 512000 * 8 * 2u);
namespace android {
static C2R MultiAccessUnitParamsSetter(
bool mayBlock, C2InterfaceHelper::C2P<C2LargeFrame::output> &me) {
(void)mayBlock;
C2R res = C2R::Ok();
if (!me.F(me.v.maxSize).supportsAtAll(me.v.maxSize)) {
res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.maxSize)));
} else if (!me.F(me.v.thresholdSize).supportsAtAll(me.v.thresholdSize)) {
res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.thresholdSize)));
} else if (me.v.maxSize < me.v.thresholdSize) {
me.set().maxSize = me.v.thresholdSize;
}
std::vector<std::unique_ptr<C2SettingResult>> failures;
res.retrieveFailures(&failures);
if (!failures.empty()) {
me.set().maxSize = 0;
me.set().thresholdSize = 0;
}
return res;
}
MultiAccessUnitInterface::MultiAccessUnitInterface(
const std::shared_ptr<C2ComponentInterface>& interface,
std::shared_ptr<C2ReflectorHelper> helper)
: C2InterfaceHelper(helper), mC2ComponentIntf(interface) {
setDerivedInstance(this);
addParameter(
DefineParam(mLargeFrameParams, C2_PARAMKEY_OUTPUT_LARGE_FRAME)
.withDefault(new C2LargeFrame::output(0u, 0, 0))
.withFields({
C2F(mLargeFrameParams, maxSize).inRange(
0, c2_min(UINT_MAX, MAX_SUPPORTED_SIZE)),
C2F(mLargeFrameParams, thresholdSize).inRange(
0, c2_min(UINT_MAX, MAX_SUPPORTED_SIZE))
})
.withSetter(MultiAccessUnitParamsSetter)
.build());
std::vector<std::shared_ptr<C2ParamDescriptor>> supportedParams;
querySupportedParams(&supportedParams);
// Adding to set to do intf seperation in query/config
for (std::shared_ptr<C2ParamDescriptor> &desc : supportedParams) {
mSupportedParamIndexSet.insert(desc->index());
}
mParamFields.emplace_back(mLargeFrameParams.get(), &(mLargeFrameParams.get()->maxSize));
mParamFields.emplace_back(mLargeFrameParams.get(), &(mLargeFrameParams.get()->thresholdSize));
if (mC2ComponentIntf) {
c2_status_t err = mC2ComponentIntf->query_vb({&mKind}, {}, C2_MAY_BLOCK, nullptr);
}
}
bool MultiAccessUnitInterface::isValidField(const C2ParamField &field) const {
return (std::find(mParamFields.begin(), mParamFields.end(), field) != mParamFields.end());
}
bool MultiAccessUnitInterface::isParamSupported(C2Param::Index index) {
return (mSupportedParamIndexSet.count(index) != 0);
}
C2LargeFrame::output MultiAccessUnitInterface::getLargeFrameParam() const {
return *mLargeFrameParams;
}
C2Component::kind_t MultiAccessUnitInterface::kind() const {
return (C2Component::kind_t)(mKind.value);
}
bool MultiAccessUnitInterface::getDecoderSampleRateAndChannelCount(
uint32_t * const sampleRate_, uint32_t * const channelCount_) const {
if (sampleRate_ == nullptr || channelCount_ == nullptr) {
return false;
}
if (mC2ComponentIntf) {
C2StreamSampleRateInfo::output sampleRate;
C2StreamChannelCountInfo::output channelCount;
c2_status_t res = mC2ComponentIntf->query_vb(
{&sampleRate, &channelCount}, {}, C2_MAY_BLOCK, nullptr);
if (res == C2_OK && sampleRate.value > 0 && channelCount.value > 0) {
*sampleRate_ = sampleRate.value;
*channelCount_ = channelCount.value;
return true;
}
}
return false;
}
bool MultiAccessUnitInterface::getMaxInputSize(
C2StreamMaxBufferSizeInfo::input* const maxInputSize) const {
if (maxInputSize == nullptr || mC2ComponentIntf == nullptr) {
return false;
}
c2_status_t err = mC2ComponentIntf->query_vb({maxInputSize}, {}, C2_MAY_BLOCK, nullptr);
if (err != OK) {
return false;
}
return true;
}
//C2MultiAccessUnitBuffer
class C2MultiAccessUnitBuffer : public C2Buffer {
public:
explicit C2MultiAccessUnitBuffer(
const std::vector<C2ConstLinearBlock> &blocks):
C2Buffer(blocks) {
}
};
//MultiAccessUnitHelper
MultiAccessUnitHelper::MultiAccessUnitHelper(
const std::shared_ptr<MultiAccessUnitInterface>& intf,
std::shared_ptr<C2BlockPool>& linearPool):
mMultiAccessOnOffAllowed(true),
mInit(false),
mInterface(intf),
mLinearPool(linearPool) {
if (mLinearPool) {
mInit = true;
}
}
MultiAccessUnitHelper::~MultiAccessUnitHelper() {
std::unique_lock<std::mutex> l(mLock);
mFrameHolder.clear();
}
bool MultiAccessUnitHelper::isEnabledOnPlatform() {
bool result = com::android::media::codec::flags::provider_->large_audio_frame();
if (!result) {
return false;
}
//TODO: remove this before launch
result = ::android::base::GetBoolProperty("debug.media.c2.large.audio.frame", true);
LOG(DEBUG) << "MultiAccessUnitHelper " << (result ? "enabled" : "disabled");
return result;
}
bool MultiAccessUnitHelper::tryReconfigure(const std::unique_ptr<C2Param> &param) {
C2LargeFrame::output *lfp = C2LargeFrame::output::From(param.get());
if (lfp == nullptr) {
return false;
}
bool isDecoder = (mInterface->kind() == C2Component::KIND_DECODER) ? true : false;
if (!isDecoder) {
C2StreamMaxBufferSizeInfo::input maxInputSize(0);
if (!mInterface->getMaxInputSize(&maxInputSize)) {
LOG(ERROR) << "Error in reconfigure: "
<< "Encoder failed to respond with a valid max input size";
return false;
}
// This is assuming a worst case compression ratio of 1:1
// In no case the encoder should give an output more than
// what is being provided to the encoder in a single call.
if (lfp->maxSize < maxInputSize.value) {
lfp->maxSize = maxInputSize.value;
}
}
lfp->maxSize =
(lfp->maxSize > MAX_SUPPORTED_SIZE) ? MAX_SUPPORTED_SIZE :
(lfp->maxSize < 0) ? 0 : lfp->maxSize;
lfp->thresholdSize =
(lfp->thresholdSize > MAX_SUPPORTED_SIZE) ? MAX_SUPPORTED_SIZE :
(lfp->thresholdSize < 0) ? 0 : lfp->thresholdSize;
C2LargeFrame::output currentConfig = mInterface->getLargeFrameParam();
if ((currentConfig.maxSize == lfp->maxSize)
&& (currentConfig.thresholdSize == lfp->thresholdSize)) {
// no need to update
return false;
}
if (isDecoder) {
bool isOnOffTransition =
(currentConfig.maxSize == 0 && lfp->maxSize != 0)
|| (currentConfig.maxSize != 0 && lfp->maxSize == 0);
if (isOnOffTransition && !mMultiAccessOnOffAllowed) {
LOG(ERROR) << "Setting new configs not allowed"
<< " MaxSize: " << lfp->maxSize
<< " ThresholdSize: " << lfp->thresholdSize;
return false;
}
}
std::vector<C2Param*> config{lfp};
std::vector<std::unique_ptr<C2SettingResult>> failures;
if (C2_OK != mInterface->config(config, C2_MAY_BLOCK, &failures)) {
LOG(ERROR) << "Dynamic config not applied for"
<< " MaxSize: " << lfp->maxSize
<< " ThresholdSize: " << lfp->thresholdSize;
return false;
}
LOG(DEBUG) << "Updated from param maxSize "
<< lfp->maxSize
<< " ThresholdSize " << lfp->thresholdSize;
return true;
}
std::shared_ptr<MultiAccessUnitInterface> MultiAccessUnitHelper::getInterface() {
return mInterface;
}
bool MultiAccessUnitHelper::getStatus() {
return mInit;
}
void MultiAccessUnitHelper::reset() {
std::lock_guard<std::mutex> l(mLock);
mFrameHolder.clear();
mMultiAccessOnOffAllowed = true;
}
c2_status_t MultiAccessUnitHelper::error(
std::list<std::unique_ptr<C2Work>> * const worklist) {
if (worklist == nullptr) {
LOG(ERROR) << "Provided null worklist for error()";
mFrameHolder.clear();
return C2_OK;
}
std::unique_lock<std::mutex> l(mLock);
for (auto frame = mFrameHolder.begin(); frame != mFrameHolder.end(); frame++) {
if (frame->mLargeWork) {
finalizeWork(*frame, 0, true);
worklist->push_back(std::move(frame->mLargeWork));
frame->reset();
}
}
mFrameHolder.clear();
mMultiAccessOnOffAllowed = true;
return C2_OK;
}
c2_status_t MultiAccessUnitHelper::flush(
std::list<std::unique_ptr<C2Work>>* const c2flushedWorks) {
c2_status_t c2res = C2_OK;
std::lock_guard<std::mutex> l(mLock);
for (auto iterWork = c2flushedWorks->begin() ; iterWork != c2flushedWorks->end(); ) {
bool foundFlushedFrame = false;
std::list<MultiAccessUnitInfo>::iterator frame =
mFrameHolder.begin();
while (frame != mFrameHolder.end() && !foundFlushedFrame) {
auto it = frame->mComponentFrameIds.find(
(*iterWork)->input.ordinal.frameIndex.peekull());
if (it != frame->mComponentFrameIds.end()) {
LOG(DEBUG) << "Multi access-unit flush "
<< (*iterWork)->input.ordinal.frameIndex.peekull()
<< " with " << frame->inOrdinal.frameIndex.peekull();
(*iterWork)->input.ordinal.frameIndex = frame->inOrdinal.frameIndex;
frame = mFrameHolder.erase(frame);
foundFlushedFrame = true;
} else {
++frame;
}
}
if (!foundFlushedFrame) {
iterWork = c2flushedWorks->erase(iterWork);
} else {
++iterWork;
}
}
return c2res;
}
c2_status_t MultiAccessUnitHelper::scatter(
std::list<std::unique_ptr<C2Work>> &largeWork,
std::list<std::list<std::unique_ptr<C2Work>>>* const processedWork) {
LOG(DEBUG) << "Multiple access-unit: scatter";
if (processedWork == nullptr) {
LOG(ERROR) << "MultiAccessUnitHelper provided with no work list";
return C2_CORRUPTED;
}
for (std::unique_ptr<C2Work>& w : largeWork) {
std::list<std::unique_ptr<C2Work>> sliceWork;
C2WorkOrdinalStruct inputOrdinal = w->input.ordinal;
// To hold correspondence and processing bits b/w input and output
MultiAccessUnitInfo frameInfo(inputOrdinal);
std::set<uint64_t>& frameSet = frameInfo.mComponentFrameIds;
uint64_t newFrameIdx = mFrameIndex++;
// TODO: Do not split buffers if component inherantly supports MultipleFrames.
// if thats case, only replace frameindex.
auto cloneInputWork = [&frameInfo, &newFrameIdx, this]
(std::unique_ptr<C2Work>& inWork, uint32_t flags) -> std::unique_ptr<C2Work> {
std::unique_ptr<C2Work> newWork(new C2Work);
newWork->input.flags = (C2FrameData::flags_t)flags;
newWork->input.ordinal = inWork->input.ordinal;
newWork->input.ordinal.frameIndex = newFrameIdx;
if (!inWork->input.configUpdate.empty()) {
for (std::unique_ptr<C2Param>& param : inWork->input.configUpdate) {
if (param->index() == C2LargeFrame::output::PARAM_TYPE) {
if (tryReconfigure(param)) {
frameInfo.mConfigUpdate.push_back(std::move(param));
}
} else {
newWork->input.configUpdate.push_back(std::move(param));
}
}
inWork->input.configUpdate.clear();
}
newWork->input.infoBuffers = (inWork->input.infoBuffers);
if (!inWork->worklets.empty() && inWork->worklets.front() != nullptr) {
newWork->worklets.emplace_back(new C2Worklet);
newWork->worklets.front()->component = inWork->worklets.front()->component;
std::vector<std::unique_ptr<C2Tuning>> tunings;
for (std::unique_ptr<C2Tuning>& tuning : inWork->worklets.front()->tunings) {
tunings.push_back(
std::unique_ptr<C2Tuning>(
static_cast<C2Tuning*>(
C2Param::Copy(*(tuning.get())).release())));
}
newWork->worklets.front()->tunings = std::move(tunings);
}
return newWork;
};
if (w->input.buffers.empty()
|| (w->input.buffers.front() == nullptr)
|| (!w->input.buffers.front()->hasInfo(
C2AccessUnitInfos::input::PARAM_TYPE))) {
LOG(DEBUG) << "Empty or MultiAU info buffer scatter frames with frameIndex "
<< inputOrdinal.frameIndex.peekull()
<< ") -> newFrameIndex " << newFrameIdx
<<" : input ts " << inputOrdinal.timestamp.peekull();
sliceWork.push_back(cloneInputWork(w, w->input.flags));
if (!w->input.buffers.empty() && w->input.buffers.front() != nullptr) {
sliceWork.back()->input.buffers = std::move(w->input.buffers);
}
frameSet.insert(newFrameIdx);
processedWork->push_back(std::move(sliceWork));
} else {
const std::vector<std::shared_ptr<C2Buffer>>& inBuffers = w->input.buffers;
if (inBuffers.front()->data().linearBlocks().size() == 0) {
LOG(ERROR) << "ERROR: Work has Large frame info but has no linear blocks.";
return C2_CORRUPTED;
}
frameInfo.mInputC2Ref = inBuffers;
const std::vector<C2ConstLinearBlock>& multiAU =
inBuffers.front()->data().linearBlocks();
std::shared_ptr<const C2AccessUnitInfos::input> auInfo =
std::static_pointer_cast<const C2AccessUnitInfos::input>(
w->input.buffers.front()->getInfo(C2AccessUnitInfos::input::PARAM_TYPE));
uint32_t offset = 0; uint32_t multiAUSize = multiAU.front().size();
bool sendEos = false;
for (int idx = 0; idx < auInfo->flexCount(); ++idx) {
std::vector<C2ConstLinearBlock> au;
const C2AccessUnitInfosStruct &info = auInfo->m.values[idx];
sendEos |= (info.flags & C2FrameData::FLAG_END_OF_STREAM);
std::unique_ptr<C2Work> newWork = cloneInputWork(w, info.flags);
frameSet.insert(newFrameIdx);
newFrameIdx = mFrameIndex++;
newWork->input.ordinal.timestamp = info.timestamp;
au.push_back(multiAU.front().subBlock(offset, info.size));
if ((offset + info.size) > multiAUSize) {
LOG(ERROR) << "ERROR: access-unit offset > buffer size"
<< " current offset " << (offset + info.size)
<< " buffer size " << multiAUSize;
return C2_CORRUPTED;
}
newWork->input.buffers.push_back(
std::shared_ptr<C2Buffer>(new C2MultiAccessUnitBuffer(au)));
LOG(DEBUG) << "Frame scatter queuing frames WITH info in ordinal "
<< inputOrdinal.frameIndex.peekull()
<< " info.size " << info.size
<< " : TS " << newWork->input.ordinal.timestamp.peekull()
<< " with index " << newFrameIdx - 1;
// add to worklist
sliceWork.push_back(std::move(newWork));
processedWork->push_back(std::move(sliceWork));
offset += info.size;
}
mFrameIndex--;
if (!sendEos && (w->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
if (!processedWork->empty()) {
std::list<std::unique_ptr<C2Work>> &sliceWork = processedWork->back();
if (!sliceWork.empty()) {
std::unique_ptr<C2Work> &work = sliceWork.back();
if (work) {
work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
}
}
}
}
}
if (!processedWork->empty()) {
C2LargeFrame::output multiAccessParams = mInterface->getLargeFrameParam();
frameInfo.mLargeFrameTuning = multiAccessParams;
std::lock_guard<std::mutex> l(mLock);
mFrameHolder.push_back(std::move(frameInfo));
mMultiAccessOnOffAllowed = false;
}
}
return C2_OK;
}
c2_status_t MultiAccessUnitHelper::gather(
std::list<std::unique_ptr<C2Work>> &c2workItems,
std::list<std::unique_ptr<C2Work>>* const processedWork) {
LOG(DEBUG) << "Multi access-unit gather process";
if (processedWork == nullptr) {
LOG(ERROR) << "Nothing provided for processed work";
return C2_CORRUPTED;
}
auto addOutWork = [&processedWork](std::unique_ptr<C2Work>& work) {
processedWork->push_back(std::move(work));
};
{
std::lock_guard<std::mutex> l(mLock);
for (auto& work : c2workItems) {
LOG(DEBUG) << "FrameHolder Size: " << mFrameHolder.size();
uint64_t thisFrameIndex = work->input.ordinal.frameIndex.peekull();
bool removeEntry = work->worklets.empty()
|| !work->worklets.front()
|| (work->worklets.front()->output.flags
& C2FrameData::FLAG_INCOMPLETE) == 0;
bool foundFrame = false;
std::list<MultiAccessUnitInfo>::iterator frame =
mFrameHolder.begin();
while (!foundFrame && frame != mFrameHolder.end()) {
c2_status_t res = C2_OK;
auto it = frame->mComponentFrameIds.find(thisFrameIndex);
if (it != frame->mComponentFrameIds.end()) {
foundFrame = true;
LOG(DEBUG) << "onWorkDone (frameIndex " << thisFrameIndex
<< " worklstsSze " << work->worklets.size()
<< ") -> frameIndex " << frame->inOrdinal.frameIndex.peekull();
if (work->result != C2_OK
|| work->worklets.empty()
|| !work->worklets.front()
|| frame->mLargeFrameTuning.maxSize == 0) {
if (removeEntry) {
frame->mComponentFrameIds.erase(it);
removeEntry = false;
}
if (frame->mLargeWork) {
finalizeWork(*frame);
addOutWork(frame->mLargeWork);
frame->reset();
}
c2_status_t workResult = work->result;
frame->mLargeWork = std::move(work);
frame->mLargeWork->input.ordinal.frameIndex =
frame->inOrdinal.frameIndex;
finalizeWork(*frame);
addOutWork(frame->mLargeWork);
frame->reset();
if (workResult != C2_OK) {
frame->mComponentFrameIds.clear();
removeEntry = false;
}
} else if (C2_OK != (res = processWorklets(*frame, work, addOutWork))) {
// Upon error in processing worklets, we return the work with
// result set to the error. This should indicate the error to the
// framework and thus doing what is necessary to handle the
// error.
LOG(DEBUG) << "Error while processing worklets";
if (frame->mLargeWork == nullptr) {
frame->mLargeWork.reset(new C2Work);
frame->mLargeWork->input.ordinal = frame->inOrdinal;
frame->mLargeWork->input.ordinal.frameIndex =
frame->inOrdinal.frameIndex;
}
frame->mLargeWork->result = res;
finalizeWork(*frame);
addOutWork(frame->mLargeWork);
frame->reset();
frame->mComponentFrameIds.clear();
removeEntry = false;
}
if (removeEntry) {
LOG(DEBUG) << "Removing entry: " << thisFrameIndex
<< " -> " << frame->inOrdinal.frameIndex.peekull();
frame->mComponentFrameIds.erase(it);
}
// This is to take care of the last bytes and to decide to send with
// FLAG_INCOMPLETE or not.
if ((frame->mWview
&& (frame->mWview->offset() >= frame->mLargeFrameTuning.thresholdSize))
|| frame->mComponentFrameIds.empty()) {
if (frame->mLargeWork) {
frame->mLargeWork->result = C2_OK;
finalizeWork(*frame);
addOutWork(frame->mLargeWork);
frame->reset();
}
}
if (frame->mComponentFrameIds.empty()) {
LOG(DEBUG) << "This frame is finished ID " << thisFrameIndex;
frame = mFrameHolder.erase(frame);
continue;
}
} else {
LOG(DEBUG) << "Received an out-of-order output " << thisFrameIndex
<< " expected: " <<mFrameHolder.front().inOrdinal.frameIndex.peekull();
}
frame++;
}
if (!foundFrame) {
LOG(ERROR) <<" Error: Frame Holder reports no frame " << thisFrameIndex;
}
}
}
return C2_OK;
}
c2_status_t MultiAccessUnitHelper::createLinearBlock(MultiAccessUnitInfo &frame) {
if (!mInit) {
LOG(ERROR) << "Large buffer allocator failed";
return C2_NO_MEMORY;
}
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
uint32_t maxOutSize = frame.mLargeFrameTuning.maxSize;
c2_status_t err = mLinearPool->fetchLinearBlock(maxOutSize, usage, &frame.mBlock);
LOG(DEBUG) << "Allocated block with offset : " << frame.mBlock->offset()
<< " size " << frame.mBlock->size() << " Capacity " << frame.mBlock->capacity();
if (err != C2_OK) {
LOG(ERROR) << "Error allocating Multi access-unit Buffer";
return err;
}
frame.mWview = std::make_shared<C2WriteView>(frame.mBlock->map().get());
LOG(DEBUG) << "Allocated buffer : requested size : " <<
frame.mLargeFrameTuning.maxSize
<< " alloc size " << frame.mWview->size();
return C2_OK;
}
/*
* For every work from the component, we try to do aggregation of work here.
*/
c2_status_t MultiAccessUnitHelper::processWorklets(MultiAccessUnitInfo &frame,
std::unique_ptr<C2Work>& work,
const std::function <void(std::unique_ptr<C2Work>&)>& addWork) {
// This will allocate work, worklet, c2Block
auto allocateWork = [&](MultiAccessUnitInfo &frame,
bool allocateWorket = false,
bool allocateBuffer = false) {
c2_status_t ret = C2_OK;
if (frame.mLargeWork == nullptr) {
frame.mLargeWork.reset(new C2Work);
frame.mLargeWork->result = C2_OK;
frame.mLargeWork->input.flags = (C2FrameData::flags_t)0;
frame.mLargeWork->input.ordinal = frame.inOrdinal;
frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
}
if (allocateWorket) {
if (frame.mLargeWork->worklets.size() == 0) {
frame.mLargeWork->worklets.emplace_back(new C2Worklet);
frame.mLargeWork->worklets.back()->output.flags = (C2FrameData::flags_t)0;
}
}
if (allocateBuffer) {
if (frame.mWview == nullptr) {
ret = createLinearBlock(frame);
}
}
return ret;
};
// we will only have one worklet.
bool foundEndOfStream = false;
for (auto worklet = work->worklets.begin();
worklet != work->worklets.end() && (*worklet) != nullptr; ++worklet) {
uint32_t flagsForNoCopy = C2FrameData::FLAG_DROP_FRAME
| C2FrameData::FLAG_DISCARD_FRAME
| C2FrameData::FLAG_CORRUPT;
if ((*worklet)->output.flags & flagsForNoCopy) {
if (frame.mLargeWork) {
finalizeWork(frame);
addWork(frame.mLargeWork);
frame.reset();
}
frame.mLargeWork = std::move(work);
frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
finalizeWork(frame, (*worklet)->output.flags, true);
addWork(frame.mLargeWork);
frame.reset();
return C2_OK;
}
int64_t sampleTimeUs = 0;
uint32_t frameSize = 0;
uint32_t sampleRate = 0;
uint32_t channelCount = 0;
if (mInterface->getDecoderSampleRateAndChannelCount(&sampleRate, &channelCount)) {
sampleTimeUs = (1000000u) / (sampleRate * channelCount * 2);
frameSize = channelCount * 2;
if (mInterface->kind() == C2Component::KIND_DECODER) {
frame.mLargeFrameTuning.maxSize =
(frame.mLargeFrameTuning.maxSize / frameSize) * frameSize;
frame.mLargeFrameTuning.thresholdSize =
(frame.mLargeFrameTuning.thresholdSize / frameSize) * frameSize;
}
}
c2_status_t c2ret = allocateWork(frame, true);
if (c2ret != C2_OK) {
return c2ret;
}
uint32_t flags = work->input.flags;
flags |= frame.mLargeWork->input.flags;
frame.mLargeWork->input.flags = (C2FrameData::flags_t)flags;
C2FrameData& outputFramedata = frame.mLargeWork->worklets.front()->output;
if (!(*worklet)->output.configUpdate.empty()) {
for (auto& configUpdate : (*worklet)->output.configUpdate) {
outputFramedata.configUpdate.push_back(std::move(configUpdate));
}
(*worklet)->output.configUpdate.clear();
}
outputFramedata.infoBuffers.insert(outputFramedata.infoBuffers.begin(),
(*worklet)->output.infoBuffers.begin(),
(*worklet)->output.infoBuffers.end());
LOG(DEBUG) << "maxOutSize " << frame.mLargeFrameTuning.maxSize
<< " threshold " << frame.mLargeFrameTuning.thresholdSize;
LOG(DEBUG) << "This worklet has " << (*worklet)->output.buffers.size() << " buffers"
<< " ts: " << (*worklet)->output.ordinal.timestamp.peekull();
int64_t workletTimestamp = (*worklet)->output.ordinal.timestamp.peekull();
int64_t timestamp = workletTimestamp;
uint32_t flagsForCopy = ((*worklet)->output.flags) & C2FrameData::FLAG_CODEC_CONFIG;
for (int bufIdx = 0; bufIdx < (*worklet)->output.buffers.size(); ++bufIdx) {
std::shared_ptr<C2Buffer>& buffer = (*worklet)->output.buffers[bufIdx];
if (!buffer || buffer->data().linearBlocks().empty()) {
continue;
}
const std::vector<C2ConstLinearBlock>& blocks = buffer->data().linearBlocks();
if (blocks.size() > 0) {
uint32_t inputOffset = 0;
uint32_t inputSize = blocks.front().size();
frame.mInfos.insert(frame.mInfos.end(),
buffer->info().begin(), buffer->info().end());
if (frameSize != 0 && (mInterface->kind() == C2Component::KIND_DECODER)) {
// For decoders we only split multiples of 16bChannelCount*2
inputSize -= (inputSize % frameSize);
}
while (inputOffset < inputSize) {
if ((frame.mWview != nullptr)
&& (frame.mWview->offset() >= frame.mLargeFrameTuning.thresholdSize)) {
frame.mLargeWork->result = C2_OK;
finalizeWork(frame, flagsForCopy);
addWork(frame.mLargeWork);
frame.reset();
}
if (mInterface->kind() == C2Component::KIND_ENCODER) {
if (inputSize > frame.mLargeFrameTuning.maxSize) {
LOG(WARNING) << "WARNING Encoder:"
<< " Output buffer too small for configuration"
<< " configured max size " << frame.mLargeFrameTuning.maxSize
<< " access unit size " << inputSize;
if (frame.mLargeWork && (frame.mWview && frame.mWview->offset() > 0)) {
frame.mLargeWork->result = C2_OK;
finalizeWork(frame, flagsForCopy);
addWork(frame.mLargeWork);
frame.reset();
}
frame.mLargeFrameTuning.maxSize = inputSize;
} else if ((frame.mWview != nullptr)
&& (inputSize > frame.mWview->size())) {
LOG(DEBUG) << "Enc: Large frame hitting bufer limit, current size "
<< frame.mWview->offset();
if (frame.mWview->offset() > 0) {
frame.mLargeWork->result = C2_OK;
finalizeWork(frame, flagsForCopy);
addWork(frame.mLargeWork);
frame.reset();
}
}
}
allocateWork(frame, true, true);
uint32_t flags = work->input.flags;
flags |= frame.mLargeWork->input.flags;
frame.mLargeWork->input.flags = (C2FrameData::flags_t)flags;
C2ReadView rView = blocks.front().map().get();
if (rView.error()) {
LOG(ERROR) << "Buffer read view error";
frame.mLargeWork->result = rView.error();
frame.mLargeWork->worklets.clear();
finalizeWork(frame, 0, true);
addWork(frame.mLargeWork);
frame.reset();
return C2_NO_MEMORY;
}
uint32_t toCopy = 0;
if (mInterface->kind() == C2Component::KIND_ENCODER) {
toCopy = inputSize;
} else {
toCopy = c2_min(frame.mWview->size(), (inputSize - inputOffset));
timestamp = workletTimestamp + inputOffset * sampleTimeUs;
LOG(DEBUG) << "ts " << timestamp
<< " copiedOutput " << inputOffset
<< " sampleTimeUs " << sampleTimeUs;
}
LOG(DEBUG) << " Copy size " << toCopy
<< " ts " << timestamp;
memcpy(frame.mWview->data(), rView.data() + inputOffset, toCopy);
frame.mWview->setOffset(frame.mWview->offset() + toCopy);
inputOffset += toCopy;
mergeAccessUnitInfo(frame, flagsForCopy, toCopy, timestamp);
}
} else {
frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(buffer));
LOG(DEBUG) << "Copying worklets without linear buffer";
}
}
uint32_t flagsForCsdOrEnd = (*worklet)->output.flags
& (C2FrameData::FLAG_END_OF_STREAM | C2FrameData::FLAG_CODEC_CONFIG);
if (flagsForCsdOrEnd) {
LOG(DEBUG) << "Output worklet has CSD/EOS data";
frame.mLargeWork->result = C2_OK;
// we can assign timestamp as this will be evaluated in finalizeWork
frame.mLargeWork->worklets.front()->output.ordinal.timestamp = timestamp;
finalizeWork(frame, flagsForCsdOrEnd, true);
addWork(frame.mLargeWork);
frame.reset();
}
}
return C2_OK;
}
c2_status_t MultiAccessUnitHelper::finalizeWork(
MultiAccessUnitInfo& frame, uint32_t inFlags, bool forceComplete) {
if (frame.mLargeWork == nullptr) {
return C2_OK;
}
//prepare input ordinal
frame.mLargeWork->input.ordinal = frame.inOrdinal;
// remove this
int64_t timeStampUs = frame.inOrdinal.timestamp.peekull();
if (!frame.mAccessUnitInfos.empty()) {
timeStampUs = frame.mAccessUnitInfos.front().timestamp;
} else if (!frame.mLargeWork->worklets.empty()) {
std::unique_ptr<C2Worklet> &worklet = frame.mLargeWork->worklets.front();
if (worklet) {
timeStampUs = worklet->output.ordinal.timestamp.peekull();
}
}
LOG(DEBUG) << "Finalizing work with input Idx "
<< frame.mLargeWork->input.ordinal.frameIndex.peekull()
<< " timestamp " << timeStampUs
<< " inFlags " << inFlags;
uint32_t finalFlags = 0;
if ((!forceComplete)
&& (frame.mLargeWork->result == C2_OK)
&& (!frame.mComponentFrameIds.empty())) {
finalFlags |= C2FrameData::FLAG_INCOMPLETE;
}
if (frame.mLargeWork->result == C2_OK) {
finalFlags |= inFlags;
}
// update worklet if present
if (!frame.mLargeWork->worklets.empty() &&
frame.mLargeWork->worklets.front() != nullptr) {
frame.mLargeWork->workletsProcessed = 1;
C2FrameData& outFrameData = frame.mLargeWork->worklets.front()->output;
outFrameData.ordinal.frameIndex = frame.inOrdinal.frameIndex.peekull();
outFrameData.ordinal.timestamp = timeStampUs;
finalFlags |= frame.mLargeWork->worklets.front()->output.flags;
outFrameData.flags = (C2FrameData::flags_t)finalFlags;
// update buffers
if (frame.mBlock && (frame.mWview->offset() > 0)) {
size_t size = frame.mWview->offset();
LOG(DEBUG) << "Finalize : Block: Large frame size set as " << size
<< " timestamp as " << timeStampUs
<< "frameIndex " << outFrameData.ordinal.frameIndex.peekull();
frame.mWview->setOffset(0);
std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateLinearBuffer(
frame.mBlock->share(0, size, ::C2Fence()));
frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(c2Buffer));
}
if (frame.mLargeWork->worklets.front()->output.buffers.size() > 0) {
std::shared_ptr<C2Buffer>& c2Buffer =
frame.mLargeWork->worklets.front()->output.buffers.front();
if (c2Buffer != nullptr) {
if (frame.mAccessUnitInfos.size() > 0) {
if (finalFlags & C2FrameData::FLAG_END_OF_STREAM) {
frame.mAccessUnitInfos.back().flags |= C2FrameData::FLAG_END_OF_STREAM;
}
std::shared_ptr<C2AccessUnitInfos::output> largeFrame =
C2AccessUnitInfos::output::AllocShared(
frame.mAccessUnitInfos.size(), 0u, frame.mAccessUnitInfos);
frame.mInfos.push_back(largeFrame);
frame.mAccessUnitInfos.clear();
}
for (auto &info : frame.mInfos) {
c2Buffer->setInfo(std::const_pointer_cast<C2Info>(info));
}
}
}
if (frame.mConfigUpdate.size() > 0) {
outFrameData.configUpdate.insert(
outFrameData.configUpdate.end(),
make_move_iterator(frame.mConfigUpdate.begin()),
make_move_iterator(frame.mConfigUpdate.end()));
}
}
frame.mConfigUpdate.clear();
frame.mInfos.clear();
frame.mBlock.reset();
frame.mWview.reset();
LOG(DEBUG) << "Multi access-unitflag setting as " << finalFlags;
return C2_OK;
}
void MultiAccessUnitHelper::mergeAccessUnitInfo(
MultiAccessUnitInfo &frame,
uint32_t flags_,
uint32_t size,
int64_t timestamp) {
// Remove flags that are not part of Access unit info
uint32_t flags = flags_ & ~(C2FrameData::FLAG_INCOMPLETE
| C2FrameData::FLAG_DISCARD_FRAME
| C2FrameData::FLAG_CORRUPT
| C2FrameData::FLAG_CORRECTED);
if (frame.mAccessUnitInfos.empty()) {
frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
return;
}
if ((mInterface->kind() == C2Component::KIND_DECODER) &&
(frame.mAccessUnitInfos.back().flags == flags)) {
// merge access units here
C2AccessUnitInfosStruct &s = frame.mAccessUnitInfos.back();
s.size += size; // don't have to update timestamp
} else {
frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
}
}
void MultiAccessUnitHelper::MultiAccessUnitInfo::reset() {
mBlock.reset();
mWview.reset();
mInfos.clear();
mConfigUpdate.clear();
mAccessUnitInfos.clear();
mLargeWork.reset();
}
} // namespace android