代码编织梦想

一、引言:

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↓↓↓↓↓↓见下面↓↓文章底部点击免费领取↓↓

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/m0_60259116/article/details/127303193

播放媒体文件找到对应解码器组件的过程_delicious_da的博客-爱代码爱编程

以下代码流程为一个媒体文件解码时找到对应插件的流程: 函数 status_t NuPlayer::instantiateDecoder(boolaudio, sp<DecoderBase> *decoder) 函数初始化创建解码器   status_t NuPlayer::instantiateDecoder(boolaudio, sp&l

Android MediaCodec学习笔记-爱代码爱编程

目录 1. 模块介绍 1.1 基本概念 1.2 软件层次 2. 工作流程 2.1 API使用 2.3 控制流 2.3.1.createByType 2.3.2.configure 2.3.3.start 2.3.4.queue/dequeue buffer 2.3.5.stop 2.3.6.release 2.4.数据流

【mediacodec】MediaRecorder--MediaCodec-爱代码爱编程

文章目录 前言查看代码重点关注log1 应用层1.1 创建1.2 获取1.3 设置VideoSource1.4 设置输出模式1.5 设置文件名1.6 设置编码方式2 获取video codec表——mediaprofile3 MediaRecorder: prepare3.1 创建MediaCodecSource(重点看MediaCodec的log

【学习】从零开发的Android音视频开发(13)——MediaCodec到OMX框架过程及其硬解码-爱代码爱编程

MediaCodec到OMX框架过程 在讲NuPlayer时,NuPlayer解码部分会创建MediaCodec,并且最终到达OMX框架,先看MediaCodec的init函数 从init函数中可以看到,首先创建了ACodec,并且初始化了ALooper、AMessage,由于ACodec继承自AHandler,那么一套消息机制就有了。最后发送kWha

omx组件消息回调机制详解-爱代码爱编程

一、引入: 在前面分享MediaCodec和ACodec状态机的时候(MediaCodec(native)状态机分析 和ACodec状态机分析),有讲过各个状态机都是根据底层OMX状态机来的,而OMX的状态想要被上层ACod