/* * Copyright 2019 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "FrameReassembler" #include #include #include "FrameReassembler.h" namespace android { static constexpr uint64_t kToleranceUs = 1000; // 1ms FrameReassembler::FrameReassembler() : mUsage{0, 0}, mSampleRate(0u), mChannelCount(0u), mEncoding(C2Config::PCM_16), mCurrentOrdinal({0, 0, 0}) { } void FrameReassembler::init( const std::shared_ptr &pool, C2MemoryUsage usage, uint32_t frameSize, uint32_t sampleRate, uint32_t channelCount, C2Config::pcm_encoding_t encoding) { mBlockPool = pool; mUsage = usage; mFrameSize = frameSize; mSampleRate = sampleRate; mChannelCount = channelCount; mEncoding = encoding; } void FrameReassembler::updateFrameSize(uint32_t frameSize) { finishCurrentBlock(&mPendingWork); mFrameSize = frameSize; } void FrameReassembler::updateSampleRate(uint32_t sampleRate) { finishCurrentBlock(&mPendingWork); mSampleRate = sampleRate; } void FrameReassembler::updateChannelCount(uint32_t channelCount) { finishCurrentBlock(&mPendingWork); mChannelCount = channelCount; } void FrameReassembler::updatePcmEncoding(C2Config::pcm_encoding_t encoding) { finishCurrentBlock(&mPendingWork); mEncoding = encoding; } void FrameReassembler::reset() { flush(); mCurrentOrdinal = {0, 0, 0}; mBlockPool.reset(); mFrameSize.reset(); mSampleRate = 0u; mChannelCount = 0u; mEncoding = C2Config::PCM_16; } FrameReassembler::operator bool() const { return mFrameSize.has_value(); } c2_status_t FrameReassembler::process( const sp &buffer, std::list> *items) { int64_t timeUs; if (!buffer->meta()->findInt64("timeUs", &timeUs)) { return C2_BAD_VALUE; } items->splice(items->end(), mPendingWork); // Fill mCurrentBlock if (mCurrentBlock) { // First check the timestamp c2_cntr64_t endTimestampUs = mCurrentOrdinal.timestamp; endTimestampUs += bytesToSamples(mWriteView->size()) * 1000000 / mSampleRate; if (timeUs < endTimestampUs.peek()) { uint64_t diffUs = (endTimestampUs - timeUs).peeku(); if (diffUs > kToleranceUs) { // The timestamp is going back in time in large amount. // TODO: b/145702136 ALOGW("timestamp going back in time! from %lld to %lld", endTimestampUs.peekll(), (long long)timeUs); } } else { // timeUs >= endTimestampUs.peek() uint64_t diffUs = (timeUs - endTimestampUs).peeku(); if (diffUs > kToleranceUs) { // The timestamp is going forward; add silence as necessary. size_t gapSamples = usToSamples(diffUs); size_t remainingSamples = (mWriteView->capacity() - mWriteView->size()) / mChannelCount / bytesPerSample(); if (gapSamples < remainingSamples) { size_t gapBytes = gapSamples * mChannelCount * bytesPerSample(); memset(mWriteView->base() + mWriteView->size(), 0u, gapBytes); mWriteView->setSize(mWriteView->size() + gapBytes); } else { finishCurrentBlock(items); } } } } if (mCurrentBlock) { // Append the data at the end of the current block size_t copySize = std::min( buffer->size(), size_t(mWriteView->capacity() - mWriteView->size())); memcpy(mWriteView->base() + mWriteView->size(), buffer->data(), copySize); buffer->setRange(buffer->offset() + copySize, buffer->size() - copySize); mWriteView->setSize(mWriteView->size() + copySize); if (mWriteView->size() == mWriteView->capacity()) { finishCurrentBlock(items); } timeUs += bytesToSamples(copySize) * 1000000 / mSampleRate; } if (buffer->size() > 0) { mCurrentOrdinal.timestamp = timeUs; mCurrentOrdinal.customOrdinal = timeUs; } size_t frameSizeBytes = mFrameSize.value() * mChannelCount * bytesPerSample(); while (buffer->size() > 0) { LOG_ALWAYS_FATAL_IF( mCurrentBlock, "There's remaining data but the pending block is not filled & finished"); std::unique_ptr work(new C2Work); c2_status_t err = mBlockPool->fetchLinearBlock(frameSizeBytes, mUsage, &mCurrentBlock); if (err != C2_OK) { return err; } size_t copySize = std::min(buffer->size(), frameSizeBytes); mWriteView = mCurrentBlock->map().get(); if (mWriteView->error() != C2_OK) { return mWriteView->error(); } ALOGV("buffer={offset=%zu size=%zu} copySize=%zu", buffer->offset(), buffer->size(), copySize); memcpy(mWriteView->base(), buffer->data(), copySize); mWriteView->setOffset(0u); mWriteView->setSize(copySize); buffer->setRange(buffer->offset() + copySize, buffer->size() - copySize); if (copySize == frameSizeBytes) { finishCurrentBlock(items); } } int32_t eos = 0; if (buffer->meta()->findInt32("eos", &eos) && eos) { finishCurrentBlock(items); } return C2_OK; } void FrameReassembler::flush() { mPendingWork.clear(); mWriteView.reset(); mCurrentBlock.reset(); } uint64_t FrameReassembler::bytesToSamples(size_t numBytes) const { return numBytes / mChannelCount / bytesPerSample(); } size_t FrameReassembler::usToSamples(uint64_t us) const { return (us * mChannelCount * mSampleRate / 1000000); } uint32_t FrameReassembler::bytesPerSample() const { return (mEncoding == C2Config::PCM_8) ? 1 : (mEncoding == C2Config::PCM_16) ? 2 : (mEncoding == C2Config::PCM_FLOAT) ? 4 : 0; } void FrameReassembler::finishCurrentBlock(std::list> *items) { if (!mCurrentBlock) { // No-op return; } if (mWriteView->size() < mWriteView->capacity()) { memset(mWriteView->base() + mWriteView->size(), 0u, mWriteView->capacity() - mWriteView->size()); mWriteView->setSize(mWriteView->capacity()); } std::unique_ptr work{std::make_unique()}; work->input.ordinal = mCurrentOrdinal; work->input.buffers.push_back(C2Buffer::CreateLinearBuffer( mCurrentBlock->share(0, mCurrentBlock->capacity(), C2Fence()))); work->worklets.clear(); work->worklets.emplace_back(new C2Worklet); items->push_back(std::move(work)); ++mCurrentOrdinal.frameIndex; mCurrentOrdinal.timestamp += mFrameSize.value() * 1000000 / mSampleRate; mCurrentOrdinal.customOrdinal = mCurrentOrdinal.timestamp; mCurrentBlock.reset(); mWriteView.reset(); } } // namespace android