一、引言:
mediacodec在framework层的代码逻辑确实比较复杂,因为对整个通路还不是很熟悉,所以这里摘要一些代码的查看心得,并在网上找了一个demo来了解一下mediacodec是怎么工作的。
二、测试demo:
测试代码摘录自何俊林,demo主要演示了如何渲染一个视频流文件到显示设备上:
MediaCodec测试demo
三、代码心得:
1.mediacodec与omx的调用逻辑:
这个图不是严格意义上的逻辑时序图,举例了mediacodec和stagefright框架在调用omx进行解码时的逻辑:
①Acodec和omxcodec分别被上层的mediacodec和awesomeplayer调用;
②Omx使用的是binder机制,acodec和omxcodec都是通过mediaplayerservice提供的服务获取到Bn端的omx对象,然后通过OMXNodeInstance去调入到omx的组件中去;
③OMX_Core.h和OMX_Component.h是标准的OMX接口,前者是上层调用的接口,后者是下层组件实现的接口,两个文件串联起了OMX上层和下层;
2.omx如何配置解码器的:
ACodec通过onAllocateComponent函数去获取了omx对象,通过configureCodec函数去配置解码器,omx解码器的配置主要通过不停地调用omx标准接口getParameter和setParameter去实现,视频和音频是分开设置的,音频是按照不同类型去设置的,在这里可以扩展对应的解码类型;
3.mediacodec是通过不停填充输入输出buffer实现的解码,那么buffer是在什么时候申请的:
答案:buffer的申请是在切换到LoadedToIdleState时申请的。
a.我们知道,ACodec中维护了多个状态对象,不同的消息会在对应的状态去处理,每当状态改变时,就会调用changeState函数,先看一下ACodec的继承关系:
struct ACodec : public AHierarchicalStateMachine, public CodecBase{
...
}
ACodec继承自AHierarchicalStateMachine,看一下changeState函数实现:
void AHierarchicalStateMachine::changeState(const sp<AState> &state) {
if (state == mState) {
// Quick exit for the easy case.
return;
}
/* state A记录的是对象中当前保存的状态 */
Vector<sp<AState> > A;
sp<AState> cur = mState;
for (;;) {
A.push(cur);
if (cur == NULL) {
break;
}
cur = cur->parentState();
}
/* state B记录的是即将切换的状态 */
Vector<sp<AState> > B;
cur = state;
for (;;) {
B.push(cur);
if (cur == NULL) {
break;
}
cur = cur->parentState();
}
// Remove the common tail.
while (A.size() > 0 && B.size() > 0 && A.top() == B.top()) {
A.pop();
B.pop();
}
mState = state;
/* 调用状态中的stateExited()函数 */
for (size_t i = 0; i < A.size(); ++i) {
A.editItemAt(i)->stateExited();
}
/* 调用状态中的stateEntered()函数 */
for (size_t i = B.size(); i-- > 0;) {
B.editItemAt(i)->stateEntered();
}
}
changeState实际上做的事就是记录即将切换的状态,并且适时地调用stateExited()和stateEntered()函数。
b.知道了changeState函数的实现之后,我们再回顾一下启用解码器过程中,上层调用与ACodec是如何控制状态的:
①上层调用createDecoderByType时,对于ACodec中的状态变化是UninitializedState->LoadedState;
②上层调用configure时,对于ACodec中状态没有改变,维持LoadedState;
③上层调用start时,对于ACodec中状态变化是LoadedState->LoadedToIdleState;
具体我们看一下ACodec中切换时的源码:
void ACodec::LoadedState::onStart() {
ALOGV("onStart");
/* 调用omx的sendCommand函数 */
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
(status_t)OK);
/* 调用changeState函数,传入的状态为LoadedToIdleState */
mCodec->changeState(mCodec->mLoadedToIdleState);
}
因为有了前面changeState的分析做铺垫,我们直接去看LoadedToIdleState对应的stateEntered函数实现:
void ACodec::LoadedToIdleState::stateEntered() {
ALOGV("[%s] Now Loaded->Idle", mCodec->mComponentName.c_str());
status_t err;
/* 1.调用allocateBuffers去申请input和output的buffer */
if ((err = allocateBuffers()) != OK) {
ALOGE("Failed to allocate buffers after transitioning to IDLE state "
"(error 0x%08x)",
err);
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
/* 2.如果申请失败,回退状态 */
mCodec->changeState(mCodec->mLoadedState);
}
这里就会去申请buffer,如果buffer申请失败了,那么就会回退状态。
3.上层如何驱动codec进行解码:
应用中,不停地去调用dequeueInputBuffer和queueInputBuffer让code往输入buffer中填充数据,然后调用dequeueOutputBuffer查询输出buffer中是否获取到了解码完后的原始数据,之后调用releaseOutputBuffer去进行渲染,具体流程可参看demo。