[Android Video Framework] MediaCodec.cpp详解

版权声明:本文为博主原创文章,未经博主允许不得转载。 https://blog.csdn.net/u012188065/article/details/86716513

MediaCodec是Android Video Framework中一个很重要的类,可以被NuPlayer直接使用
也可以被MediaCodec.java通过JNI的方式直接调用,可以赋予app很高的自由性,
是多媒体开发中的一个很重要的类

1、创建MediaCodec

创建MediaCodec有两种方式,一种是 CreateByType, 根据应用或NuPlayer传递下来的MIME来创建;
另一种是 CreateByComponentName,根据传递下来的ComponentName来创建。
具体方法如下:

CreateByType:

sp<MediaCodec> MediaCodec::CreateByType(
        const sp<ALooper> &looper, const AString &mime, bool encoder, status_t *err, pid_t pid,
        uid_t uid) {
    VTRACE_CALL();
    sp<MediaCodec> 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> MediaCodec::CreateByComponentName(
        const sp<ALooper> &looper, const AString &name, status_t *err, pid_t pid, uid_t uid) {
    VTRACE_CALL();
    sp<MediaCodec> 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 消息,并等待返回

1.1 创建ACodec

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<CodecBase> 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, 此时我们应当记住两点:

扫描二维码关注公众号,回复: 5656705 查看本文章
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呢,我们继续往下看。

1.2、 与 ACodec, ACodecBufferChannel等类建立消息关联机制

status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
……
    mCodec->setCallback(
            std::unique_ptr<CodecBase::CodecCallback>(
                    new CodecCallback(new AMessage(kWhatCodecNotify, this))));
    mBufferChannel = mCodec->getBufferChannel();
    mBufferChannel->setCallback(
            std::unique_ptr<CodecBase::BufferCallback>(
                    new BufferCallback(new AMessage(kWhatCodecNotify, this))));
……

1.2.1 mCodec->setCallback

上文可知, mCodec即 ACodec
给ACodec设置回调函数,将 CodecCallback 类设置给 ACodec中的 mCallback
见CodecBase.h中的setCallback函数

    inline void setCallback(std::unique_ptr<CodecCallback> &&callback) {
        mCallback = std::move(callback);
    }

而CodecCallback的传入参数为 kWhatCodecNotify 类型的AMessage,即 CodecCallback中的 mNotify 为 kWhatCodecNotify 类型的AMessage, 见 CodecCallback 构造函数:

CodecCallback::CodecCallback(const sp<AMessage> &notify) : mNotify(notify) {}

至此,我们明白了ACodec中调用 mCallback 类中的方法,即调用 CodecCallback类中的方法
而 mNotify为MediaCodec中 kWhatCodecNotify 类型的AMessage,post后MediaCodec会去处理,
至此建立了 ACodec -> CodecCallback -> MediaCodec 通道。

1.2.2 mCodec->getBufferChannel

查看ACodec::getBufferChannel 方法

std::shared_ptr<BufferChannelBase> ACodec::getBufferChannel() {
    if (!mBufferChannel) {
        mBufferChannel = std::make_shared<ACodecBufferChannel>(
                new AMessage(kWhatInputBufferFilled, this),
                new AMessage(kWhatOutputBufferDrained, this));
    }
    return mBufferChannel;
}

此方法很简单,创建 一 ACodecBufferChannel 并返回,ACodecBufferChannel的两个参数为
kWhatInputBufferFilled和kWhatOutputBufferDrained类型的消息,分别被 mInputBufferFilled 和 mOutputBufferDrained 接收
详见ACodecBufferChannel.cpp中的ACodecBufferChannel构造函数

ACodecBufferChannel::ACodecBufferChannel(
        const sp<AMessage> &inputBufferFilled, const sp<AMessage> &outputBufferDrained)
    : mInputBufferFilled(inputBufferFilled),
      mOutputBufferDrained(outputBufferDrained),
      mHeapSeqNum(-1) {
}

至此可知, MediaCodec类中的 mBufferChannel 和 ACodec中的 mBufferChannel是同一个东西,为 ACodecBufferChannel
建立了 MediaCodec–> ACodecBufferChannel 和 ACodec–> ACodecBufferChannel 通道

1.2.3 mBufferChannel->setCallback

给ACodecBufferChannel设置回调函数,将 CodecBase::BufferCallback 设置给 ACodecBufferChannel的mCallback
见CodecBase.h中 BufferChannelBase的 setCallback 函数:

    inline void setCallback(std::unique_ptr<CodecBase::BufferCallback> &&callback) {
        mCallback = std::move(callback);
    }

而 BufferCallback 的入参也为 kWhatCodecNotify 类型的消息,被 BufferCallback的mNotify接收

BufferCallback::BufferCallback(const sp<AMessage> &notify)
    : mNotify(notify) {}

post mNotify后,会被MediaCodec的onMessageReceived方法接收并处理
至此建立了 ACodecBufferChannel --> BufferCallback --> MediaCodec 的通路。

MediaCodec, ACodec, ACodecBufferChannel, CodecCallback 以及 BufferCallback 类的关系如下:
在这里插入图片描述
有点像五星~ 容易联想起五星出东方利中国。

1.3 发送 kWhatInit 消息

发送 kWhatInit 消息,并等待返回

status_t MediaCodec::init(const AString &name, bool nameIsType, bool encoder) {
……
    sp<AMessage> 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<MediaResource> 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<AMessage> 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<AMessage> &msg) {
    switch (msg->what()) {
    ……
        case kWhatInit:
        {
            sp<AReplyToken> 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;
        }
        ……

此时MediaCodec变为了 INITIALIZING, 正在初始化的状态,
此处主要看ACodec::initiateAllocateComponent 函数

void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) {
    msg->setWhat(kWhatAllocateComponent);
    msg->setTarget(this);
    msg->post();
}

发出了 kWhatAllocateComponent类型的消息,注意当前我们的ACodec为 未初始化状态,故去
ACodec::UninitializedState::onMessageReceived函数中处理此消息

bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &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<AMessage> notify(mNotify->dup());
    notify->setInt32("what", kWhatComponentAllocated);
    notify->setString("componentName", componentName);
    notify->post();
}

发出的消息被 MediaCodec::onMessageReceived接收,处理 kWhatCodecNotify分支中的 kWhatComponentAllocated 消息

void MediaCodec::onMessageReceived(const sp<AMessage> &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。

2、配置MediaCodec

未完待续……

猜你喜欢

转载自blog.csdn.net/u012188065/article/details/86716513
今日推荐