高通CamX架构

1,302 阅读15分钟

Android开发了一个相机硬件接口,允许OEM为终端用户提供高质量的相机解决方案。

Camera2 Java API与HAL3结合(Camera2直接接到HAL3上)为Camera应用程序开发提供了足够的灵活性,大多数场景都可以使用Camera2+HAL3来实现,但是,当java层负责控制执行流程时,其他引擎(如CPU、GPU或DSP)上的延迟或潜在的低效处理是不可接受的。

QCOM作为平台厂商会根据谷歌定义的HAL3接口来实现自己的Camera HAL3,新的QCOM Camera HAL3 架构就是CamX了。

文章目录

一、CamX总体结构

CamX总体结构

  • Camx的架构入口为Camx包中的camxhal3entry.cpp,Camx中是高通平台Camx架构的核心跳转及处理业务的代码,一般手机厂商不会去更改,代码目录在vendor/qcom/proprietary/camx/下,编译结果是camera.qcom.so

  • Camx通过chxentensionInterface调用到 chi-cdk 包下的代码,这里面一般是手机厂商自己定制功能的地方,代码目录在vendor/qcom/proprietary/chi-cdk/,编译结果是com.qti.chi.override.so

  • 从上面这张图可知,一个request交给CamX处理,但会经过chi-cdk进行request定制化重新打包再交给CamX实际去执行或和kernel driver层进行交互,CamX部分代码是核心流程管控的代码,而chi-cdk正是手机厂商想要实现自己定制化功能的代码地方。

  • Chi对Camx的操作,需要通过 ExtensionModule 进行操作,因此,CamX对外提供的接口扩展需要通过ExtensionModule进行,里面一个重要的变量就是g_chiContextOps

  • Camx对Chi的操作,是通过HAL3Module接口的m_ChiAppCallbacks进行的,因此chi里面释放的接口,都会在m_ChiAppCallbacks里面体现。

二、CamX架构提出原因

为了更精细化控制底层硬件(Sensor/ISP等关键硬件),同时方便手机厂商自定义一些功能,现在提出了CamX-CHI架构. 它将一些高度统一的功能性接口抽离出来放到CamX中,将可定制化的部分放在CHI中供不同厂商进行修改,实现各自独有的特色功能.

这样设计的好处显而易见,那便是即便开发者对于CamX并不是很了解,但是依然可以很方便的加入自定义的功能,从而降低了开发者在高通平台的开发门槛。

三、CamX-CHI基本目录结构

接下来我们以最直观的目录结构入手对该架构做一个简单的认识,以下便是CamX-CHI基本目录结构:

CamX-CHI基本目录结构

该部分代码主要位于 vendor/qcom/proprietary/ 目录

  • camx 代表了通用功能性接口的代码实现集合(CamX)
  • chi-cdk代表了可定制化需求的代码实现集合(CHI)

从图中可以看出Camx部分对上作为HAL3接口的实现,对下通过v4l2框架与Kernel保持通讯,中间通过互相dlopen so库并获取对方操作接口的方式保持着与CHI的交互。

3.1 camx 目录介绍

camx中有如下几个主要目录:

  • core/ : 用于存放camx的核心实现模块,其中还包含了主要用于实现hal3接口的hal/目录,以及负责与CHI进行交互的chi/目录
  • hwl/: 用于存放自身具有独立运算能力的硬件node,该部分node受csl管理
  • swl/: 用于存放自身并不具有独立运算能力,必须依靠CPU才能实现的node
  • csl/: 用于存放主要负责camx与camera driver的通讯模块,为camx提供了统一的Camera driver控制接口

3.2 chi-cdk 目录介绍

chi-cdk/中有如下几个主要目录:

  • chioverride/: 用于存放CHI实现的核心模块,负责与camx进行交互并且实现了CHI的总体框架以及具体的业务处理。
  • bin/: 用于存放平台相关的配置项
  • topology/: 用于存放用户自定的Usecase xml配置文件
  • node/: 用于存放用户自定义功能的node
  • module/: 用于存放不同sensor的配置文件,该部分在初始化sensor的时候需要用到
  • tuning/: 用于存放不同场景下的效果参数的配置文件
  • sensor/: 用于存放不同sensor的私有信息以及寄存器配置参数
  • actuator/: 用于存放不同对焦模块的配置信息
  • ois/: 用于存放防抖模块的配置信息
  • flash/: 存放着闪光灯模块的配置信息
  • eeprom/: 存放着eeprom外部存储模块的配置信息
  • fd/: 存放了人脸识别模块的配置信息

四、CamX 基本组件

4.1 Usecase

作为CamX-CHI中最大的抽象概念,其中包含了多条实现特定功能的Pipeline,具体实现是在CHI中通过Usecase类完成的,该类主要负责了其中的业务处理以及资源的管理。

Usecase类,提供了一系列通用接口,作为现有的所有Usecase的基类,其中,AdvancedCameraUsecase又继承于CameraUsecaseBase,相机中绝大部分场景会通过实例化AdvancedCameraUsecase来完成,它包括了几个主要接口:

  • Create(): 该方法是静态方法,用于创建一个AdvancedCameraUsecase实例,在其构造方法中会去获取XML中的相应的Usecase配置信息。
  • ExecuteCaptureRequest(): 该方法用于下发一次Request请求。
  • ProcessResultCb(): 该方法会在创建Session的过程中,作为回调方法注册到其中,一旦Session数据处理完成的时候便会调用该方法将结果发送到AdvancedCameraUsecase中。
  • ProcessDriverPartialCaptureResult(): 该方法会在创建Session的过程中,作为回调方法注册到其中,一旦Session中产生了partial meta data的时候,便会调用该方法将其发送至AdvancedCameraUsecase中。
  • ProcessMessageCb(): 该方法会在创建Session的过程中,作为回调方法注册到其中,一旦Session产生任何事件,便会调用该方法通知到AdvancedCameraUsecase中。
  • ExecuteFlush(): 该方法用于刷新AdvancedCameraUsecase。
  • Destroy(): 该方法用于安全销毁AdvancedCameraUsecase。

Usecase的可定制化部分被抽象出来放在了common_usecase.xml文件中,这里简单介绍其中的几个主要的标签含义:

  • UsecaseName: 代表了该Usecase的名字,后期根据这个名字找到这个Usecase的定义。
  • Targets: 用于表示用于输出的数据流的集合,其中包括了数据流的格式,输出Size的范围等。
  • Pipeline: 用于定义该Usecase可以是使用的所有Pipeline,这里必须至少定义一条Pipeline。

4.2 Feature

Feature代表了一个特定的功能,该功能需要多条Pipeline组合起来实现,受Usecase统一管理,在CHI中通过Feature类进行实现,在XML中没有对应的定义,具体的Feature选取工作是在Usecase中完成的,通过在创建Feature的时候,传入Usecase的实例的方式,来和Usecase进行相互访问各自的资源。

以下是现有的Feature,其中Feature作为基类存在,定义了一系列通用方法。

CamX Feature的概念

几个常用的Feature:

  • FeatureHDR: 用于实现HDR功能,它负责管理内部的一条或者几条pipeline的资源以及它们的流转,最终输出具有HDR效果的图像。
  • FeatureMFNR: 用于实现MFNR功能,内部分为几个大的流程,分别包括Prefiltering、Blending、Postfilter以及最终的OfflineNoiseReproces(这一个是可选择使能的),每一个小功能中包含了各自的pipeline。
  • FeatureASD: 用于AI功能的实现,在预览的时候,接收每一帧数据,并且进行分析当前场景的AI识别输出结果,并其通过诸如到metadata方式给到上层,进行后续的处理。

4.3 Session

用于管理pipeline的抽象控制单元,一个Session中至少拥有一个pipeine,并且控制着所有的硬件资源,管控着每一个内部pipeline的request的流转以及数据的输入输出,它没有可定制化的部分,所以在CHI中的XML文件中并没有将Session作为一个独立的单元进行定义。

Session的实现主要通过CamX中的Session类,其主要接口如下:

  • Initialize(): 根据传入的参数SessionCreateData进行Session的初始化工作。
  • NotifyResult(): 内部的Pipeline通过该接口将结果发送到Session中。
  • ProcessCaptureRequest(): 该方法用于用户决定发送一个Request到Session中的时候调用。
  • StreamOn(): 通过传入的Pipeline句柄,开始硬件的数据传输。
  • StreamOff(): 通过传入的Pipeline句柄,停止硬件的数据传输。

4.4 Pipeline

作为提供单一特定功能的所有资源的集合,维护着所有硬件资源以及数据的流转,每一个Pipeline包括了其中的Node/Link,在CamX中通过Pipeline类进行实现,负责整条Pipeline的软硬件资源的维护以及业务逻辑的处理,接下来我们简单看下该类的几个主要接口:

  • Create(): 该方法是一个静态方法,根据传入的PipelineCreateInputData信息来实例化一个Pipeline对象。
  • StreamOn(): 通知Pipeline开始硬件的数据传输
  • StreamOff(): 通知Pipeline停止硬件的数据传输
  • FinalizePipeline(): 用于完成Pipeline的设置工作
  • OpenRequest(): open一个CSL用于流转的Request
  • ProcessRequest(): 开始下发Request
  • NotifyNodeMetadataDone(): 该方法是Pipeline提供给Node,当Node内部生成了metadata,便会调用该方法来通知metadata已经完成,最后当所有Node都通知Pipeline metadata已经完成,Pipeline 便会调用ProcessMetadataRequestIdDone通知Session。
  • NotifyNodePartialMetadataDone(): 该方法是Pipeline提供给Node,当Node内部生成了partial metadata,便会调用该方法来通知metadata已经完成,最后当所有Node都通知Pipeline metadata已经完成,Pipeline 便会调用ProcessPartialMetadataRequestIdDone通知Session。
  • SinkPortFenceSignaled(): 用来通知Session 某个sink port的fence处于被触发的状态。
  • NonSinkPortFenceSignaled(): 用来通知Session 某个non sink port的fence处于被触发的状态。

Pipeline中的Node以及连接方式都在XML中被定义,其主要包含了以下几个标签定义:

  • PipelineName: 用来定义该条Pipeline的名称
  • NodeList: 该标签中定义了该条Pipeline的所有的Node
  • PortLinkages: 该标签定义了Node上不同端口之间的连接关系

4.5 Node

作为单个具有独立处理功能的抽象模块,可以是硬件单元也可以是软件单元,关于Node的具体实现是CamX中的Node类来完成的,其中CamX-CHI中主要分为两个大类:

  • 一个是高通自己实现的Node包括硬件Node
  • 一个是CHI中提供给用户进行实现的Node

其主要方法如下:

  • Create(): 该方法是静态方法,用于实例化一个Node对象。
  • ExecuteProcessRequest(): 该方法用于针对hwl node下发request的操作。
  • ProcessRequestIdDone(): 一旦该Node当前request已经处理完成,便会通过调用该方法通知Pipeline。
  • ProcessMetadataDone(): 一旦该Node的当前request的metadata已经生成,便会通过调用该方法通知到Pipeline。
  • ProcessPartialMetadataDone(): 一旦该Node的当前request的partial metadata已经生成,便会通过调用该方法通知到Pipeline。
  • CreateImageBufferManager(): 创建ImageBufferManager

其可定制化的部分作为标签在XML中进行定义:

  • NodeName: 用来定义该Node的名称
  • NodeId: 用来指定该Node的ID,其中IPE NodeId为65538,IFE NodeId为65536,用户自定义的NodeId为255。
  • NodeInstance: 用于定义该Node的当前实例的名称。
  • NodeInstanceId: 用于指定该Node实例的Id。

4.6 Link

用于定义不同Port的连接,一个Port可以根据需要建立多条与其它从属于不同Node的Port的连接,它通过标签来进行定义,其中包括了作为输入端口,作为输出端口。 一个Link中包含了一个SrcPort和一个DstPort,分别代表了输入端口和输出端口,然后BufferProperties用于表示两个端口之间的buffer配置。

4.7 Port

作为Node的输入输出的端口,在XML文件中,标签用来定义一个输入端口,标签用来定义输出端口,每一个Node都可以根据需要使用一个或者多个输入输出端口,使用OutputPort以及InputPort结构体来进行在代码中定义。

  • PortId: 该端口的Id: 该端口的名称
  • NodeName: 该端口从属的Node名称
  • NodeId: 该端口从属的Node的Id
  • NodeInstance: 该端口从属的Node的实例名称
  • NodeInstanceId: 该端口从属的Node的实例的Id

五、CamX组件之间关系

通过之前的介绍,我们对于几个基本组件有了一个比较清晰地认识,但是任何一个框架体系并不是仅靠组件胡乱堆砌而成的,相反,它们都必须基于各自的定位,按照各自所独有的行为模式,同时按照约定俗称的一系列规则组合起来,共同完成整个框架某一特定的功能。所以这里不得不产生一个疑问,在该框架中它们到底是如何组织起来的呢?它们之间的关系又是如何的呢? 接下来我们以下图入手开始进行分析:

CamX Feature的概念

由上图可以看到,几者是通过包含关系组合起来的,Usecase 包含Feature,而Feature包含了Session,Session又维护了内部的Pipeline的流转,而每一条pipeline中又通过Link将所有Node都连接了起来,接下我们就这几种关系详细讲解下:

  • 首先,一个Usecase代表了某个特定的图像采集场景,比如人像场景,后置拍照场景等等,在初始化的时候通过根据上层传入的一些具体信息来进行创建,这个过程中,一方面实例化了特定的Usecase,这个实例是用来管理整个场景的所有资源,同时也负责了其中的业务处理逻辑,另一方面,获取了定义在XML中的特定Usecase,获取了用于实现某些特定功能的pipeline。
  • 其次,在Usecase中,Feature是一个可选项,如果当前用户选择了HDR模式或者需要在Zoom下进行拍照等特殊功能的话,在Usecase创建过程中,便会根据需要创建一个或者多个Feature,一般一个Feature对应着一个特定的功能,如果场景中并不需要任何特定的功能,则也完全可以不使用也不创建任何Feature。
  • 然后,每一个Usecase或者Feature都可以包含一个或者多个Session,每一个Session都是直接管理并负责了内部的Pipeline的数据流转,其中每一次的Request都是Usecase或者Featuret通过Session下发到内部的Pipeline进行处理,数据处理完成之后也是通过Session的方法将结果给到CHI中,之后是直接给到上层还是将数据封装下再次下发到另一个Session中进行后处理,这都交由CHI来决定。
  • 其中,Session和Pipeline是一对多的关系,通常一个Session只包含了一条Pipeline,用于某个特定图像处理功能的实现,但是也不绝对,比如FeatureMFNR中包含的Session就包括了三条pipeline,又比如后置人像预览,也是用一个Session包含了两条分别用于主副双摄预览的Pipeline,主要是要看当前功能需要的pipeline数量以及它们之间是否存在一定关联。
  • 同时,根据上面关于Pipeline的定义,它内部包含了一定数量的Node,并且实现的功能越复杂,所包含的Node也就越多,同时Node之间的连接也就越错综复杂,比如后置人像预览虚化效果的实现就是将拿到的主副双摄的图像通过RTBOfflinePreview这一条Pipeline将两帧图像合成一帧具有虚化效果的图像,从而完成了虚化功能。
  • 最后Pipeline中的Node的连接方式是通过XML文件中的Link来进行描述的,每一个Link定义了一个输入端和输出端分别对应着不同Node上面的输入输出端口,通过这种方式就将其中的一个Node的输出端与另外一个Node的输入端,一个一个串联起来,等到图像数据从Pipeline的起始端开始输入的时候,便可以按照这种定义好的轨迹在一个一个Node之间进行流转,而在流转的过程中每经过一个Node都会在内部对数据进行处理,这样等到数据从起始端一直流转到最后一个Node的输出端的时候,数据就经过了很多次处理,这些处理效果最后叠加在一起便是该Pipeline所要实现的功能,比如降噪、虚化等等。

转载来源deepinout.com/qcom-camx-c…


*本人从事Android Camera相关开发已有5年,

*目前在深圳上班,

*欢迎关注我的微信公众号: 小驰笔记

*希望和更多的小伙伴一起交流学习~

----2021.05.01 深圳 16:33