blob: 9221a24cbd32837d5bc1d7042a46a1a45938a8b6 [file] [log] [blame]
Arun Johnsonfb946102023-12-27 01:10:34 +00001/*
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
30namespace android {
31
32static 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
54MultiAccessUnitInterface::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
82bool MultiAccessUnitInterface::isParamSupported(C2Param::Index index) {
83 return (mSupportedParamIndexSet.count(index) != 0);
84}
85
86C2LargeFrame::output MultiAccessUnitInterface::getLargeFrameParam() const {
87 return *mLargeFrameParams;
88}
89
90C2Component::kind_t MultiAccessUnitInterface::kind() const {
91 return (C2Component::kind_t)(mKind.value);
92}
93
94void 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
109class C2MultiAccessUnitBuffer : public C2Buffer {
110 public:
111 explicit C2MultiAccessUnitBuffer(
112 const std::vector<C2ConstLinearBlock> &blocks):
113 C2Buffer(blocks) {
114 }
115};
116
117//MultiAccessUnitHelper
118MultiAccessUnitHelper::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
129MultiAccessUnitHelper::~MultiAccessUnitHelper() {
130 std::unique_lock<std::mutex> l(mLock);
131 mFrameHolder.clear();
132}
133
134bool MultiAccessUnitHelper::isEnabledOnPlatform() {
135 bool result = com::android::media::codec::flags::provider_->large_audio_frame();
136 if (!result) {
Arun Johnsonf5be2952024-02-05 21:05:53 +0000137 return false;
Arun Johnsonfb946102023-12-27 01:10:34 +0000138 }
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
145std::shared_ptr<MultiAccessUnitInterface> MultiAccessUnitHelper::getInterface() {
146 return mInterface;
147}
148
149bool MultiAccessUnitHelper::getStatus() {
150 return mInit;
151}
152
153void MultiAccessUnitHelper::reset() {
154 std::lock_guard<std::mutex> l(mLock);
155 mFrameHolder.clear();
156}
157
158c2_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
176c2_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);
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000180 for (auto iterWork = c2flushedWorks->begin() ; iterWork != c2flushedWorks->end(); ) {
Arun Johnsonfb946102023-12-27 01:10:34 +0000181 bool foundFlushedFrame = false;
182 std::list<MultiAccessUnitInfo>::iterator frame =
183 mFrameHolder.begin();
184 while (frame != mFrameHolder.end() && !foundFlushedFrame) {
185 auto it = frame->mComponentFrameIds.find(
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000186 (*iterWork)->input.ordinal.frameIndex.peekull());
Arun Johnsonfb946102023-12-27 01:10:34 +0000187 if (it != frame->mComponentFrameIds.end()) {
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000188 LOG(DEBUG) << "Multi access-unit flush "
189 << (*iterWork)->input.ordinal.frameIndex.peekull()
Arun Johnsonfb946102023-12-27 01:10:34 +0000190 << " with " << frame->inOrdinal.frameIndex.peekull();
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000191 (*iterWork)->input.ordinal.frameIndex = frame->inOrdinal.frameIndex;
Arun Johnsonfb946102023-12-27 01:10:34 +0000192 frame = mFrameHolder.erase(frame);
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000193 foundFlushedFrame = true;
Arun Johnsonfb946102023-12-27 01:10:34 +0000194 } else {
195 ++frame;
196 }
197 }
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000198 if (!foundFlushedFrame) {
199 iterWork = c2flushedWorks->erase(iterWork);
200 } else {
201 ++iterWork;
202 }
Arun Johnsonfb946102023-12-27 01:10:34 +0000203 }
204 return c2res;
205}
206
207c2_status_t MultiAccessUnitHelper::scatter(
208 std::list<std::unique_ptr<C2Work>> &largeWork,
209 std::list<std::list<std::unique_ptr<C2Work>>>* const processedWork) {
210 LOG(DEBUG) << "Multiple access-unit: scatter";
211 if (processedWork == nullptr) {
212 LOG(ERROR) << "MultiAccessUnitHelper provided with no work list";
213 return C2_CORRUPTED;
214 }
215 for (std::unique_ptr<C2Work>& w : largeWork) {
216 std::list<std::unique_ptr<C2Work>> sliceWork;
217 C2WorkOrdinalStruct inputOrdinal = w->input.ordinal;
218 // To hold correspondence and processing bits b/w input and output
219 MultiAccessUnitInfo frameInfo(inputOrdinal);
220 std::set<uint64_t>& frameSet = frameInfo.mComponentFrameIds;
221 uint64_t newFrameIdx = mFrameIndex++;
222 // TODO: Do not split buffers if component inherantly supports MultipleFrames.
223 // if thats case, only replace frameindex.
224 auto cloneInputWork = [&newFrameIdx](std::unique_ptr<C2Work>& inWork, uint32_t flags) {
225 std::unique_ptr<C2Work> newWork(new C2Work);
226 newWork->input.flags = (C2FrameData::flags_t)flags;
227 newWork->input.ordinal = inWork->input.ordinal;
228 newWork->input.ordinal.frameIndex = newFrameIdx;
229 if (!inWork->input.configUpdate.empty()) {
230 for (std::unique_ptr<C2Param>& param : inWork->input.configUpdate) {
231 newWork->input.configUpdate.push_back(
232 std::move(C2Param::Copy(*(param.get()))));
233 }
234 }
235 newWork->input.infoBuffers = (inWork->input.infoBuffers);
236 if (!inWork->worklets.empty() && inWork->worklets.front() != nullptr) {
237 newWork->worklets.emplace_back(new C2Worklet);
238 newWork->worklets.front()->component = inWork->worklets.front()->component;
239 std::vector<std::unique_ptr<C2Tuning>> tunings;
240 for (std::unique_ptr<C2Tuning>& tuning : inWork->worklets.front()->tunings) {
241 tunings.push_back(std::move(
242 std::unique_ptr<C2Tuning>(
243 static_cast<C2Tuning*>(
244 C2Param::Copy(*(tuning.get())).release()))));
245 }
246 newWork->worklets.front()->tunings = std::move(tunings);
247 }
248 return newWork;
249 };
250 if (w->input.buffers.empty()
251 || (w->input.buffers.front() == nullptr)
252 || (!w->input.buffers.front()->hasInfo(
253 C2AccessUnitInfos::input::PARAM_TYPE))) {
254 LOG(DEBUG) << "Empty or MultiAU info buffer scatter frames with frameIndex "
255 << inputOrdinal.frameIndex.peekull()
256 << ") -> newFrameIndex " << newFrameIdx
257 <<" : input ts " << inputOrdinal.timestamp.peekull();
258 sliceWork.push_back(std::move(cloneInputWork(w, w->input.flags)));
259 if (!w->input.buffers.empty() && w->input.buffers.front() != nullptr) {
260 sliceWork.back()->input.buffers = std::move(w->input.buffers);
261 }
262 frameSet.insert(newFrameIdx);
263 processedWork->push_back(std::move(sliceWork));
264 } else {
265 const std::vector<std::shared_ptr<C2Buffer>>& inBuffers = w->input.buffers;
266 if (inBuffers.front()->data().linearBlocks().size() == 0) {
267 LOG(ERROR) << "ERROR: Work has Large frame info but has no linear blocks.";
268 return C2_CORRUPTED;
269 }
270 const std::vector<C2ConstLinearBlock>& multiAU =
271 inBuffers.front()->data().linearBlocks();
272 std::shared_ptr<const C2AccessUnitInfos::input> auInfo =
273 std::static_pointer_cast<const C2AccessUnitInfos::input>(
274 w->input.buffers.front()->getInfo(C2AccessUnitInfos::input::PARAM_TYPE));
275 uint32_t offset = 0; uint32_t multiAUSize = multiAU.front().size();
Arun Johnson2bd4df72024-01-29 20:07:27 +0000276 bool sendEos = false;
Arun Johnsonfb946102023-12-27 01:10:34 +0000277 for (int idx = 0; idx < auInfo->flexCount(); ++idx) {
278 std::vector<C2ConstLinearBlock> au;
279 const C2AccessUnitInfosStruct &info = auInfo->m.values[idx];
Arun Johnson2bd4df72024-01-29 20:07:27 +0000280 sendEos |= (info.flags & C2FrameData::FLAG_END_OF_STREAM);
Arun Johnsonfb946102023-12-27 01:10:34 +0000281 std::unique_ptr<C2Work> newWork = cloneInputWork(w, info.flags);
282 frameSet.insert(newFrameIdx);
283 newFrameIdx = mFrameIndex++;
284 newWork->input.ordinal.timestamp = info.timestamp;
285 au.push_back(multiAU.front().subBlock(offset, info.size));
286 if ((offset + info.size) > multiAUSize) {
287 LOG(ERROR) << "ERROR: access-unit offset > buffer size"
288 << " current offset " << (offset + info.size)
289 << " buffer size " << multiAUSize;
290 return C2_CORRUPTED;
291 }
292 newWork->input.buffers.push_back(
293 std::shared_ptr<C2Buffer>(new C2MultiAccessUnitBuffer(au)));
294 LOG(DEBUG) << "Frame scatter queuing frames WITH info in ordinal "
295 << inputOrdinal.frameIndex.peekull()
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000296 << " info.size " << info.size
297 << " : TS " << newWork->input.ordinal.timestamp.peekull()
298 << " with index " << newFrameIdx - 1;
Arun Johnsonfb946102023-12-27 01:10:34 +0000299 // add to worklist
300 sliceWork.push_back(std::move(newWork));
301 processedWork->push_back(std::move(sliceWork));
302 offset += info.size;
303 }
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000304 mFrameIndex--;
Arun Johnson2bd4df72024-01-29 20:07:27 +0000305 if (!sendEos && (w->input.flags & C2FrameData::FLAG_END_OF_STREAM)) {
306 if (!processedWork->empty()) {
307 std::list<std::unique_ptr<C2Work>> &sliceWork = processedWork->back();
308 if (!sliceWork.empty()) {
309 std::unique_ptr<C2Work> &work = sliceWork.back();
310 if (work) {
311 work->input.flags = C2FrameData::FLAG_END_OF_STREAM;
312 }
313 }
314 }
315 }
Arun Johnsonfb946102023-12-27 01:10:34 +0000316 }
317 if (!processedWork->empty()) {
318 {
319 C2LargeFrame::output multiAccessParams = mInterface->getLargeFrameParam();
320 if (mInterface->kind() == C2Component::KIND_DECODER) {
321 uint32_t sampleRate = 0;
322 uint32_t channelCount = 0;
323 uint32_t frameSize = 0;
324 mInterface->getDecoderSampleRateAndChannelCount(
325 sampleRate, channelCount);
326 if (sampleRate > 0 && channelCount > 0) {
327 frameSize = channelCount * 2;
328 multiAccessParams.maxSize =
329 (multiAccessParams.maxSize / frameSize) * frameSize;
330 multiAccessParams.thresholdSize =
331 (multiAccessParams.thresholdSize / frameSize) * frameSize;
332 }
333 }
334 frameInfo.mLargeFrameTuning = multiAccessParams;
335 std::lock_guard<std::mutex> l(mLock);
336 mFrameHolder.push_back(std::move(frameInfo));
337 }
338 }
339 }
340 return C2_OK;
341}
342
343c2_status_t MultiAccessUnitHelper::gather(
344 std::list<std::unique_ptr<C2Work>> &c2workItems,
345 std::list<std::unique_ptr<C2Work>>* const processedWork) {
346 LOG(DEBUG) << "Multi access-unit gather process";
347 if (processedWork == nullptr) {
348 LOG(ERROR) << "Nothing provided for processed work";
349 return C2_CORRUPTED;
350 }
351 auto addOutWork = [&processedWork](std::unique_ptr<C2Work>& work) {
352 processedWork->push_back(std::move(work));
353 };
354 {
355 std::lock_guard<std::mutex> l(mLock);
356 for (auto& work : c2workItems) {
357 LOG(DEBUG) << "FrameHolder Size: " << mFrameHolder.size();
358 uint64_t thisFrameIndex = work->input.ordinal.frameIndex.peekull();
359 bool removeEntry = work->worklets.empty()
360 || !work->worklets.front()
361 || (work->worklets.front()->output.flags
362 & C2FrameData::FLAG_INCOMPLETE) == 0;
363 bool foundFrame = false;
364 std::list<MultiAccessUnitInfo>::iterator frame =
365 mFrameHolder.begin();
366 while (!foundFrame && frame != mFrameHolder.end()) {
367 auto it = frame->mComponentFrameIds.find(thisFrameIndex);
368 if (it != frame->mComponentFrameIds.end()) {
369 foundFrame = true;
370 LOG(DEBUG) << "onWorkDone (frameIndex " << thisFrameIndex
371 << " worklstsSze " << work->worklets.size()
372 << ") -> frameIndex " << frame->inOrdinal.frameIndex.peekull();
373 if (work->result != C2_OK
374 || work->worklets.empty()
375 || !work->worklets.front()
376 || (frame->mLargeFrameTuning.thresholdSize == 0
377 || frame->mLargeFrameTuning.maxSize == 0)) {
378 if (removeEntry) {
379 frame->mComponentFrameIds.erase(it);
380 removeEntry = false;
381 }
382 if (frame->mLargeWork) {
383 finalizeWork(*frame);
384 addOutWork(frame->mLargeWork);
385 frame->reset();
386 }
387 c2_status_t workResult = work->result;
388 frame->mLargeWork = std::move(work);
389 frame->mLargeWork->input.ordinal.frameIndex =
390 frame->inOrdinal.frameIndex;
391 finalizeWork(*frame);
392 addOutWork(frame->mLargeWork);
393 frame->reset();
394 if (workResult != C2_OK) {
395 frame->mAccessUnitInfos.clear();
396 }
397 } else if (C2_OK != processWorklets(*frame, work, addOutWork)) {
398 LOG(DEBUG) << "Error while processing work";
399 }
400 if (removeEntry) {
401 LOG(DEBUG) << "Removing entry: " << thisFrameIndex
402 << " -> " << frame->inOrdinal.frameIndex.peekull();
403 frame->mComponentFrameIds.erase(it);
404 }
405 // This is to take care of the last bytes and to decide to send with
406 // FLAG_INCOMPLETE or not.
407 if ((frame->mWview
408 && (frame->mWview->offset() > frame->mLargeFrameTuning.thresholdSize))
409 || frame->mComponentFrameIds.empty()) {
410 if (frame->mLargeWork) {
411 finalizeWork(*frame);
412 addOutWork(frame->mLargeWork);
413 frame->reset();
414 }
415 }
416 if (frame->mComponentFrameIds.empty()) {
417 LOG(DEBUG) << "This frame is finished ID " << thisFrameIndex;
418 frame = mFrameHolder.erase(frame);
419 continue;
420 }
421 } else {
422 LOG(DEBUG) << "Received an out-of-order output " << thisFrameIndex
423 << " expected: " <<mFrameHolder.front().inOrdinal.frameIndex.peekull();
424 }
425 frame++;
426 }
427 if (!foundFrame) {
428 LOG(ERROR) <<" Error: Frame Holder reports no frame " << thisFrameIndex;
429 }
430 }
431 }
432 return C2_OK;
433}
434
435c2_status_t MultiAccessUnitHelper::createLinearBlock(MultiAccessUnitInfo &frame) {
436 if (!mInit) {
437 LOG(ERROR) << "Large buffer allocator failed";
438 return C2_NO_MEMORY;
439 }
440 C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
441 uint32_t maxOutSize = frame.mLargeFrameTuning.maxSize;
442 c2_status_t err = mLinearPool->fetchLinearBlock(maxOutSize, usage, &frame.mBlock);
443 LOG(DEBUG) << "Allocated block with offset : " << frame.mBlock->offset()
444 << " size " << frame.mBlock->size() << " Capacity " << frame.mBlock->capacity();
445 if (err != C2_OK) {
446 LOG(ERROR) << "Error allocating Multi access-unit Buffer";
447 return err;
448 }
449 frame.mWview = std::make_shared<C2WriteView>(frame.mBlock->map().get());
450 LOG(DEBUG) << "Allocated buffer : requested size : " <<
451 frame.mLargeFrameTuning.maxSize
452 << " alloc size " << frame.mWview->size();
453 return C2_OK;
454}
455
456/*
457 * For every work from the component, we try to do aggregation of work here.
458*/
459c2_status_t MultiAccessUnitHelper::processWorklets(MultiAccessUnitInfo &frame,
460 std::unique_ptr<C2Work>& work,
461 const std::function <void(std::unique_ptr<C2Work>&)>& addWork) {
462 // This will allocate work, worklet, c2Block
463 auto allocateWork = [&](MultiAccessUnitInfo &frame,
464 bool allocateWorket = false,
465 bool allocateBuffer = false) {
466 c2_status_t ret = C2_OK;
467 if (frame.mLargeWork == nullptr) {
468 frame.mLargeWork.reset(new C2Work);
469 frame.mLargeWork->input.ordinal = frame.inOrdinal;
470 frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
471 }
472 if (allocateWorket) {
473 if (frame.mLargeWork->worklets.size() == 0) {
474 frame.mLargeWork->worklets.emplace_back(new C2Worklet);
475 }
476 }
477 if (allocateBuffer) {
478 if (frame.mWview == nullptr) {
479 ret = createLinearBlock(frame);
480 }
481 }
482 return ret;
483 };
484 // we will only have one worklet.
485 bool foundEndOfStream = false;
486 for (auto worklet = work->worklets.begin();
487 worklet != work->worklets.end() && (*worklet) != nullptr; ++worklet) {
488 uint32_t flagsForNoCopy = C2FrameData::FLAG_DROP_FRAME
489 | C2FrameData::FLAG_DISCARD_FRAME
490 | C2FrameData::FLAG_CORRUPT;
491 if ((*worklet)->output.flags & flagsForNoCopy) {
492 if (frame.mLargeWork) {
493 finalizeWork(frame);
494 addWork(frame.mLargeWork);
495 frame.reset();
496 }
497 frame.mLargeWork = std::move(work);
498 frame.mLargeWork->input.ordinal.frameIndex = frame.inOrdinal.frameIndex;
Arun Johnsond43dc8c2024-02-07 01:14:15 +0000499 finalizeWork(frame, (*worklet)->output.flags, true);
Arun Johnsonfb946102023-12-27 01:10:34 +0000500 addWork(frame.mLargeWork);
501 frame.reset();
502 return C2_OK;
503 }
504 c2_status_t c2ret = allocateWork(frame, true);
505 if (c2ret != C2_OK) {
506 return c2ret;
507 }
508 C2FrameData& outputFramedata = frame.mLargeWork->worklets.front()->output;
509 if (!(*worklet)->output.configUpdate.empty()) {
510 for (auto& configUpdate : (*worklet)->output.configUpdate) {
511 outputFramedata.configUpdate.push_back(std::move(configUpdate));
512 }
513 (*worklet)->output.configUpdate.clear();
514 }
515 outputFramedata.infoBuffers.insert(outputFramedata.infoBuffers.begin(),
516 (*worklet)->output.infoBuffers.begin(),
517 (*worklet)->output.infoBuffers.end());
518 int64_t sampleTimeUs = 0;
519 uint32_t frameSize = 0;
520 uint32_t sampleRate = 0;
521 uint32_t channelCount = 0;
522 mInterface->getDecoderSampleRateAndChannelCount(sampleRate, channelCount);
523 if (sampleRate > 0 && channelCount > 0) {
524 sampleTimeUs = (1000000u) / (sampleRate * channelCount * 2);
525 frameSize = channelCount * 2;
526 }
527 LOG(DEBUG) << "maxOutSize " << frame.mLargeFrameTuning.maxSize
528 << " threshold " << frame.mLargeFrameTuning.thresholdSize;
529 if ((*worklet)->output.buffers.size() > 0) {
530 allocateWork(frame, true, true);
531 }
532 LOG(DEBUG) << "This worklet has " << (*worklet)->output.buffers.size() << " buffers"
533 << " ts: " << (*worklet)->output.ordinal.timestamp.peekull();
534 int64_t workletTimestamp = (*worklet)->output.ordinal.timestamp.peekull();
535 int64_t timestamp = workletTimestamp;
536 uint32_t flagsForCopy = ((*worklet)->output.flags) & C2FrameData::FLAG_CODEC_CONFIG;
537 for (int bufIdx = 0; bufIdx < (*worklet)->output.buffers.size(); ++bufIdx) {
538 std::shared_ptr<C2Buffer>& buffer = (*worklet)->output.buffers[bufIdx];
539 if (!buffer || buffer->data().linearBlocks().empty()) {
540 continue;
541 }
542 const std::vector<C2ConstLinearBlock>& blocks = buffer->data().linearBlocks();
543 if (blocks.size() > 0) {
544 uint32_t inputOffset = 0;
545 uint32_t inputSize = blocks.front().size();
546 frame.mInfos.insert(frame.mInfos.end(),
547 buffer->info().begin(), buffer->info().end());
548 if (frameSize != 0 && (mInterface->kind() == C2Component::KIND_DECODER)) {
549 // For decoders we only split multiples of 16bChannelCount*2
550 inputSize -= (inputSize % frameSize);
551 }
552 while (inputOffset < inputSize) {
553 if (frame.mWview->offset() >= frame.mLargeFrameTuning.thresholdSize) {
554 frame.mLargeWork->result = C2_OK;
555 finalizeWork(frame, flagsForCopy);
556 addWork(frame.mLargeWork);
557 frame.reset();
558 allocateWork(frame, true, true);
559 }
560 if (mInterface->kind() == C2Component::KIND_ENCODER) {
561 if (inputSize > frame.mLargeFrameTuning.maxSize) {
562 LOG(ERROR) << "Enc: Output buffer too small for AU, configured with "
563 << frame.mLargeFrameTuning.maxSize
564 << " block size: " << blocks.front().size()
565 << "alloc size " << frame.mWview->size();
566 if (frame.mLargeWork
567 && frame.mWview && frame.mWview->offset() > 0) {
568 finalizeWork(frame, flagsForCopy);
569 addWork(frame.mLargeWork);
570 frame.reset();
571 allocateWork(frame, true, false);
572 }
573 frame.mLargeWork->result = C2_NO_MEMORY;
574 finalizeWork(frame, 0, true);
575 addWork(frame.mLargeWork);
576 frame.reset();
577 return C2_NO_MEMORY;
578 } else if (inputSize > frame.mWview->size()) {
579 LOG(DEBUG) << "Enc: Large frame hitting bufer limit, current size "
580 << frame.mWview->offset();
581 if (frame.mLargeWork
582 && frame.mWview && frame.mWview->offset() > 0) {
583 finalizeWork(frame, flagsForCopy);
584 addWork(frame.mLargeWork);
585 frame.reset();
586 allocateWork(frame, true, true);
587 }
588 }
589 }
590 C2ReadView rView = blocks.front().map().get();
591 if (rView.error()) {
592 LOG(ERROR) << "Buffer read view error";
593 frame.mLargeWork->result = rView.error();
594 frame.mLargeWork->worklets.clear();
595 finalizeWork(frame, 0, true);
596 addWork(frame.mLargeWork);
597 frame.reset();
598 return C2_NO_MEMORY;
599 }
600 uint32_t toCopy = 0;
601 if (mInterface->kind() == C2Component::KIND_ENCODER) {
602 toCopy = inputSize;
603 } else {
604 toCopy = c2_min(frame.mWview->size(), (inputSize - inputOffset));
605 timestamp = workletTimestamp + inputOffset * sampleTimeUs;
606 LOG(DEBUG) << "ts " << timestamp
607 << " copiedOutput " << inputOffset
608 << " sampleTimeUs " << sampleTimeUs;
609 }
610 LOG(DEBUG) << " Copy size " << toCopy
611 << " ts " << timestamp;
612 memcpy(frame.mWview->data(), rView.data() + inputOffset, toCopy);
613 frame.mWview->setOffset(frame.mWview->offset() + toCopy);
614 inputOffset += toCopy;
615 mergeAccessUnitInfo(frame, flagsForCopy, toCopy, timestamp);
616 }
617 } else {
618 frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(buffer));
619 LOG(DEBUG) << "Copying worklets without linear buffer";
620 }
621 }
622 uint32_t flagsForCsdOrEnd = (*worklet)->output.flags
623 & (C2FrameData::FLAG_END_OF_STREAM | C2FrameData::FLAG_CODEC_CONFIG);
624 if (flagsForCsdOrEnd) {
625 LOG(DEBUG) << "Output worklet has CSD/EOS data";
626 frame.mLargeWork->result = C2_OK;
627 // we can assign timestamp as this will be evaluated in finalizeWork
628 frame.mLargeWork->worklets.front()->output.ordinal.timestamp = timestamp;
629 finalizeWork(frame, flagsForCsdOrEnd, true);
630 addWork(frame.mLargeWork);
631 frame.reset();
632 }
633 }
634 return C2_OK;
635}
636
637c2_status_t MultiAccessUnitHelper::finalizeWork(
638 MultiAccessUnitInfo& frame, uint32_t inFlags, bool forceComplete) {
639 if (frame.mLargeWork == nullptr) {
640 return C2_OK;
641 }
642 //prepare input ordinal
643 frame.mLargeWork->input.ordinal = frame.inOrdinal;
644 // remove this
645 int64_t timeStampUs = frame.inOrdinal.timestamp.peekull();
646 if (!frame.mAccessUnitInfos.empty()) {
647 timeStampUs = frame.mAccessUnitInfos.front().timestamp;
648 } else if (!frame.mLargeWork->worklets.empty()) {
649 std::unique_ptr<C2Worklet> &worklet = frame.mLargeWork->worklets.front();
650 if (worklet) {
651 timeStampUs = worklet->output.ordinal.timestamp.peekull();
652 }
653 }
654 LOG(DEBUG) << "Finalizing work with input Idx "
655 << frame.mLargeWork->input.ordinal.frameIndex.peekull()
656 << " timestamp " << timeStampUs;
657 uint32_t finalFlags = 0;
658 if ((!forceComplete)
659 && (frame.mLargeWork->result == C2_OK)
660 && (!frame.mComponentFrameIds.empty())) {
661 finalFlags |= C2FrameData::FLAG_INCOMPLETE;
662 }
663 if (frame.mLargeWork->result == C2_OK) {
664 finalFlags |= inFlags;
665 }
666 // update worklet if present
667 if (!frame.mLargeWork->worklets.empty() &&
668 frame.mLargeWork->worklets.front() != nullptr) {
669 frame.mLargeWork->workletsProcessed = 1;
670 C2FrameData& outFrameData = frame.mLargeWork->worklets.front()->output;
671 outFrameData.ordinal.frameIndex = frame.inOrdinal.frameIndex.peekull();
672 outFrameData.ordinal.timestamp = timeStampUs;
673 finalFlags |= frame.mLargeWork->worklets.front()->output.flags;
674 outFrameData.flags = (C2FrameData::flags_t)finalFlags;
675 // update buffers
676 if (frame.mBlock && (frame.mWview->offset() > 0)) {
677 size_t size = frame.mWview->offset();
678 LOG(DEBUG) << "Finalize : Block: Large frame size set as " << size
679 << " timestamp as " << timeStampUs
680 << "frameIndex " << outFrameData.ordinal.frameIndex.peekull();
681 frame.mWview->setOffset(0);
682 std::shared_ptr<C2Buffer> c2Buffer = C2Buffer::CreateLinearBuffer(
683 frame.mBlock->share(0, size, ::C2Fence()));
684 if (frame.mAccessUnitInfos.size() > 0) {
685 if (finalFlags & C2FrameData::FLAG_END_OF_STREAM) {
686 frame.mAccessUnitInfos.back().flags |=
687 C2FrameData::FLAG_END_OF_STREAM;
688 }
689 std::shared_ptr<C2AccessUnitInfos::output> largeFrame =
690 C2AccessUnitInfos::output::AllocShared(
691 frame.mAccessUnitInfos.size(), 0u, frame.mAccessUnitInfos);
692 frame.mInfos.push_back(largeFrame);
693 frame.mAccessUnitInfos.clear();
694 }
695 for (auto &info : frame.mInfos) {
696 c2Buffer->setInfo(std::const_pointer_cast<C2Info>(info));
697 }
698 frame.mLargeWork->worklets.front()->output.buffers.push_back(std::move(c2Buffer));
699 frame.mInfos.clear();
700 frame.mBlock.reset();
701 frame.mWview.reset();
702 }
703 }
704 LOG(DEBUG) << "Multi access-unitflag setting as " << finalFlags;
705 return C2_OK;
706}
707
708void MultiAccessUnitHelper::mergeAccessUnitInfo(
709 MultiAccessUnitInfo &frame,
710 uint32_t flags_,
711 uint32_t size,
712 int64_t timestamp) {
713 // Remove flags that are not part of Access unit info
714 uint32_t flags = flags_ & ~(C2FrameData::FLAG_INCOMPLETE
715 | C2FrameData::FLAG_DISCARD_FRAME
716 | C2FrameData::FLAG_CORRUPT
717 | C2FrameData::FLAG_CORRECTED);
718 if (frame.mAccessUnitInfos.empty()) {
719 frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
720 return;
721 }
722 if ((mInterface->kind() == C2Component::KIND_DECODER) &&
723 (frame.mAccessUnitInfos.back().flags == flags)) {
724 // merge access units here
725 C2AccessUnitInfosStruct &s = frame.mAccessUnitInfos.back();
726 s.size += size; // don't have to update timestamp
727 } else {
728 frame.mAccessUnitInfos.emplace_back(flags, size, timestamp);
729 }
730}
731
732void MultiAccessUnitHelper::MultiAccessUnitInfo::reset() {
733 mBlock.reset();
734 mWview.reset();
735 mInfos.clear();
736 mAccessUnitInfos.clear();
737 mLargeWork.reset();
738}
739
740} // namespace android