Android Media Framework(七)MediaCodecService

news2024/11/18 9:46:53

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-service
    class main
    user mediacodec
    group camera drmrpc mediadrm
    ioprio rt 4
    task_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 services
    using 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 IOmx
    Return<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_recipient
    void serviceDied(uint64_t cookie, const wp<IBase>& who) override;

    // Method for OMXNodeInstance
    status_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 mParser
        const 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.coloradmin.cn/o/1858663.html

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

相关文章

LeetCode:经典题之1491、896 题解与延伸

系列目录 88.合并两个有序数组 52.螺旋数组 567.字符串的排列 643.子数组最大平均数 150.逆波兰表达式 61.旋转链表 160.相交链表 83.删除排序链表中的重复元素 389.找不同 1491.去掉最低工资和最高工资后的工资平均值 896.单调序列 206.反转链表 92.反转链表II 141.环形链表 …

HarmonyOS开发 弹窗组件

1.HarmonyOS开发 弹窗组件 弹窗是移动应用中常见的一种用户界面元素&#xff0c;常用于显示一些重要的信息、提示用户进行操作或收集用户输入。ArkTS提供了多种内置的弹窗供开发者使用&#xff0c;除此之外还支持自定义弹窗&#xff0c;来满足各种不同的需求。 1.1. 示例 1.…

基于Java蛋糕甜品商城系统设计和实现(源码+LW+调试文档+讲解等)

&#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN作者、博客专家、全栈领域优质创作者&#xff0c;博客之星、平台优质作者、专注于Java、小程序技术领域和毕业项目实战✌&#x1f497; &#x1f31f;文末获取源码数据库&#x1f31f;感兴趣的可以先收藏起来&#xff0c;还…

封装了一个优雅的iOS转场动画

效果图 代码 // // LBTransition.m // LBWaterFallLayout_Example // // Created by mac on 2024/6/16. // Copyright © 2024 liuboliu. All rights reserved. //#import "LBTransition.h"interface LBPushAnimation:NSObject<UIViewControllerAnimated…

Halcon 如何对区域进行交集,补集,反选,合并操作

1 Intersection交集 dev_open_window(0,0,512,512,black,WindowHandle)gen_circle(Circle1,114.5,127.5,89.3588)gen_circle(Circle2,163.5,171.5,94.8472)intersection(Circle1,Circle2,RegionIntersection)dev_clear_window()dev_display(RegionIntersection)2 Differece 补集…

人机恋爱新趋势:与AI男友谈恋爱的甜蜜与挑战

"我曾经把ChatGPT当成工具&#xff0c;从未追过星&#xff0c;也没有嗑过CP。没想到&#xff0c;到了36岁&#xff0c;我竟然嗑上了AI男友。Open AI&#xff0c;你赢了。你不仅是最好的AI公司&#xff0c;还是乙女游戏公司。" 转行大龄互联网人&#xff0c;走遍20国…

相位和展开相位

相位 (Phase) 相位是一个周期信号在一个周期内的位置&#xff0c;通常以角度&#xff08;度或弧度&#xff09;表示。在许多应用中&#xff0c;相位被限制在一个周期内。例如&#xff0c;相位通常被限定在 −180∘到 180∘ 或 0∘ 到 360∘ 之间。 示例 −90∘ 表示信号在周…

【网络协议】精讲ARP协议工作原理!图解超赞超详细!!!

亲爱的用户&#xff0c;打开微信&#xff0c;搜索公众号&#xff1a;“风云说通信”&#xff0c;即可免费阅读该文章~~ 目录 前言 1. ARP协议介绍 1.1 ARP协议功能 1.2 ARP请求报文 1.3 ARP工作原理 2. ARP 缓存超时 2.1 RARP 3. ARP 攻击 3.1 ARP 攻击分类 前言 首先…

【Linux】—Apache Hive 安装部署

文章目录 前言认识Metadata认识Metastoremetastore三种配置方式 一、安装前准备二、下载hive-3.1.2安装包三、下载完成后&#xff0c;通过xftp6上传到Linux服务器上四、解压Hive安装包五、配置Hive六、内嵌模型安装—Hive元数据配置到Derby七、本地模式安装—Hive元数据配置到M…

Linux:基础IO(三.软硬链接、动态库和静态库、动精态库的制作和加载)

上次介绍了基础IO&#xff08;二&#xff09;&#xff1a;Linux&#xff1a;基础IO&#xff08;二.缓冲区、模拟一下缓冲区、详细讲解文件系统&#xff09; 文章目录 1.软硬链接1.1硬链接1.2软链接使用场景 2.动态库和静态库1.1回顾1.2静态库的制作和使用为什么要有库制作者角度…

基于单片机的智能浇花系统设计与实现

摘要: 设计了一种智能湿度感应浇花系统 。 系统以单片机 AT89S52 为控制芯片&#xff0c;利用 SLHT5-1 土壤湿度传感器来检测土壤的相对湿度&#xff0c;再通过单片机进行信息处理&#xff0c;采用模糊控制方法&#xff0c;输出控制信号&#xff0c;控制继电器的动作&…

[RPI4] 树莓派4b安装istoreos及使用 -- 1. 系统安装

最近在研究家庭智能化的一些东西,其中包括网络,智能家居等一系列内容,然后看过的资料有的想再回来看的时候就找不到了,然后就想着开这么一个系列,做一些记录,先从智能家居开始吧。 1 安装istoreos系统 iStoreOS 目标是提供一个人人会用的路由兼轻 NAS 系统,不管是作为路…

MES管理系统中的仓库管理功能有哪些用途

在当今制造业迅猛发展的背景下&#xff0c;企业对于车间生产调度的需求日益迫切。为此&#xff0c;MES管理系统应运而生&#xff0c;它作为一款专注于车间生产调度的管理信息系统&#xff0c;正逐步成为制造业提升生产效率、优化资源配置的利器。特别是其在仓储和物流管理方面的…

算法社区-从零开始构建(一)

好久没动笔了&#xff0c;一是要处理的东西很多&#xff0c;二则写出来未见得深刻&#xff0c;感觉沉淀得不够&#xff0c;太浅显的东西就没必要分享。 正好最近在研究算法层面的东西&#xff0c;感觉挺受用的&#xff0c;就想着把这些东西整理出来&#xff0c;有点像社区的雏形…

NAPI篇【4】——NAPI应用点亮一个LED

OpenHarmony的NAPI功能为开发者提供了JS与C/C不同语言模块之间的相互访问&#xff0c;交互的能力&#xff0c;使得开发者使用C或者C语言实现应用的关键功能。如操作开发板中某个GPIO节点的状态&#xff08;OpenHarmony并没有提供直接操作GPIO口状态的API&#xff09;&#xff0…

Vue-Cli 创建vue2.0 + TypeScript 项目

这里写目录标题 一、创建项目名称二、选择 Manually select features三、勾选配置项四、选择vue版本五、其它配置 一、创建项目名称 vue create 项目名称&#xff08;项目名字不能含义大写字母&#xff09;二、选择 Manually select features &#xff08;按箭头上下进行移动…

万物皆对象,你信吗?

**内存空间和数据都消失&#xff0c;数据怎么会消失的&#xff1f;**空间没了&#xff0c;数据自然也跟着消失。因为数据就是在空间里面的。就像宇宙大爆炸&#xff0c;我们还能存在嘛&#xff0c;是不是已经undefined了。「一块小内存上有2种数据类型」 内部存储的数据 地址值…

使用Fiddler如何创造大量数据

在调试和分析网络流量时&#xff0c;您是否曾为无法深入了解请求和响应的数据而感到困惑&#xff1f;如果有一种工具可以帮助您轻松抓取和分析网络流量&#xff0c;您的工作效率将大大提升。Fiddler正是这样一款功能强大的抓包工具&#xff0c;广受开发者和测试人员的青睐。 Fi…

专业140+总分400+武汉理工大学855信号与系统考研经验电子信息与通信工程,真题,大纲,参考书

专业855信号与系统140&#xff0c;总分400&#xff0c;今年顺利上岸武汉理工大学&#xff0c;总结一下自己的复习经历&#xff0c;希望对报考武理工的同学有所帮助。专业课&#xff1a;855信号与系统 首先教材&#xff1a; 《信号与系统》高等教育出版社 作者&#xff1a;刘泉…

详解三种常用标准化 Batch Norm Layer Norm RMSNorm

参考&#xff1a; BN究竟起了什么作用&#xff1f;一个闭门造车的分析《动手学深度学习》7.5 节 深度学习中&#xff0c;归一化是常用的稳定训练的手段&#xff0c;CV 中常用 Batch Norm&#xff1b; Transformer 类模型中常用 layer norm&#xff0c;而 RMSNorm 是近期很流行…