输出buffer整体的管理流程主要可以分为三个部分:
外部设置Surface进来,然后把输入buffer 输入,等待输出buffer 的回调,回调回来后 根据音视频同步的策略。在合适的时机renderOutput 送到MediaCodec。
需要了解的几个方面
外部的setSurface调用到 MediaCodec的kWhatSetSurface
nativeWindowConnect(surface.get(), "connectToSurface(reconnect)");
根据bufferChanned的信息配置surface,比如配置deuque buffer 的超时时间、
dequeue最大的buffer数,当然这些值在后续可能还会改变,后续在解码器中解码出来的delay改变的话 回重新设置这个delay,
然后在handlework 重新设置最大的可dequeue的buffer 数。赋值mOutputSurface的相关变量。
Mutexed::Locked output(mOutputSurface);
output->surface = newSurface;
output->generation = generation;
在ccodecbufferchannel的start中调用configureProducer设置外部surface的GraphicBufferProducer到
bufferQueueBlockpopl中。
outputSurface = output->surface ?
output->surface->getIGraphicBufferProducer() : nullptr;
if (outputSurface) {
mComponent->setOutputSurface(
outputPoolId_,
outputSurface,
outputGeneration,
maxDequeueCount);
}
Return Component::setOutputSurface(
uint64_t blockPoolId,
const sp& surface) {
std::shared_ptr pool;
GetCodec2BlockPool(blockPoolId, mComponent, &pool);
if (pool && pool->getAllocatorId() == C2PlatformAllocatorStore::BUFFERQUEUE) {
if (bqPool) {
bqPool->setRenderCallback(cb);
bqPool->configureProducer(surface);
}
}
return Status::OK;
}
void configureProducer(const sp &producer,
native_handle_t *syncHandle,
uint64_t producerId,
uint32_t generation,
uint64_t usage,
bool bqInformation) {
if (producer) {
mProducer = producer;
mProducerId = producerId;
mGeneration = bqInformation ? generation : 0;
}
}
start 中跟输出buffer 相关的主要是两个方面
具体来说:
hevc'解码为例
ps_dec_op->i4_reorder_depth =
ps_sps->ai1_sps_max_num_reorder_pics[ps_sps->i1_sps_max_sub_layers - 1];
mOutputDelay = ps_decode_op->i4_reorder_depth;
ALOGV("New Output delay %d ", mOutputDelay);
C2PortActualDelayTuning::output outputDelay(mOutputDelay);
std::vector> failures;
c2_status_t err =
mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
if (err == OK) {
work->worklets.front()->output.configUpdate.push_back(
C2Param::Copy(outputDelay));
}
bool CCodecBufferChannel::handleWork(
std::unique_ptr work,
const sp &outputFormat,
const C2StreamInitDataInfo::output *initData) {
while (!worklet->output.configUpdate.empty()) {
std::unique_ptr param;
worklet->output.configUpdate.back().swap(param);
worklet->output.configUpdate.pop_back();
if (param->forOutput()) {
C2PortActualDelayTuning::output outputDelay;
if (outputDelay.updateFrom(*param)) {
ALOGE("[%s] onWorkDone: updating output delay %u",
mName, outputDelay.value);
(void)mPipelineWatcher.lock()->outputDelay(outputDelay.value);
newOutputDelay = outputDelay.value;
needMaxDequeueBufferCountUpdate = true;
}
}
break;
if (needMaxDequeueBufferCountUpdate) {
int maxDequeueCount = 0;
{
Mutexed::Locked output(mOutputSurface);
maxDequeueCount = output->maxDequeueBuffers =
numOutputSlots + reorderDepth + kRenderingDepth;
if (output->surface) {
output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
}
}
if (maxDequeueCount > 0) {
mComponent->setOutputSurfaceMaxDequeueCount(maxDequeueCount);
}
}
}
constexpr size_t kSmoothnessFactor = 4;
constexpr size_t kRenderingDepth = 3;
C2PortActualDelayTuning::output outputDelay(0);
c2_status_t err = mComponent->query(
{
&iStreamFormat,
&oStreamFormat,
&kind,
&reorderDepth,
&reorderKey,
&inputDelay,
&pipelineDelay,
&outputDelay,
&secureMode,
},
{},
C2_DONT_BLOCK,
nullptr);
size_t numOutputSlots = outputDelayValue + kSmoothnessFactor
sp outputSurface;
uint32_t outputGeneration;
int maxDequeueCount = 0;
{
Mutexed::Locked output(mOutputSurface);
maxDequeueCount = output->maxDequeueBuffers = numOutputSlots +
reorderDepth.value + kRenderingDepth;
outputSurface = output->surface ?
output->surface->getIGraphicBufferProducer() : nullptr;
if (outputSurface) {
output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
}
outputGeneration = output->generation;
constexpr uint32_t kDefaultOutputDelay = 8;
addParameter(
DefineParam(mActualOutputDelay, C2_PARAMKEY_OUTPUT_DELAY)
.withDefault(new C2PortActualDelayTuning::output(kDefaultOutputDelay))
.withFields({C2F(mActualOutputDelay, value).inRange(0, kMaxOutputDelay)})
.withSetter(Setter::StrictValueWithNoDeps)
.build());
if (ps_decode_op->i4_reorder_depth >= 0 && mOutputDelay != ps_decode_op->i4_reorder_depth) {
mOutputDelay = ps_decode_op->i4_reorder_depth;
ALOGV("New Output delay %d ", mOutputDelay);
C2PortActualDelayTuning::output outputDelay(mOutputDelay);
std::vector> failures;
c2_status_t err =
mIntf->config({&outputDelay}, C2_MAY_BLOCK, &failures);
if (err == OK) {
work->worklets.front()->output.configUpdate.push_back(
C2Param::Copy(outputDelay));
} else {
ALOGE("Cannot set output delay");
mSignalledError = true;
work->workletsProcessed = 1u;
work->result = C2_CORRUPTED;
return;
}
}
在CCodecBufferChannel::start的时候决定,在下面代码中将pools的allocatedID转为
C2BufferQueueBlockPool。 在这之后调用mComponent->createBlockPool。Codec2Client::Component::createBlockPool调用c2store的c2_status_t createBlockPool()然后调用_createBlockPool,在之前设置了是BUFFERQUEUE,这边就保存了创建好的C2BufferQueueBlockPool。 在后面解码的流程中fetchGrallocBlock,使用的是这个类型的
C2BufferQueueBlockPool。
poolmask的默认值:
int GetCodec2PoolMask() {
return property_get_int32(
"debug.stagefright.c2-poolmask",
1 << C2PlatformAllocatorStore::ION |
1 << C2PlatformAllocatorStore::BUFFERQUEUE);
}
int poolMask = GetCodec2PoolMask();
申请的buffer的类型函数是bufferqueue
if (pools->outputAllocatorId == C2PlatformAllocatorStore::GRALLOC
&& err != C2_OK
&& ((poolMask >> C2PlatformAllocatorStore::BUFFERQUEUE) & 1)) {
pools->outputAllocatorId = C2PlatformAllocatorStore::BUFFERQUEUE;
}
}
bufferqueue的申请调用的是C2PlatformAllocatorStoreImpl的fetchAllocator
case C2PlatformAllocatorStore::BUFFERQUEUE:
res = allocatorStore->fetchAllocator(
C2PlatformAllocatorStore::BUFFERQUEUE, &allocator);
if (res == C2_OK) {
std::shared_ptr ptr(
new C2BufferQueueBlockPool(allocator, poolId), deleter);
*pool = ptr;
mBlockPools[poolId] = ptr;
mComponents[poolId].insert(
mComponents[poolId].end(),
components.begin(), components.end());
}
break;
fetchAllocator返回gralloc的allocator。
std::shared_ptr C2PlatformAllocatorStoreImpl::fetchBufferQueueAllocator() {
static std::mutex mutex;
static std::weak_ptr grallocAllocator;
std::lock_guard lock(mutex);
std::shared_ptr allocator = grallocAllocator.lock();
if (allocator == nullptr) {
allocator = std::make_shared(
C2PlatformAllocatorStore::BUFFERQUEUE, true);
grallocAllocator = allocator;
}
return allocator;
}
### fetchGraphicBlock 流程
fetch经过一系列判断和处理 最终调用mProducer的dequeueBuffer
c2_status_t fetchGraphicBlock(
uint32_t width,
uint32_t height,
uint32_t format,
C2MemoryUsage usage,
std::shared_ptr *block /* nonnull */,
C2Fence *fence) {
c2_status_t status = fetchFromIgbp_l(width, height, format, usage, block, fence);
c2Status = dequeueBuffer(width, height, format, usage,
&slot, &bufferNeedsReallocation, &fence);
if (fence) {
static constexpr int kFenceWaitTimeMs = 10;
status_t status = fence->wait(kFenceWaitTimeMs);
}
其中 dequeueBuffer
Return transResult = mProducer->dequeueBuffer(
Input{
width,
height,
format,
androidUsage.asGrallocUsage()},
[&status, slot, needsRealloc,
fence](HStatus hStatus,
int32_t hSlot,
Output const& hOutput) {
*slot = static_cast(hSlot);
if (!h2b(hStatus, &status) ||
!h2b(hOutput.fence, fence)) {
status = ::android::BAD_VALUE;
} else {
*needsRealloc =
hOutput.bufferNeedsReallocation;
}
});
Fence是一种同步机制,用于GraphicBuffer的同步。用来处理跨硬件平台不同的情况(CPU和GPU),尤其是CPU、GPU和HWC之间的同步。另外,也可用于多个时间点之间的同步,当Graphics Buffer的生产者或消费者在对buffer处理完之后,通过fence发出信号,这样系统可以异步queue当前不需要但有可能接下来会使用读写的buffer。
简言之,在合适的时间发一种信号,将先到的buffer拦住,等后来的到达,两者步调一致再一起走。也就是dequeuebuffer 之后并不能直接用这块buffer,需要等待buffer的fence发送上来之后 才可以使用这块buffer。
dequeueBuffer ---->(获取到slot或fence) fence->wait -----> mProducer->requestBuffer(通过slot 获取到buffer)
将从gralloc 获取到的buffer (native_handle_t)通过调用android::WrapNativeCodec2GrallocHandle转化为C2Handle
这个C2Handle 会生成C2AllocationGralloc,这个alloc最后会new 封装成C2GraphicBlock。这个block就是返回给外部解码申请的地方。
经过上面的这个流程 解码要的共享的buffer 就从gralloc这边申请出来了,然后这个buffer就可以给到后面的解码器使用了,如果是软解就map出虚拟地址,然后将软解后的数据拷贝到里面。但一般厂商不会用软解,正常的实现是这块buffer给到硬件,硬解数据直接写到这块buffer。
解码的buffer准备好之后,会把grallocblock的buffer 转换为c2buffer 然后会放到c2work中output buffers里面。
std::shared_ptr buffer
= createGraphicBuffer(std::move(entry->outblock),
C2Rect(mWidth, mHeight).at(left, top));
解码后的哪些信息是携带在work里面的, 解码的buffer,
work->worklets.front()->output.flags = (C2FrameData::flags_t)0;
work->worklets.front()->output.buffers.clear();
work->worklets.front()->output.buffers.push_back(buffer);
work->worklets.front()->output.ordinal = work->input.ordinal;
work->workletsProcessed = 1u;
mCallback->onOutputBufferAvailable(index, outBuffer);
这个callback 是从何而来的。 在mediacodec的init的时候会新建一个codec 并将codec设置到codec2里面。mCodec->setCallback(
std::unique_ptrCodecBase::CodecCallback(
new CodecCallback(new AMessage(kWhatCodecNotify, this))));
首先从解码这边出来的是C2GraphicBlock,会在codecbufferchannel中转为index 传递出去给mediacodec 转换过程是 内部有一个
mBuffers数组,在handlework先pushToStash到这里面。然后从这里面取出来。popFromStashAndRegister是这个里面去转换为mediacodec的buffer 和index 的。转换的MediaCodecBuffer, 就是把c2buffer的一个结构体赋值到Codec2Buffer中。c2Buffer->copy(buffer)。
getBufferQueueAssignment(block, &generation, &bqId, &bqSlot)
status = outputIgbp->queueBuffer(static_cast(bqSlot),
input, output);