MediaCodec是Android Video Framework中一个很重要的类,可以被NuPlayer直接使用
也可以被MediaCodec.java通过JNI的方式直接调用,可以赋予app很高的自由性,
是多媒体开发中的一个很重要的类
创建MediaCodec有两种方式,一种是 CreateByType, 根据应用或NuPlayer传递下来的MIME来创建;
另一种是 CreateByComponentName,根据传递下来的ComponentName来创建。
具体方法如下:
CreateByType:
sp MediaCodec::CreateByType(
const sp &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
uid_t uid) {
VTRACE_CALL();
sp codec = new MediaCodec(looper, pid, uid);
const status_t ret = codec->init(mime, true /* nameIsType */, encoder);
if (err != NULL) {
*err = ret;
}
return ret == OK ? codec : NULL; // NULL deallocates codec.
}
CreateByComponentName:
// static
sp MediaCodec::CreateByComponentName(
const sp &looper, const AString &name, status_t *err, pid_t pid, uid_t uid) {
VTRACE_CALL();
sp codec = new MediaCodec(looper, pid, uid);
const status_t ret = codec->init(name, false /* nameIsType */, false /* encoder */);
if (err != NULL) {
*err = ret;
}
return ret == OK ? codec : NULL; // NULL deallocates codec.
}
这两个方法较简单,
1、 调用 MediaCodec 构造函数
2、调用 init 函数
构造函数很简单,没有什么逻辑,我们分析一下 init 函数
MediaCodec::init 函数中逻辑较多
主要分为以下几步:
1、 创建ACodec
2、 与 ACodec, ACodecBufferChannel等类建立消息关联机制
3、发送 kWhatInit 消息,并等待返回
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
VTRACE_METHOD();
mResourceManagerService->init();
// save init parameters for reset
mInitName = name;
mInitNameIsType = nameIsType;
mInitIsEncoder = encoder;
// Current video decoders do not return from OMX_FillThisBuffer
// quickly, violating the OpenMAX specs, until that is remedied
// we need to invest in an extra looper to free the main event
// queue.
mCodec = GetCodecBase(name, nameIsType); //创建ACodec
if (mCodec == NULL) {
return NAME_NOT_FOUND;
}
……
详解查看 GetCodecBase 函数:
sp MediaCodec::GetCodecBase(const AString &name, bool nameIsType) {
// at this time only ACodec specifies a mime type.
if (nameIsType || name.startsWithIgnoreCase("omx.")) {
return new ACodec;
} else if (name.startsWithIgnoreCase("android.filter.")) {
return new MediaFilter;
} else {
return NULL;
}
}
我们使用MediaCodec主要是创建编解码的Codec,
目前很少用到滤波器 filter,故我们着重分析MediaCodec -> ACodec及ACodec向下的流程,不分析 MediaFilter的流程。
查看ACodec 构造函数
ACodec::ACodec()
: mSampleRate(0),
mNodeGeneration(0),
mUsingNativeWindow(false),
mNativeWindowUsageBits(0),
mLastNativeWindowDataSpace(HAL_DATASPACE_UNKNOWN),
mIsVideo(false),
mIsEncoder(false),
mFatalError(false),
mShutdownInProgress(false),
mExplicitShutdown(false),
mIsLegacyVP9Decoder(false),
mEncoderDelay(0),
mEncoderPadding(0),
mRotationDegrees(0),
mChannelMaskPresent(false),
mChannelMask(0),
mDequeueCounter(0),
mMetadataBuffersToSubmit(0),
mNumUndequeuedBuffers(0),
mRepeatFrameDelayUs(-1ll),
mMaxPtsGapUs(-1ll),
mMaxFps(-1),
mFps(-1.0),
mCaptureFps(-1.0),
mCreateInputBuffersSuspended(false),
mLatency(0),
mTunneled(false),
mDescribeColorAspectsIndex((OMX_INDEXTYPE)0),
mDescribeHDRStaticInfoIndex((OMX_INDEXTYPE)0),
mStateGeneration(0),
mVendorExtensionsStatus(kExtensionsUnchecked) {
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);
mTrebleFlag = false;
}
ACodec构造函数创建了 UninitializedState, LoadedState 等9个类,用来控制和管理 component的不同状态。
并通过
changeState(mUninitializedState);
将ACodec的状态设置为 mUninitializedState, 此时我们应当记住两点:
1、刚创建的ACodec是 mUninitializedState, 未初始化的状态;
2、MediaCodec中的mCodec, 就是ACodec,调用mCodec的方法,我们应该去ACodec类中查找。
至此,我们建立了MediaCodec调用ACodec的方法,即通过MediaCodec类中的 mCodec 成员来调用。
在mUninitializedState状态下,MediaCodec调用ACodec的方法,或者底层回调ACodec的方法,
我们均应该从 ACodec::UninitializedState 类中查找,如果没有,我们再去父类ACodec::BaseState中查找。
只有了解了正确的状态机制,我们才能正确的跟踪ACodec的代码。
总结: 建立了 MediaCodec --> ACodec 通道
MediaCodec通过mCodec去调用ACodec类,并根据ACodec类的不同状态,调用到不同类中的函数。
那么ACodec是如何回调MediaCodec呢,我们继续往下看。
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
……
mCodec->setCallback(
std::unique_ptr(
new CodecCallback(new AMessage(kWhatCodecNotify, this))));
mBufferChannel = mCodec->getBufferChannel();
mBufferChannel->setCallback(
std::unique_ptr(
new BufferCallback(new AMessage(kWhatCodecNotify, this))));
……
上文可知, mCodec即 ACodec
给ACodec设置回调函数,将 CodecCallback 类设置给 ACodec中的 mCallback
见CodecBase.h中的setCallback函数
inline void setCallback(std::unique_ptr &&callback) {
mCallback = std::move(callback);
}
而CodecCallback的传入参数为 kWhatCodecNotify 类型的AMessage,即 CodecCallback中的 mNotify 为 kWhatCodecNotify 类型的AMessage, 见 CodecCallback 构造函数:
CodecCallback::CodecCallback(const sp ¬ify) : mNotify(notify) {}
至此,我们明白了ACodec中调用 mCallback 类中的方法,即调用 CodecCallback类中的方法
而 mNotify为MediaCodec中 kWhatCodecNotify 类型的AMessage,post后MediaCodec会去处理,
至此建立了 ACodec -> CodecCallback -> MediaCodec 通道。
查看ACodec::getBufferChannel 方法
std::shared_ptr ACodec::getBufferChannel() {
if (!mBufferChannel) {
mBufferChannel = std::make_shared(
new AMessage(kWhatInputBufferFilled, this),
new AMessage(kWhatOutputBufferDrained, this));
}
return mBufferChannel;
}
此方法很简单,创建 一 ACodecBufferChannel 并返回,ACodecBufferChannel的两个参数为
kWhatInputBufferFilled和kWhatOutputBufferDrained类型的消息,分别被 mInputBufferFilled 和 mOutputBufferDrained 接收
详见ACodecBufferChannel.cpp中的ACodecBufferChannel构造函数
ACodecBufferChannel::ACodecBufferChannel(
const sp &inputBufferFilled, const sp &outputBufferDrained)
: mInputBufferFilled(inputBufferFilled),
mOutputBufferDrained(outputBufferDrained),
mHeapSeqNum(-1) {
}
至此可知, MediaCodec类中的 mBufferChannel 和 ACodec中的 mBufferChannel是同一个东西,为 ACodecBufferChannel
建立了 MediaCodec–> ACodecBufferChannel 和 ACodec–> ACodecBufferChannel 通道
给ACodecBufferChannel设置回调函数,将 CodecBase::BufferCallback 设置给 ACodecBufferChannel的mCallback
见CodecBase.h中 BufferChannelBase的 setCallback 函数:
inline void setCallback(std::unique_ptr &&callback) {
mCallback = std::move(callback);
}
而 BufferCallback 的入参也为 kWhatCodecNotify 类型的消息,被 BufferCallback的mNotify接收
BufferCallback::BufferCallback(const sp ¬ify)
: mNotify(notify) {}
post mNotify后,会被MediaCodec的onMessageReceived方法接收并处理
至此建立了 ACodecBufferChannel --> BufferCallback --> MediaCodec 的通路。
MediaCodec, ACodec, ACodecBufferChannel, CodecCallback 以及 BufferCallback 类的关系如下:
有点像五星~ 容易联想起五星出东方利中国。
发送 kWhatInit 消息,并等待返回
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
……
sp msg = new AMessage(kWhatInit, this);
msg->setString("name", name);
msg->setInt32("nameIsType", nameIsType);
if (nameIsType) {
msg->setInt32("encoder", encoder);
}
if (mAnalyticsItem != NULL) {
if (nameIsType) {
// name is the mime type
mAnalyticsItem->setCString(kCodecMime, name.c_str());
} else {
mAnalyticsItem->setCString(kCodecCodec, name.c_str());
}
mAnalyticsItem->setCString(kCodecMode, mIsVideo ? "video" : "audio");
if (nameIsType)
mAnalyticsItem->setInt32(kCodecEncoder, encoder);
}
status_t err;
Vector resources;
MediaResource::Type type =
secureCodec ? MediaResource::kSecureCodec : MediaResource::kNonSecureCodec;
MediaResource::SubType subtype =
mIsVideo ? MediaResource::kVideoCodec : MediaResource::kAudioCodec;
resources.push_back(MediaResource(type, subtype, 1));
for (int i = 0; i <= kMaxRetry; ++i) {
if (i > 0) {
// Don't try to reclaim resource for the first time.
if (!mResourceManagerService->reclaimResource(resources)) {
break;
}
}
sp response;
err = PostAndAwaitResponse(msg, &response);
if (!isResourceError(err)) {
break;
}
}
VTRACE_STRING(mIsVideo? "type: video" : "type: audio");
return err;
}
kWhatInit 消息发出后,会被 MediaCodec::onMessageReceived 函数接收并处理
void MediaCodec::onMessageReceived(const sp &msg) {
switch (msg->what()) {
……
case kWhatInit:
{
sp replyID;
CHECK(msg->senderAwaitsResponse(&replyID));
if (mState != UNINITIALIZED) {
PostReplyWithError(replyID, INVALID_OPERATION);
break;
}
mReplyID = replyID;
setState(INITIALIZING); //注意状态切换
AString name;
CHECK(msg->findString("name", &name));
int32_t nameIsType;
int32_t encoder = false;
CHECK(msg->findInt32("nameIsType", &nameIsType));
if (nameIsType) {
CHECK(msg->findInt32("encoder", &encoder));
}
sp format = new AMessage;
if (nameIsType) {
format->setString("mime", name.c_str());
format->setInt32("encoder", encoder);
} else {
format->setString("componentName", name.c_str());
}
mCodec->initiateAllocateComponent(format);
break;
}
……
此时MediaCodec变为了 INITIALIZING, 正在初始化的状态,
此处主要看ACodec::initiateAllocateComponent 函数
void ACodec::initiateAllocateComponent(const sp &msg) {
msg->setWhat(kWhatAllocateComponent);
msg->setTarget(this);
msg->post();
}
发出了 kWhatAllocateComponent类型的消息,注意当前我们的ACodec为 未初始化状态,故去
ACodec::UninitializedState::onMessageReceived函数中处理此消息
bool ACodec::UninitializedState::onMessageReceived(const sp &msg) {
bool handled = false;
switch (msg->what()) {
case ACodec::kWhatSetup:
{
onSetup(msg);
handled = true;
break;
}
case ACodec::kWhatAllocateComponent:
{
onAllocateComponent(msg);
handled = true;
break;
}
……
再看 onAllocateComponent 函数,主要做了三件事
1、 err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
根据MediaCodec传的MIME查找到具体的componentName,或者 MediaCodec直接传过来的componentName
分配component,即创建了底层真正的codec
2、mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());
根据五星图,可知调用至 CodecCallback::onComponentAllocated
void CodecCallback::onComponentAllocated(const char *componentName) {
sp notify(mNotify->dup());
notify->setInt32("what", kWhatComponentAllocated);
notify->setString("componentName", componentName);
notify->post();
}
发出的消息被 MediaCodec::onMessageReceived接收,处理 kWhatCodecNotify分支中的 kWhatComponentAllocated 消息
void MediaCodec::onMessageReceived(const sp &msg) {
VTRACE_METHOD();
switch (msg->what()) {
case kWhatCodecNotify:
{
int32_t what;
CHECK(msg->findInt32("what", &what));
switch (what) {
……
case kWhatComponentAllocated:
{
CHECK_EQ(mState, INITIALIZING);
setState(INITIALIZED);
mFlags |= kFlagIsComponentAllocated;
CHECK(msg->findString("componentName", &mComponentName));
if (mComponentName.c_str()) {
mAnalyticsItem->setCString(kCodecCodec, mComponentName.c_str());
}
if (mComponentName.startsWith("OMX.google.")) {
mFlags |= kFlagUsesSoftwareRenderer;
} else {
mFlags &= ~kFlagUsesSoftwareRenderer;
}
MediaResource::Type resourceType;
if (mComponentName.endsWith(".secure")) {
mFlags |= kFlagIsSecure;
resourceType = MediaResource::kSecureCodec;
mAnalyticsItem->setInt32(kCodecSecure, 1);
} else {
mFlags &= ~kFlagIsSecure;
resourceType = MediaResource::kNonSecureCodec;
mAnalyticsItem->setInt32(kCodecSecure, 0);
}
if (mIsVideo) {
// audio codec is currently ignored.
addResource(resourceType, MediaResource::kVideoCodec, 1);
}
(new AMessage)->postReply(mReplyID);
break;
}
……
此处将 MediaCodec的状态从 INITIALIZING转换为 INITIALIZED,
mFlags并置上已分配的标志 kFlagIsComponentAllocated
并通过 (new AMessage)->postReply(mReplyID); 发送 kWhatInit 消息回执,
通知MediaCodec的 kWhatInit 消息已经处理完毕,可以继续向下执行。
3、mCodec->changeState(mCodec->mLoadedState);
将ACodec的状态设置为 LoadedState, 之后去 ACodec::LoadedState 查找对应方法或处理消息。
至此完整的MediaCodec已经创建完成,此时
1、MediaCodec状态为 INITIALIZED
2、ACodec的状态为 LoadedState
3、真正的codec已经被分配
简单流程如下:
解码工作第一步准备工作已经完成,下一步就是配置已经创建好的codec。
未完待续……