Android Media Framework(七)MediaCodecService

Android引入Treble架构后,OpenMAX框架以HIDL Service的形式为System分区提供服务,本文将探讨该服务是如何启动,服务提供了什么内容,以及服务是如何被应用层所使用的。

1 概述

在Android的Treble架构中,为了确保系统的稳定性和模块化,厂商的实现通常被放置在Vendor分区。因此,之前提到的由厂商实现的库libstagefrighthw.so和组件实现libvdec_xxx.so需要被编译到Vendor分区。由于System分区和Vendor分区之间的资源是隔离的,不能直接互通,所以当系统或应用需要访问Vendor分区中的资源时,就需要使用HIDL(Hardware Interface Definition Language)来定义接口,并通过hwbinder机制来实现跨分区的通信。

为了让System分区能够调用到Vendor分区中厂商实现的OMX组件,Android定义了相关的HIDL接口,并实现了HIDL Service。相关文件路径如下:

HIDL接口定义位于:hardware/interfaces/media/omx/1.0,编译生成的C++文件位于out/soong/.intermediates/hardware/interfaces/media/1.0

HIDL接口实现位于:frameworks/av/media/libstagefright/omx/1.0

服务启动文件位于:/frameworks/av/services/mediacodec/main_codecservice.cpp

实现HIDL Service除了需要代码实现外,还需要以下支持文件:

兼容性矩阵:hardware/interfaces/compatibility_matrices/compatibility_matrix.3.xml

清单文件:device/google/cuttlefish/shared/config/android.hardware.media.omx@1.0.xml

启动脚本:frameworks/av/services/mediacodec/android.hardware.media.omx@1.0-service.rc

2 服务启动

service vendor.media.omx /vendor/bin/hw/android.hardware.media.omx@1.0-serviceclass mainuser mediacodecgroup camera drmrpc mediadrmioprio rt 4task_profiles ProcessCapacityHigh

开机启动时,程序将加载以上脚本,执行二进制文件/vendor/bin/hw/android.hardware.media.omx@1.0-service。bin文件由main_codecservice.cpp编译而成:

int main(int argc __unused, char** argv)
{strcpy(argv[0], "media.codec");LOG(INFO) << "mediacodecservice starting";signal(SIGPIPE, SIG_IGN);android::ProcessState::initWithDriver("/dev/vndbinder");android::ProcessState::self()->startThreadPool();// 配置HIDL Service线程池::android::hardware::configureRpcThreadpool(64, false);// Default codec servicesusing namespace ::android::hardware::media::omx::V1_0;sp<IOmx> omx = new implementation::Omx();if (omx == nullptr) {LOG(ERROR) << "Cannot create IOmx HAL service.";} else if (omx->registerAsService() != OK) {LOG(ERROR) << "Cannot register IOmx HAL service.";} else {LOG(INFO) << "IOmx HAL service created.";}sp<IOmxStore> omxStore = new implementation::OmxStore(property_get_int64("vendor.media.omx", 1) ? omx : nullptr);if (omxStore == nullptr) {LOG(ERROR) << "Cannot create IOmxStore HAL service.";} else if (omxStore->registerAsService() != OK) {LOG(ERROR) << "Cannot register IOmxStore HAL service.";}::android::hardware::joinRpcThreadpool();
}

main函数创建了一个Omx对象和一个OmxStore对象,之后将这两个对象注册到hwbinder驱动当中,也就是说进程提供了两个服务。在Android手机或电视上执行ps -A可以看到该进程正在运行,服务名称为media.codec:

console:/ $ ps -A | grep media.codec
mediacodec     683     1      56196  11684 0                   0 S media.codec

configureRpcThreadpool设定服务线程数量最大为64,理论上来说,我们最多能够同时创建32路组件。

2、IOmx

IOmx.hal

interface IOmx {listNodes() generates (Status status,vec<ComponentInfo> nodeList);allocateNode(string name,IOmxObserver observer) generates (Status status,IOmxNode omxNode);createInputSurface() generates (Status status,IGraphicBufferProducer producer,IGraphicBufferSource source);
}

IOmx.hal声明了三个接口,我们暂且先了解前面两个:

  • listNodes:从OMXStore中获取组件信息,该方法不是给ACodec使用,而是给OmxStore使用;
  • allocateNode:根据组件名称创建一个组件;

构造函数

struct Omx : public IOmx, public hidl_death_recipient {Omx();virtual ~Omx();// Methods from IOmxReturn<void> listNodes(listNodes_cb _hidl_cb) override;Return<void> allocateNode(const hidl_string& name,const sp<IOmxObserver>& observer,allocateNode_cb _hidl_cb) override;Return<void> createInputSurface(createInputSurface_cb _hidl_cb) override;// Method from hidl_death_recipientvoid serviceDied(uint64_t cookie, const wp<IBase>& who) override;// Method for OMXNodeInstancestatus_t freeNode(sp<OMXNodeInstance> const& instance);protected:OMXStore* mStore;Mutex mLock;KeyedVector<wp<IBase>, sp<OMXNodeInstance> > mLiveNodes;KeyedVector<OMXNodeInstance*, wp<IBase> > mNode2Observer;MediaCodecsXmlParser mParser;
};

Omx继承于IOmx,除了实现了IOmx的三个接口,Omx还有新增一个接口freeNode。细想一下,IOmx服务只提供了创建组件的接口,但是没有提供销毁组件的接口,是不是有些奇怪,组件应该如何销毁呢?Omx提供的freeNode方法该如何被调用呢?先把疑问留在这里,后续的文章将解决这个问题。

Omx::Omx() :mStore(new OMXStore()),mParser() {(void)mParser.parseXmlFilesInSearchDirs();(void)mParser.parseXmlPath(mParser.defaultProfilingResultsXmlPath);
}

Omx的构造函数实例化了两个成员:

  • OMXStore:该对象在前一篇文章已经做过了解,它同于加载平台提供的软硬件编解码组件信息,管理组件的创建与销毁,是不是可以猜到allocateNode通过调用makeComponentInstance创建组件。
  • MediaCodecsXmlParser:xml解析工具类,对于Omx服务来说该类没有太大作用,我们放到OmxStore一节再做了解。

listNodes

Return<void> Omx::listNodes(listNodes_cb _hidl_cb) {std::list<::android::IOMX::ComponentInfo> list;char componentName[256];for (OMX_U32 index = 0;mStore->enumerateComponents(componentName, sizeof(componentName), index) == OMX_ErrorNone;++index) {list.push_back(::android::IOMX::ComponentInfo());::android::IOMX::ComponentInfo& info = list.back();info.mName = componentName;::android::Vector<::android::String8> roles;OMX_ERRORTYPE err =mStore->getRolesOfComponent(componentName, &roles);if (err == OMX_ErrorNone) {for (OMX_U32 i = 0; i < roles.size(); ++i) {info.mRoles.push_back(roles[i]);}}}hidl_vec<ComponentInfo> tList;tList.resize(list.size());size_t i = 0;for (auto const& info : list) {convertTo(&(tList[i++]), info);}_hidl_cb(toStatus(OK), tList);return Void();
}

listNodes方法循环调用OMXStore的enumerateComponents和getRolesOfComponent方法,获取平台提供的所有组件信息,将这些信息以ComponentInfo的形式回传给调用者。要注意的是,这个方法中用到了两种ComponentInfo,一种是::android::IOMX::ComponentInfo,它是一个普通的结构体;另一种是ComponentInfo,它是可以通过HIDL调用传递的类型,在IOmx.hal中定义。

allocateNode

Return<void> Omx::allocateNode(const hidl_string& name,const sp<IOmxObserver>& observer,allocateNode_cb _hidl_cb) {using ::android::IOMXNode;using ::android::IOMXObserver;sp<OMXNodeInstance> instance;{// 1.Mutex::Autolock autoLock(mLock);if (mLiveNodes.size() == kMaxNodeInstances) {_hidl_cb(toStatus(NO_MEMORY), nullptr);return Void();}// 2. 创建OMXNodeInstance实例instance = new OMXNodeInstance(this, new LWOmxObserver(observer), name.c_str());// 2. 创建OMX组件OMX_COMPONENTTYPE *handle;OMX_ERRORTYPE err = mStore->makeComponentInstance(name.c_str(), &OMXNodeInstance::kCallbacks,instance.get(), &handle);// 3. 将OMX组件绑定到OMXNodeInstance实例instance->setHandle(handle);// 4. 寻找组件的quirks并且设定给OMXNodeInstance// Find quirks from mParserconst auto& codec = mParser.getCodecMap().find(name.c_str());if (codec == mParser.getCodecMap().cend()) {LOG(WARNING) << "Failed to obtain quirks for omx component ""'" << name.c_str() << "' ""from XML files";} else {uint32_t quirks = 0;for (const auto& quirk : codec->second.quirkSet) {if (quirk == "quirk::requires-allocate-on-input-ports") {quirks |= OMXNodeInstance::kRequiresAllocateBufferOnInputPorts;}if (quirk == "quirk::requires-allocate-on-output-ports") {quirks |= OMXNodeInstance::kRequiresAllocateBufferOnOutputPorts;}}instance->setQuirks(quirks);}// 5. 将创建的OMXNodeInstance实例存储到键值列表中mLiveNodes.add(observer.get(), instance);mNode2Observer.add(instance.get(), observer.get());}observer->linkToDeath(this, 0);// 6. 将创建的OMXNodeInstance实例返回给应用层_hidl_cb(toStatus(OK), new TWOmxNode(instance));return Void();
}

allocateNode方法是本篇重点了解内容之一,客户端将利用该方法来创建组件:

  1. 正式创建组件之前要先检查当前已经运行了多少个组件实例,IOmx定义的kMaxNodeInstances值为16,也就是说最大支持16个组件同时运行;
  2. 创建OMXNodeInstance实例,将客户端创建的IOmxObserver(callback)对象传递给它;
  3. 使用OMXStore的makeComponentInstance方法创建OMX组件,传入OMXNodeInstance句柄和OMXNodeInstance::kCallbacks;
  4. 将创建的OMX组件句柄绑定到OMXNodeInstance实例;
  5. 在解析的xml文件中搜寻组件相关的quirks并且设定给OMXNodeInstance实例;
  6. 将创建的OMXNodeInstance实例存储到键值列表中;
  7. 将创建的OMXNodeInstance实例返回给应用层;

OMXNodeInstance就是我们在Spec阅读中提到的IL Client,它在整个OpenMAX框架中起着承上启下的作用,向下它封装了OMX Core API的调用,屏蔽了Core API的调用细节;向上重新封装了更为简单的API给应用层使用。

请添加图片描述

整体架构参考上图,OMXNodeInstance和OMX Component互相持有对方的句柄,OMXNodeInstance调用OMX Core API,传入组件句柄即可操作指定组件;OMX Component调用OMXNodeInstance实现的callback,传入OMXNodeInstance句柄即可向指定客户端实例发送消息。

关注公众号《青山渺渺》阅读全文

请添加图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/859949.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

面试经典150题

打家劫舍 class Solution { public:int rob(vector<int>& nums) {int n nums.size();if(n 1){return nums[0];}vector<int> dp(n, 0);dp[0] nums[0];//有一间房可以偷//有两间房可以偷if(nums[1] > nums[0]){dp[1] nums[1];}else{dp[1] nums[0];}for …

react18 实现具名插槽

效果预览 技术要点 当父组件给子组件传递的 JSX 超过一个标签时&#xff0c;子组件接收到的 children 是一个数组&#xff0c;通过解析数组中各 JSX 的属性 slot &#xff0c;即可实现具名插槽的分发&#xff01; 代码实现 Father.jsx import Child from "./Child";…

【D3.js in Action 3 精译】第一部分 D3.js 基础知识

第一部分 D3.js 基础知识 欢迎来到 D3.js 的世界&#xff01;可能您已经迫不及待想要构建令人惊叹的数据可视化项目了。我们保证&#xff0c;这一目标很快就能达成&#xff01;但首先&#xff0c;我们必须确保您已经掌握了 D3.js 的基础知识。这一部分提到的概念将会在您后续的…

探秘神经网络激活函数:Sigmoid、Tanh和ReLU,解析非线性激活函数的神奇之处

引言 在神经网络中&#xff0c;激活函数扮演着至关重要的角色。它们赋予神经网络非线性的能力&#xff0c;使得网络具备学习和表示复杂函数关系的能力。本文将详细解析三种常见的激活函数&#xff1a;Sigmoid、Tanh和ReLU&#xff0c;揭开它们在神经网络中的奥秘。无论你是初学…

【十一】【QT开发应用】模拟腾讯会议登录界面设计UI

ui 加入会议的样式表 QPushButton { /* 前景色 */ color:#0054E6; /* 背景色 */ background-color:rgb(255,255,255); /* 边框风格 */ border-style:outset; /* 边框宽度 */ border-width:0.5px; /* 边框颜色 */ border-color:gray; /* 边框倒角 */ border-radius…

日常-----最爱的人

今日话题 大家好嗷&#xff0c;今天聊的技术可比之前的重要的多啊&#xff0c;哼哼&#xff0c;也不是今天&#xff0c;大家像我看齐嗷&#xff0c;我宣布个事情&#xff01;&#xff01;&#xff01; 于2024年6月21日晚上&#xff0c;本人遇到了这一生最爱的人 嘿嘿 这种事…

微信小程序 引入MiniProgram Design失败

这tm MiniProgramDesign 是我用过最垃圾的框架没有之一 我按照官网的指示安装居然能安装不成功,牛! 这里说明我是用js开发的 到以上步骤没有报错什么都没有,然后在引入组件的时候报错 Component is not found in path “./miniprogram _npm/vant/weapp/button/index” (using…

CSS阴影优化气泡框样式

<body> <div class"pop">气泡框</div> </body>body{display: flex;justify-content: center;align-items: center;height: 100% } .pop{display: flex;justify-content: center;align-items: center;background: #409eff;width: 150px;heigh…

03-Shell编程之循环语句与函数

目录 3.1 for循环语句 3.1.1for语句的结构 3.1.2 for语句应用实例 3.2 使用whlie循环语句 1.打印数字1到5 3.3 使用until循环语句 3.3.1until的实例 1.打印数字1到5&#xff08;使用until的逆向逻辑&#xff09; 2.等待用户输入特定内容 3.4 函数 3.4.1Shell函数的基…

自学C语言-10

第10章 指针 指针是C语言的一个重要组成部分&#xff0c;是C语言的核心、精髓所在。用好指针&#xff0c;可以在C语言开发中起到事半功倍的效果。一方面&#xff0c;可以提高程序的编译效率、执行速度&#xff0c;以及动态存储分配&#xff1b;另一方面&#xff0c;可使程序更加…

playwright录制脚本原理

Paywright录制工具UI 在上一篇博客中介绍了如何从0构建一款具备录制UI测试的小工具。此篇博客将从源码层面上梳理playwright录制原理。当打开playwright vscode插件时&#xff0c;点击录制按钮&#xff0c;会开启一个新浏览器&#xff0c;如下图所示&#xff0c;在新开浏览器页…

Hive基础知识(十八):Hive 函数的使用

1. 系统内置函数 1&#xff09;查看系统自带的函数 hive (hive3)> show functions; Time taken: 0.085 seconds, Fetched: 289 row(s) 2&#xff09;显示自带的函数的用法 hive (hive3)> desc function upper; OK tab_name upper(str)- Returns str with all characters…

“了解MySQL中的enum枚举数据类型“

目录 # 开篇 1. 创建包含枚举类型的表 2. 插入枚举类型的数据 3. 查询包含枚举类型的表 4. 更新枚举类型的数据 5. 使用枚举类型的好处 注意事项 示例总结 附加 # 开篇 在数据库中&#xff0c;枚举&#xff08;ENUM&#xff09;是一种数据类型&#xff0c;用于存储一组…

即插即用篇 | 手把手教你 YOLOv10 添加注意力机制 | 20+ 种全打通!

YOLOv10 添加注意力机制 ! 视频教程地址-哔哩哔哩 文章目录 YOLOv10 添加注意力机制 !注意力机制介绍注意力机制的分类1. SE 注意力模块1.1 原理1.2 代码2. CBAM 注意力模块2.1 原理2.2 代码3. ECA 注意力模块3.1 原理3.2 代码4. CA 注意力模块4.1 原理4.2 代码5. 添加方式�…

构建开源多模态RAG系统

在这个新的冒险中&#xff0c;我们将深入研究使用开源大型语言多模态&#xff08;LLMM&#xff09;构建检索增强型生成&#xff08;RAG&#xff09;系统的过程。值得注意的是&#xff0c;我们的重点是在不依赖LangChain或Llama索引的情况下实现这一点&#xff1b;相反&#xff…

LabVIEW在机器人研究所中的应用

机器人研究所致力于机器人技术的研究与开发&#xff0c;涵盖工业机器人、服务机器人、医疗机器人等多个领域。研究所需要一个高效、灵活的实验控制和数据采集系统&#xff0c;以进行复杂的机器人实验&#xff0c;并对实验数据进行实时处理和分析。 项目需求 实时控制与监控&am…

NC--介绍-未加密加密后-流量抓包对比

免责声明:本节仅做技术交流与学习... 目录 介绍: 用法: 未加密--流量抓包 加密: 攻击端 靶机 抓包分析: 介绍: nc 是一个Linux环境下常用的工具命令&#xff0c;可以用来帮助开发者查询和解决网路问题&#xff0c;通常被认为是 NetCat 工具的缩写&#xff0c;在网络工具…

Hi3861 OpenHarmony嵌入式应用入门--轮询按键

本篇介绍使用轮询方式读取gpio状态来判断按键状态。 原理图如下 GPIO API API名称 说明 hi_u32 hi_gpio_init(hi_void); GPIO模块初始化 hi_u32 hi_io_set_pull(hi_io_name id, hi_io_pull val); 设置某个IO上下拉功能。 hi_u32 hi_gpio_set_dir(hi_gpio_idx id, hi_gpi…

MySQL理解-下载-安装

MySQL理解: mysql:是一种关系型数据库管理系统。 下载&#xff1a; 进入官网MySQLhttps://www.mysql.com/ 找到download 滑动到最下方&#xff1a;有一个开源社区版的链接地址&#xff1a; 然后就下载完成了 安装&#xff1a; 双击&#xff1a; 一直next 一直next这一步&…

仓颉编程语言入门

华为在 2024 年 6 月 21 日的华为开发者大会上&#xff0c;华为终端 BG 软件部总裁龚体正式官宣了华为自研仓颉编程语言&#xff0c;并发布了 HarmonyOS NEXT 仓颉语言开发者预览版。 仓颉编程语言文件后缀名为 .cj, 以下是第一个入门代码输出&#xff1a;你好&#xff0c;仓颉…