Android 13 - Media框架(20)- ACodec(二)
这一节开始我们就来学习 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
:
void ACodec::LoadedState::stateEntered() {
ALOGV("[%s] Now Loaded", mCodec->mComponentName.c_str());
mCodec->mPortEOS[kPortIndexInput] =
mCodec->mPortEOS[kPortIndexOutput] = false;
mCodec->mInputEOSResult = OK;
mCodec->mDequeueCounter = 0;
mCodec->mMetadataBuffersToSubmit = 0;
mCodec->mRepeatFrameDelayUs = -1LL;
mCodec->mInputFormat.clear();
mCodec->mOutputFormat.clear();
mCodec->mBaseOutputFormat.clear();
mCodec->mGraphicBufferSource.clear();
if (mCodec->mShutdownInProgress) {
bool keepComponentAllocated = mCodec->mKeepComponentAllocated;
mCodec->mShutdownInProgress = false;
mCodec->mKeepComponentAllocated = false;
onShutdown(keepComponentAllocated);
}
mCodec->mExplicitShutdown = false;
mCodec->processDeferredMessages();
}
LoadedState::stateEntered 会对编解码过程中记录信息的成员变量进行重置,后面是关于 shutdown 的处理流程,这里暂时不看。
到这里我们先做一个小结:MediaCodec 创建完成后,ACodec 最终会进入到 LoadedState,这个状态代表了内部的 OMX 组件已经创建完成,UninitializedState 则表示OMX组件还未创建或者是已经销毁的状态。
3、initiateConfigureComponent
组件创建完成后,就要开始配置组件了,这时候状态在 LoadedState,所以我们去这个状态下找对应的处理:
bool ACodec::LoadedState::onConfigureComponent(
const sp<AMessage> &msg) {
ALOGV("onConfigureComponent");
CHECK(mCodec->mOMXNode != NULL);
status_t err = OK;
// 检查 mime type,调用 configureCodec 方法
AString mime;
if (!msg->findString("mime", &mime)) {
err = BAD_VALUE;
} else {
err = mCodec->configureCodec(mime.c_str(), msg);
}
if (err != OK) {
ALOGE("[%s] configureCodec returning error %d",
mCodec->mComponentName.c_str(), err);
mCodec->signalError(OMX_ErrorUndefined, makeNoSideEffectStatus(err));
return false;
}
// 调用 Callback
mCodec->mCallback->onComponentConfigured(mCodec->mInputFormat, mCodec->mOutputFormat);
return true;
}
onConfigureComponent 会先去检查 mime type,如果没有将会返回error,如果有则会再调用 ACodec 的 configureCodec 方法做组件的配置,这个方法会比较复杂,我们留到下一节来讲,最后会调用 callback 完成 MediaCodec 阻塞调用,同时把 input format 和 output format 回传给 MediaCodec,需要注意的是这里的 output format 并不一定是准确的,可能是 omx 设定的默认值,在decoder解出相关序列信息之后会把真正的 output format 再回传回来。
本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。 如若内容造成侵权/违法违规/事实不符,请联系我的编程经验分享网邮箱:veading@qq.com进行投诉反馈,一经查实,立即删除!