这一篇我们一起来了解ACodec的Buffer分配流程。
1、initiateStart
首先对上一篇内容做一点补充,configureCodec执行完成后组件的状态没有变化,仍处在OMX_StateLoaded。因此,当我们调用initiateStart时,发出的消息将由ACodec::LoadedState来处理。
bool ACodec::LoadedState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case ACodec::kWhatStart:
{
onStart();
handled = true;
break;
}
}
}
onStart是ACodec::LoadedState内部的方法,调用此方法首先会发送命令给OMX组件,将状态切换至OMX_StateIdle,命令送出成功后就将ACodec状态切换至LoadedToIdleState。
void ACodec::LoadedState::onStart() {
ALOGV("onStart");
status_t err = mCodec->mOMXNode->sendCommand(OMX_CommandStateSet, OMX_StateIdle);
if (err != OK) {
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
} else {
mCodec->changeState(mCodec->mLoadedToIdleState);
}
}
命令发出后OMX组件会进入到LoadedToIdleState这个中间状态等待buffer分配。
2、LoadedToIdleState
ACodec进入到LoadedToIdleState后,buffer分配就开始了。如果分配成功,ACodec会等待组件状态切换完成的消息;如果分配失败则会用callback上抛error,同时清理已经分配的buffer。
void ACodec::LoadedToIdleState::stateEntered() {
ALOGV("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());
status_t err;
if ((err = allocateBuffers()) != OK) {
ALOGE("Failed to allocate buffers after transitioning to IDLE state "
"(error 0x%08x)",
err);
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
// ...
mCodec->changeState(mCodec->mLoadedState);
}
}
buffer分配通过调用allocateBuffers方法完成:
status_t ACodec::LoadedToIdleState::allocateBuffers() {
status_t err = mCodec->allocateBuffersOnPort(kPortIndexInput);
if (err != OK) {
return err;
}
err = mCodec->allocateBuffersOnPort(kPortIndexOutput);
if (err != OK) {
return err;
}
mCodec->mCallback->onStartCompleted();
return OK;
}
ACodec会调用allocateBuffersOnPort先为input端口分配buffer,然后再为output端口分配buffer,因此我们看到的input buffer id总是比output buffer id小。
buffer分配完成后立刻调用CodecCallback通知MediaCodec start执行完成。这里提出一个小小的疑问:为什么没有等OMX组件的event发回来再调用CodecCallback呢?
3、allocateBuffersOnPort
本文将allocateBuffersOnPort分为两部分来了解:
- Decoder Output且使用NativeWindow的情况;
- 其他情况;
这一节我们先看“其他”,这部分比较简单。
进入函数体,首先会获取要分配的buffer size,这些信息都存储在组件的PortDefinition中,我们可以通过调用getParameter获取端口定义,从中拿到端口使用的buffer size。
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
err = mOMXNode->getParameter(
OMX_IndexParamPortDefinition, &def, sizeof(def));
如果是Encoder且输入要用Metadata,即PortMode为kPortModeDynamicANWBuffer或kPortModeDynamicNativeHandle,这时候input buffer size需要重新计算,大小等于Metadata size。
const IOMX::PortMode &mode = mPortMode[portIndex];
size_t bufSize = def.nBufferSize;
if (mode == IOMX::kPortModeDynamicANWBuffer) {
bufSize = sizeof(VideoNativeMetadata);
} else if (mode == IOMX::kPortModeDynamicNativeHandle) {
bufSize = sizeof(VideoNativeHandleMetadata);
}
接下来是对buffer size做校验,如果大小超过kMaxCodecBufferSize(8192 * 4096 * 4 bytes)就返回error。这段代码中我们看到DataConverter,这个类是做数据转换用的。
size_t conversionBufferSize = 0;
sp<DataConverter> converter = mConverter[portIndex];
if (converter != NULL) {
if (portIndex == kPortIndexInput) {
conversionBufferSize = converter->sourceSize(bufSize);
} else {
conversionBufferSize = converter->targetSize(bufSize);
}
}
size_t alignment = 32;
if (bufSize == 0 || max(bufSize, conversionBufferSize) > kMaxCodecBufferSize) {
ALOGE("b/22885421");
return NO_MEMORY;
}
size_t alignedSize = align(bufSize, alignment);
size_t alignedConvSize = align(conversionBufferSize, alignment);
if (def.nBufferCountActual > SIZE_MAX / (alignedSize + alignedConvSize)) {
ALOGE("b/22885421");
return NO_MEMORY;
}
下面这部分是在获取Allocator,在PortMode被设置为kPortModePresetSecureBuffer的情况下,会要求组件分配buffer。而在其他情况下,则是使用TAllocator来分配buffer。
if (mode != IOMX::kPortModePresetSecureBuffer) {
mAllocator[portIndex] = TAllocator::getService("ashmem");
if (mAllocator[portIndex] == nullptr) {
ALOGE("hidl allocator on port %d is null",
(int)portIndex);
return NO_MEMORY;
}
}
3.1、BufferInfo
每次循环分配buffer之前都会先创建出一个BufferInfo出来,ACodec用它记录buffer信息和运行过程中buffer所处的状态。
struct BufferInfo {
enum Status {
OWNED_BY_US,
OWNED_BY_COMPONENT,
OWNED_BY_UPSTREAM,
OWNED_BY_DOWNSTREAM,
OWNED_BY_NATIVE_WINDOW,
UNRECOGNIZED, // not a tracked buffer
};
static inline Status getSafeStatus(BufferInfo *info) {
return info == NULL ? UNRECOGNIZED : info->mStatus;
}
IOMX::buffer_id mBufferID;
Status mStatus;
unsigned mDequeuedAt;
sp<MediaCodecBuffer> mData; // the client's buffer; if not using data conversion, this is
// the codec buffer; otherwise, it is allocated separately
sp<RefBase> mMemRef; // and a reference to the IMemory, so it does not go away
sp<MediaCodecBuffer> mCodecData; // the codec's buffer
sp<RefBase> mCodecRef; // and a reference to the IMemory
sp<GraphicBuffer> mGraphicBuffer;
bool mNewGraphicBuffer;
int mFenceFd;
FrameRenderTracker::Info *mRenderInfo;
// The following field and 4 methods are used for debugging only
bool mIsReadFence;
// Store |fenceFd| and set read/write flag. Log error, if there is already a fence stored.
void setReadFence(int fenceFd, const char *dbg);
void setWriteFence(int fenceFd, const char *dbg);
// Log error, if the current fence is not a read/write fence.
void checkReadFence(const char *dbg);
void checkWriteFence(const char *dbg);
};
BufferInfo的各个字段意义如下:
4、allocateOutputMetadataBuffers
5、allocateOutputBuffersFromNativeWindow
6、IdleToExecutingState
关注公众号《青山渺渺》阅读全文