| Wonsik Kim | e1104ca | 2020-11-24 15:01:33 -0800 | [diff] [blame] | 1 | /* | 
|  | 2 | * Copyright 2019 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 "FrameReassembler" | 
|  | 19 |  | 
|  | 20 | #include <log/log.h> | 
|  | 21 |  | 
|  | 22 | #include <media/stagefright/foundation/AMessage.h> | 
|  | 23 |  | 
|  | 24 | #include "FrameReassembler.h" | 
|  | 25 |  | 
|  | 26 | namespace android { | 
|  | 27 |  | 
|  | 28 | static constexpr uint64_t kToleranceUs = 1000;  // 1ms | 
|  | 29 |  | 
|  | 30 | FrameReassembler::FrameReassembler() | 
|  | 31 | : mUsage{0, 0}, | 
|  | 32 | mSampleRate(0u), | 
|  | 33 | mChannelCount(0u), | 
|  | 34 | mEncoding(C2Config::PCM_16), | 
|  | 35 | mCurrentOrdinal({0, 0, 0}) { | 
|  | 36 | } | 
|  | 37 |  | 
|  | 38 | void FrameReassembler::init( | 
|  | 39 | const std::shared_ptr<C2BlockPool> &pool, | 
|  | 40 | C2MemoryUsage usage, | 
|  | 41 | uint32_t frameSize, | 
|  | 42 | uint32_t sampleRate, | 
|  | 43 | uint32_t channelCount, | 
|  | 44 | C2Config::pcm_encoding_t encoding) { | 
|  | 45 | mBlockPool = pool; | 
|  | 46 | mUsage = usage; | 
|  | 47 | mFrameSize = frameSize; | 
|  | 48 | mSampleRate = sampleRate; | 
|  | 49 | mChannelCount = channelCount; | 
|  | 50 | mEncoding = encoding; | 
|  | 51 | } | 
|  | 52 |  | 
|  | 53 | void FrameReassembler::updateFrameSize(uint32_t frameSize) { | 
|  | 54 | finishCurrentBlock(&mPendingWork); | 
|  | 55 | mFrameSize = frameSize; | 
|  | 56 | } | 
|  | 57 |  | 
|  | 58 | void FrameReassembler::updateSampleRate(uint32_t sampleRate) { | 
|  | 59 | finishCurrentBlock(&mPendingWork); | 
|  | 60 | mSampleRate = sampleRate; | 
|  | 61 | } | 
|  | 62 |  | 
|  | 63 | void FrameReassembler::updateChannelCount(uint32_t channelCount) { | 
|  | 64 | finishCurrentBlock(&mPendingWork); | 
|  | 65 | mChannelCount = channelCount; | 
|  | 66 | } | 
|  | 67 |  | 
|  | 68 | void FrameReassembler::updatePcmEncoding(C2Config::pcm_encoding_t encoding) { | 
|  | 69 | finishCurrentBlock(&mPendingWork); | 
|  | 70 | mEncoding = encoding; | 
|  | 71 | } | 
|  | 72 |  | 
|  | 73 | void FrameReassembler::reset() { | 
|  | 74 | flush(); | 
|  | 75 | mCurrentOrdinal = {0, 0, 0}; | 
|  | 76 | mBlockPool.reset(); | 
|  | 77 | mFrameSize.reset(); | 
|  | 78 | mSampleRate = 0u; | 
|  | 79 | mChannelCount = 0u; | 
|  | 80 | mEncoding = C2Config::PCM_16; | 
|  | 81 | } | 
|  | 82 |  | 
|  | 83 | FrameReassembler::operator bool() const { | 
|  | 84 | return mFrameSize.has_value(); | 
|  | 85 | } | 
|  | 86 |  | 
|  | 87 | c2_status_t FrameReassembler::process( | 
|  | 88 | const sp<MediaCodecBuffer> &buffer, | 
|  | 89 | std::list<std::unique_ptr<C2Work>> *items) { | 
|  | 90 | int64_t timeUs; | 
| Wonsik Kim | cc59ad8 | 2021-08-11 18:15:19 -0700 | [diff] [blame] | 91 | if (!buffer->meta()->findInt64("timeUs", &timeUs)) { | 
| Wonsik Kim | e1104ca | 2020-11-24 15:01:33 -0800 | [diff] [blame] | 92 | return C2_BAD_VALUE; | 
|  | 93 | } | 
|  | 94 |  | 
|  | 95 | items->splice(items->end(), mPendingWork); | 
|  | 96 |  | 
|  | 97 | // Fill mCurrentBlock | 
|  | 98 | if (mCurrentBlock) { | 
|  | 99 | // First check the timestamp | 
|  | 100 | c2_cntr64_t endTimestampUs = mCurrentOrdinal.timestamp; | 
|  | 101 | endTimestampUs += bytesToSamples(mWriteView->size()) * 1000000 / mSampleRate; | 
|  | 102 | if (timeUs < endTimestampUs.peek()) { | 
|  | 103 | uint64_t diffUs = (endTimestampUs - timeUs).peeku(); | 
|  | 104 | if (diffUs > kToleranceUs) { | 
|  | 105 | // The timestamp is going back in time in large amount. | 
|  | 106 | // TODO: b/145702136 | 
|  | 107 | ALOGW("timestamp going back in time! from %lld to %lld", | 
|  | 108 | endTimestampUs.peekll(), (long long)timeUs); | 
|  | 109 | } | 
|  | 110 | } else {  // timeUs >= endTimestampUs.peek() | 
|  | 111 | uint64_t diffUs = (timeUs - endTimestampUs).peeku(); | 
|  | 112 | if (diffUs > kToleranceUs) { | 
|  | 113 | // The timestamp is going forward; add silence as necessary. | 
|  | 114 | size_t gapSamples = usToSamples(diffUs); | 
|  | 115 | size_t remainingSamples = | 
|  | 116 | (mWriteView->capacity() - mWriteView->size()) | 
|  | 117 | / mChannelCount / bytesPerSample(); | 
|  | 118 | if (gapSamples < remainingSamples) { | 
|  | 119 | size_t gapBytes = gapSamples * mChannelCount * bytesPerSample(); | 
|  | 120 | memset(mWriteView->base() + mWriteView->size(), 0u, gapBytes); | 
|  | 121 | mWriteView->setSize(mWriteView->size() + gapBytes); | 
|  | 122 | } else { | 
|  | 123 | finishCurrentBlock(items); | 
|  | 124 | } | 
|  | 125 | } | 
|  | 126 | } | 
|  | 127 | } | 
|  | 128 |  | 
|  | 129 | if (mCurrentBlock) { | 
|  | 130 | // Append the data at the end of the current block | 
|  | 131 | size_t copySize = std::min( | 
|  | 132 | buffer->size(), | 
|  | 133 | size_t(mWriteView->capacity() - mWriteView->size())); | 
|  | 134 | memcpy(mWriteView->base() + mWriteView->size(), buffer->data(), copySize); | 
|  | 135 | buffer->setRange(buffer->offset() + copySize, buffer->size() - copySize); | 
|  | 136 | mWriteView->setSize(mWriteView->size() + copySize); | 
|  | 137 | if (mWriteView->size() == mWriteView->capacity()) { | 
|  | 138 | finishCurrentBlock(items); | 
|  | 139 | } | 
|  | 140 | timeUs += bytesToSamples(copySize) * 1000000 / mSampleRate; | 
|  | 141 | } | 
|  | 142 |  | 
|  | 143 | if (buffer->size() > 0) { | 
|  | 144 | mCurrentOrdinal.timestamp = timeUs; | 
| Aniket Kumar Lata | 51844db | 2021-03-22 11:02:24 -0700 | [diff] [blame] | 145 | mCurrentOrdinal.customOrdinal = timeUs; | 
| Wonsik Kim | e1104ca | 2020-11-24 15:01:33 -0800 | [diff] [blame] | 146 | } | 
|  | 147 |  | 
|  | 148 | size_t frameSizeBytes = mFrameSize.value() * mChannelCount * bytesPerSample(); | 
|  | 149 | while (buffer->size() > 0) { | 
|  | 150 | LOG_ALWAYS_FATAL_IF( | 
|  | 151 | mCurrentBlock, | 
|  | 152 | "There's remaining data but the pending block is not filled & finished"); | 
|  | 153 | std::unique_ptr<C2Work> work(new C2Work); | 
|  | 154 | c2_status_t err = mBlockPool->fetchLinearBlock(frameSizeBytes, mUsage, &mCurrentBlock); | 
|  | 155 | if (err != C2_OK) { | 
|  | 156 | return err; | 
|  | 157 | } | 
|  | 158 | size_t copySize = std::min(buffer->size(), frameSizeBytes); | 
|  | 159 | mWriteView = mCurrentBlock->map().get(); | 
|  | 160 | if (mWriteView->error() != C2_OK) { | 
|  | 161 | return mWriteView->error(); | 
|  | 162 | } | 
| Wonsik Kim | 0379ae8 | 2020-11-24 15:01:33 -0800 | [diff] [blame] | 163 | ALOGV("buffer={offset=%zu size=%zu} copySize=%zu", | 
| Wonsik Kim | e1104ca | 2020-11-24 15:01:33 -0800 | [diff] [blame] | 164 | buffer->offset(), buffer->size(), copySize); | 
|  | 165 | memcpy(mWriteView->base(), buffer->data(), copySize); | 
|  | 166 | mWriteView->setOffset(0u); | 
|  | 167 | mWriteView->setSize(copySize); | 
|  | 168 | buffer->setRange(buffer->offset() + copySize, buffer->size() - copySize); | 
|  | 169 | if (copySize == frameSizeBytes) { | 
|  | 170 | finishCurrentBlock(items); | 
|  | 171 | } | 
|  | 172 | } | 
|  | 173 |  | 
|  | 174 | int32_t eos = 0; | 
|  | 175 | if (buffer->meta()->findInt32("eos", &eos) && eos) { | 
|  | 176 | finishCurrentBlock(items); | 
|  | 177 | } | 
|  | 178 |  | 
|  | 179 | return C2_OK; | 
|  | 180 | } | 
|  | 181 |  | 
|  | 182 | void FrameReassembler::flush() { | 
|  | 183 | mPendingWork.clear(); | 
|  | 184 | mWriteView.reset(); | 
|  | 185 | mCurrentBlock.reset(); | 
|  | 186 | } | 
|  | 187 |  | 
|  | 188 | uint64_t FrameReassembler::bytesToSamples(size_t numBytes) const { | 
|  | 189 | return numBytes / mChannelCount / bytesPerSample(); | 
|  | 190 | } | 
|  | 191 |  | 
|  | 192 | size_t FrameReassembler::usToSamples(uint64_t us) const { | 
|  | 193 | return (us * mChannelCount * mSampleRate / 1000000); | 
|  | 194 | } | 
|  | 195 |  | 
|  | 196 | uint32_t FrameReassembler::bytesPerSample() const { | 
|  | 197 | return (mEncoding == C2Config::PCM_8) ? 1 | 
|  | 198 | : (mEncoding == C2Config::PCM_16) ? 2 | 
|  | 199 | : (mEncoding == C2Config::PCM_FLOAT) ? 4 : 0; | 
|  | 200 | } | 
|  | 201 |  | 
|  | 202 | void FrameReassembler::finishCurrentBlock(std::list<std::unique_ptr<C2Work>> *items) { | 
|  | 203 | if (!mCurrentBlock) { | 
|  | 204 | // No-op | 
|  | 205 | return; | 
|  | 206 | } | 
|  | 207 | if (mWriteView->size() < mWriteView->capacity()) { | 
|  | 208 | memset(mWriteView->base() + mWriteView->size(), 0u, | 
|  | 209 | mWriteView->capacity() - mWriteView->size()); | 
|  | 210 | mWriteView->setSize(mWriteView->capacity()); | 
|  | 211 | } | 
|  | 212 | std::unique_ptr<C2Work> work{std::make_unique<C2Work>()}; | 
|  | 213 | work->input.ordinal = mCurrentOrdinal; | 
|  | 214 | work->input.buffers.push_back(C2Buffer::CreateLinearBuffer( | 
|  | 215 | mCurrentBlock->share(0, mCurrentBlock->capacity(), C2Fence()))); | 
|  | 216 | work->worklets.clear(); | 
|  | 217 | work->worklets.emplace_back(new C2Worklet); | 
|  | 218 | items->push_back(std::move(work)); | 
|  | 219 |  | 
|  | 220 | ++mCurrentOrdinal.frameIndex; | 
|  | 221 | mCurrentOrdinal.timestamp += mFrameSize.value() * 1000000 / mSampleRate; | 
| Aniket Kumar Lata | 51844db | 2021-03-22 11:02:24 -0700 | [diff] [blame] | 222 | mCurrentOrdinal.customOrdinal = mCurrentOrdinal.timestamp; | 
| Wonsik Kim | e1104ca | 2020-11-24 15:01:33 -0800 | [diff] [blame] | 223 | mCurrentBlock.reset(); | 
|  | 224 | mWriteView.reset(); | 
|  | 225 | } | 
|  | 226 |  | 
|  | 227 | }  // namespace android |