Android音视频 MediaCodec框架-启动编码
简述
上一节我们介绍了MediaCodec框架创建编码器流程,编解码的流程其实基本是一样的,只是底层的最终的实现组件不同,所以我们只看启动编码流程。
MediaCodec启动编码
从MediaCodec的start方法开始。
1.1 MediaCodec.start
调用jni方法native_start
public final void start() {
native_start();
}
1.2 native_start
调用JMediaCodec的start方法。
static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
ALOGV("android_media_MediaCodec_start");
sp<JMediaCodec> codec = getMediaCodec(env, thiz);
// ... JMediaCodec状态检测
// 详见1.3
status_t err = codec->start();
// ...
}
1.3 JMediaCodec::start
调用MediaCodec的start方法。
status_t JMediaCodec::start() {
// 详见1.4
return mCodec->start();
}
1.4 MediaCodec::start
发送kWhatStart的AMessage通知CCodec2 start,这个流程和init很类似,处理消息start的流程详见1.5
status_t MediaCodec::start() {
sp<AMessage> msg = new AMessage(kWhatStart, this);
sp<AMessage> callback;
status_t err;
std::vector<MediaResourceParcel> resources;
resources.push_back(MediaResource::CodecResource(mFlags & kFlagIsSecure,
toMediaResourceSubType(mDomain)));
resources.push_back(MediaResource::GraphicMemoryResource(1));
for (int i = 0; i <= kMaxRetry; ++i) {
if (i > 0) {
// ...
sp<AMessage> response;
err = PostAndAwaitResponse(mConfigureMsg, &response);
// ...
}
// ...
}
return err;
}
1.5 MediaCodec::onMessageReceived
消息处理方法,调用CCodec的initiateStart
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
// ...
case kWhatStart:
{
// ...
// 更新状态到STARTING
setState(STARTING);
// 调用CCodec的initiateStart,详见1.6
mCodec->initiateStart();
break;
}
// ...
}
1.6 CCodec::initiateStart
这里只是修改了状态到STARTING,然后做了消息转发。
void CCodec::initiateStart() {
auto setStarting = [this] {
Mutexed<State>::Locked state(mState);
if (state->get() != ALLOCATED) {
return UNKNOWN_ERROR;
}
state->set(STARTING);
return OK;
};
if (tryAndReportOnError(setStarting) != OK) {
return;
}
(new AMessage(kWhatStart, this))->post();
}
1.7 CCodec::onMessageReceived
调用CCodec::start方法。
void CCodec::onMessageReceived(const sp<AMessage> &msg) {
// ...
case kWhatStart: {
// 调用start方法
setDeadline(now, 1500ms, "start");
start();
break;
}
// ...
}
1.8 CCodec::start
该方法主要做了几件事:
调用Component的start方法,Codec2的Component都是基于SimpleC2Component实现的,SimpleC2Component中处理了一些状态管理的逻辑,不同的编解码组件继承了SimpleC2Component,实现生命周期回调方法,例如onInit等。
从CCodecConfig获取mOutputFormat或者inputFormat信息,前者是解码时会有,后者是编码才会有的。
调用CCodecBufferChannel::start。
void CCodec::start() {
// ...
// 调用Component start,Codec2的Component都是基于SimpleC2Component实现的,SimpleC2Component中处理了一些状态管理的逻辑
// 不同的编解码最终实现都继承于SimpleC2Component,然后实现对应的生命周期回调方法做自己的事,由于我们H264的实现里没有做什么事,就不看了。
c2_status_t err = comp->start();
if (err != C2_OK) {
mCallback->onError(toStatusT(err, C2_OPERATION_Component_start),
ACTION_CODE_FATAL);
return;
}
sp<AMessage> inputFormat;
sp<AMessage> outputFormat;
status_t err2 = OK;
bool buffersBoundToCodec = false;
{
// CCodecConfig是在之前初始化的,里面有配置信息,这里mOutputFormat是解码时候才有的,而inputFormat则是编码的时候才有
// mInputSurface是编码时候配置了InputSurface时传入的参数,表示编码数据来源于一个Surface
// 我们之前在SurfaceFlinger章节说过,Surface是表示一个窗口,可以作为图像BufferQueue的一个生产者。
Mutexed<std::unique_ptr<Config>>::Locked configLocked(mConfig);
const std::unique_ptr<Config> &config = *configLocked;
inputFormat = config->mInputFormat;
outputFormat = config->mOutputFormat = config->mOutputFormat->dup();
if (config->mInputSurface) {
err2 = config->mInputSurface->start();
config->mInputSurfaceDataspace = config->mInputSurface->getDataspace();
}
buffersBoundToCodec = config->mBuffersBoundToCodec;
}
if (err2 != OK) {
mCallback->onError(err2, ACTION_CODE_FATAL);
return;
}
// 调用CCodecBufferChannel::start,详见1.9
err2 = mChannel->start(inputFormat, outputFormat, buffersBoundToCodec);
if (err2 != OK) {
mCallback->onError(err2, ACTION_CODE_FATAL);
return;
}
// ... 更新状态
std::map<size_t, sp<MediaCodecBuffer>> clientInputBuffers;
// 根据input里numSlots数量,调用input->buffers->requestNewBuffer创建buffer填充clientInputBuffers。
err2 = mChannel->prepareInitialInputBuffers(&clientInputBuffers);
if (err2 != OK) {
ALOGE("Initial preparation for Input Buffers failed");
mCallback->onError(err2, ACTION_CODE_FATAL);
return;
}
// 回调MediaCodec onStartCompleted
mCallback->onStartCompleted();
mChannel->requestInitialInputBuffers(std::move(clientInputBuffers));
}
1.9 CCodecBufferChannel::start
这个方法很长,主要是一些参数的初始化,分为编码和解码的情况,其中最重要的参数就是C2BlockPool和input/output。
以编码为例,input里面有一个buffers,这个buffers有多种类型,buffers会持有一个C2BlockPool来分配内存,而C2BlockPool又会通过Allocator分配C2Buffer,这里的Allocator也是在hal层,Allocator是通过AllocatorStore到hal获取到。
所以相当于在上层使用一个buffers来控制buffer到分配释放等,这个buffers类型比如LinearInputBuffers,GraphicInputBuffers,GraphicMetadataInputBuffers等,而buffers会通过C2BlockPool分配buffer,而C2BlockPool会通过Allocator到hal层获取buffer。
解码的情况类似,这里就不细说了。
status_t CCodecBufferChannel::start(
const sp<AMessage> &inputFormat,
const sp<AMessage> &outputFormat,
bool buffersBoundToCodec) {
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);
// ... 参数初始化以及检测
// 由C2AllocateStore来获取Allocation,而Allocation是用于分配buffer的,有不同类型的buffer,对应底层分配的内存可能也不同,例如dma
std::shared_ptr<C2AllocatorStore> allocatorStore = GetCodec2PlatformAllocatorStore();
int poolMask = GetCodec2PoolMask();
C2PlatformAllocatorStore::id_t preferredLinearId = GetPreferredLinearAllocatorId(poolMask);
// 编码
if (inputFormat != nullptr) {
// ... 参数配置
// 构造C2BlockPool
std::shared_ptr<C2BlockPool> pool;
{
Mutexed<BlockPools>::Locked pools(mBlockPools);
// ... 参数检查配置
if ((poolMask >> pools->inputAllocatorId) & 1) {
// 根据inputAllocatorId构造C2BlockPool,C2BlockPool里面持有Allocator,通过擦欧总Allocator管理Buffer的构建
err = CreateCodec2BlockPool(pools->inputAllocatorId, nullptr, &pool);
// ...
} else {
err = C2_NOT_FOUND;
}
// ...异常处理
pools->inputPool = pool;
}
bool forceArrayMode = false;
Mutexed<Input>::Locked input(mInput);
// ...构造填充input对象
// 其中input->buffers会根据buffer的类型不同而不同
// 这里的关系是input->buffers会最终提供给CCodec分配Buffer的能力,而input->buffers是通过前面构造的C2BlockPool来获取或者释放buffer
// 而C2BlockPool通过持有的Allocator来分配不同Buffer,不同的Buffer的区别在于底层可能使用不同的系统调用来分配内存,可能是dma之类的。
input->buffers->setFormat(inputFormat);
if (err == C2_OK) {
input->buffers->setPool(pool);
} else {
// TODO: error
}
if (forceArrayMode) {
input->buffers = input->buffers->toArrayMode(numInputSlots);
}
}
// 解码,这里和上面编码类似,主要是初始化一些必要对象。
if (outputFormat != nullptr) {
sp<IGraphicBufferProducer> outputSurface;
uint32_t outputGeneration;
int maxDequeueCount = 0;
{
Mutexed<OutputSurface>::Locked output(mOutputSurface);
maxDequeueCount = output->maxDequeueBuffers = numOutputSlots +
reorderDepth.value + mRenderingDepth;
outputSurface = output->surface ?
output->surface->getIGraphicBufferProducer() : nullptr;
if (outputSurface) {
output->surface->setMaxDequeuedBufferCount(output->maxDequeueBuffers);
}
outputGeneration = output->generation;
}
bool graphic = (oStreamFormat.value == C2BufferData::GRAPHIC);
C2BlockPool::local_id_t outputPoolId_;
C2BlockPool::local_id_t prevOutputPoolId;
{
Mutexed<BlockPools>::Locked pools(mBlockPools);
// ... 初始化BlockPools,和解码流程构造C2BlockPools类似。
}
Mutexed<Output>::Locked output(mOutput);
// ... 构造output,和上面input类似
// 如果接受输出是一个Surface,通知给Component
if (outputSurface) {
mComponent->setOutputSurface(
outputPoolId_,
outputSurface,
outputGeneration,
maxDequeueCount);
} else {
// ...
}
// ...
}
// 编解码监测器初始化
if (inputFormat || outputFormat) {
Mutexed<PipelineWatcher>::Locked watcher(mPipelineWatcher);
watcher->inputDelay(inputDelayValue)
.pipelineDelay(pipelineDelayValue)
.outputDelay(outputDelayValue)
.smoothnessFactor(kSmoothnessFactor);
watcher->flush();
}
mInputMetEos = false;
// 初始化buffer的锁
mSync.start();
return OK;
}
MediaCodec 获取buffer index
在创建并启动解码器后,我们会通过dequeueInputBuffer获取一个Buffer index,然后再通过getInputBuffers获取所有Buffer数组,然后根据索引在Buffer数组中获取buffer,往里面写入需要编码的数据,接下来我们来看看这个流程。
2.1 MediaCodec.dequeueInputBuffer
jni调用native_dequeueInputBuffer。
public final int dequeueInputBuffer(long timeoutUs) {
// ...
// 详见2.2
int res = native_dequeueInputBuffer(timeoutUs);
// ...
return res;
}
2.2 android_media_MediaCodec_dequeueInputBuffer
调用JMediaCodec::dequeueInputBuffer
static jint android_media_MediaCodec_dequeueInputBuffer(
JNIEnv *env, jobject thiz, jlong timeoutUs) {
// ...
size_t index;
// JMediaCodec::dequeueInputBuffer,详见2.3
status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
if (err == OK) {
return (jint) index;
}
return throwExceptionAsNecessary(env, err, codec);
}
2.3 JMediaCodec::dequeueInputBuffer
C++层的MediaCodec,MediaCodec::dequeueInputBuffer
status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
// 详见2.4
return mCodec->dequeueInputBuffer(index, timeoutUs);
}
2.4 MediaCodec::dequeueInputBuffer
发送kWhatDequeueInputBuffer消息。
status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, this);
msg->setInt64("timeoutUs", timeoutUs);
sp<AMessage> response;
status_t err;
// 发送kWhatDequeueInputBuffer消息,处理逻辑详见2.5
if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
return err;
}
CHECK(response->findSize("index", index));
return OK;
}
2.5 MediaCodec::onMessageReceived
调用handleDequeueInputBuffer处理,并且发送kWhatDequeueInputTimedOut消息来配置超时监测。
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
//...
case kWhatDequeueInputBuffer:
{
sp<AReplyToken> replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
// ...
// 详见2.6
if (handleDequeueInputBuffer(replyID, true /* new request */)) {
break;
}
// ... 通过发送kWhatDequeueInputTimedOut消息配置超时监测
break;
}
// ...
}
}
2.6 MediaCodec::handleDequeueInputBuffer
通过dequeuePortBuffer获取buffer index,然后返回index。
bool MediaCodec::handleDequeueInputBuffer(const sp<AReplyToken> &replyID, bool newRequest) {
// ...异常处理
// 详见2.7
ssize_t index = dequeuePortBuffer(kPortIndexInput);
if (index < 0) {
CHECK_EQ(index, -EAGAIN);
return false;
}
// 返回index
sp<AMessage> response = new AMessage;
response->setSize("index", index);
response->postReply(replyID);
return true;
}
2.7 MediaCodec::dequeuePortBuffer
mAvailPortBuffers里是两个int数组,分别给编码和解码使用,这里的int数组用于存储可以使用的buffer index。这个方法做的事就是从这个int数组里获取一个index,并且将它从数组里移除。
ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
BufferInfo *info = peekNextPortBuffer(portIndex);
if (!info) {
return -EAGAIN;
}
// 从availBuffers获取可用buffer index,并且把他从数组中移除。
std::list<size_t> *availBuffers = &mAvailPortBuffers[portIndex];
size_t index = *availBuffers->begin();
CHECK_EQ(info, &mPortBuffers[portIndex][index]);
availBuffers->erase(availBuffers->begin());
// ...
return index;
}
MediaCodec 获取input buffer数组
3.1 MediaCodec.getInputBuffers
public ByteBuffer[] getInputBuffers() {
synchronized (mBufferLock) {
// ...
// java层有一个buffer数组缓存,第一次需要通过jni去获取,详见3.2
if (mCachedInputBuffers == null) {
cacheBuffersLocked(true /* input */);
}
// ...
return mCachedInputBuffers;
}
}
3.2 MediaCodec.cacheBuffersLocked
通过jni获取buffers,并缓存记录。
private void cacheBuffersLocked(boolean input) {
ByteBuffer[] buffers = null;
try {
// 通过jni获取buffer,详见3.3
buffers = getBuffers(input);
invalidateByteBuffersLocked(buffers);
} catch (IllegalStateException e) {
// we don't get buffers in async mode
}
// ...
// 缓存记录结果
if (input) {
mCachedInputBuffers = buffers;
} else {
mCachedOutputBuffers = buffers;
}
}
3.3 JMediaCodec::getBuffers
jni方法通过调用JMediaCodec的getBuffers,而JMediaCodec又通过调用c++层的MediaCodec的getInputBuffers来获取buffers。
status_t JMediaCodec::getBuffers(
JNIEnv *env, bool input, jobjectArray *bufArray) const {
Vector<sp<MediaCodecBuffer> > buffers;
// 详见3.4
status_t err =
input
? mCodec->getInputBuffers(&buffers)
: mCodec->getOutputBuffers(&buffers);
// ...将C++层的buffers通过jni关联给java层
return OK;
}
3.4 MediaCodec::getInputBuffers
发送kWhatGetBuffers。
status_t MediaCodec::getInputBuffers(Vector<sp<MediaCodecBuffer> > *buffers) const {
sp<AMessage> msg = new AMessage(kWhatGetBuffers, this);
msg->setInt32("portIndex", kPortIndexInput);
msg->setPointer("buffers", buffers);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
3.5 MediaCodec::onMessageReceived
我们目前流程是在获取inputBuffer,通过inputBuffer给编码器传入编码前的数据,而如果我们配置了InputSurface,以InputSurface为输入,则不需要获取Buffer。
通过CCodecBufferChannel::getInputBufferArray来获取Buffer数组。
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
// ...
case kWhatGetBuffers:
{
// ...异常处理
dstBuffers->clear();
// 通过getInputBufferArray获取buffer,如果配置来InputSurface则不会走这个模式。详见3.6
if (portIndex != kPortIndexInput || !mHaveInputSurface) {
if (portIndex == kPortIndexInput) {
mBufferChannel->getInputBufferArray(dstBuffers);
} else {
mBufferChannel->getOutputBufferArray(dstBuffers);
}
}
mApiUsageMetrics.isArrayMode = true;
(new AMessage)->postReply(replyID);
break;
}
// ...
}
3.6 CCodecBufferChannel::getInputBufferArray
这里会调用input->buffers->getArray来获取Buffer,这里input->buffers是在start的过程中就已经初始化的,具体是在1.8节,调用mChannel->prepareInitialInputBuffers时候分配的,其中会循环调用buffers的requestNewBuffer方法,我们之前说过buffers里面有pool,而pool会通过allocator来分配buffer。
void CCodecBufferChannel::getInputBufferArray(Vector<sp<MediaCodecBuffer>> *array) {
array->clear();
Mutexed<Input>::Locked input(mInput);
if (!input->buffers) {
ALOGE("getInputBufferArray: No Input Buffers allocated");
return;
}
if (!input->buffers->isArrayMode()) {
input->buffers = input->buffers->toArrayMode(input->numSlots);
}
input->buffers->getArray(array);
}
MediacCodec queueInputBuffer
4.1 MediaCodec.queueInputBuffer
调用jni层native_queueInputBuffer
public final void queueInputBuffer(
int index,
int offset, int size, long presentationTimeUs, int flags)
throws CryptoException {
// ...
try {
// 详见4.2
native_queueInputBuffer(
index, offset, size, presentationTimeUs, flags);
} catch (CryptoException | IllegalStateException e) {
revalidateByteBuffer(mCachedInputBuffers, index, true /* input */);
throw e;
}
}
4.2 android_media_MediaCodec_queueInputBuffer
和之前一样,jni会调用JMediaCodec的queueInputBuffer方法。
static void android_media_MediaCodec_queueInputBuffer(
JNIEnv *env,
jobject thiz,
jint index,
jint offset,
jint size,
jlong timestampUs,
jint flags) {
// ...
// 详见4.3
status_t err = codec->queueInputBuffer(
index, offset, size, timestampUs, flags, &errorDetailMsg);
throwExceptionAsNecessary(
env, err, ACTION_CODE_FATAL,
codec->getExceptionMessage(errorDetailMsg.c_str()).c_str());
}
4.3 JMediaCodec::queueInputBuffer
调用MediaCodec的queueInputBuffer
status_t JMediaCodec::queueInputBuffer(
size_t index,
size_t offset, size_t size, int64_t timeUs, uint32_t flags,
AString *errorDetailMsg) {
//详见4.4
return mCodec->queueInputBuffer(
index, offset, size, timeUs, flags, errorDetailMsg);
}
4.4 MediaCodec::queueInputBuffer
发送kWhatQueueInputBuffer消息。
status_t MediaCodec::queueInputBuffer(
size_t index,
size_t offset,
size_t size,
int64_t presentationTimeUs,
uint32_t flags,
AString *errorDetailMsg) {
if (errorDetailMsg != NULL) {
errorDetailMsg->clear();
}
sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, this);
msg->setSize("index", index);
msg->setSize("offset", offset);
msg->setSize("size", size);
msg->setInt64("timeUs", presentationTimeUs);
msg->setInt32("flags", flags);
msg->setPointer("errorDetailMsg", errorDetailMsg);
sp<AMessage> response;
return PostAndAwaitResponse(msg, &response);
}
4.5 MediaCodec::onMessageReceived
调用onQueueInputBuffer处理任务。
void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
//...
case kWhatQueueInputBuffer:
{
// ...
status_t err = UNKNOWN_ERROR;
if (!mLeftover.empty()) {
mLeftover.push_back(msg);
size_t index;
msg->findSize("index", &index);
err = handleLeftover(index);
} else {
// 详见4.6
err = onQueueInputBuffer(msg);
}
PostReplyWithError(replyID, err);
break;
}
// ...
}
4.6 MediaCodec::onQueueInputBuffer
处理一些参数的检查和传递,这里分需要加密和不需要加密的两种场景,我们不看加密的逻辑,这里主要构建来一个新的buffer,把数据填充,并且调用attachBuffer把queue进来的buffer里面的数据拷贝到新建到buffer里。
最后调用queueInputBuffer。
status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
// ... 参数预处理和检查,将一些参数配置到buffer
// ... 加密逻辑
if (c2Buffer || memory) {
sp<AMessage> tunings = NULL;
if (msg->findMessage("tunings", &tunings) && tunings != NULL) {
onSetParameters(tunings);
}
status_t err = OK;
if (c2Buffer) {
// 拷贝数据到新的buffer
err = mBufferChannel->attachBuffer(c2Buffer, buffer);
} else if (memory) {
AString errorDetailMsg;
err = mBufferChannel->attachEncryptedBuffer(
memory, (mFlags & kFlagIsSecure), key, iv, mode, pattern,
offset, subSamples, numSubSamples, buffer, &errorDetailMsg);
if (err != OK && hasCryptoOrDescrambler()
&& (mFlags & kFlagUseCryptoAsync)) {
// ... 加密逻辑
}
} else {
// error log
}
// ...
}
// ...
if (hasCryptoOrDescrambler() && !c2Buffer && !memory) {
// 需要加密场景
} else {
// 调用queueInputBuffer,详见4.7
err = mBufferChannel->queueInputBuffer(buffer);
// ...
}
// ...
return err;
}
4.7 CCodecBufferChannel::queueInputBuffer
调用queueInputBufferInternal。
status_t CCodecBufferChannel::queueInputBuffer(const sp<MediaCodecBuffer> &buffer) {
QueueGuard guard(mSync);
if (!guard.isRunning()) {
ALOGD("[%s] No more buffers should be queued at current state.", mName);
return -ENOSYS;
}
return queueInputBufferInternal(buffer);
}
4.8 CCodecBufferChannel::queueInputBufferInternal
这里主要会解析buffer,然后将buffer里的信息封装到一个C2Work中,然后把C2Work存到一个C2Work数组,调用Component的queue来处理这个这个任务。
这里Component的queue会在hal层调用到对方的queue_nb方法,这里就是SimpleC2Component的::SimpleC2Component。
status_t CCodecBufferChannel::queueInputBufferInternal(
sp<MediaCodecBuffer> buffer,
std::shared_ptr<C2LinearBlock> encryptedBlock,
size_t blockSize) {
// ...获取buffer里的参数
// ...构造一个C2Work数组和一个C2Work,后续最终会提交这个数组,将信息封装在C2Work中
std::list<std::unique_ptr<C2Work>> items;
std::unique_ptr<C2Work> work(new C2Work);
work->input.ordinal.timestamp = timeUs;
work->input.ordinal.frameIndex = mFrameIndex++;
work->input.ordinal.customOrdinal = timeUs;
work->input.buffers.clear();
sp<Codec2Buffer> copy;
bool usesFrameReassembler = false;
if (buffer->size() > 0u) {
Mutexed<Input>::Locked input(mInput);
std::shared_ptr<C2Buffer> c2buffer;
// 将buffer数据存到c2buffer
if (!input->buffers->releaseBuffer(buffer, &c2buffer, false)) {
return -ENOENT;
}
// ...
if (input->frameReassembler) {
usesFrameReassembler = true;
input->frameReassembler.process(buffer, &items);
} else {
// ...
// 将c2buffer存到C2Work里。
work->input.buffers.push_back(c2buffer);
if (encryptedBlock) {
work->input.infoBuffers.emplace_back(C2InfoBuffer::CreateLinearBuffer(
kParamIndexEncryptedBuffer,
encryptedBlock->share(0, blockSize, C2Fence())));
}
}
} else if (eos) {
// ...
}
if (usesFrameReassembler) {
// ...
} else {
work->input.flags = (C2FrameData::flags_t)flags;
// TODO: fill info's
work->input.configUpdate = std::move(mParamsToBeSet);
if (tunnelFirstFrame) {
C2StreamTunnelHoldRender::input tunnelHoldRender{
0u /* stream */,
C2_TRUE /* value */
};
work->input.configUpdate.push_back(C2Param::Copy(tunnelHoldRender));
}
work->worklets.clear();
work->worklets.emplace_back(new C2Worklet);
// 将C2Work存到items里
items.push_back(std::move(work));
eos = eos && buffer->size() > 0u;
}
if (eos) {
// ...
}
c2_status_t err = C2_OK;
if (!items.empty()) {
// ...
// 将C2Work数组提交给Component(通过hal层到真正实现编解码逻辑实现),这里是SimpleC2Component::queue_nb,详见4.9
err = mComponent->queue(&items);
}
if (err != C2_OK) {
// ...
} else {
// ...释放buffer
}
feedInputBufferIfAvailableInternal();
return err;
}
4.9 SimpleC2Component::queue_nb
这里把前面传过来的C2Work都放到mWorkQueue中,然后发送了kWhatProcess消息触发处理逻辑。
c2_status_t
SimpleC2Component::queue_nb(std::list<std::unique_ptr<C2Work>> *const items) {
{
Mutexed<ExecState>::Locked state(mExecState);
if (state->mState != RUNNING) {
return C2_BAD_STATE;
}
}
bool queueWasEmpty = false;
{
Mutexed<WorkQueue>::Locked queue(mWorkQueue);
queueWasEmpty = queue->empty();
// 将items里面的数据放到mWorkQueue中
while (!items->empty()) {
queue->push_back(std::move(items->front()));
items->pop_front();
}
}
if (queueWasEmpty) {
// 发送kWhatProcess,详见4.10
(new AMessage(WorkHandler::kWhatProcess, mHandler))->post();
}
return C2_OK;
}
4.10 SimpleC2Component::processQueue
这里主要就是会调用process方法,这个方法是由子类实现的,SimpleC2Component是用于管理流程框架的模版,它的子类来实现具体编解码逻辑。
例如H264的编解码就是由子类C2SoftAacDec和C2SoftAacEnc实现的,我们本节不会介绍编解码的细节,下一节会介绍H264编解码的一些核心原理。
bool SimpleC2Component::processQueue() {
// ...
{
Mutexed<WorkQueue>::Locked queue(mWorkQueue);
if (queue->empty()) {
return false;
}
generation = queue->generation();
drainMode = queue->drainMode();
isFlushPending = queue->popPendingFlush();
// 从队列中取出最前面的任务
work = queue->pop_front();
hasQueuedWork = !queue->empty();
}
if (isFlushPending) {
// flush的回调,这里SimpleC2Component是一个处理流程的架构,最终需要由它的子类来实现对应生命周期所做的事。
c2_status_t err = onFlush_sm();
// ...
}
if (!mOutputBlockPool) {
// ...创建用于管理输出Buffer的Pool
}
// ...
// ... input内buffer的检测
// 调用 process,也是由子类实现的。
// 传入的参数是work和用于管理输出buffer的Pool
process(work, mOutputBlockPool);
// ...
return hasQueuedWork;
}
小结
本节介绍了MediaCodec编解码的流程,从java层MediaCodec调用到JMediaCodec,然后调用C++层到MediaCodec,通过发送对应流程的消息,触发CCodec,而里面通过一个CCodecBufferChannel来管理这些逻辑,CCodecBufferChannel里面分input和output分别用于管理编解码,他们中都有一个buffers用于管理所有buffer,通过一个C2BlockPool来管理buffer的分配,C2BlockPool内有一个Allocator,会通过hal层进行最终的buffer分配释放。
CCodecBufferChannel工作会通过Component调用hal层,传参数到SimpleC2Component,SimpleC2Component的子类进行最终的编解码操作。
下一节我们会介绍H264编解码的核心原理。