Arun Johnson | fb94610 | 2023-12-27 01:10:34 +0000 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2023 The Android Open Source Project |
| 3 | * |
| 4 | * Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | * you may not use this file except in compliance with the License. |
| 6 | * You may obtain a copy of the License at |
| 7 | * |
| 8 | * http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | * |
| 10 | * Unless required by applicable law or agreed to in writing, software |
| 11 | * distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | * See the License for the specific language governing permissions and |
| 14 | * limitations under the License. |
| 15 | */ |
| 16 | |
| 17 | //#define LOG_NDEBUG 0 |
| 18 | #define LOG_TAG "Codec2-MultiAccessUnitHelper" |
| 19 | #include <android-base/logging.h> |
| 20 | |
| 21 | #include <com_android_media_codec_flags.h> |
| 22 | |
| 23 | #include <codec2/common/MultiAccessUnitHelper.h> |
| 24 | #include <android-base/properties.h> |
| 25 | |
| 26 | #include <C2BufferPriv.h> |
| 27 | #include <C2Debug.h> |
| 28 | #include <C2PlatformSupport.h> |
| 29 | |
| 30 | namespace android { |
| 31 | |
| 32 | static C2R MultiAccessUnitParamsSetter( |
| 33 | bool mayBlock, C2InterfaceHelper::C2P<C2LargeFrame::output> &me) { |
| 34 | (void)mayBlock; |
| 35 | C2R res = C2R::Ok(); |
| 36 | if (!me.F(me.v.maxSize).supportsAtAll(me.v.maxSize)) { |
| 37 | res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.maxSize))); |
| 38 | } else if (!me.F(me.v.thresholdSize).supportsAtAll(me.v.thresholdSize)) { |
| 39 | res = res.plus(C2SettingResultBuilder::BadValue(me.F(me.v.thresholdSize))); |
| 40 | } else if (me.v.maxSize < me.v.thresholdSize) { |
| 41 | me.set().maxSize = me.v.thresholdSize; |
| 42 | } else if (me.v.thresholdSize == 0 && me.v.maxSize > 0) { |
| 43 | me.set().thresholdSize = me.v.maxSize; |
| 44 | } |
| 45 | std::vector<std::unique_ptr<C2SettingResult>> failures; |
| 46 | res.retrieveFailures(&failures); |
| 47 | if (!failures.empty()) { |
| 48 | me.set().maxSize = 0; |
| 49 | me.set().thresholdSize = 0; |
| 50 | } |
| 51 | return res; |
| 52 | } |
| 53 | |
| 54 | MultiAccessUnitInterface::MultiAccessUnitInterface( |
| 55 | const std::shared_ptr<C2ComponentInterface>& interface, |
| 56 | std::shared_ptr<C2ReflectorHelper> helper) |
| 57 | : C2InterfaceHelper(helper), mC2ComponentIntf(interface) { |
| 58 | setDerivedInstance(this); |
| 59 | addParameter( |
| 60 | DefineParam(mLargeFrameParams, C2_PARAMKEY_OUTPUT_LARGE_FRAME) |
| 61 | .withDefault(new C2LargeFrame::output(0u, 0, 0)) |
| 62 | .withFields({ |
| 63 | C2F(mLargeFrameParams, maxSize).inRange( |
| 64 | 0, c2_min(UINT_MAX, 10 * 512000 * 8 * 2u)), |
| 65 | C2F(mLargeFrameParams, thresholdSize).inRange( |
| 66 | 0, c2_min(UINT_MAX, 10 * 512000 * 8 * 2u)) |
| 67 | }) |
| 68 | .withSetter(MultiAccessUnitParamsSetter) |
| 69 | .build()); |
| 70 | std::vector<std::shared_ptr<C2ParamDescriptor>> supportedParams; |
| 71 | querySupportedParams(&supportedParams); |
| 72 | // Adding to set to do intf seperation in query/config |
| 73 | for (std::shared_ptr<C2ParamDescriptor> &desc : supportedParams) { |
| 74 | mSupportedParamIndexSet.insert(desc->index()); |
| 75 | } |
| 76 | |
| 77 | if (mC2ComponentIntf) { |
| 78 | c2_status_t err = mC2ComponentIntf->query_vb({&mKind}, {}, C2_MAY_BLOCK, nullptr); |
| 79 | } |
| 80 | } |
| 81 | |
| 82 | bool MultiAccessUnitInterface::isParamSupported(C2Param::Index index) { |
| 83 | return (mSupportedParamIndexSet.count(index) != 0); |
| 84 | } |
| 85 | |
| 86 | C2LargeFrame::output MultiAccessUnitInterface::getLargeFrameParam() const { |
| 87 | return *mLargeFrameParams; |
| 88 | } |
| 89 | |
| 90 | C2Component::kind_t MultiAccessUnitInterface::kind() const { |
| 91 | return (C2Component::kind_t)(mKind.value); |
| 92 | } |
| 93 | |
| 94 | void MultiAccessUnitInterface::getDecoderSampleRateAndChannelCount( |
| 95 | uint32_t &sampleRate_, uint32_t &channelCount_) const { |
| 96 | if (mC2ComponentIntf) { |
| 97 | C2StreamSampleRateInfo::output sampleRate; |
| 98 | C2StreamChannelCountInfo::output channelCount; |
| 99 | c2_status_t res = mC2ComponentIntf->query_vb( |
| 100 | {&sampleRate, &channelCount}, {}, C2_MAY_BLOCK, nullptr); |
| 101 | if (res == C2_OK) { |
| 102 | sampleRate_ = sampleRate.value; |
| 103 | channelCount_ = channelCount.value; |
| 104 | } |
| 105 | } |
| 106 | } |
| 107 | |
| 108 | //C2MultiAccessUnitBuffer |
| 109 | class C2MultiAccessUnitBuffer : public C2Buffer { |
| 110 | public: |
| 111 | explicit C2MultiAccessUnitBuffer( |
| 112 | const std::vector<C2ConstLinearBlock> &blocks): |
| 113 | C2Buffer(blocks) { |
| 114 | } |
| 115 | }; |
| 116 | |
| 117 | //MultiAccessUnitHelper |
| 118 | MultiAccessUnitHelper::MultiAccessUnitHelper( |
| 119 | const std::shared_ptr<MultiAccessUnitInterface>& intf): |
| 120 | mInit(false), |
| 121 | mInterface(intf) { |
| 122 | std::shared_ptr<C2AllocatorStore> store = GetCodec2PlatformAllocatorStore(); |
| 123 | if(store->fetchAllocator(C2AllocatorStore::DEFAULT_LINEAR, &mLinearAllocator) == C2_OK) { |
| 124 | mLinearPool = std::make_shared<C2PooledBlockPool>(mLinearAllocator, ++mBlockPoolId); |
| 125 | mInit = true; |
| 126 | } |
| 127 | } |
| 128 | |
| 129 | MultiAccessUnitHelper::~MultiAccessUnitHelper() { |
| 130 | std::unique_lock<std::mutex> l(mLock); |
| 131 | mFrameHolder.clear(); |
| 132 | } |
| 133 | |
| 134 | bool MultiAccessUnitHelper::isEnabledOnPlatform() { |
| 135 | bool result = com::android::media::codec::flags::provider_->large_audio_frame(); |
| 136 | if (!result) { |
| 137 | false; |
| 138 | } |
| 139 | //TODO: remove this before launch |
| 140 | result = ::android::base::GetBoolProperty("debug.media.c2.large.audio.frame", true); |
| 141 | LOG(DEBUG) << "MultiAccessUnitHelper " << (result ? "enabled" : "disabled"); |
| 142 | return result; |
| 143 | } |
| 144 | |
| 145 | std::shared_ptr<MultiAccessUnitInterface> MultiAccessUnitHelper::getInterface() { |
| 146 | return mInterface; |
| 147 | } |
| 148 | |
| 149 | bool MultiAccessUnitHelper::getStatus() { |
| 150 | return mInit; |
| 151 | } |
| 152 | |
| 153 | void MultiAccessUnitHelper::reset() { |
| 154 | std::lock_guard<std::mutex> l(mLock); |
| 155 | mFrameHolder.clear(); |
| 156 | } |
| 157 | |
| 158 | c2_status_t MultiAccessUnitHelper::error( |
| 159 | std::list<std::unique_ptr<C2Work>> * const worklist) { |
| 160 | if (worklist == nullptr) { |
| 161 | LOG(ERROR) << "Provided null worklist for error()"; |
| 162 | return C2_OK; |
| 163 | } |
| 164 | std::unique_lock<std::mutex> l(mLock); |
| 165 | for (auto frame = mFrameHolder.begin(); frame != mFrameHolder.end(); frame++) { |
| 166 | if (frame->mLargeWork) { |
| 167 | finalizeWork(*frame, 0, true); |
| 168 | worklist->push_back(std::move(frame->mLargeWork)); |
| 169 | frame->reset(); |
| 170 | } |
| 171 | } |
| 172 | mFrameHolder.clear(); |
| 173 | return C2_OK; |
| 174 | } |
| 175 | |
| 176 | c2_status_t MultiAccessUnitHelper::flush( |
| 177 | std::list<std::unique_ptr<C2Work>>* const c2flushedWorks) { |
| 178 | c2_status_t c2res = C2_OK; |
| 179 | std::lock_guard<std::mutex> l(mLock); |
| 180 | for (std::unique_ptr<C2Work>& w : *c2flushedWorks) { |
| 181 | bool foundFlushedFrame = false; |
| 182 | std::list<MultiAccessUnitInfo>::iterator frame = |
| 183 | mFrameHolder.begin(); |
| 184 | while (frame != mFrameHolder.end() && !foundFlushedFrame) { |
| 185 | auto it = frame->mComponentFrameIds.find( |
| 186 | w->input.ordinal.frameIndex.peekull()); |
| 187 | if (it != frame->mComponentFrameIds.end()) { |
| 188 | LOG(DEBUG) << "Multi access-unit flush" |
| 189 | << w->input.ordinal.frameIndex.peekull() |
| 190 | << " with " << frame->inOrdinal.frameIndex.peekull(); |
| 191 | w->input.ordinal.frameIndex = frame->inOrdinal.frameIndex; |
| 192 | bool removeEntry = w->worklets.empty() |
| 193 | || !w->worklets.front() |
| 194 | || (w->worklets.front()->output.flags |
| 195 | & C2FrameData::FLAG_INCOMPLETE) == 0; |
| 196 | if (removeEntry) { |
| 197 | frame->mComponentFrameIds.erase(it); |
| 198 | } |
| 199 | foundFlushedFrame = true; |
| 200 | } |
| 201 | if (frame->mComponentFrameIds.empty()) { |
| 202 | frame = mFrameHolder.erase(frame); |
| 203 | } else { |
| 204 | ++frame; |
| 205 | } |
| 206 | } |
| 207 | } |
| 208 | return c2res; |
| 209 | } |
| 210 | |
| 211 | c2_status_t MultiAccessUnitHelper::scatter( |
| 212 | std::list<std::unique_ptr<C2Work>> &largeWork, |
| 213 | std::list<std::list<std::unique_ptr<C2Work>>>* const processedWork) { |
| 214 | LOG(DEBUG) << "Multiple access-unit: scatter"; |
| 215 | if (processedWork == nullptr) { |
| 216 | LOG(ERROR) << "MultiAccessUnitHelper provided with no work list"; |
| 217 | return C2_CORRUPTED; |
| 218 | } |
| 219 | for (std::unique_ptr<C2Work>& w : largeWork) { |
| 220 | std::list<std::unique_ptr<C2Work>> sliceWork; |
| 221 | C2WorkOrdinalStruct inputOrdinal = w->input.ordinal; |
| 222 | // To hold correspondence and processing bits b/w input and output |
| 223 | MultiAccessUnitInfo frameInfo(inputOrdinal); |
| 224 | std::set<uint64_t>& frameSet = frameInfo.mComponentFrameIds; |
| 225 | uint64_t newFrameIdx = mFrameIndex++; |
| 226 | // TODO: Do not split buffers if component inherantly supports MultipleFrames. |
| 227 | // if thats case, only replace frameindex. |
| 228 | auto cloneInputWork = [&newFrameIdx](std::unique_ptr<C2Work>& inWork, uint32_t flags) { |
| 229 | std::unique_ptr<C2Work> newWork(new C2Work); |
| 230 | newWork->input.flags = (C2FrameData::flags_t)flags; |
| 231 | newWork->input.ordinal = inWork->input.ordinal; |
| 232 | newWork->input.ordinal.frameIndex = newFrameIdx; |
| 233 | if (!inWork->input.configUpdate.empty()) { |
| 234 | for (std::unique_ptr<C2Param>& param : inWork->input.configUpdate) { |
| 235 | newWork->input.configUpdate.push_back( |
| 236 | std::move(C2Param::Copy(*(param.get())))); |
| 237 | } |
| 238 | } |
| 239 | newWork->input.infoBuffers = (inWork->input.infoBuffers); |
| 240 | if (!inWork->worklets.empty() && inWork->worklets.front() != nullptr) { |
| 241 | newWork->worklets.emplace_back(new C2Worklet); |
| 242 | newWork->worklets.front()->component = inWork->worklets.front()->component; |
| 243 | std::vector<std::unique_ptr<C2Tuning>> tunings; |
| 244 | for (std::unique_ptr<C2Tuning>& tuning : inWork->worklets.front()->tunings) { |
| 245 | tunings.push_back(std::move( |
| 246 | std::unique_ptr<C2Tuning>( |
| 247 | static_cast<C2Tuning*>( |
| 248 | C2Param::Copy(*(tuning.get())).release())))); |
| 249 | } |
| 250 | newWork->worklets.front()->tunings = std::move(tunings); |
| 251 | } |
| 252 | return newWork; |
| 253 | }; |
| 254 | if (w->input.buffers.empty() |
| 255 | || (w->input.buffers.front() == nullptr) |
| 256 | || (!w->input.buffers.front()->hasInfo( |
| 257 | C2AccessUnitInfos::input::PARAM_TYPE))) { |
| 258 | LOG(DEBUG) << "Empty or MultiAU info buffer scatter frames with frameIndex " |
| 259 | << inputOrdinal.frameIndex.peekull() |
| 260 | << ") -> newFrameIndex " << newFrameIdx |
| 261 | <<" : input ts " << inputOrdinal.timestamp.peekull(); |
| 262 | sliceWork.push_back(std::move(cloneInputWork(w, w->input.flags))); |
| 263 | if (!w->input.buffers.empty() && w->input.buffers.front() != nullptr) { |
| 264 | sliceWork.back()->input.buffers = std::move(w->input.buffers); |
| 265 | } |
| 266 | frameSet.insert(newFrameIdx); |
| 267 | processedWork->push_back(std::move(sliceWork)); |
| 268 | } else { |
| 269 | const std::vector<std::shared_ptr<C2Buffer>>& inBuffers = w->input.buffers; |
| 270 | if (inBuffers.front()->data().linearBlocks().size() == 0) { |
| 271 | LOG(ERROR) << "ERROR: Work has Large frame info but has no linear blocks."; |
| 272 | return C2_CORRUPTED; |
| 273 | } |
| 274 | const std::vector<C2ConstLinearBlock>& multiAU = |
| 275 | inBuffers.front()->data().linearBlocks(); |
| 276 | std::shared_ptr<const C2AccessUnitInfos::input> auInfo = |
| 277 | std::static_pointer_cast<const C2AccessUnitInfos::input>( |
| 278 | w->input.buffers.front()->getInfo(C2AccessUnitInfos::input::PARAM_TYPE)); |
| 279 | uint32_t offset = 0; uint32_t multiAUSize = multiAU.front().size(); |
| 280 | for (int idx = 0; idx < auInfo->flexCount(); ++idx) { |
| 281 | std::vector<C2ConstLinearBlock> au; |
| 282 | const C2AccessUnitInfosStruct &info = auInfo->m.values[idx]; |
| 283 | std::unique_ptr<C2Work> newWork = cloneInputWork(w, info.flags); |
| 284 | frameSet.insert(newFrameIdx); |
| 285 | newFrameIdx = mFrameIndex++; |
| 286 | newWork->input.ordinal.timestamp = info.timestamp; |
| 287 | au.push_back(multiAU.front().subBlock(offset, info.size)); |
| 288 | if ((offset + info.size) > multiAUSize) { |
| 289 | LOG(ERROR) << "ERROR: access-unit offset > buffer size" |
| 290 | << " current offset " << (offset + info.size) |
| 291 | << " buffer size " << multiAUSize; |
| 292 | return C2_CORRUPTED; |
| 293 | } |
| 294 | newWork->input.buffers.push_back( |
| 295 | std::shared_ptr<C2Buffer>(new C2MultiAccessUnitBuffer(au))); |
| 296 | LOG(DEBUG) << "Frame scatter queuing frames WITH info in ordinal " |
| 297 | << inputOrdinal.frameIndex.peekull() |
| 298 | << " total offset " << offset << " info.size " << info.size |
| 299 | << " : TS " << newWork->input.ordinal.timestamp.peekull(); |
| 300 | // add to worklist |
| 301 | sliceWork.push_back(std::move(newWork)); |
| 302 | processedWork->push_back(std::move(sliceWork)); |
| 303 | offset += info.size; |
| 304 | } |
| 305 | } |
| 306 | if (!processedWork->empty()) { |
| 307 | { |
| 308 | C2LargeFrame::output multiAccessParams = mInterface->getLargeFrameParam(); |
| 309 | if (mInterface->kind() == C2Component::KIND_DECODER) { |
| 310 | uint32_t sampleRate = 0; |
| 311 | uint32_t channelCount = 0; |
| 312 | uint32_t frameSize = 0; |
| 313 | mInterface->getDecoderSampleRateAndChannelCount( |
| 314 | sampleRate, channelCount); |
| 315 | if (sampleRate > 0 && channelCount > 0) { |
| 316 | frameSize = channelCount * 2; |
| 317 | multiAccessParams.maxSize = |
| 318 | (multiAccessParams.maxSize / frameSize) * frameSize; |
| 319 | multiAccessParams.thresholdSize = |
| 320 | (multiAccessParams.thresholdSize / frameSize) * frameSize; |
| 321 | } |
| 322 | } |
| 323 | frameInfo.mLargeFrameTuning = multiAccessParams; |
| 324 | std::lock_guard<std::mutex> l(mLock); |
| 325 | mFrameHolder.push_back(std::move(frameInfo)); |
| 326 | } |
| 327 | } |
| 328 | } |
| 329 | return C2_OK; |
| 330 | } |
| 331 | |
| 332 | c2_status_t MultiAccessUnitHelper::gather( |
| 333 | std::list<std::unique_ptr<C2Work>> &c2workItems, |
| 334 | std::list<std::unique_ptr<C2Work>>* const processedWork) { |
| 335 | LOG(DEBUG) << "Multi access-unit gather process"; |
| 336 | if (processedWork == nullptr) { |
| 337 | LOG(ERROR) << "Nothing provided for processed work"; |
| 338 | return C2_CORRUPTED; |
| 339 | } |
| 340 | auto addOutWork = [&processedWork](std::unique_ptr<C2Work>& work) { |
| 341 | processedWork->push_back(std::move(work)); |
| 342 | }; |
| 343 | { |
| 344 | std::lock_guard<std::mutex> l(mLock); |
| 345 | for (auto& work : c2workItems) { |
| 346 | LOG(DEBUG) << "FrameHolder Size: " << mFrameHolder.size(); |
| 347 | uint64_t thisFrameIndex = work->input.ordinal.frameIndex.peekull(); |
| 348 | bool removeEntry = work->worklets.empty() |
| 349 | || !work->worklets.front() |
| 350 | || (work->worklets.front()->output.flags |
| 351 | & C2FrameData::FLAG_INCOMPLETE) == 0; |
| 352 | bool foundFrame = false; |
| 353 | std::list<MultiAccessUnitInfo>::iterator frame = |
| 354 | mFrameHolder.begin(); |
| 355 | while (!foundFrame && frame != mFrameHolder.end()) { |
| 356 | auto it = frame->mComponentFrameIds.find(thisFrameIndex); |
| 357 | if (it != frame->mComponentFrameIds.end()) { |
| 358 | foundFrame = true; |
| 359 | LOG(DEBUG) << "onWorkDone (frameIndex " << thisFrameIndex |
| 360 | << " worklstsSze " << work->worklets.size() |
| 361 | << ") -> frameIndex " << frame->inOrdinal.frameIndex.peekull(); |
| 362 | if (work->result != C2_OK |
| 363 | || work->worklets.empty() |
| 364 | || !work->worklets.front() |
| 365 | || (frame->mLargeFrameTuning.thresholdSize == 0 |
| 366 | || frame->mLargeFrameTuning.maxSize == 0)) { |
| 367 | if (removeEntry) { |
| 368 | frame->mComponentFrameIds.erase(it); |
| 369 | removeEntry = false; |
| 370 | } |
| 371 | if (frame->mLargeWork) { |
| 372 | finalizeWork(*frame); |
| 373 | addOutWork(frame->mLargeWork); |
| 374 | frame->reset(); |
| 375 | } |
| 376 | c2_status_t workResult = work->result; |
| 377 | frame->mLargeWork = std::move(work); |
| 378 | frame->mLargeWork->input.ordinal.frameIndex = |
| 379 | frame->inOrdinal.frameIndex; |
| 380 | finalizeWork(*frame); |
| 381 | addOutWork(frame->mLargeWork); |
| 382 | frame->reset(); |
| 383 | if (workResult != C2_OK) { |
| 384 | frame->mAccessUnitInfos.clear(); |
| 385 | } |
| 386 | } else if (C2_OK != processWorklets(*frame, work, addOutWork)) { |
| 387 | LOG(DEBUG) << "Error while processing work"; |
| 388 | } |
| 389 | if (removeEntry) { |
| 390 | LOG(DEBUG) << "Removing entry: " << thisFrameIndex |
| 391 | << " -> " << frame->inOrdinal.frameIndex.peekull(); |
| 392 | frame->mComponentFrameIds.erase(it); |
| 393 | } |
| 394 | // This is to take care of the last bytes and to decide to send with |
| 395 | // FLAG_INCOMPLETE or not. |
| 396 | if ((frame->mWview |
| 397 | && (frame->mWview->offset() > frame->mLargeFrameTuning.thresholdSize)) |
| 398 | || frame->mComponentFrameIds.empty()) { |
| 399 | if (frame->mLargeWork) { |
| 400 | finalizeWork(*frame); |
| 401 | addOutWork(frame->mLargeWork); |
| 402 | frame->reset(); |
| 403 | } |
| 404 | } |
| 405 | if (frame->mComponentFrameIds.empty()) { |
| 406 | LOG(DEBUG) << "This frame is finished ID " << thisFrameIndex; |
| 407 | frame = mFrameHolder.erase(frame); |
| 408 | continue; |
| 409 | } |
| 410 | } else { |
| 411 | LOG(DEBUG) << "Received an out-of-order output " << thisFrameIndex |
| 412 | << " expected: " <<mFrameHolder.front().inOrdinal.frameIndex.peekull(); |
| 413 | } |
| 414 | frame++; |
| 415 | } |
| 416 | if (!foundFrame) { |
| 417 | LOG(ERROR) <<" Error: Frame Holder reports no frame " << thisFrameIndex; |
| 418 | } |
| 419 | } |
| 420 | } |
| 421 | return C2_OK; |
| 422 | } |
| 423 | |
| 424 | c2_status_t MultiAccessUnitHelper::createLinearBlock(MultiAccessUnitInfo &frame) { |
| 425 | if (!mInit) { |
| 426 | LOG(ERROR) << "Large buffer allocator failed"; |
| 427 | return C2_NO_MEMORY; |
| 428 | } |
| 429 | C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE }; |
| 430 | uint32_t maxOutSize = frame.mLargeFrameTuning.maxSize; |
| 431 | c2_status_t err = mLinearPool->fetchLinearBlock(maxOutSize, usage, &frame.mBlock); |
| 432 | LOG(DEBUG) << "Allocated block with offset : " << frame.mBlock->offset() |
| 433 | << " size " << frame.mBlock->size() << " Capacity " << frame.mBlock->capacity(); |
| 434 | if (err != C2_OK) { |
| 435 | LOG(ERROR) << "Error allocating Multi access-unit Buffer"; |
| 436 | return err; |
| 437 | } |
| 438 | frame.mWview = std::make_shared<C2WriteView>(frame.mBlock->map().get()); |
| 439 | LOG(DEBUG) << "Allocated buffer : requested size : " << |
| 440 | frame.mLargeFrameTuning.maxSize |
| 441 | << " alloc size " << frame.mWview->size(); |
| 442 | return C2_OK; |
| 443 | } |
| 444 | |
| 445 | /* |
| 446 | * For every work from the component, we try to do aggregation of work here. |
| 447 | */ |
| 448 | c2_status_t MultiAccessUnitHelper::processWorklets(MultiAccessUnitInfo &frame, |
| 449 | std::unique_ptr<C2Work>& work, |
| 450 | const std::function <void(std::unique_ptr<C2Work>&)>& addWork) { |
| 451 | // This will allocate work, worklet, c2Block |
| 452 | auto allocateWork = [&](MultiAccessUnitInfo &frame, |
| 453 | bool allocateWorket = false, |
| 454 | bool allocateBuffer = false) { |
| 455 | c2_status_t ret = C2_OK; |
| 456 | if (frame.mLargeWork == nullptr) { |
| 457 | frame.mLargeWork.reset(new C2Work); |
| 458 | frame.mLargeWork->input.ordinal = frame.inOrdinal; |
| 459 | frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex; |
| 460 | } |
| 461 | if (allocateWorket) { |
| 462 | if (frame.mLargeWork->worklets.size() == 0) { |
| 463 | frame.mLargeWork->worklets.emplace_back(new C2Worklet); |
| 464 | } |
| 465 | } |
| 466 | if (allocateBuffer) { |
| 467 | if (frame.mWview == nullptr) { |
| 468 | ret = createLinearBlock(frame); |
| 469 | } |
| 470 | } |
| 471 | return ret; |
| 472 | }; |
| 473 | // we will only have one worklet. |
| 474 | bool foundEndOfStream = false; |
| 475 | for (auto worklet = work->worklets.begin(); |
| 476 | worklet != work->worklets.end() && (*worklet) != nullptr; ++worklet) { |
| 477 | uint32_t flagsForNoCopy = C2FrameData::FLAG_DROP_FRAME |
| 478 | | C2FrameData::FLAG_DISCARD_FRAME |
| 479 | | C2FrameData::FLAG_CORRUPT; |
| 480 | if ((*worklet)->output.flags & flagsForNoCopy) { |
| 481 | if (frame.mLargeWork) { |
| 482 | finalizeWork(frame); |
| 483 | addWork(frame.mLargeWork); |
| 484 | frame.reset(); |
| 485 | } |
| 486 | frame.mLargeWork = std::move(work); |
| 487 | frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex; |
| 488 | finalizeWork(frame); |
| 489 | addWork(frame.mLargeWork); |
| 490 | frame.reset(); |
| 491 | return C2_OK; |
| 492 | } |
| 493 | c2_status_t c2ret = allocateWork(frame, true); |
| 494 | if (c2ret != C2_OK) { |
| 495 | return c2ret; |
| 496 | } |
| 497 | C2FrameData& outputFramedata = frame.mLargeWork->worklets.front()->output; |
| 498 | if (!(*worklet)->output.configUpdate.empty()) { |
| 499 | for (auto& configUpdate : (*worklet)->output.configUpdate) { |
| 500 | outputFramedata.configUpdate.push_back(std::move(configUpdate)); |
| 501 | } |
| 502 | (*worklet)->output.configUpdate.clear(); |
| 503 | } |
| 504 | outputFramedata.infoBuffers.insert(outputFramedata.infoBuffers.begin(), |
| 505 | (*worklet)->output.infoBuffers.begin(), |
| 506 | (*worklet)->output.infoBuffers.end()); |
| 507 | int64_t sampleTimeUs = 0; |
| 508 | uint32_t frameSize = 0; |
| 509 | uint32_t sampleRate = 0; |
| 510 | uint32_t channelCount = 0; |
| 511 | mInterface->getDecoderSampleRateAndChannelCount(sampleRate, channelCount); |
| 512 | if (sampleRate > 0 && channelCount > 0) { |
| 513 | sampleTimeUs = (1000000u) / (sampleRate * channelCount * 2); |
| 514 | frameSize = channelCount * 2; |
| 515 | } |
| 516 | LOG(DEBUG) << "maxOutSize " << frame.mLargeFrameTuning.maxSize |
| 517 | << " threshold " << frame.mLargeFrameTuning.thresholdSize; |
| 518 | if ((*worklet)->output.buffers.size() > 0) { |
| 519 | allocateWork(frame, true, true); |
| 520 | } |
| 521 | LOG(DEBUG) << "This worklet has " << (*worklet)->output.buffers.size() << " buffers" |
| 522 | << " ts: " << (*worklet)->output.ordinal.timestamp.peekull(); |
| 523 | int64_t workletTimestamp = (*worklet)->output.ordinal.timestamp.peekull(); |
| 524 | int64_t timestamp = workletTimestamp; |
| 525 | uint32_t flagsForCopy = ((*worklet)->output.flags) & C2FrameData::FLAG_CODEC_CONFIG; |
| 526 | for (int bufIdx = 0; bufIdx < (*worklet)->output.buffers.size(); ++bufIdx) { |
| 527 | std::shared_ptr<C2Buffer>& buffer = (*worklet)->output.buffers[bufIdx]; |
| 528 | if (!buffer || buffer->data().linearBlocks().empty()) { |
| 529 | continue; |
| 530 | } |
| 531 | const std::vector<C2ConstLinearBlock>& blocks = buffer->data().linearBlocks(); |
| 532 | if (blocks.size() > 0) { |
| 533 | uint32_t inputOffset = 0; |
| 534 | uint32_t inputSize = blocks.front().size(); |
| 535 | frame.mInfos.insert(frame.mInfos.end(), |
| 536 | buffer->info().begin(), buffer->info().end()); |
| 537 | if (frameSize != 0 && (mInterface->kind() == C2Component::KIND_DECODER)) { |
| 538 | // For decoders we only split multiples of 16bChannelCount*2 |
| 539 | inputSize -= (inputSize % frameSize); |
| 540 | } |
| 541 | while (inputOffset < inputSize) { |
| 542 | if (frame.mWview->offset() >= frame.mLargeFrameTuning.thresholdSize) { |
| 543 | frame.mLargeWork->result = C2_OK; |
| 544 | finalizeWork(frame, flagsForCopy); |
| 545 | addWork(frame.mLargeWork); |
| 546 | frame.reset(); |
| 547 | allocateWork(frame, true, true); |
| 548 | } |
| 549 | if (mInterface->kind() == C2Component::KIND_ENCODER) { |
| 550 | if (inputSize > frame.mLargeFrameTuning.maxSize) { |
| 551 | LOG(ERROR) << "Enc: Output buffer too small for AU, configured with " |
| 552 | << frame.mLargeFrameTuning.maxSize |
| 553 | << " block size: " << blocks.front().size() |
| 554 | << "alloc size " << frame.mWview->size(); |
| 555 | if (frame.mLargeWork |
| 556 | && frame.mWview && frame.mWview->offset() > 0) { |
| 557 | finalizeWork(frame, flagsForCopy); |
| 558 | addWork(frame.mLargeWork); |
| 559 | frame.reset(); |
| 560 | allocateWork(frame, true, false); |
| 561 | } |
| 562 | frame.mLargeWork->result = C2_NO_MEMORY; |
| 563 | finalizeWork(frame, 0, true); |
| 564 | addWork(frame.mLargeWork); |
| 565 | frame.reset(); |
| 566 | return C2_NO_MEMORY; |
| 567 | } else if (inputSize > frame.mWview->size()) { |
| 568 | LOG(DEBUG) << "Enc: Large frame hitting bufer limit, current size " |
| 569 | << frame.mWview->offset(); |
| 570 | if (frame.mLargeWork |
| 571 | && frame.mWview && frame.mWview->offset() > 0) { |
| 572 | finalizeWork(frame, flagsForCopy); |
| 573 | addWork(frame.mLargeWork); |
| 574 | frame.reset(); |
| 575 | allocateWork(frame, true, true); |
| 576 | } |
| 577 | } |
| 578 | } |
| 579 | C2ReadView rView = blocks.front().map().get(); |
| 580 | if (rView.error()) { |
| 581 | LOG(ERROR) << "Buffer read view error"; |
| 582 | frame.mLargeWork->result = rView.error(); |
| 583 | frame.mLargeWork->worklets.clear(); |
| 584 | finalizeWork(frame, 0, true); |
| 585 | addWork(frame.mLargeWork); |
| 586 | frame.reset(); |
| 587 | return C2_NO_MEMORY; |
| 588 | } |
| 589 | uint32_t toCopy = 0; |
| 590 | if (mInterface->kind() == C2Component::KIND_ENCODER) { |
| 591 | toCopy = inputSize; |
| 592 | } else { |
| 593 | toCopy = c2_min(frame.mWview->size(), (inputSize - inputOffset)); |
| 594 | timestamp = workletTimestamp + inputOffset * sampleTimeUs; |
| 595 | LOG(DEBUG) << "ts " << timestamp |
| 596 | << " copiedOutput " << inputOffset |
| 597 | << " sampleTimeUs " << sampleTimeUs; |
| 598 | } |
| 599 | LOG(DEBUG) << " Copy size " << toCopy |
| 600 | << " ts " << timestamp; |
| 601 | memcpy(frame.mWview->data(), rView.data() + inputOffset, toCopy); |
| 602 | frame.mWview->setOffset(frame.mWview->offset() + toCopy); |
| 603 | inputOffset += toCopy; |
| 604 | mergeAccessUnitInfo(frame, flagsForCopy, toCopy, timestamp); |
| 605 | } |
| 606 | } else { |
| 607 | frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(buffer)); |
| 608 | LOG(DEBUG) << "Copying worklets without linear buffer"; |
| 609 | } |
| 610 | } |
| 611 | uint32_t flagsForCsdOrEnd = (*worklet)->output.flags |
| 612 | & (C2FrameData::FLAG_END_OF_STREAM | C2FrameData::FLAG_CODEC_CONFIG); |
| 613 | if (flagsForCsdOrEnd) { |
| 614 | LOG(DEBUG) << "Output worklet has CSD/EOS data"; |
| 615 | frame.mLargeWork->result = C2_OK; |
| 616 | // we can assign timestamp as this will be evaluated in finalizeWork |
| 617 | frame.mLargeWork->worklets.front()->output.ordinal.timestamp = timestamp; |
| 618 | finalizeWork(frame, flagsForCsdOrEnd, true); |
| 619 | addWork(frame.mLargeWork); |
| 620 | frame.reset(); |
| 621 | } |
| 622 | } |
| 623 | return C2_OK; |
| 624 | } |
| 625 | |
| 626 | c2_status_t MultiAccessUnitHelper::finalizeWork( |
| 627 | MultiAccessUnitInfo& frame, uint32_t inFlags, bool forceComplete) { |
| 628 | if (frame.mLargeWork == nullptr) { |
| 629 | return C2_OK; |
| 630 | } |
| 631 | //prepare input ordinal |
| 632 | frame.mLargeWork->input.ordinal = frame.inOrdinal; |
| 633 | // remove this |
| 634 | int64_t timeStampUs = frame.inOrdinal.timestamp.peekull(); |
| 635 | if (!frame.mAccessUnitInfos.empty()) { |
| 636 | timeStampUs = frame.mAccessUnitInfos.front().timestamp; |
| 637 | } else if (!frame.mLargeWork->worklets.empty()) { |
| 638 | std::unique_ptr<C2Worklet> &worklet = frame.mLargeWork->worklets.front(); |
| 639 | if (worklet) { |
| 640 | timeStampUs = worklet->output.ordinal.timestamp.peekull(); |
| 641 | } |
| 642 | } |
| 643 | LOG(DEBUG) << "Finalizing work with input Idx " |
| 644 | << frame.mLargeWork->input.ordinal.frameIndex.peekull() |
| 645 | << " timestamp " << timeStampUs; |
| 646 | uint32_t finalFlags = 0; |
| 647 | if ((!forceComplete) |
| 648 | && (frame.mLargeWork->result == C2_OK) |
| 649 | && (!frame.mComponentFrameIds.empty())) { |
| 650 | finalFlags |= C2FrameData::FLAG_INCOMPLETE; |
| 651 | } |
| 652 | if (frame.mLargeWork->result == C2_OK) { |
| 653 | finalFlags |= inFlags; |
| 654 | } |
| 655 | // update worklet if present |
| 656 | if (!frame.mLargeWork->worklets.empty() && |
| 657 | frame.mLargeWork->worklets.front() != nullptr) { |
| 658 | frame.mLargeWork->workletsProcessed = 1; |
| 659 | C2FrameData& outFrameData = frame.mLargeWork->worklets.front()->output; |
| 660 | outFrameData.ordinal.frameIndex = frame.inOrdinal.frameIndex.peekull(); |
| 661 | outFrameData.ordinal.timestamp = timeStampUs; |
| 662 | finalFlags |= frame.mLargeWork->worklets.front()->output.flags; |
| 663 | outFrameData.flags = (C2FrameData::flags_t)finalFlags; |
| 664 | // update buffers |
| 665 | if (frame.mBlock && (frame.mWview->offset() > 0)) { |
| 666 | size_t size = frame.mWview->offset(); |
| 667 | LOG(DEBUG) << "Finalize : Block: Large frame size set as " << size |
| 668 | << " timestamp as " << timeStampUs |
| 669 | << "frameIndex " << outFrameData.ordinal.frameIndex.peekull(); |
| 670 | frame.mWview->setOffset(0); |
| 671 | std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateLinearBuffer( |
| 672 | frame.mBlock->share(0, size, ::C2Fence())); |
| 673 | if (frame.mAccessUnitInfos.size() > 0) { |
| 674 | if (finalFlags & C2FrameData::FLAG_END_OF_STREAM) { |
| 675 | frame.mAccessUnitInfos.back().flags |= |
| 676 | C2FrameData::FLAG_END_OF_STREAM; |
| 677 | } |
| 678 | std::shared_ptr<C2AccessUnitInfos::output> largeFrame = |
| 679 | C2AccessUnitInfos::output::AllocShared( |
| 680 | frame.mAccessUnitInfos.size(), 0u, frame.mAccessUnitInfos); |
| 681 | frame.mInfos.push_back(largeFrame); |
| 682 | frame.mAccessUnitInfos.clear(); |
| 683 | } |
| 684 | for (auto &info : frame.mInfos) { |
| 685 | c2Buffer->setInfo(std::const_pointer_cast<C2Info>(info)); |
| 686 | } |
| 687 | frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(c2Buffer)); |
| 688 | frame.mInfos.clear(); |
| 689 | frame.mBlock.reset(); |
| 690 | frame.mWview.reset(); |
| 691 | } |
| 692 | } |
| 693 | LOG(DEBUG) << "Multi access-unitflag setting as " << finalFlags; |
| 694 | return C2_OK; |
| 695 | } |
| 696 | |
| 697 | void MultiAccessUnitHelper::mergeAccessUnitInfo( |
| 698 | MultiAccessUnitInfo &frame, |
| 699 | uint32_t flags_, |
| 700 | uint32_t size, |
| 701 | int64_t timestamp) { |
| 702 | // Remove flags that are not part of Access unit info |
| 703 | uint32_t flags = flags_ & ~(C2FrameData::FLAG_INCOMPLETE |
| 704 | | C2FrameData::FLAG_DISCARD_FRAME |
| 705 | | C2FrameData::FLAG_CORRUPT |
| 706 | | C2FrameData::FLAG_CORRECTED); |
| 707 | if (frame.mAccessUnitInfos.empty()) { |
| 708 | frame.mAccessUnitInfos.emplace_back(flags, size, timestamp); |
| 709 | return; |
| 710 | } |
| 711 | if ((mInterface->kind() == C2Component::KIND_DECODER) && |
| 712 | (frame.mAccessUnitInfos.back().flags == flags)) { |
| 713 | // merge access units here |
| 714 | C2AccessUnitInfosStruct &s = frame.mAccessUnitInfos.back(); |
| 715 | s.size += size; // don't have to update timestamp |
| 716 | } else { |
| 717 | frame.mAccessUnitInfos.emplace_back(flags, size, timestamp); |
| 718 | } |
| 719 | } |
| 720 | |
| 721 | void MultiAccessUnitHelper::MultiAccessUnitInfo::reset() { |
| 722 | mBlock.reset(); |
| 723 | mWview.reset(); |
| 724 | mInfos.clear(); |
| 725 | mAccessUnitInfos.clear(); |
| 726 | mLargeWork.reset(); |
| 727 | } |
| 728 | |
| 729 | } // namespace android |