全新系列文章已更新:
- Android Media Framework - 开篇
- Android Media Framework(一)OpenMAX 框架简介
- Android Media Framework(二)OpenMAX 类型阅读与分析
- Android Media Framework(三)OpenMAX API阅读与分析
- Android Media Framework(四)Non-Tunneled组件的状态转换与buffer分配过程分析
- Android Media Framework(五)Tunnel Mode
- Android Media Framework(六)插件式编程与OMXStore
- Android Media Framework(七)MediaCodecService
- Android Media Framework(八)OMXNodeInstance - Ⅰ
- Android Media Framework(九)OMXNodeInstance - Ⅱ
- Android Media Framework(十)OMXNodeInstance - Ⅲ
这一节开始我们就来学习 ACodec 的实现
1、创建 ACodec
ACodec 是在 MediaCodec 中创建的,这里先贴出创建部分的代码:
mCodec = mGetCodecBase(name, owner);
if (mCodec == NULL) {
ALOGE("Getting codec base with name '%s' (owner='%s') failed", name.c_str(), owner);
return NAME_NOT_FOUND;
}
if (mDomain == DOMAIN_VIDEO) {
// video codec needs dedicated looper
if (mCodecLooper == NULL) {
status_t err = OK;
mCodecLooper = new ALooper;
mCodecLooper->setName("CodecLooper");
err = mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
if (OK != err) {
ALOGE("Codec Looper failed to start");
return err;
}
}
mCodecLooper->registerHandler(mCodec);
} else {
mLooper->registerHandler(mCodec);
}
从前面的学习我们可以知道,MediaCodec 使用的是异步消息处理的机制,创建MediaCodec 时需要传入一个 ALooper 对象用于处理发送给 MediaCodec 的消息。同样的 ACodec 也是用的异步消息处理机制,它也需要一个 ALooper,这个 ALooper 应该由上一级 MediaCodec 传递,从上面的代码我们可以知道,如果创建的是音频解码器,那么 ACodec 将会复用 MediaCodec 的 ALooper,也就是它们的消息处理会在相同线程当中;如果是视频解码器,那么 MediaCodec 会创建一个专门的 ALooper 给 ACodec 使用,ACodec 和 MediaCodec 的消息处理在不同线程中。
ACodec::ACodec()
: mSampleRate(0),
mNodeGeneration(0),
mUsingNativeWindow(false),
mNativeWindowUsageBits(0),
mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN),
mIsVideo(false),
mIsImage(false),
mIsEncoder(false),
mFatalError(false),
mShutdownInProgress(false),
mExplicitShutdown(false),
mIsLegacyVP9Decoder(false),
mIsStreamCorruptFree(false),
mIsLowLatency(false),
mEncoderDelay(0),
mEncoderPadding(0),
mRotationDegrees(0),
mChannelMaskPresent(false),
mChannelMask(0),
mDequeueCounter(0),
mMetadataBuffersToSubmit(0),
mNumUndequeuedBuffers(0),
mRepeatFrameDelayUs(-1LL),
mMaxPtsGapUs(0LL),
mMaxFps(-1),
mFps(-1.0),
mCaptureFps(-1.0),
mCreateInputBuffersSuspended(false),
mTunneled(false),
mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),
mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),
mDescribeHDR10PlusInfoIndex((OMX_INDEXTYPE)0),
mStateGeneration(0),
mVendorExtensionsStatus(kExtensionsUnchecked) {
memset(&mLastHDRStaticInfo, 0, sizeof(mLastHDRStaticInfo));
mUninitializedState = new UninitializedState(this);
mLoadedState = new LoadedState(this);
mLoadedToIdleState = new LoadedToIdleState(this);
mIdleToExecutingState = new IdleToExecutingState(this);
mExecutingState = new ExecutingState(this);
mOutputPortSettingsChangedState =
new OutputPortSettingsChangedState(this);
mExecutingToIdleState = new ExecutingToIdleState(this);
mIdleToLoadedState = new IdleToLoadedState(this);
mFlushingState = new FlushingState(this);
mPortEOS[kPortIndexInput] = mPortEOS[kPortIndexOutput] = false;
mInputEOSResult = OK;
mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;
mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;
memset(&mLastNativeWindowCrop, 0, sizeof(mLastNativeWindowCrop));
changeState(mUninitializedState);
}
ACodec 的构造函数主要是初始化了成员对象,实例化了各个状态对象
,并且将状态切换到了 UninitializedState
,这时候我们就要去查看它的 stateEntered 方法:
void ACodec::UninitializedState::stateEntered() {
ALOGV("Now uninitialized");
if (mDeathNotifier != NULL) {
if (mCodec->mOMXNode != NULL) {
auto tOmxNode = mCodec->mOMXNode->getHalInterface<IOmxNode>();
if (tOmxNode) {
tOmxNode->unlinkToDeath(mDeathNotifier);
}
}
mDeathNotifier.clear();
}
mCodec->mUsingNativeWindow = false;
mCodec->mNativeWindow.clear();
mCodec->mNativeWindowUsageBits = 0;
mCodec->mOMX.clear();
mCodec->mOMXNode.clear();
mCodec->mFlags = 0;
mCodec->mPortMode[kPortIndexInput] = IOMX::kPortModePresetByteBuffer;
mCodec->mPortMode[kPortIndexOutput] = IOMX::kPortModePresetByteBuffer;
mCodec->mConverter[0].clear();
mCodec->mConverter[1].clear();
mCodec->mComponentName.clear();
}
UninitializedState::stateEntered 主要是将与 OMX 组件相关的成员对象重置初始化。
2、initiateAllocateComponent
创建 MediaCodec 时,ACodec 也就被创建了,随后就会调用 initiateAllocateComponent 方法创建 OMX 组件,ACodec 创建之后处在 UninitializedState,所以消息最终在该状态中被处理:
bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
ALOGV("onAllocateComponent");
CHECK(mCodec->mOMXNode == NULL);
mCodec->mFatalError = false;
// 创建 Callback 消息,并且设置好 notify
sp<AMessage> notify = new AMessage(kWhatOMXMessageList, mCodec);
// notify 的 generation 为 nodegeneration + 1,这是因为进入 loaded 状态后,mNodeGeneration 会 + 1
notify->setInt32("generation", mCodec->mNodeGeneration + 1);
// 需要检查 codecInfo 才能创建 OMXNode
sp<RefBase> obj;
CHECK(msg->findObject("codecInfo", &obj));
sp<MediaCodecInfo> info = (MediaCodecInfo *)obj.get();
if (info == nullptr) {
ALOGE("Unexpected nullptr for codec information");
mCodec->signalError(OMX_ErrorUndefined, UNKNOWN_ERROR);
return false;
}
AString owner = (info->getOwnerName() == nullptr) ? "default" : info->getOwnerName();
AString componentName;
CHECK(msg->findString("componentName", &componentName));
// 创建 callback 对象
sp<CodecObserver> observer = new CodecObserver(notify);
sp<IOMX> omx;
sp<IOMXNode> omxNode;
status_t err = NAME_NOT_FOUND;
// 创建 OMXClient
OMXClient client;
// 获取 IOmx 服务
if (client.connect(owner.c_str()) != OK) {
mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
return false;
}
// 将获取到的 IOmx 服务代理封装为 Legacy 模式
omx = client.interface();
pid_t tid = gettid();
int prevPriority = androidGetThreadPriority(tid);
androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
// 创建 IOmxNode 服务代理,并且封装为 IOMXNode
err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
androidSetThreadPriority(tid, prevPriority);
mDeathNotifier = new DeathNotifier(new AMessage(kWhatOMXDied, mCodec));
auto tOmxNode = omxNode->getHalInterface<IOmxNode>();
if (tOmxNode && !tOmxNode->linkToDeath(mDeathNotifier, 0)) {
mDeathNotifier.clear();
}
// 记录新的状态下的 ACodec 状态
++mCodec->mNodeGeneration;
mCodec->mComponentName = componentName;
mCodec->mRenderTracker.setComponentName(componentName);
mCodec->mFlags = 0;
// 记录是否创建的是 secure 组件
if (componentName.endsWith(".secure")) {
mCodec->mFlags |= kFlagIsSecure;
mCodec->mFlags |= kFlagIsGrallocUsageProtected;
mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
}
mCodec->mOMX = omx;
mCodec->mOMXNode = omxNode;
// 调用 callback 通知 MediaCodec 完成阻塞调用
mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());
// 切换状态到 LoadedState
mCodec->changeState(mCodec->mLoadedState);
return true;
}
这里涉及的内容比较多:
- 创建 notify 对象,并且传入到 CodecObserver 对象中,CodecObserver 会把 OMX 发送回来的消息重新封装,再通过 notify message 转发给 ACodec,最后在不同状态中处理。这里有个是 mNodeGeneration 用于检查 OMX 消息及时性的,但是实际并未启用。这里有一点需要注意,这些 State 状态类都是 ACodec 的内部类,C++11之后内部类可以访问外部类的私有成员以及私有方法,所以虽然这些类并不是 ACodec 的友元,但是同样是可以调用 ACodec 所有方法的。
- 创建 OMXNode 之前,会先检查从 MediaCodec 层获取到的 MediaCodecInfo,如果没有这个信息将会报错,这里算是一个双重检查,防止强行越过 MediaCodecList 的检查;
- 调用 OMXClient 的方法获取 IOmx 的代理,并用该代理创建 IOmxNode 代理,传入参数为组件名称;
- 如果组件名称以 secure 结尾,那么说明需要创建安全组件,并且记录到 ACodec mFlags 成员中;
- 调用 onComponentAllocated 通知 MediaCodec 完成阻塞调用;
- 切换状态到
LoadedState
;
在看 LoadedState 的 stateEntered 方法之前,我们要先看下 BaseState 给出的 stateExited
方法,这里用到了 mStateGeneration
,用来记录 ACodec 当前的状态变化,在处理消息时,如果传来的消息 generation 不等于当前的generation,说明状态机发生错误,这和之前看到的部分是不一样的,具体什么情况会出现不一样我们后续再做了解。
void ACodec::BaseState::stateExited() {
++mCodec->mStateGeneration;
}
从上面的代码我们可以知道,每次状态切换,mStateGeneration数都会加 1 。
接下来看 LoadedState 的 stateEntered
: