一、引言:
nuplayer播放器是使用MediaCodec来进行编解码的,而OMX组件则是MediaCodec的解码核心,但是因为涉及的面太过底层,往往是芯片公司才会涉及到这一块,所以就做一个简单分析,对通路有个大致了解就行。
二、流程跟踪:
从MediaCodec的api来分析下流程:
1.构造函数:
MediaCodec::CreateByType()和MediaCodec::CreateByComponentName()只是对外初始化MediaCodec的不同接口,前者是以接收mimetype来进行初始化,后者则是通过确定的codec名字来实例化,在构造了MediaCodec之后最终都会调入到MediaCodec::init中;
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
2.MediaCodec::init分析:
status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {.../* 1.实例化ACodec */mCodec = new ACodec;.../* 2.注册hanlder用于消息处理 */mLooper->registerHandler(this);mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, id()));/* 3.处理kWhatInit消息 */sp<AMessage> msg = new AMessage(kWhatInit, id());msg->setString("name", name);msg->setInt32("nameIsType", nameIsType);if (nameIsType) {msg->setInt32("encoder", encoder);}sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}
先来看下ACodec的实例化,ACodec可以理解为Android对于解码器的封装层,对下衔接OMX解码组件,对上回调必要信息到MediaCodec:
ACodec::ACodec(): mQuirks(0),mNode(0),mSentFormat(false),mIsEncoder(false),mUseMetadataOnEncoderOutput(false),mFatalError(false),mShutdownInProgress(false),mExplicitShutdown(false),mEncoderDelay(0),mEncoderPadding(0),mRotationDegrees(0),mChannelMaskPresent(false),mChannelMask(0),mDequeueCounter(0),mStoreMetaDataInOutputBuffers(false),mMetaDataBuffersToSubmit(0),mRepeatFrameDelayUs(-1ll),mMaxPtsGapUs(-1ll),mTimePerFrameUs(-1ll),mTimePerCaptureUs(-1ll),mCreateInputBuffersSuspended(false),mTunneled(false) {mFastOutput = false;mHisiVdp = false;mIsVideo = false;mSaveES = false;mVideoFile = NULL;mAudioFile = NULL;mStats = false;sEnableLogcatV = false;char value[PROPERTY_VALUE_MAX];if (property_get("service.media.codec.logcat", value, "false")&& (!strcasecmp("true", value))) {sEnableLogcatV = true;}if (property_get("service.media.codec.savees", value, "false")&& (!strcasecmp("true", value))) {ALOGI("Will save ES streams");mSaveES = true;}if (property_get("service.media.codec.stats", value, "false")&& (!strcasecmp("true", value))) {mStats = true;}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;changeState(mUninitializedState);
}
ACodec构造最主要的操作是实例化了各种状态值,通过消息机制来对OMX进行操作。最后再来看下kWhatInit
消息的处理:
case kWhatInit:{uint32_t 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<AMessage> 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;}
这里的mCodec
即ACodec,看一下OMX组件初始化:
void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) {msg->setWhat(kWhatAllocateComponent);msg->setTarget(id());msg->post();
}
跟进消息:
case ACodec::kWhatAllocateComponent:
{onAllocateComponent(msg);handled = true;break;
}
bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {ALOGV("onAllocateComponent");CHECK(mCodec->mNode == NULL);/* 1.获取OMX的binder对象 */OMXClient client;CHECK_EQ(client.connect(), (status_t)OK);sp<IOMX> omx = client.interface(); .../* 2.找到底层支持的解码器 */OMXCodec::findMatchingCodecs(mime.c_str(),encoder, // createEncoderNULL, // matchComponentName0, // flags&matchingCodecs);.../* 3.申请omx组件 */status_t err = omx->allocateNode(componentName.c_str(), observer, &node); .../* 4.回调通知MediaCodec */{sp<AMessage> notify = mCodec->mNotify->dup();notify->setInt32("what", CodecBase::kWhatComponentAllocated);notify->setString("componentName", mCodec->mComponentName.c_str());notify->post();}/* 切换ACodec的状态 */mCodec->changeState(mCodec->mLoadedState);
}
这里需要注意下,OMX的Bn端在OMX.CPP中,看一下allocateNode实现:
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓
status_t OMX::allocateNode(const char *name, const sp<IOMXObserver> &observer, node_id *node) {Mutex::Autolock autoLock(mLock);*node = 0;/* 实例化instance */OMXNodeInstance *instance = new OMXNodeInstance(this, observer, name);OMX_COMPONENTTYPE *handle;/* 获取组件 */OMX_ERRORTYPE err = mMaster->makeComponentInstance(name, &OMXNodeInstance::kCallbacks,instance, &handle);if (err != OMX_ErrorNone) {ALOGE("FAILED to allocate omx component '%s'", name);instance->onGetHandleFailed();return UNKNOWN_ERROR;}*node = makeNodeID(instance);mDispatchers.add(*node, new CallbackDispatcher(instance));instance->setHandle(*node, handle);mLiveNodes.add(observer->asBinder(), instance);observer->asBinder()->linkToDeath(this);return OK;
}
这里有一个特别需要注意的点是,omx组件是可以由芯片厂商自行扩展的,所以,在OMXMaster
中会决定是加载Android原生的软解码还是芯片厂商的硬解码,我所调试的环境为海思平台,故加载的为海思插件。
3.configure分析:
status_t MediaCodec::configure(const sp<AMessage> &format,const sp<Surface> &nativeWindow,const sp<ICrypto> &crypto,uint32_t flags) {sp<AMessage> msg = new AMessage(kWhatConfigure, id());msg->setMessage("format", format);msg->setInt32("flags", flags);...sp<AMessage> response;status_t err = PostAndAwaitResponse(msg, &response);...
}
看下kWhatConfigure消息处理:
case kWhatConfigure:{...mCodec->initiateConfigureComponent(format);break;}
再看下MediaCodec中initiateConfigureComponent
函数的消息:
void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) {msg->setWhat(kWhatConfigureComponent);msg->setTarget(id());msg->post();
}
case ACodec::kWhatConfigureComponent:
{onConfigureComponent(msg);handled = true;break;
}
onConfigureComponent函数非常长,主要是为了给omx组件设置参数,这里就不去具体分析了。
4.start函数分析:
status_t MediaCodec::start() {sp<AMessage> msg = new AMessage(kWhatStart, id());sp<AMessage> response;return PostAndAwaitResponse(msg, &response);
}
case kWhatStart:{uint32_t replyID;CHECK(msg->senderAwaitsResponse(&replyID));if (mState == FLUSHED) {setState(STARTED);mCodec->signalResume();PostReplyWithError(replyID, OK);break;} else if (mState != CONFIGURED) {PostReplyWithError(replyID, INVALID_OPERATION);break;}mReplyID = replyID;setState(STARTING);mCodec->initiateStart();break;}
和之前的逻辑一样,MediaCodec对start的逻辑也是先更新自己维护的状态,然后再调用到ACodec中:
void ACodec::initiateStart() {(new AMessage(kWhatStart, id()))->post();
}
case ACodec::kWhatStart:{onStart();handled = true;break;}
void ACodec::LoadedState::onStart() {ALOGV("onStart");CHECK_EQ(mCodec->mOMX->sendCommand(mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),(status_t)OK);mCodec->changeState(mCodec->mLoadedToIdleState);
}
这里就会调用到下层OMX组件中,由组件来执行响应的操作了。MediaCodec对到OMX组件的流程比较绕,Android在这里弄的比较复杂,对于不是芯片厂商的开发人员而言会比较难理解一些,在这里对这些流程也只是一个简单跟踪,里面还有很多内容没有详细扩展,需要根据工作中的具体情况去做分析了。
如果你对音视频开发感兴趣,觉得文章对您有帮助,别忘了点赞、收藏哦!或者对本文的一些阐述有自己的看法,有任何问题,欢迎在下方评论区讨论!
本文福利, 免费领取C++音视频学习资料包、技术视频,内容包括(音视频开发,面试题,FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓