CCodecBufferChannel是整个Codec2框架最复杂的部分,涉及到input buffer分配,input/output buffer的处理,内部封装了一些全新的机制,比如reorder、pipeline control等。本篇内容主要对CCodecBufferChannel的start流程进行了解,涉及到的C2BlockPool、C2Allocator、InputBuffers、OutputBuffers本篇不做展开。
1、CCodecBufferChannel
先来看CCodecBufferChannel的构造函数:
CCodecBufferChannel::CCodecBufferChannel(
const std::shared_ptr<CCodecCallback> &callback)
: mHeapSeqNum(-1),
mCCodecCallback(callback),
mFrameIndex(0u),
mFirstValidFrameIndex(0u),
mAreRenderMetricsEnabled(areRenderMetricsEnabled()),
mIsSurfaceToDisplay(false),
mHasPresentFenceTimes(false),
mRenderingDepth(3u),
mMetaMode(MODE_NONE),
mInputMetEos(false),
mSendEncryptedInfoBuffer(false) {
{
Mutexed<Input>::Locked input(mInput);
input->buffers.reset(new DummyInputBuffers(""));
input->extraBuffers.flush();
input->inputDelay = 0u;
input->pipelineDelay = 0u;
input->numSlots = kSmoothnessFactor;
input->numExtraSlots = 0u;
input->lastFlushIndex = 0u;
}
{
Mutexed<Output>::Locked output(mOutput);
output->outputDelay = 0u;
output->numSlots = kSmoothnessFactor;
output->bounded = false;
}
{
Mutexed<BlockPools>::Locked pools(mBlockPools);
pools->outputPoolId = C2BlockPool::BASIC_LINEAR;
}
std::string value = GetServerConfigurableFlag("media_native", "ccodec_rendering_depth", "3");
android::base::ParseInt(value, &mRenderingDepth);
mOutputSurface.lock()->maxDequeueBuffers = kSmoothnessFactor + mRenderingDepth;
}
构造函数初始化了一些成员变量,以下是部分成员的意义:
- mFrameIndex:写入到组件的数据的序号;
- mRenderingDepth:我的理解是surface持有的已填冲数据的output buffer的数量,默认值为3;
- mMetaMode:记录编码数据的类型是否为ANW Meta data;
CCodecBufferChannel使用Input来存储所有与输入buffer相关的信息:
struct Input {
Input();
std::unique_ptr<InputBuffers> buffers;
size_t numSlots;
FlexBuffersImpl extraBuffers;
size_t numExtraSlots;
uint32_t inputDelay;
uint32_t pipelineDelay;
c2_cntr64_t lastFlushIndex;
FrameReassembler frameReassembler;
};
Input的成员字段含义如下:
- buffers:使用InputBuffers存储所有分配的输入buffer;
- numSlots:input buffer的数量;
- extraBuffers:从变量名来看是额外的buffer,在启动过程分配的buffer都存在buffers中,其他情况下需要再分配buffer时,分配的buffer会存到extraBuffers;
- numExtraSlots:额外buffer的数量;
- inputDelay:延迟输入的buffer数量;
- pipelineDelay:组件持有的buffer数量;
- lastFlushIndex:上次执行flush时的输入索引;
- frameReassembler:帧重组,用于audio encoder;
CCodecBufferChannel使用Output来存储所有与输出buffer相关的信息:
struct Output {
std::unique_ptr<OutputBuffers> buffers;
size_t numSlots;
uint32_t outputDelay;
bool bounded;
};
Output的成员字段含义如下:
- buffers:使用OutputBuffers存储所有分配的输出buffer;
- numSlots:output buffer的数量;
- outputDelay:输出buffer的延迟;
- bounded:输出是否和Native Window绑定;
CCodecBufferChannel使用BlockPools来记录与input/output buffer分配的信息:
struct BlockPools {
C2Allocator::id_t inputAllocatorId;
std::shared_ptr<C2BlockPool> inputPool;
C2Allocator::id_t outputAllocatorId;
C2BlockPool::local_id_t outputPoolId;
std::shared_ptr<Codec2Client::Configurable> outputPoolIntf;
};
BlockPools的成员字段含义如下:
- inputAllocatorId:分配输入buffer的Allocator id;
- inputPool:管理输入buffer的blockpool;
- outputAllocatorId:分配输出buffer的Allocator id;
- outputPoolId:分配输出buffer的blockpool id;
- outputPoolIntf:output blockpool的配置接口;
mInput、mOutput和mBlockPools都是受锁保护的,它们会在CCodec和MediaCodec线程中被用到。
void CCodecBufferChannel::setComponent(
const std::shared_ptr<Codec2Client::Component> &component) {
mComponent = component;
mComponentName = component->getName() + StringPrintf("#%d", int(uintptr_t(component.get()) % 997));
mName = mComponentName.c_str();
}
组件创建后会调用setComponent方法将组件设定给CCodecBufferChannel,因此CCodecBufferChannel是可以独立操作组件的。CCodecBufferChannel的mComponentName是以组件名+一个随机数字组成的,方便我们在多实例的情况下debug。
D CCodecBufferChannel: [start 1233] [c2.xxx.video.avc.decoder#740] inputDelayValue = 6
和OMX框架相同,Codec2框架也是在start之后才会分配buffer。既然要分配buffer,就会有几个问题:
- 分配什么类型的buffer?
- 用什么分配buffer?
- 分配几个buffer,buffer大小是多少?
想要搞清楚这些问题就要仔细阅读CCodecBufferChannel的start方法,方法比较长,我们分段解析。
2、buffer count calculate
status_t start(
const sp<AMessage> &inputFormat,
const sp<AMessage> &outputFormat,
bool buffersBoundToCodec);
start需要传入三个参数,分别为输入格式、输出格式以及buffer是否要和Codec绑定,buffersBoundToCodec默认为true(与Codec相绑定)。
C2StreamBufferTypeSetting::input iStreamFormat(0u);
C2StreamBufferTypeSetting::output oStreamFormat(0u);
C2ComponentKindSetting kind;
C2PortReorderBufferDepthTuning::output reorderDepth;
C2PortReorderKeySetting::output reorderKey;
C2PortActualDelayTuning::input inputDelay(0);
C2PortActualDelayTuning::output outputDelay(0);
C2ActualPipelineDelayTuning pipelineDelay(0);
C2SecureModeTuning secureMode(C2Config::SM_UNPROTECTED);
c2_status_t err = mComponent->query(
{
&iStreamFormat,
&oStreamFormat,
&kind,
&reorderDepth,
&reorderKey,
&inputDelay,
&pipelineDelay,
&outputDelay,
&secureMode,
},
{},
C2_DONT_BLOCK,
nullptr);
进入函数体首先会向组件请求一组参数,iStreamFormat和oStreamFormat存储的是输入输出buffer的类型,用枚举表示:
class C2BufferData {
public:
enum type_t : uint32_t {
INVALID, ///< invalid buffer type. Do not use.
LINEAR, ///< the buffer contains a single linear block
LINEAR_CHUNKS, ///< the buffer contains one or more linear blocks
GRAPHIC, ///< the buffer contains a single graphic block
GRAPHIC_CHUNKS, ///< the buffer contains one of more graphic blocks
};
}
Codec2框架将数据划分为两个类型:LINEAR(1D)和GRAPHIC(2D)。一维数据被看作是线性的,1D数据包括压缩的音频和视频数据,以及解码后的音频数据;二维数据指的是图像数据,包含解码后的视频数据和需要编码的图像数据。