关注公众号免费阅读全文,进入音视频开发技术分享群!

这一节我们将会一起了解 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 alive
bool 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);
};

Android 13 - Media框架(19)- ACodec(一)的更多相关文章

  1. 简析Android 兼容性测试框架CTS使用

    一.什么是兼容性测试? 1)为用户提供最好的用户体验,让更多高质量的APP可以顺利的运行在此平台上 2)让程序员能为此平台写更多的高质量的应用程序 3)可以更好的利用Android应用市场 二.CTS ...

  2. 15类Android通用流行框架

    15类Android通用流行框架 Android流行框架 缓存 DiskLruCache Java实现基于LRU的磁盘缓存 图片加载 Android Universal Image Loader 一个 ...

  3. android开源项目框架大全:

    android开源项目框架大全: 1.多页切换TabHost9 高仿网易云音乐客户端的Home页面切换Tabhost 高仿网易云音乐客户端的Home页面切换Tabhost,并且三角形是透明的,实现方式 ...

  4. 各种Android UI开源框架 开源库

    各种Android UI开源框架 开源库 转 https://blog.csdn.net/zhangdi_gdk2016/article/details/84643668 自己总结的Android开源 ...

  5. 25类Android常用开源框架

    1.图片加载,缓存,处理 框架名称 功能描述 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库,已过时 Picasso 一个强大的图片下载与缓存的库 F ...

  6. [Android]Android端ORM框架——RapidORM(v2.0)

    以下内容为原创,欢迎转载,转载请注明 来自天天博客:http://www.cnblogs.com/tiantianbyconan/p/5626716.html [Android]Android端ORM ...

  7. 15 个 Android 通用流行框架大全(转)

    1. 缓存 DiskLruCache    Java实现基于LRU的磁盘缓存 2.图片加载 Android Universal Image Loader 一个强大的加载,缓存,展示图片的库 Picas ...

  8. Android 通用流行框架

    原文出处: http://android.jobbole.com/83028/ 1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 名称 描述 Andro ...

  9. 经受时间沉淀的15 个 Android 通用流行框架大全

    1. 缓存 名称描述 DiskLruCache: Java实现基于LRU的磁盘缓存 2.图片加载 名称描述 Android    Universal Image Loader 一个强大的加载,缓存,展 ...

  10. Android通用流行框架大全

    1. 缓存 名称 描述 DiskLruCache Java实现基于LRU的磁盘缓存 2.图片加载 名称 描述 Android Universal Image Loader 一个强大的加载,缓存,展示图 ...

随机推荐

  1. openGauss资源池化开发者入门指南(一)

    openGauss资源池化开发者入门指南(一) 一.内容简介 openGauss 资源池化是 openGauss 推出的一种新型的集群架构.通过 DMS 和 DSS 组件,实现集群中多个节点的底层存储 ...

  2. 这些Git事故灾难, 你经历过几个?

    前言 关于Git, 相信大家最常用的就是pull和push. 但随着协作规模的提升, 遇到的问题也会越来越多. 本篇文章并不科普一些命令的详细用法, 更多的是分享在工作中遇到的Git场景问题以及踩过的 ...

  3. 说说对 Node 中的 Buffer 的理解?应用场景?

    一.是什么 在Node应用中,需要处理网络协议.操作数据库.处理图片.接收上传文件等,在网络流和文件的操作中,要处理大量二进制数据,而Buffer就是在内存中开辟一片区域(初次初始化为8KB),用来存 ...

  4. 云原生之在kubernetes集群下部署mysql应用

    一.Mysql介绍 数据库(Database)是按照数据结构来组织.存储和管理数据的仓库.MySQL是一种开源的关系型数据库管理系统,可将数据保存在不同的表中,而不是将所有数据放在一个大的仓库内,从而 ...

  5. etcd 历史版本回溯的方法

    在使用 etcd 作为配置存储或者其他的场景,如果因为误操作对其中 key 的值进行了修改,如果想要找回原来的值,可以利用 etcd 的版本机制进行回溯找回以前的值.在具体操作之前,我们首先获取一下 ...

  6. Java面试题:请谈谈对ThreadLocal的理解?

    ThreadLocal是一种特殊的变量存储机制,它提供了一种方式,可以在每个线程中保存数据,而不会受到其他线程的影响.这种机制在多线程编程中非常有用,因为它允许每个线程拥有自己的数据副本,从而避免了数 ...

  7. nuxt使用介绍[学习记录]

    服务端渲染 传统服务端渲染 单页面应用 SPA nuxt 是什么 Nuxt.js 是一个基于 Vue.js 的通用应用框架. 通过对客户端/服务端基础架构的抽象组织,Nuxt.js 主要关注的是应用的 ...

  8. 使用Databricks进行零售业需求预测的应用实践

    ​简介:本文从零售业需求预测痛点.商店商品模型预测的实践演示,介绍Databricks如何助力零售商进行需求.库存预测,实现成本把控和营收增长. 作者:李锦桂 阿里云开源大数据平台开发工程师 本文从零 ...

  9. 深入分析 Flutter 渲染性能

    简介: Flutter 有很多优点,特别是对于开发者来说,跨平台多端支持,丰富的 UI 组件库和交互效果,声明式 UI,React 的更新方式,Hot-reload 提高开发效率等等.虽然它在渲染性能 ...

  10. VSCode 打开ESP32工程问题

    一.无法跳转 问题现象: 打开ESP32工程头文件提示波浪线不跳转,如下图所示: 解决办法: 删除工程中.vsccode文件夹下的所有文件 VSCode 中打开命令行搜索 ESP-IDF 找到`添加 ...