一、引言:
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, // createEncoder
NULL, // matchComponentName
0, // 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)↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓