ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

Android 13 - Media框架(19)- ACodec(一)

2026/9/15 5:47:40 拓冰建站 浏览量
Android 13 - Media框架(19)- ACodec(一)

这一节我们将会一起了解 ACodec 的设计方式,在看具体的实现细节前我们要先了解它内部的状态转换机制,这也是ACodec的核心难点之一。

1、AHierarchicalStateMachine

ACodec 封装了OMX调用,我们在OpenMax(三)一节中了解到 OMX
有很多状态(Loaded、Idle、Executing),自然 ACodec 也会有很多状态。先来看 ACodec 的头文件:

struct ACodec : public AHierarchicalStateMachine, public CodecBase

可以看到 ACodec 除了继承于 CodecBase 外,还继承于 AHierarchicalStateMachine,翻译过来就是分层状态机,具体为什么叫分层状态机可能是因为在不同的状态(层次)下,每个函数都会对应不同的动作。

struct AHierarchicalStateMachine {AHierarchicalStateMachine();protected:virtual ~AHierarchicalStateMachine();virtual void handleMessage(const sp<AMessage> &msg);// Only to be called in response to a message.void changeState(const sp<AState> &state);private:sp<AState> mState;DISALLOW_EVIL_CONSTRUCTORS(AHierarchicalStateMachine);
};

AHierarchicalStateMachine 只有一个公有的构造函数,和几个访问权限为 protected 的方法:

  • changeState:用于切换状态;
  • handleMessage:用于处理消息;

AHierarchicalStateMachine 中还维护了一个 AState 对象,这个对象就代表着当前状态机处在什么状态下,AState 定义如下:

struct AState : public RefBase {AState(const sp<AState> &parentState = NULL);sp<AState> parentState();protected:virtual ~AState();virtual void stateEntered();virtual void stateExited();virtual bool onMessageReceived(const sp<AMessage> &msg) = 0;private:friend struct AHierarchicalStateMachine;sp<AState> mParentState;DISALLOW_EVIL_CONSTRUCTORS(AState);
};

这个类就是不同状态的基类了,所有的状态都需要继承自 AState,实现其 onMessageReceived 方法(注意这里不要和 AHandler 的方法混淆了)。AState 主要有三个方法:

  • stateEntered:进入某个状态需要执行的动作;
  • stateExited:退出某个状态需要执行的动作;
  • onMessageReceived:具体的状态下的事件处理方法;

前面两个方法 AState 给出了默认实现,也就是说如果不覆写,那么进入/退出状态将不不会有任何动作。

了解 AState 之后我们再来看 AHierarchicalStateMachine 给出的 changeState 和 handleMessage 的实现:

void AHierarchicalStateMachine::handleMessage(const sp<AMessage> &msg) {sp<AState> save = mState;sp<AState> cur = mState;while (cur != NULL && !cur->onMessageReceived(msg)) {// If you claim not to have handled the message you shouldn't// have called setState...CHECK(save == mState);cur = cur->parentState();}if (cur != NULL) {return;}ALOGW("Warning message %s unhandled in root state.",msg->debugString().c_str());
}

从以上代码我们可以了解到,状态机处理消息,其实调用的就是 AState 的 onMessageReceived 方法。

再来看状态切换方法 changeState :

void AHierarchicalStateMachine::changeState(const sp<AState> &state) {// 如果和当前状态相同则直接退出if (state == mState) {// Quick exit for the easy case.return;}// 获取当前状态,并且将前一个状态依次添加到一个容器中Vector<sp<AState> > A;sp<AState> cur = mState;for (;;) {A.push(cur);if (cur == NULL) {break;}cur = cur->parentState();}// 获取要切换的状态,将要切换的状态的前一个状态依次加入到容器中Vector<sp<AState> > B;cur = state;for (;;) {B.push(cur);if (cur == NULL) {break;}cur = cur->parentState();}// 将两个容器相同的尾部移除// Remove the common tail.while (A.size() > 0 && B.size() > 0 && A.top() == B.top()) {A.pop();B.pop();}// 切换状态mState = state;// 调用老状态的 退出方法for (size_t i = 0; i < A.size(); ++i) {A.editItemAt(i)->stateExited();}// 调用新状态的 进入方法for (size_t i = B.size(); i > 0;) {i--;B.editItemAt(i)->stateEntered();}
}

这里写的比较复杂,目前看来这边属于冗余设计,主要是调用最下面的 stateExitedstateEntered 方法。在我们想要切换状态时,调用状态机的 changeState 方法,传入想要切换的状态的对象,替换掉状态机内 mState 指向内容,即可完成切换。

2、ACodec

当有消息发送给 ACodec 来处理时,ACodec 调用自身的 onMessageReceived 方法,内部调用状态机的 handleMessage 方法,即可调用到不同状态下的实现了

    virtual void onMessageReceived(const sp<AMessage> &msg) {handleMessage(msg);}

请添加图片描述

ACodec 中有如下状态,均以内部类的方式实现:

    struct BaseState;struct UninitializedState;struct LoadedState;struct LoadedToIdleState;struct IdleToExecutingState;struct ExecutingState;struct OutputPortSettingsChangedState;struct ExecutingToIdleState;struct IdleToLoadedState;struct FlushingState;struct DeathNotifier;

BaseState 是其他状态的基类,其继承于 AState,因此我们大概可以猜到,ACodec 切换状态时调用 changeState 时,传入的参数就是这些状态类对象,最后 ACodec 的消息也由这些状态类来最终执行。

BaseState 持有一个 ACodec 对象指针,使得它与相关的派生类状态可以操作 ACodec;BaseState 还定义有一些 protected 方法,这些方法是一些默认实现可供子类使用,如果需要自定义子类也可以覆写这些方法;BaseState 还实现有一些私有方法,这些方法是各个状态共同使用的部分。

struct ACodec::BaseState : public AState {explicit BaseState(ACodec *codec, const sp<AState> &parentState = NULL);protected:enum PortMode {KEEP_BUFFERS,RESUBMIT_BUFFERS,FREE_BUFFERS,};ACodec *mCodec;virtual PortMode getPortMode(OMX_U32 portIndex);virtual void stateExited();virtual bool onMessageReceived(const sp<AMessage> &msg);virtual bool onOMXEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);virtual void onOutputBufferDrained(const sp<AMessage> &msg);virtual void onInputBufferFilled(const sp<AMessage> &msg);void postFillThisBuffer(BufferInfo *info);void maybePostExtraOutputMetadataBufferRequest() {if (!mPendingExtraOutputMetadataBufferRequest) {(new AMessage(kWhatSubmitExtraOutputMetadataBuffer, mCodec))->post();mPendingExtraOutputMetadataBufferRequest = true;}}private:// Handles an OMX message. Returns true iff message was handled.bool onOMXMessage(const sp<AMessage> &msg);// Handles a list of messages. Returns true iff messages were handled.bool onOMXMessageList(const sp<AMessage> &msg);// returns true iff this message is for this component and the component is alivebool checkOMXMessage(const sp<AMessage> &msg);bool onOMXEmptyBufferDone(IOMX::buffer_id bufferID, int fenceFd);bool onOMXFillBufferDone(IOMX::buffer_id bufferID,size_t rangeOffset, size_t rangeLength,OMX_U32 flags,int64_t timeUs,int fenceFd);virtual bool onOMXFrameRendered(int64_t mediaTimeUs, nsecs_t systemNano);void getMoreInputDataIfPossible();bool mPendingExtraOutputMetadataBufferRequest;DISALLOW_EVIL_CONSTRUCTORS(BaseState);
};