MediaCodec对接到OMX的简单分析

一、引言:
nuplayer播放器是使用MediaCodec来进行编解码的,而OMX组件则是MediaCodec的解码核心,但是因为涉及的面太过底层,往往是芯片公司才会涉及到这一块,所以就做一个简单分析,对通路有个大致了解就行。

二、流程跟踪:
从MediaCodec的api来分析下流程:
在这里插入图片描述
1.构造函数:
MediaCodec::CreateByType()MediaCodec::CreateByComponentName()只是对外初始化MediaCodec的不同接口,前者是以接收mimetype来进行初始化,后者则是通过确定的codec名字来实例化,在构造了MediaCodec之后最终都会调入到MediaCodec::init中;

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实现:

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在这里弄的比较复杂,对于不是芯片厂商的开发人员而言会比较难理解一些,在这里对这些流程也只是一个简单跟踪,里面还有很多内容没有详细扩展,需要根据工作中的具体情况去做分析了。

  • 6
    点赞
  • 11
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
在Android中从MediaCodec读取数据并将其存储到AVFrame中,需要进行以下步骤: 1. 初始化AVFrame 在使用AVFrame之前,需要先初始化它。可以使用下面的代码初始化AVFrame: ``` AVFrame *frame = av_frame_alloc(); frame->format = AV_PIX_FMT_YUV420P; // 设置像素格式 frame->width = width; // 设置帧宽度 frame->height = height; // 设置帧高度 ``` 2. 从MediaCodec读取数据 可以使用MediaCodec的dequeueOutputBuffer()方法从MediaCodec读取输出缓冲区。以下是一个示例代码: ``` MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); int outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US); while (outputBufferIndex >= 0) { ByteBuffer outputBuffer = mediaCodec.getOutputBuffer(outputBufferIndex); if (outputBuffer != null) { // 将数据存储到AVFrame中 // ... // 释放输出缓冲区 mediaCodec.releaseOutputBuffer(outputBufferIndex, false); } outputBufferIndex = mediaCodec.dequeueOutputBuffer(bufferInfo, TIMEOUT_US); } ``` 在从MediaCodec读取数据后,可以将数据存储到AVFrame中。以下是一个示例代码: ``` byte[] yuvData = new byte[width * height * 3 / 2]; outputBuffer.get(yuvData); // 将数据存储到AVFrame中 frame->data[0] = yuvData; // Y数据 frame->data[1] = yuvData + width * height; // U数据 frame->data[2] = yuvData + width * height * 5 / 4; // V数据 // 设置AVFrame的时间戳 frame->pts = bufferInfo.presentationTimeUs * AV_TIME_BASE / 1000000; ``` 在存储完数据后,需要释放MediaCodec的输出缓冲区。最后,我们可以在循环中继续读取数据,直到读取完所有数据。 需要注意的是,在从MediaCodec读取数据时,需要根据实际情况进行一些设置,例如超时时间和输出缓冲区的标志位等。另外,需要根据实际情况设置AVFrame的时间戳。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值