blob: d313f33a45c2e5b45960f29b76b5f18efb286151 [file] [log] [blame]
/*
* 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 "CCodecBuffers"
#include <utils/Log.h>
#include <numeric>
#include <C2AllocatorGralloc.h>
#include <C2PlatformSupport.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/MediaDefs.h>
#include <media/stagefright/CodecBase.h>
#include <media/stagefright/MediaCodecConstants.h>
#include <media/stagefright/SkipCutBuffer.h>
#include <mediadrm/ICrypto.h>
#include "CCodecBuffers.h"
#include "Codec2Mapper.h"
namespace android {
namespace {
constexpr uint32_t PIXEL_FORMAT_UNKNOWN = 0;
sp<GraphicBlockBuffer> AllocateInputGraphicBuffer(
const std::shared_ptr<C2BlockPool> &pool,
const sp<AMessage> &format,
uint32_t pixelFormat,
const C2MemoryUsage &usage,
const std::shared_ptr<LocalBufferPool> &localBufferPool) {
int32_t width, height;
if (!format->findInt32("width", &width) || !format->findInt32("height", &height)) {
ALOGD("format lacks width or height");
return nullptr;
}
int64_t usageValue = 0;
(void)format->findInt64("android._C2MemoryUsage", &usageValue);
C2MemoryUsage fullUsage{usageValue | usage.expected};
std::shared_ptr<C2GraphicBlock> block;
c2_status_t err = pool->fetchGraphicBlock(
width, height, pixelFormat, fullUsage, &block);
if (err != C2_OK) {
ALOGD("fetch graphic block failed: %d", err);
return nullptr;
}
return GraphicBlockBuffer::Allocate(
format,
block,
[localBufferPool](size_t capacity) {
return localBufferPool->newBuffer(capacity);
});
}
} // namespace
// CCodecBuffers
void CCodecBuffers::setFormat(const sp<AMessage> &format) {
CHECK(format != nullptr);
mFormat = format;
}
sp<AMessage> CCodecBuffers::dupFormat() {
return mFormat != nullptr ? mFormat->dup() : nullptr;
}
void CCodecBuffers::handleImageData(const sp<Codec2Buffer> &buffer) {
sp<ABuffer> imageDataCandidate = buffer->getImageData();
if (imageDataCandidate == nullptr) {
if (mFormatWithImageData) {
// We previously sent the format with image data, so use the same format.
buffer->setFormat(mFormatWithImageData);
}
return;
}
if (!mLastImageData
|| imageDataCandidate->size() != mLastImageData->size()
|| memcmp(imageDataCandidate->data(),
mLastImageData->data(),
mLastImageData->size()) != 0) {
ALOGD("[%s] updating image-data", mName);
mFormatWithImageData = dupFormat();
mLastImageData = imageDataCandidate;
mFormatWithImageData->setBuffer("image-data", imageDataCandidate);
MediaImage2 *img = (MediaImage2*)imageDataCandidate->data();
if (img->mNumPlanes > 0 && img->mType != img->MEDIA_IMAGE_TYPE_UNKNOWN) {
int32_t stride = img->mPlane[0].mRowInc;
mFormatWithImageData->setInt32(KEY_STRIDE, stride);
mFormatWithImageData->setInt32(KEY_WIDTH, img->mWidth);
mFormatWithImageData->setInt32(KEY_HEIGHT, img->mHeight);
ALOGD("[%s] updating stride = %d, width: %d, height: %d",
mName, stride, img->mWidth, img->mHeight);
if (img->mNumPlanes > 1 && stride > 0) {
int64_t offsetDelta =
(int64_t)img->mPlane[1].mOffset - (int64_t)img->mPlane[0].mOffset;
int32_t vstride = int32_t(offsetDelta / stride);
mFormatWithImageData->setInt32(KEY_SLICE_HEIGHT, vstride);
ALOGD("[%s] updating vstride = %d", mName, vstride);
buffer->setRange(
img->mPlane[0].mOffset,
buffer->size() - img->mPlane[0].mOffset);
}
}
}
buffer->setFormat(mFormatWithImageData);
}
uint32_t CCodecBuffers::getPixelFormatIfApplicable() { return PIXEL_FORMAT_UNKNOWN; }
bool CCodecBuffers::resetPixelFormatIfApplicable() { return false; }
// InputBuffers
sp<Codec2Buffer> InputBuffers::cloneAndReleaseBuffer(const sp<MediaCodecBuffer> &buffer) {
sp<Codec2Buffer> copy = createNewBuffer();
if (copy == nullptr) {
return nullptr;
}
std::shared_ptr<C2Buffer> c2buffer;
if (!releaseBuffer(buffer, &c2buffer, true)) {
return nullptr;
}
if (!copy->canCopy(c2buffer)) {
return nullptr;
}
if (!copy->copy(c2buffer)) {
return nullptr;
}
copy->meta()->extend(buffer->meta());
return copy;
}
// MultiAccessUnitSkipCutBuffer for buffer and bufferInfos
class MultiAccessUnitSkipCutBuffer : public SkipCutBuffer {
public:
explicit MultiAccessUnitSkipCutBuffer(
int32_t skip, int32_t cut, size_t num16BitChannels):
SkipCutBuffer(skip, cut, num16BitChannels),
mFrontPaddingDelay(0), mSize(0) {
}
void clearAll() {
mInfos.clear();
mFrontPaddingDelay = 0;
mSize = 0;
SkipCutBuffer::clear();
}
virtual ~MultiAccessUnitSkipCutBuffer() {
}
void submitMultiAccessUnits(
const sp<MediaCodecBuffer>& buffer,
int32_t sampleRate, size_t num16BitChannels,
std::shared_ptr<const C2AccessUnitInfos::output> &infos) {
if (infos == nullptr) {
// there is nothing to do more.
SkipCutBuffer::submit(buffer);
return;
}
typedef WrapperObject<std::vector<AccessUnitInfo>> BufferInfosWrapper;
CHECK_EQ(mSize, SkipCutBuffer::size());
sp<BufferInfosWrapper> bufferInfos{new BufferInfosWrapper(decltype(bufferInfos->value)())};
uint32_t availableSize = buffer->size() + SkipCutBuffer::size();
uint32_t frontPadding = mFrontPadding;
int32_t lastEmptyAccessUnitIndex = -1;
int64_t byteInUs = 0;
if (sampleRate > 0 && num16BitChannels > 0) {
byteInUs = (1000000u / (sampleRate * num16BitChannels * 2));
}
if (frontPadding > 0) {
mInfos.clear();
mSize = 0;
}
for (int i = 0 ; i < infos->flexCount() && frontPadding > 0; i++) {
uint32_t flagsInPadding = 0;
int64_t timeInPadding = 0;
if (infos->m.values[i].size <= frontPadding) {
// we have more front padding so this buffer is not going to be used.
int32_t consumed = infos->m.values[i].size;
frontPadding -= consumed;
mFrontPaddingDelay += byteInUs * (consumed);
availableSize -= consumed;
flagsInPadding |= toMediaCodecFlags(infos->m.values[i].flags);
timeInPadding = infos->m.values[i].timestamp;
} else {
C2AccessUnitInfosStruct info = infos->m.values[i];
mFrontPaddingDelay += byteInUs * (frontPadding);
info.size -= frontPadding;
info.timestamp -= mFrontPaddingDelay;
availableSize -= frontPadding;
flagsInPadding |= toMediaCodecFlags(infos->m.values[i].flags);
timeInPadding = infos->m.values[i].timestamp;
frontPadding = 0;
mInfos.push_back(info);
mSize += info.size;
}
if (flagsInPadding != 0) {
bufferInfos->value.emplace_back(
flagsInPadding, 0, timeInPadding);
}
lastEmptyAccessUnitIndex = i;
}
if (frontPadding <= 0) {
// process what's already in the buffer first
auto it = mInfos.begin();
while (it != mInfos.end() && availableSize > mBackPadding) {
// we have samples to send out.
if ((availableSize - it->size) >= mBackPadding) {
// this is totally used here.
int32_t consumed = it->size;
bufferInfos->value.emplace_back(
toMediaCodecFlags(it->flags), consumed, it->timestamp);
availableSize -= consumed;
mSize -= consumed;
it = mInfos.erase(it);
} else {
int32_t consumed = availableSize - mBackPadding;
bufferInfos->value.emplace_back(
toMediaCodecFlags(it->flags),
consumed,
it->timestamp);
it->size -= consumed;
it->timestamp += consumed * byteInUs;
availableSize -= consumed;
mSize -= consumed;
it++;
}
}
// if buffer has more process all of it and keep the remaining info.
for (int i = (lastEmptyAccessUnitIndex + 1) ; i < infos->flexCount() ; i++) {
// upddate updatedInfo and mInfos
if (availableSize > mBackPadding) {
// we have to take data from the new buffer.
if (availableSize - infos->m.values[i].size >= mBackPadding) {
// we are using this info
int32_t consumed = infos->m.values[i].size;
bufferInfos->value.emplace_back(
toMediaCodecFlags(infos->m.values[i].flags),
consumed,
infos->m.values[i].timestamp - mFrontPaddingDelay);
availableSize -= consumed;
} else {
// if we need to update the size
C2AccessUnitInfosStruct info = infos->m.values[i];
int32_t consumed = availableSize - mBackPadding;
bufferInfos->value.emplace_back(
toMediaCodecFlags(infos->m.values[i].flags),
consumed,
infos->m.values[i].timestamp - mFrontPaddingDelay);
info.size -= consumed;
info.timestamp = info.timestamp - mFrontPaddingDelay +
consumed * byteInUs;
mInfos.push_back(info);
availableSize -= consumed;
mSize += info.size;
}
} else {
// we have to maintain infos
C2AccessUnitInfosStruct info = infos->m.values[i];
info.timestamp -= mFrontPaddingDelay;
mInfos.push_back(info);
mSize += info.size;
}
}
}
SkipCutBuffer::submit(buffer);
infos = nullptr;
if (!bufferInfos->value.empty()) {
buffer->meta()->setObject("accessUnitInfo", bufferInfos);
}
}
protected:
// Flags can come with individual BufferInfos
// when used with large frame audio
constexpr static std::initializer_list<std::pair<uint32_t, uint32_t>> flagList = {
{BUFFER_FLAG_CODEC_CONFIG, C2FrameData::FLAG_CODEC_CONFIG},
{BUFFER_FLAG_END_OF_STREAM, C2FrameData::FLAG_END_OF_STREAM},
{BUFFER_FLAG_DECODE_ONLY, C2FrameData::FLAG_DROP_FRAME}
};
static uint32_t toMediaCodecFlags(uint32_t flags) {
return std::transform_reduce(
flagList.begin(), flagList.end(),
0u,
std::bit_or{},
[flags](const std::pair<uint32_t, uint32_t> &entry) {
return (flags & entry.second) ? entry.first : 0;
});
}
std::list<C2AccessUnitInfosStruct> mInfos;
int64_t mFrontPaddingDelay;
size_t mSize;
};
// OutputBuffers
OutputBuffers::OutputBuffers(const char *componentName, const char *name)
: CCodecBuffers(componentName, name) { }
OutputBuffers::~OutputBuffers() = default;
void OutputBuffers::initSkipCutBuffer(
int32_t delay, int32_t padding, int32_t sampleRate, int32_t channelCount) {
CHECK(mSkipCutBuffer == nullptr);
mDelay = delay;
mPadding = padding;
mSampleRate = sampleRate;
mChannelCount = channelCount;
setSkipCutBuffer(delay, padding);
}
void OutputBuffers::updateSkipCutBuffer(int32_t sampleRate, int32_t channelCount) {
if (mSkipCutBuffer == nullptr) {
return;
}
if (mSampleRate == sampleRate && mChannelCount == channelCount) {
return;
}
int32_t delay = mDelay;
int32_t padding = mPadding;
if (sampleRate != mSampleRate) {
delay = ((int64_t)delay * sampleRate) / mSampleRate;
padding = ((int64_t)padding * sampleRate) / mSampleRate;
}
mSampleRate = sampleRate;
mChannelCount = channelCount;
setSkipCutBuffer(delay, padding);
}
void OutputBuffers::updateSkipCutBuffer(const sp<AMessage> &format) {
AString mediaType;
if (format->findString(KEY_MIME, &mediaType)
&& mediaType == MIMETYPE_AUDIO_RAW) {
int32_t channelCount;
int32_t sampleRate;
if (format->findInt32(KEY_CHANNEL_COUNT, &channelCount)
&& format->findInt32(KEY_SAMPLE_RATE, &sampleRate)) {
updateSkipCutBuffer(sampleRate, channelCount);
}
}
}
void OutputBuffers::submit(const sp<MediaCodecBuffer> &buffer) {
if (mSkipCutBuffer != nullptr) {
mSkipCutBuffer->submit(buffer);
}
}
bool OutputBuffers::submit(const sp<MediaCodecBuffer> &buffer, int32_t sampleRate,
int32_t channelCount, std::shared_ptr<const C2AccessUnitInfos::output> &infos) {
if (mSkipCutBuffer == nullptr) {
return false;
}
mSkipCutBuffer->submitMultiAccessUnits(buffer, sampleRate, channelCount, infos);
return true;
}
void OutputBuffers::setSkipCutBuffer(int32_t skip, int32_t cut) {
if (mSkipCutBuffer != nullptr) {
size_t prevSize = mSkipCutBuffer->size();
if (prevSize != 0u) {
ALOGD("[%s] Replacing SkipCutBuffer holding %zu bytes", mName, prevSize);
}
}
mSkipCutBuffer = new MultiAccessUnitSkipCutBuffer(skip, cut, mChannelCount);
}
bool OutputBuffers::convert(
const std::shared_ptr<C2Buffer> &src, sp<Codec2Buffer> *dst) {
if (src && src->data().type() != C2BufferData::LINEAR) {
return false;
}
int32_t configEncoding = kAudioEncodingPcm16bit;
int32_t codecEncoding = kAudioEncodingPcm16bit;
if (mFormat->findInt32("android._codec-pcm-encoding", &codecEncoding)
&& mFormat->findInt32("android._config-pcm-encoding", &configEncoding)) {
if (mSrcEncoding != codecEncoding || mDstEncoding != configEncoding) {
if (codecEncoding != configEncoding) {
mDataConverter = AudioConverter::Create(
(AudioEncoding)codecEncoding, (AudioEncoding)configEncoding);
ALOGD_IF(mDataConverter, "[%s] Converter created from %d to %d",
mName, codecEncoding, configEncoding);
mFormatWithConverter = mFormat->dup();
mFormatWithConverter->setInt32(KEY_PCM_ENCODING, configEncoding);
} else {
mDataConverter = nullptr;
mFormatWithConverter = nullptr;
}
mSrcEncoding = codecEncoding;
mDstEncoding = configEncoding;
}
if (int encoding; !mFormat->findInt32(KEY_PCM_ENCODING, &encoding)
|| encoding != mDstEncoding) {
}
}
if (!mDataConverter) {
return false;
}
sp<MediaCodecBuffer> srcBuffer;
if (src) {
srcBuffer = ConstLinearBlockBuffer::Allocate(mFormat, src);
} else {
srcBuffer = new MediaCodecBuffer(mFormat, new ABuffer(0));
}
if (!srcBuffer) {
return false;
}
if (!*dst) {
*dst = new Codec2Buffer(
mFormat,
new ABuffer(mDataConverter->targetSize(srcBuffer->size())));
}
sp<MediaCodecBuffer> dstBuffer = *dst;
status_t err = mDataConverter->convert(srcBuffer, dstBuffer);
if (err != OK) {
ALOGD("[%s] buffer conversion failed: %d", mName, err);
return false;
}
dstBuffer->setFormat(mFormatWithConverter);
return true;
}
void OutputBuffers::clearStash() {
mPending.clear();
mReorderStash.clear();
mDepth = 0;
mKey = C2Config::ORDINAL;
}
void OutputBuffers::flushStash() {
for (StashEntry& e : mPending) {
e.notify = false;
}
for (StashEntry& e : mReorderStash) {
e.notify = false;
}
}
uint32_t OutputBuffers::getReorderDepth() const {
return mDepth;
}
void OutputBuffers::setReorderDepth(uint32_t depth) {
mPending.splice(mPending.end(), mReorderStash);
mDepth = depth;
}
void OutputBuffers::setReorderKey(C2Config::ordinal_key_t key) {
mPending.splice(mPending.end(), mReorderStash);
mKey = key;
}
void OutputBuffers::pushToStash(
const std::shared_ptr<C2Buffer>& buffer,
bool notify,
int64_t timestamp,
int32_t flags,
const sp<AMessage>& format,
const C2WorkOrdinalStruct& ordinal) {
bool eos = flags & BUFFER_FLAG_END_OF_STREAM;
if (!buffer && eos) {
// TRICKY: we may be violating ordering of the stash here. Because we
// don't expect any more emplace() calls after this, the ordering should
// not matter.
mReorderStash.emplace_back(
buffer, notify, timestamp, flags, format, ordinal);
} else {
flags = flags & ~BUFFER_FLAG_END_OF_STREAM;
auto it = mReorderStash.begin();
for (; it != mReorderStash.end(); ++it) {
if (less(ordinal, it->ordinal)) {
break;
}
}
mReorderStash.emplace(it,
buffer, notify, timestamp, flags, format, ordinal);
if (eos) {
mReorderStash.back().flags =
mReorderStash.back().flags | BUFFER_FLAG_END_OF_STREAM;
}
}
while (!mReorderStash.empty() && mReorderStash.size() > mDepth) {
mPending.push_back(mReorderStash.front());
mReorderStash.pop_front();
}
ALOGV("[%s] %s: pushToStash -- pending size = %zu", mName, __func__, mPending.size());
}
OutputBuffers::BufferAction OutputBuffers::popFromStashAndRegister(
std::shared_ptr<C2Buffer>* c2Buffer,
size_t* index,
sp<MediaCodecBuffer>* outBuffer) {
if (mPending.empty()) {
return SKIP;
}
// Retrieve the first entry.
StashEntry &entry = mPending.front();
*c2Buffer = entry.buffer;
sp<AMessage> outputFormat = entry.format;
if (entry.notify && mFormat != outputFormat) {
updateSkipCutBuffer(outputFormat);
// Trigger image data processing to the new format
mLastImageData.clear();
ALOGV("[%s] popFromStashAndRegister: output format reference changed: %p -> %p",
mName, mFormat.get(), outputFormat.get());
ALOGD("[%s] popFromStashAndRegister: at %lldus, output format changed to %s",
mName, (long long)entry.timestamp, outputFormat->debugString().c_str());
setFormat(outputFormat);
}
// Flushing mReorderStash because no other buffers should come after output
// EOS.
if (entry.flags & BUFFER_FLAG_END_OF_STREAM) {
// Flush reorder stash
setReorderDepth(0);
}
if (!entry.notify) {
mPending.pop_front();
return DISCARD;
}
// Try to register the buffer.
status_t err = registerBuffer(*c2Buffer, index, outBuffer);
if (err != OK) {
if (err != WOULD_BLOCK) {
return REALLOCATE;
}
return RETRY;
}
// Append information from the front stash entry to outBuffer.
(*outBuffer)->meta()->setInt64("timeUs", entry.timestamp);
(*outBuffer)->meta()->setInt32("flags", entry.flags);
(*outBuffer)->meta()->setInt64("frameIndex", entry.ordinal.frameIndex.peekll());
ALOGV("[%s] popFromStashAndRegister: "
"out buffer index = %zu [%p] => %p + %zu (%lld)",
mName, *index, outBuffer->get(),
(*outBuffer)->data(), (*outBuffer)->size(),
(long long)entry.timestamp);
// The front entry of mPending will be removed now that the registration
// succeeded.
mPending.pop_front();
return NOTIFY_CLIENT;
}
bool OutputBuffers::popPending(StashEntry *entry) {
if (mPending.empty()) {
return false;
}
*entry = mPending.front();
mPending.pop_front();
return true;
}
void OutputBuffers::deferPending(const OutputBuffers::StashEntry &entry) {
mPending.push_front(entry);
}
bool OutputBuffers::hasPending() const {
return !mPending.empty();
}
bool OutputBuffers::less(
const C2WorkOrdinalStruct &o1, const C2WorkOrdinalStruct &o2) const {
switch (mKey) {
case C2Config::ORDINAL: return o1.frameIndex < o2.frameIndex;
case C2Config::TIMESTAMP: return o1.timestamp < o2.timestamp;
case C2Config::CUSTOM: return o1.customOrdinal < o2.customOrdinal;
default:
ALOGD("Unrecognized key; default to timestamp");
return o1.frameIndex < o2.frameIndex;
}
}
// LocalBufferPool
constexpr size_t kInitialPoolCapacity = kMaxLinearBufferSize;
constexpr size_t kMaxPoolCapacity = kMaxLinearBufferSize * 32;
std::shared_ptr<LocalBufferPool> LocalBufferPool::Create() {
return std::shared_ptr<LocalBufferPool>(new LocalBufferPool(kInitialPoolCapacity));
}
sp<ABuffer> LocalBufferPool::newBuffer(size_t capacity) {
Mutex::Autolock lock(mMutex);
auto it = std::find_if(
mPool.begin(), mPool.end(),
[capacity](const std::vector<uint8_t> &vec) {
return vec.capacity() >= capacity;
});
if (it != mPool.end()) {
sp<ABuffer> buffer = new VectorBuffer(std::move(*it), shared_from_this());
mPool.erase(it);
return buffer;
}
if (mUsedSize + capacity > mPoolCapacity) {
while (!mPool.empty()) {
mUsedSize -= mPool.back().capacity();
mPool.pop_back();
}
while (mUsedSize + capacity > mPoolCapacity && mPoolCapacity * 2 <= kMaxPoolCapacity) {
ALOGD("Increasing local buffer pool capacity from %zu to %zu",
mPoolCapacity, mPoolCapacity * 2);
mPoolCapacity *= 2;
}
if (mUsedSize + capacity > mPoolCapacity) {
ALOGD("mUsedSize = %zu, capacity = %zu, mPoolCapacity = %zu",
mUsedSize, capacity, mPoolCapacity);
return nullptr;
}
}
std::vector<uint8_t> vec(capacity);
mUsedSize += vec.capacity();
return new VectorBuffer(std::move(vec), shared_from_this());
}
LocalBufferPool::VectorBuffer::VectorBuffer(
std::vector<uint8_t> &&vec, const std::shared_ptr<LocalBufferPool> &pool)
: ABuffer(vec.data(), vec.capacity()),
mVec(std::move(vec)),
mPool(pool) {
}
LocalBufferPool::VectorBuffer::~VectorBuffer() {
std::shared_ptr<LocalBufferPool> pool = mPool.lock();
if (pool) {
// If pool is alive, return the vector back to the pool so that
// it can be recycled.
pool->returnVector(std::move(mVec));
}
}
void LocalBufferPool::returnVector(std::vector<uint8_t> &&vec) {
Mutex::Autolock lock(mMutex);
mPool.push_front(std::move(vec));
}
// FlexBuffersImpl
size_t FlexBuffersImpl::assignSlot(const sp<Codec2Buffer> &buffer) {
for (size_t i = 0; i < mBuffers.size(); ++i) {
if (mBuffers[i].clientBuffer == nullptr
&& mBuffers[i].compBuffer.expired()) {
mBuffers[i].clientBuffer = buffer;
return i;
}
}
mBuffers.push_back({ buffer, std::weak_ptr<C2Buffer>() });
return mBuffers.size() - 1;
}
bool FlexBuffersImpl::releaseSlot(
const sp<MediaCodecBuffer> &buffer,
std::shared_ptr<C2Buffer> *c2buffer,
bool release) {
sp<Codec2Buffer> clientBuffer;
size_t index = mBuffers.size();
for (size_t i = 0; i < mBuffers.size(); ++i) {
if (mBuffers[i].clientBuffer == buffer) {
clientBuffer = mBuffers[i].clientBuffer;
if (release) {
mBuffers[i].clientBuffer.clear();
}
index = i;
break;
}
}
if (clientBuffer == nullptr) {
ALOGV("[%s] %s: No matching buffer found", mName, __func__);
return false;
}
std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
if (!result) {
result = clientBuffer->asC2Buffer();
clientBuffer->clearC2BufferRefs();
mBuffers[index].compBuffer = result;
}
if (c2buffer) {
*c2buffer = result;
}
return true;
}
bool FlexBuffersImpl::expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
for (size_t i = 0; i < mBuffers.size(); ++i) {
std::shared_ptr<C2Buffer> compBuffer =
mBuffers[i].compBuffer.lock();
if (!compBuffer || compBuffer != c2buffer) {
continue;
}
mBuffers[i].compBuffer.reset();
ALOGV("[%s] codec released buffer #%zu", mName, i);
return true;
}
ALOGV("[%s] codec released an unknown buffer", mName);
return false;
}
void FlexBuffersImpl::flush() {
ALOGV("[%s] buffers are flushed %zu", mName, mBuffers.size());
mBuffers.clear();
}
size_t FlexBuffersImpl::numActiveSlots() const {
return std::count_if(
mBuffers.begin(), mBuffers.end(),
[](const Entry &entry) {
return (entry.clientBuffer != nullptr
|| !entry.compBuffer.expired());
});
}
size_t FlexBuffersImpl::numComponentBuffers() const {
return std::count_if(
mBuffers.begin(), mBuffers.end(),
[](const Entry &entry) {
return !entry.compBuffer.expired();
});
}
// BuffersArrayImpl
void BuffersArrayImpl::initialize(
const FlexBuffersImpl &impl,
size_t minSize,
std::function<sp<Codec2Buffer>()> allocate) {
mImplName = impl.mImplName + "[N]";
mName = mImplName.c_str();
for (size_t i = 0; i < impl.mBuffers.size(); ++i) {
sp<Codec2Buffer> clientBuffer = impl.mBuffers[i].clientBuffer;
bool ownedByClient = (clientBuffer != nullptr);
if (!ownedByClient) {
clientBuffer = allocate();
}
mBuffers.push_back({ clientBuffer, impl.mBuffers[i].compBuffer, ownedByClient });
}
ALOGV("[%s] converted %zu buffers to array mode of %zu", mName, mBuffers.size(), minSize);
for (size_t i = impl.mBuffers.size(); i < minSize; ++i) {
mBuffers.push_back({ allocate(), std::weak_ptr<C2Buffer>(), false });
}
}
status_t BuffersArrayImpl::grabBuffer(
size_t *index,
sp<Codec2Buffer> *buffer,
std::function<bool(const sp<Codec2Buffer> &)> match) {
// allBuffersDontMatch remains true if all buffers are available but
// match() returns false for every buffer.
bool allBuffersDontMatch = true;
for (size_t i = 0; i < mBuffers.size(); ++i) {
if (!mBuffers[i].ownedByClient && mBuffers[i].compBuffer.expired()) {
if (match(mBuffers[i].clientBuffer)) {
mBuffers[i].ownedByClient = true;
*buffer = mBuffers[i].clientBuffer;
(*buffer)->meta()->clear();
(*buffer)->setRange(0, (*buffer)->capacity());
*index = i;
return OK;
}
} else {
allBuffersDontMatch = false;
}
}
return allBuffersDontMatch ? NO_MEMORY : WOULD_BLOCK;
}
bool BuffersArrayImpl::returnBuffer(
const sp<MediaCodecBuffer> &buffer,
std::shared_ptr<C2Buffer> *c2buffer,
bool release) {
sp<Codec2Buffer> clientBuffer;
size_t index = mBuffers.size();
for (size_t i = 0; i < mBuffers.size(); ++i) {
if (mBuffers[i].clientBuffer == buffer) {
if (!mBuffers[i].ownedByClient) {
ALOGD("[%s] Client returned a buffer it does not own according to our record: %zu",
mName, i);
}
clientBuffer = mBuffers[i].clientBuffer;
if (release) {
mBuffers[i].ownedByClient = false;
}
index = i;
break;
}
}
if (clientBuffer == nullptr) {
ALOGV("[%s] %s: No matching buffer found", mName, __func__);
return false;
}
ALOGV("[%s] %s: matching buffer found (index=%zu)", mName, __func__, index);
std::shared_ptr<C2Buffer> result = mBuffers[index].compBuffer.lock();
if (!result) {
result = clientBuffer->asC2Buffer();
clientBuffer->clearC2BufferRefs();
mBuffers[index].compBuffer = result;
}
if (c2buffer) {
*c2buffer = result;
}
return true;
}
bool BuffersArrayImpl::expireComponentBuffer(const std::shared_ptr<C2Buffer> &c2buffer) {
for (size_t i = 0; i < mBuffers.size(); ++i) {
std::shared_ptr<C2Buffer> compBuffer =
mBuffers[i].compBuffer.lock();
if (!compBuffer) {
continue;
}
if (c2buffer == compBuffer) {
if (mBuffers[i].ownedByClient) {
// This should not happen.
ALOGD("[%s] codec released a buffer owned by client "
"(index %zu)", mName, i);
}
mBuffers[i].compBuffer.reset();
ALOGV("[%s] codec released buffer #%zu(array mode)", mName, i);
return true;
}
}
ALOGV("[%s] codec released an unknown buffer (array mode)", mName);
return false;
}
void BuffersArrayImpl::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
array->clear();
for (const Entry &entry : mBuffers) {
array->push(entry.clientBuffer);
}
}
void BuffersArrayImpl::flush() {
for (Entry &entry : mBuffers) {
entry.ownedByClient = false;
}
}
void BuffersArrayImpl::realloc(std::function<sp<Codec2Buffer>()> alloc) {
size_t size = mBuffers.size();
mBuffers.clear();
for (size_t i = 0; i < size; ++i) {
mBuffers.push_back({ alloc(), std::weak_ptr<C2Buffer>(), false });
}
}
void BuffersArrayImpl::grow(
size_t newSize, std::function<sp<Codec2Buffer>()> alloc) {
CHECK_LT(mBuffers.size(), newSize);
while (mBuffers.size() < newSize) {
mBuffers.push_back({ alloc(), std::weak_ptr<C2Buffer>(), false });
}
}
size_t BuffersArrayImpl::numActiveSlots() const {
return std::count_if(
mBuffers.begin(), mBuffers.end(),
[](const Entry &entry) {
return entry.ownedByClient || !entry.compBuffer.expired();
});
}
size_t BuffersArrayImpl::arraySize() const {
return mBuffers.size();
}
// InputBuffersArray
void InputBuffersArray::initialize(
const FlexBuffersImpl &impl,
size_t minSize,
std::function<sp<Codec2Buffer>()> allocate) {
mAllocate = allocate;
mImpl.initialize(impl, minSize, allocate);
}
void InputBuffersArray::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
mImpl.getArray(array);
}
bool InputBuffersArray::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
sp<Codec2Buffer> c2Buffer;
status_t err = mImpl.grabBuffer(index, &c2Buffer);
if (err == OK) {
c2Buffer->setFormat(mFormat);
handleImageData(c2Buffer);
*buffer = c2Buffer;
return true;
}
return false;
}
bool InputBuffersArray::releaseBuffer(
const sp<MediaCodecBuffer> &buffer,
std::shared_ptr<C2Buffer> *c2buffer,
bool release) {
return mImpl.returnBuffer(buffer, c2buffer, release);
}
bool InputBuffersArray::expireComponentBuffer(
const std::shared_ptr<C2Buffer> &c2buffer) {
return mImpl.expireComponentBuffer(c2buffer);
}
void InputBuffersArray::flush() {
mImpl.flush();
}
size_t InputBuffersArray::numActiveSlots() const {
return mImpl.numActiveSlots();
}
sp<Codec2Buffer> InputBuffersArray::createNewBuffer() {
return mAllocate();
}
// SlotInputBuffers
bool SlotInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
sp<Codec2Buffer> newBuffer = createNewBuffer();
*index = mImpl.assignSlot(newBuffer);
*buffer = newBuffer;
return true;
}
bool SlotInputBuffers::releaseBuffer(
const sp<MediaCodecBuffer> &buffer,
std::shared_ptr<C2Buffer> *c2buffer,
bool release) {
return mImpl.releaseSlot(buffer, c2buffer, release);
}
bool SlotInputBuffers::expireComponentBuffer(
const std::shared_ptr<C2Buffer> &c2buffer) {
return mImpl.expireComponentBuffer(c2buffer);
}
void SlotInputBuffers::flush() {
mImpl.flush();
}
std::unique_ptr<InputBuffers> SlotInputBuffers::toArrayMode(size_t) {
TRESPASS("Array mode should not be called at non-legacy mode");
return nullptr;
}
size_t SlotInputBuffers::numActiveSlots() const {
return mImpl.numActiveSlots();
}
sp<Codec2Buffer> SlotInputBuffers::createNewBuffer() {
return new DummyContainerBuffer{mFormat, nullptr};
}
// LinearInputBuffers
bool LinearInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
sp<Codec2Buffer> newBuffer = createNewBuffer();
if (newBuffer == nullptr) {
return false;
}
*index = mImpl.assignSlot(newBuffer);
*buffer = newBuffer;
return true;
}
bool LinearInputBuffers::releaseBuffer(
const sp<MediaCodecBuffer> &buffer,
std::shared_ptr<C2Buffer> *c2buffer,
bool release) {
return mImpl.releaseSlot(buffer, c2buffer, release);
}
bool LinearInputBuffers::expireComponentBuffer(
const std::shared_ptr<C2Buffer> &c2buffer) {
return mImpl.expireComponentBuffer(c2buffer);
}
void LinearInputBuffers::flush() {
// This is no-op by default unless we're in array mode where we need to keep
// track of the flushed work.
mImpl.flush();
}
std::unique_ptr<InputBuffers> LinearInputBuffers::toArrayMode(size_t size) {
std::unique_ptr<InputBuffersArray> array(
new InputBuffersArray(mComponentName.c_str(), "1D-Input[N]"));
array->setPool(mPool);
array->setFormat(mFormat);
array->initialize(
mImpl,
size,
[pool = mPool, format = mFormat] () -> sp<Codec2Buffer> {
return Alloc(pool, format);
});
return std::move(array);
}
size_t LinearInputBuffers::numActiveSlots() const {
return mImpl.numActiveSlots();
}
// static
sp<Codec2Buffer> LinearInputBuffers::Alloc(
const std::shared_ptr<C2BlockPool> &pool, const sp<AMessage> &format) {
int32_t capacity = kLinearBufferSize;
(void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
if ((size_t)capacity > kMaxLinearBufferSize) {
ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
capacity = kMaxLinearBufferSize;
}
int64_t usageValue = 0;
(void)format->findInt64("android._C2MemoryUsage", &usageValue);
C2MemoryUsage usage{usageValue | C2MemoryUsage::CPU_READ | C2MemoryUsage::CPU_WRITE};
std::shared_ptr<C2LinearBlock> block;
c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block);
if (err != C2_OK) {
return nullptr;
}
return LinearBlockBuffer::Allocate(format, block);
}
sp<Codec2Buffer> LinearInputBuffers::createNewBuffer() {
return Alloc(mPool, mFormat);
}
// EncryptedLinearInputBuffers
EncryptedLinearInputBuffers::EncryptedLinearInputBuffers(
bool secure,
const sp<MemoryDealer> &dealer,
const sp<ICrypto> &crypto,
int32_t heapSeqNum,
size_t capacity,
size_t numInputSlots,
const char *componentName, const char *name)
: LinearInputBuffers(componentName, name),
mUsage({0, 0}),
mDealer(dealer),
mCrypto(crypto),
mMemoryVector(new std::vector<Entry>){
if (secure) {
mUsage = { C2MemoryUsage::READ_PROTECTED, 0 };
} else {
mUsage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
}
for (size_t i = 0; i < numInputSlots; ++i) {
sp<IMemory> memory = mDealer->allocate(capacity);
if (memory == nullptr) {
ALOGD("[%s] Failed to allocate memory from dealer: only %zu slots allocated",
mName, i);
break;
}
mMemoryVector->push_back({std::weak_ptr<C2LinearBlock>(), memory, heapSeqNum});
}
}
std::unique_ptr<InputBuffers> EncryptedLinearInputBuffers::toArrayMode(size_t size) {
std::unique_ptr<InputBuffersArray> array(
new InputBuffersArray(mComponentName.c_str(), "1D-EncryptedInput[N]"));
array->setPool(mPool);
array->setFormat(mFormat);
array->initialize(
mImpl,
size,
[pool = mPool,
format = mFormat,
usage = mUsage,
memoryVector = mMemoryVector] () -> sp<Codec2Buffer> {
return Alloc(pool, format, usage, memoryVector);
});
return std::move(array);
}
// static
sp<Codec2Buffer> EncryptedLinearInputBuffers::Alloc(
const std::shared_ptr<C2BlockPool> &pool,
const sp<AMessage> &format,
C2MemoryUsage usage,
const std::shared_ptr<std::vector<EncryptedLinearInputBuffers::Entry>> &memoryVector) {
int32_t capacity = kLinearBufferSize;
(void)format->findInt32(KEY_MAX_INPUT_SIZE, &capacity);
if ((size_t)capacity > kMaxLinearBufferSize) {
ALOGD("client requested %d, capped to %zu", capacity, kMaxLinearBufferSize);
capacity = kMaxLinearBufferSize;
}
sp<IMemory> memory;
size_t slot = 0;
int32_t heapSeqNum = -1;
for (; slot < memoryVector->size(); ++slot) {
if (memoryVector->at(slot).block.expired()) {
memory = memoryVector->at(slot).memory;
heapSeqNum = memoryVector->at(slot).heapSeqNum;
break;
}
}
if (memory == nullptr) {
return nullptr;
}
int64_t usageValue = 0;
(void)format->findInt64("android._C2MemoryUsage", &usageValue);
usage = C2MemoryUsage(usage.expected | usageValue);
std::shared_ptr<C2LinearBlock> block;
c2_status_t err = pool->fetchLinearBlock(capacity, usage, &block);
if (err != C2_OK || block == nullptr) {
return nullptr;
}
memoryVector->at(slot).block = block;
return new EncryptedLinearBlockBuffer(format, block, memory, heapSeqNum);
}
sp<Codec2Buffer> EncryptedLinearInputBuffers::createNewBuffer() {
// TODO: android_2020
return nullptr;
}
// GraphicMetadataInputBuffers
GraphicMetadataInputBuffers::GraphicMetadataInputBuffers(
const char *componentName, const char *name)
: InputBuffers(componentName, name),
mImpl(mName),
mStore(GetCodec2PlatformAllocatorStore()) { }
bool GraphicMetadataInputBuffers::requestNewBuffer(
size_t *index, sp<MediaCodecBuffer> *buffer) {
sp<Codec2Buffer> newBuffer = createNewBuffer();
if (newBuffer == nullptr) {
return false;
}
*index = mImpl.assignSlot(newBuffer);
*buffer = newBuffer;
return true;
}
bool GraphicMetadataInputBuffers::releaseBuffer(
const sp<MediaCodecBuffer> &buffer,
std::shared_ptr<C2Buffer> *c2buffer,
bool release) {
return mImpl.releaseSlot(buffer, c2buffer, release);
}
bool GraphicMetadataInputBuffers::expireComponentBuffer(
const std::shared_ptr<C2Buffer> &c2buffer) {
return mImpl.expireComponentBuffer(c2buffer);
}
void GraphicMetadataInputBuffers::flush() {
// This is no-op by default unless we're in array mode where we need to keep
// track of the flushed work.
}
std::unique_ptr<InputBuffers> GraphicMetadataInputBuffers::toArrayMode(
size_t size) {
std::shared_ptr<C2Allocator> alloc;
c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
if (err != C2_OK) {
return nullptr;
}
std::unique_ptr<InputBuffersArray> array(
new InputBuffersArray(mComponentName.c_str(), "2D-MetaInput[N]"));
array->setPool(mPool);
array->setFormat(mFormat);
array->initialize(
mImpl,
size,
[format = mFormat, alloc]() -> sp<Codec2Buffer> {
return new GraphicMetadataBuffer(format, alloc);
});
return std::move(array);
}
size_t GraphicMetadataInputBuffers::numActiveSlots() const {
return mImpl.numActiveSlots();
}
sp<Codec2Buffer> GraphicMetadataInputBuffers::createNewBuffer() {
std::shared_ptr<C2Allocator> alloc;
c2_status_t err = mStore->fetchAllocator(mPool->getAllocatorId(), &alloc);
if (err != C2_OK) {
return nullptr;
}
return new GraphicMetadataBuffer(mFormat, alloc);
}
// GraphicInputBuffers
GraphicInputBuffers::GraphicInputBuffers(
const char *componentName, const char *name)
: InputBuffers(componentName, name),
mImpl(mName),
mLocalBufferPool(LocalBufferPool::Create()),
mPixelFormat(PIXEL_FORMAT_UNKNOWN) { }
bool GraphicInputBuffers::requestNewBuffer(size_t *index, sp<MediaCodecBuffer> *buffer) {
sp<Codec2Buffer> newBuffer = createNewBuffer();
if (newBuffer == nullptr) {
return false;
}
*index = mImpl.assignSlot(newBuffer);
handleImageData(newBuffer);
*buffer = newBuffer;
return true;
}
bool GraphicInputBuffers::releaseBuffer(
const sp<MediaCodecBuffer> &buffer,
std::shared_ptr<C2Buffer> *c2buffer,
bool release) {
return mImpl.releaseSlot(buffer, c2buffer, release);
}
bool GraphicInputBuffers::expireComponentBuffer(
const std::shared_ptr<C2Buffer> &c2buffer) {
return mImpl.expireComponentBuffer(c2buffer);
}
void GraphicInputBuffers::flush() {
// This is no-op by default unless we're in array mode where we need to keep
// track of the flushed work.
}
static uint32_t extractPixelFormat(const sp<AMessage> &format) {
int32_t frameworkColorFormat = 0;
if (!format->findInt32("android._color-format", &frameworkColorFormat)) {
return PIXEL_FORMAT_UNKNOWN;
}
uint32_t pixelFormat = PIXEL_FORMAT_UNKNOWN;
if (C2Mapper::mapPixelFormatFrameworkToCodec(frameworkColorFormat, &pixelFormat)) {
return pixelFormat;
}
return PIXEL_FORMAT_UNKNOWN;
}
std::unique_ptr<InputBuffers> GraphicInputBuffers::toArrayMode(size_t size) {
std::unique_ptr<InputBuffersArray> array(
new InputBuffersArray(mComponentName.c_str(), "2D-BB-Input[N]"));
array->setPool(mPool);
array->setFormat(mFormat);
uint32_t pixelFormat = extractPixelFormat(mFormat);
array->initialize(
mImpl,
size,
[pool = mPool, format = mFormat, lbp = mLocalBufferPool, pixelFormat]()
-> sp<Codec2Buffer> {
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
return AllocateInputGraphicBuffer(
pool, format, pixelFormat, usage, lbp);
});
return std::move(array);
}
size_t GraphicInputBuffers::numActiveSlots() const {
return mImpl.numActiveSlots();
}
sp<Codec2Buffer> GraphicInputBuffers::createNewBuffer() {
C2MemoryUsage usage = { C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE };
mPixelFormat = extractPixelFormat(mFormat);
return AllocateInputGraphicBuffer(
mPool, mFormat, mPixelFormat, usage, mLocalBufferPool);
}
uint32_t GraphicInputBuffers::getPixelFormatIfApplicable() { return mPixelFormat; }
bool GraphicInputBuffers::resetPixelFormatIfApplicable() {
mPixelFormat = PIXEL_FORMAT_UNKNOWN;
return true;
}
// OutputBuffersArray
void OutputBuffersArray::initialize(
const FlexBuffersImpl &impl,
size_t minSize,
std::function<sp<Codec2Buffer>()> allocate) {
mAlloc = allocate;
mImpl.initialize(impl, minSize, allocate);
}
status_t OutputBuffersArray::registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
sp<MediaCodecBuffer> *clientBuffer) {
sp<Codec2Buffer> c2Buffer;
status_t err = mImpl.grabBuffer(
index,
&c2Buffer,
[buffer](const sp<Codec2Buffer> &clientBuffer) {
return clientBuffer->canCopy(buffer);
});
if (err == WOULD_BLOCK) {
ALOGV("[%s] buffers temporarily not available", mName);
return err;
} else if (err != OK) {
ALOGD("[%s] grabBuffer failed: %d", mName, err);
return err;
}
c2Buffer->setFormat(mFormat);
if (!convert(buffer, &c2Buffer) && !c2Buffer->copy(buffer)) {
ALOGD("[%s] copy buffer failed", mName);
return WOULD_BLOCK;
}
if (buffer && buffer->hasInfo(C2AccessUnitInfos::output::PARAM_TYPE)) {
std::shared_ptr<const C2AccessUnitInfos::output> bufferMetadata =
std::static_pointer_cast<const C2AccessUnitInfos::output>(
buffer->getInfo(C2AccessUnitInfos::output::PARAM_TYPE));
if (submit(c2Buffer, mSampleRate, mChannelCount, bufferMetadata)) {
buffer->removeInfo(C2AccessUnitInfos::output::PARAM_TYPE);
}
} else {
submit(c2Buffer);
}
handleImageData(c2Buffer);
*clientBuffer = c2Buffer;
ALOGV("[%s] grabbed buffer %zu", mName, *index);
return OK;
}
status_t OutputBuffersArray::registerCsd(
const C2StreamInitDataInfo::output *csd,
size_t *index,
sp<MediaCodecBuffer> *clientBuffer) {
sp<Codec2Buffer> c2Buffer;
status_t err = mImpl.grabBuffer(
index,
&c2Buffer,
[csd](const sp<Codec2Buffer> &clientBuffer) {
return clientBuffer->base() != nullptr
&& clientBuffer->capacity() >= csd->flexCount();
});
if (err != OK) {
return err;
}
memcpy(c2Buffer->base(), csd->m.value, csd->flexCount());
c2Buffer->setRange(0, csd->flexCount());
c2Buffer->setFormat(mFormat);
*clientBuffer = c2Buffer;
return OK;
}
bool OutputBuffersArray::releaseBuffer(
const sp<MediaCodecBuffer> &buffer, std::shared_ptr<C2Buffer> *c2buffer) {
return mImpl.returnBuffer(buffer, c2buffer, true);
}
void OutputBuffersArray::flush(const std::list<std::unique_ptr<C2Work>> &flushedWork) {
(void)flushedWork;
mImpl.flush();
if (mSkipCutBuffer != nullptr) {
mSkipCutBuffer->clearAll();
}
}
void OutputBuffersArray::getArray(Vector<sp<MediaCodecBuffer>> *array) const {
mImpl.getArray(array);
}
size_t OutputBuffersArray::numActiveSlots() const {
return mImpl.numActiveSlots();
}
void OutputBuffersArray::realloc(const std::shared_ptr<C2Buffer> &c2buffer) {
switch (c2buffer->data().type()) {
case C2BufferData::LINEAR: {
uint32_t size = kLinearBufferSize;
const std::vector<C2ConstLinearBlock> &linear_blocks = c2buffer->data().linearBlocks();
const uint32_t block_size = linear_blocks.front().size();
if (block_size < kMaxLinearBufferSize / 2) {
size = block_size * 2;
} else {
size = kMaxLinearBufferSize;
}
mAlloc = [format = mFormat, size] {
return new LocalLinearBuffer(format, new ABuffer(size));
};
ALOGD("[%s] reallocating with linear buffer of size %u", mName, size);
break;
}
case C2BufferData::GRAPHIC: {
// This is only called for RawGraphicOutputBuffers.
mAlloc = [format = mFormat,
lbp = LocalBufferPool::Create()] {
return ConstGraphicBlockBuffer::AllocateEmpty(
format,
[lbp](size_t capacity) {
return lbp->newBuffer(capacity);
});
};
ALOGD("[%s] reallocating with graphic buffer: format = %s",
mName, mFormat->debugString().c_str());
break;
}
case C2BufferData::INVALID: [[fallthrough]];
case C2BufferData::LINEAR_CHUNKS: [[fallthrough]];
case C2BufferData::GRAPHIC_CHUNKS: [[fallthrough]];
default:
ALOGD("Unsupported type: %d", (int)c2buffer->data().type());
return;
}
mImpl.realloc(mAlloc);
}
void OutputBuffersArray::grow(size_t newSize) {
mImpl.grow(newSize, mAlloc);
}
void OutputBuffersArray::transferFrom(OutputBuffers* source) {
mFormat = source->mFormat;
mSkipCutBuffer = source->mSkipCutBuffer;
mPending = std::move(source->mPending);
mReorderStash = std::move(source->mReorderStash);
mDepth = source->mDepth;
mKey = source->mKey;
}
// FlexOutputBuffers
status_t FlexOutputBuffers::registerBuffer(
const std::shared_ptr<C2Buffer> &buffer,
size_t *index,
sp<MediaCodecBuffer> *clientBuffer) {
sp<Codec2Buffer> newBuffer;
if (!convert(buffer, &newBuffer)) {
newBuffer = wrap(buffer);
if (newBuffer == nullptr) {
return NO_MEMORY;
}
newBuffer->setFormat(mFormat);
}
*index = mImpl.assignSlot(newBuffer);
handleImageData(newBuffer);
*clientBuffer = newBuffer;
extractPixelFormatFromC2Buffer(buffer);
ALOGV("[%s] registered buffer %zu", mName, *index);
return OK;
}
status_t FlexOutputBuffers::registerCsd(
const C2StreamInitDataInfo::output *csd,
size_t *index,
sp<MediaCodecBuffer> *clientBuffer) {
sp<Codec2Buffer> newBuffer = new LocalLinearBuffer(
mFormat, ABuffer::CreateAsCopy(csd->m.value, csd->flexCount()));
*index = mImpl.assignSlot(newBuffer);
*clientBuffer = newBuffer;
return OK;
}
bool FlexOutputBuffers::releaseBuffer(
const sp<MediaCodecBuffer> &buffer,
std::shared_ptr<C2Buffer> *c2buffer) {
return mImpl.releaseSlot(buffer, c2buffer, true);
}
void FlexOutputBuffers::flush(
const std::list<std::unique_ptr<C2Work>> &flushedWork) {
(void) flushedWork;
// This is no-op by default unless we're in array mode where we need to keep
// track of the flushed work.
}
std::unique_ptr<OutputBuffersArray> FlexOutputBuffers::toArrayMode(size_t size) {
std::unique_ptr<OutputBuffersArray> array(new OutputBuffersArray(mComponentName.c_str()));
array->transferFrom(this);
std::function<sp<Codec2Buffer>()> alloc = getAlloc();
array->initialize(mImpl, size, alloc);
return array;
}
size_t FlexOutputBuffers::numActiveSlots() const {
return mImpl.numActiveSlots();
}
bool FlexOutputBuffers::extractPixelFormatFromC2Buffer(const std::shared_ptr<C2Buffer> &buffer) {
if (buffer == nullptr) {
return false;
}
const C2BufferData &data = buffer->data();
// only extract the first pixel format in a metric session.
if (mPixelFormat != PIXEL_FORMAT_UNKNOWN || data.type() != C2BufferData::GRAPHIC
|| data.graphicBlocks().empty()) {
return false;
}
const C2Handle *const handle = data.graphicBlocks().front().handle();
uint32_t pf = ExtractFormatFromCodec2GrallocHandle(handle);
if (pf == PIXEL_FORMAT_UNKNOWN) {
return false;
}
mPixelFormat = pf;
return true;
}
bool FlexOutputBuffers::resetPixelFormatIfApplicable() {
mPixelFormat = PIXEL_FORMAT_UNKNOWN;
return true;
}
uint32_t FlexOutputBuffers::getPixelFormatIfApplicable() { return mPixelFormat; }
// LinearOutputBuffers
void LinearOutputBuffers::flush(
const std::list<std::unique_ptr<C2Work>> &flushedWork) {
if (mSkipCutBuffer != nullptr) {
mSkipCutBuffer->clearAll();
}
FlexOutputBuffers::flush(flushedWork);
}
sp<Codec2Buffer> LinearOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
if (buffer == nullptr) {
ALOGV("[%s] using a dummy buffer", mName);
return new LocalLinearBuffer(mFormat, new ABuffer(0));
}
if (buffer->data().type() != C2BufferData::LINEAR) {
ALOGV("[%s] non-linear buffer %d", mName, buffer->data().type());
// We expect linear output buffers from the component.
return nullptr;
}
if (buffer->data().linearBlocks().size() != 1u) {
ALOGV("[%s] no linear buffers", mName);
// We expect one and only one linear block from the component.
return nullptr;
}
sp<Codec2Buffer> clientBuffer = ConstLinearBlockBuffer::Allocate(mFormat, buffer);
if (clientBuffer == nullptr) {
ALOGD("[%s] ConstLinearBlockBuffer::Allocate failed", mName);
return nullptr;
}
submit(clientBuffer);
return clientBuffer;
}
std::function<sp<Codec2Buffer>()> LinearOutputBuffers::getAlloc() {
return [format = mFormat]{
// TODO: proper max output size
return new LocalLinearBuffer(format, new ABuffer(kLinearBufferSize));
};
}
// GraphicOutputBuffers
sp<Codec2Buffer> GraphicOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
return new DummyContainerBuffer(mFormat, buffer);
}
std::function<sp<Codec2Buffer>()> GraphicOutputBuffers::getAlloc() {
return [format = mFormat]{
return new DummyContainerBuffer(format);
};
}
// RawGraphicOutputBuffers
RawGraphicOutputBuffers::RawGraphicOutputBuffers(
const char *componentName, const char *name)
: FlexOutputBuffers(componentName, name),
mLocalBufferPool(LocalBufferPool::Create()) { }
sp<Codec2Buffer> RawGraphicOutputBuffers::wrap(const std::shared_ptr<C2Buffer> &buffer) {
if (buffer == nullptr) {
return new Codec2Buffer(mFormat, new ABuffer(nullptr, 0));
} else {
return ConstGraphicBlockBuffer::Allocate(
mFormat,
buffer,
[lbp = mLocalBufferPool](size_t capacity) {
return lbp->newBuffer(capacity);
});
}
}
std::function<sp<Codec2Buffer>()> RawGraphicOutputBuffers::getAlloc() {
return [format = mFormat, lbp = mLocalBufferPool]{
return ConstGraphicBlockBuffer::AllocateEmpty(
format,
[lbp](size_t capacity) {
return lbp->newBuffer(capacity);
});
};
}
} // namespace android