/* * 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 #include #include #include #include #include #include static inline constexpr uint32_t MAX_SUPPORTED_SIZE = ( 10 * 512000 * 8 * 2u); namespace android { static C2R MultiAccessUnitParamsSetter( bool mayBlock, C2InterfaceHelper::C2P &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> failures; res.retrieveFailures(&failures); if (!failures.empty()) { me.set().maxSize = 0; me.set().thresholdSize = 0; } return res; } MultiAccessUnitInterface::MultiAccessUnitInterface( const std::shared_ptr& interface, std::shared_ptr 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> supportedParams; querySupportedParams(&supportedParams); // Adding to set to do intf seperation in query/config for (std::shared_ptr &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 &blocks): C2Buffer(blocks) { } }; //MultiAccessUnitHelper MultiAccessUnitHelper::MultiAccessUnitHelper( const std::shared_ptr& intf, std::shared_ptr& linearPool): mMultiAccessOnOffAllowed(true), mInit(false), mInterface(intf), mLinearPool(linearPool) { if (mLinearPool) { mInit = true; } } MultiAccessUnitHelper::~MultiAccessUnitHelper() { std::unique_lock 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 ¶m) { 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 config{lfp}; std::vector> 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 MultiAccessUnitHelper::getInterface() { return mInterface; } bool MultiAccessUnitHelper::getStatus() { return mInit; } void MultiAccessUnitHelper::reset() { std::lock_guard l(mLock); mFrameHolder.clear(); mMultiAccessOnOffAllowed = true; } c2_status_t MultiAccessUnitHelper::error( std::list> * const worklist) { if (worklist == nullptr) { LOG(ERROR) << "Provided null worklist for error()"; mFrameHolder.clear(); return C2_OK; } std::unique_lock 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>* const c2flushedWorks) { c2_status_t c2res = C2_OK; std::lock_guard l(mLock); for (auto iterWork = c2flushedWorks->begin() ; iterWork != c2flushedWorks->end(); ) { bool foundFlushedFrame = false; std::list::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> &largeWork, std::list>>* 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& w : largeWork) { std::list> sliceWork; C2WorkOrdinalStruct inputOrdinal = w->input.ordinal; // To hold correspondence and processing bits b/w input and output MultiAccessUnitInfo frameInfo(inputOrdinal); std::set& 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& inWork, uint32_t flags) -> std::unique_ptr { std::unique_ptr 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& 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> tunings; for (std::unique_ptr& tuning : inWork->worklets.front()->tunings) { tunings.push_back(std::move( std::unique_ptr( static_cast( 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(std::move(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>& 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& multiAU = inBuffers.front()->data().linearBlocks(); std::shared_ptr auInfo = std::static_pointer_cast( 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 au; const C2AccessUnitInfosStruct &info = auInfo->m.values[idx]; sendEos |= (info.flags & C2FrameData::FLAG_END_OF_STREAM); std::unique_ptr 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(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> &sliceWork = processedWork->back(); if (!sliceWork.empty()) { std::unique_ptr &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 l(mLock); mFrameHolder.push_back(std::move(frameInfo)); mMultiAccessOnOffAllowed = false; } } return C2_OK; } c2_status_t MultiAccessUnitHelper::gather( std::list> &c2workItems, std::list>* 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& work) { processedWork->push_back(std::move(work)); }; { std::lock_guard 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::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: " <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(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& work, const std::function &)>& 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& buffer = (*worklet)->output.buffers[bufIdx]; if (!buffer || buffer->data().linearBlocks().empty()) { continue; } const std::vector& 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 &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::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 = 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 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(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