一、CAM CHI API功能介绍:
- 没有接口可以单独访问ISP内部固定引擎
- 没有接口可以显示的对一个use case申请一个处理流程
- use case可以宽泛的定义,具体的处理流程可以实现的不一样
- framework之前的请求必须全部有返回结果
- Pipeline深度基于use cases调整,实现太复杂的pipeline对于大部分HAL3 相机驱动来说请求太多
- 对于应用程序来说没有"fast path"来简化处理流程和降低延迟
1.Qualcomm Spectra 2xx相机驱动程序有五个关键的可定制组件,使OEM能够充分利用CHI进行相机应用开发:
- CHI Override 模块补充了Google HAL3接口,允许符合HAL3标准的相机应用程序直接控制图像处理流水线的生成、引擎选择和多帧控制等。
- 对于Camera2/HAL3实现,可以利用XML文件指定与现有Camera2/HAL3 use case 的拓扑图,然后通过程序直接调用CHI来实现pipeline。
- CHI统计信息覆盖(包括3A)机制允许覆盖任何QTI的默认值统计算法,且无需改驱动程序。外部统计算法可以存储私有数据,也可以由自定义节点访问。
- CHI传感器XML允许设备制造商为其特定硬件配置参数,包括相机模块,图像传感器, 执行器,电子可擦除可编程只读存储器(EEPROM)和 闪存组件。
2.关键术语:
Use case:相机管道的特定配置,实现了良好的定义的功能,例如,带有ZSL的20 MP快照和2k上的预览显示是就是一个用例。 CHI API允许开发者在底层硬件的限制范围内自行配置use case,并且不需要修改驱动。
Session:相机管道被创建开始处理图像到被销毁的过程,称之为会话,同时可以存在多个会话。
Request请求处理从图像传感器中提取的数据帧,或从内存中提取的数据帧,驱动处理结果必须返回到相机应用程序。
Sub-request
Stream:具有相同大小和格式的缓冲区序列,用于处理图像数据。可以将不同类型的多个stream指定为输入和输出到camera pipeline。stream是定义Camera2 / HAL3 use case的关键组成部分。
Per-session settings:影响相机管道处理的设置, 会话开始后就无法被更改。例如, 允许图像稳定处理。
Per-request settings:影响各个请求的设置。例如,设置手动曝光值。
Topology:有向无环图(DAG)由一系列处理节点和一组链接组成,它描述了那些正在被节点处理的缓冲区。拓扑通过XML文件指定。
Engine:用于处理数据的硬件。 如Spectra ISP,Snapdragon CPU, Adreno和DSP 是CHI API可用的引擎示例。
Node:像机管道中的逻辑功能块(节点),节点链接在一起形成拓扑。在初始版本中 CHI API,ISP外部的所有节点都通过CPU代码通过本地API(OpenCL和FastCV之类的引擎)调用,Chi API可以在将来扩展到允许在不重用本地api的情况下缓存和重用硬件命令。
Pipeline:启用数据操作的唯一上下文。每个管道都可以维护自己的状态跨多个请求,而不受其他管道的影响。管道利拓扑结构定义Engine使用和数据处理流程。
Statistics:包括3A算法,用于自动控制图像传感器和相机ISP实现更好的图像质量。这些特定领域的算法被处理成为CHI API的专用部分。
Live stream:从图像传感器接收数据的处理进程,不能修改之前请求的任何数据。如果处理速率和传感器数据传输不匹配则会移至Offline stream。
Offline stream:不从图像传感器直接获取数据处理的进程,在Chi API中,Offline stream可以与Live stream成对存在而不造成额外的延迟,处理结果可以返回到相机。
二、CHI 体系结构模式
1.基本拓扑关系图
2.Chi 硬件接口
谷歌HAL3的扩展接口,允许通过请求调整拓扑结构和低延迟控制,一些使用特性如下:
- 自定义ZSL
- 多帧请求生成 和 处理
- 图像稳定
- 低延迟后处理
CHI驱动程序提供默认节点来启用相机use cases。oem厂商可以在现有的CHI驱动上添加功能,以获得独特的相机体验。这是一个简单而强大的接口,可以在相机pipeline中无缝添加图像处理功能。
下面的时序图大致描述了一个CHI的use case创建流程:
其中CameraType定义如下:
- Nodes:每个请求都会访问所有节点,也可以忽略某些请求不需要的节点。
- Links: 链接指定了所有使用到的缓冲区格式和大小(无论作为源还是接收器)。节点之间的链接可以根据父节点上输出端口的最小数量指定尽可能多的格式,以及在子节点上的接收端口的数量。
- Buffers: Topology 控制给定类型的缓冲区数量,应用程序可以根据需要多少缓冲区微调内存使用情况以减少处理延迟。
三、Metadata
CHI中的沟通渠道可以分为以下几类:
元数据标签可以是预定义的Android tags,也可以是定制的vendor tags。元数据标签使CHI内外的组件能够与每个组件通信另外还有面向应用程序的摄像头API。在CHI中,Androidtags通过不可变值来预定义,而vendor tags不能是固定绝对值,要取决于目标扩展组件的数量和类型。CHI使用动态索引(base +偏移量)来使供应vendor tags组件能够彼此通信。
元数据标记ID是一个32位的值,它被限制在特定的部分中。每一个section以0x1_0000偏移量开始。tags,供应商部分是预定在0x8000_0000之后开始。在初始化期间,当CHI扫描目标中的可用组件时,它为发布自定义组件的每个组件分配一个vendor tags,
metadata空间示例,包括ChiVendorTags和两个ExtCompVendorTags(EXT_COMP_1和EXT_COMP_2),如下图所示:
四、加载外部二进制文件
在camera服务启动时会加载初始化自定义的处理模块,包括那些由QTI的模块。
1.外部模块命名要求
每个节点必须由单独的.so实现,其命名结构如下:
<vendor> ―― 对应厂商定义的节点模块。例如,节点由QTI提供,则命名为< QTI >。 <category> ―― 模块类型,有效值为 <node> 和 <stats>。 <algorithm> - so对应的独有算法名。示例:
-
QTI提供的3A算法命名为:com.qti.3a.aec.so
- com.qti.node.clearsight.so
对于nodes,名称必须和拓扑XML中指定的名称对应。
对于stats,有效值只有 <af>、<aec>、<awb>、<asd>和<afd>。
所有.so文件必须位于/sys/data/camx/components/中。
2.初始化流程
HAL3模块。在初始化CHI HAL3模块期间,驱动程序会查询并加载/sys/data/camx/components/下面正确命名的.so文件。
(1)导出入口函数:ChiNodeEntry()
根据.so文件的名称,CHI将导出.so对应的ChiNodeEntry()函数,驱动程序是阻塞调用entry函数,直到其处理完成。entry函数主要工作是初始化接口函数指针,另外不作任何进一步处理。此期间初始化的接口函数指针是用于相机会话对外部组件以及CHI驱动程序的调用。
(2)组件初始化函数:
每个组件都有一个名为ChiSetupComponent的函数作为其接口的一部分,CHI驱动会创建一个单独的线程来调用ChiSetupComponent函数,以便并行初始化其他组件。每个组件的ChiSetupComponent函数都接受一个SectionIdentifier,即用于填充CHI驱动程序和谷歌相机框架,包括该组件所需的自定义vendor tags。
(此处应有流程时序图。。。待画)
五、拓扑图XML解析
相机子系统中每个节点是一个功能逻辑块,实现一个use case需要多个node相互配合,用来描述节点之间联系及数据流通的结构称为拓扑。use case由一组要被处理的目标和每个会话如何处理数据的设置构成,每个use case可以由拓扑结构表示,定义了HAL3 API 之间如何进行信息传递和数据处理。在configure_streams期间会根据XML中的<Targets>和< SystemwideSettings >两个部分来选择一个用例:
- XSD模式定义了XML结构。
- 工具用于将XML文件打包为二进制文件,供CHI驱动程序使用。
1.
节点代表拓扑结构中的硬件或软件处理组件,包括QTI提供的默认节点,以及为使用CHI驱动自定义的节点。节点有一组输入端口和一组输出端口,输出数据到HAL3图像缓冲区的输出端口称为SinkBuffer,也有一些输出端口不输出到任何图像数据,仅用于发出该节点正在被使用的信号。拓扑中的DAG(有向无环图)是通过将节点的输入端口连接到前一个节点的输出端口形成,或者在需获取反馈结果的情况下,节点的输出端口也可以连接到自己的输入端口。各个节点之间的链接还包含有使用到的缓冲区的必要信息。
图解:
2.配置项解析
<Targets> <Target> <TargetName>TARGET_BUFFER_PREVIEW</TargetName> <TargetDirection>TargetOutput</TargetDirection> <Formats>YUV420NV12</Formats> <Range> <MinW>0</MinW> <MinH>0</MinH> <MaxW>1920</MaxW> <MaxH>1080</MaxH> </Range> </Target> </Targets>
<SystemwideSettings> <SettingName>EIS</SettingName> <SettingDataType>BOOL</SettingDataType> <SettingMatch>FALSE</SettingMatch> </SystemwideSettings>
<Topology> :描述了驱动对每个节点的顺序和依赖关系的解析,以处理上层请求。
Valid tag:
- Value field. Exactly 1 is required.
- Exactly 1 is required.
<TopologyNodesList> :包含在Topology中的Node列表,形成处理框架。
Valid tag:
<Node> <NodeProperty> <PropertyName>PropXYZ</PropertyName> <PropertyDataType>UINT</PropertyDataType> <PropertyValue>6</PropertyValue> </NodeProperty> <NodeName>NodeXYZ</NodeName> <NodeId>1</NodeId> <NodeInstance>NodeXYZInstanceName0</NodeInstance> <NodeInstanceId>0</NodeInstanceId> </Node>
驱动程序根据nodeId解析节点类型,对于所有自定义节点该字段的值为255,并带有一个NodeProperty来告知驱动程序切确的自定义节点类型。如下:
<Node> <NodeProperty> <PropertyName>CustomNodeLibrary</PropertyName> <PropertyDataType>STRING</PropertyDataType> <PropertyValue>customnodelib.so</PropertyValue> </NodeProperty> <NodeName>CustomNode</NodeName> <NodeId>255</NodeId> <NodeInstance>CustomNodeInstanceName0</NodeInstance> <NodeInstanceId>0</NodeInstanceId> </Node>
<Link> <SrcPort> <PortName>IFEOutputPortFull</PortName> <PortId>0</PortId> <NodeName>IFE</NodeName> <NodeId>65536</NodeId> <NodeInstance>IFEInstanceName0</NodeInstance> <NodeInstanceId>0</NodeInstanceId> </SrcPort> <DstPort> <PortName>IPEInputPortFull</PortName> <PortId>0</PortId> <NodeName>IPE</NodeName> <NodeId>65538</NodeId> <NodeInstance>IPEInstanceName0</NodeInstance> <NodeInstanceId>0</NodeInstanceId> </DstPort> <BufferProperties> <BatchMode>false</BatchMode> <BufferFormat>YUV420NV12</BufferFormat> <BufferQueueDepth>8</BufferQueueDepth> <BufferHeap>Ion</BufferHeap> <BufferFlags>MemFlagHw</BufferFlags> </BufferProperties> </Link>
<TargetName>:用于指定输出端口对应的缓冲区。
》》》以下标签指定了封装标签要用的值,不能在其中嵌入额外的标签:
<UsecaseName> :用于标识 Use case 的字符串。Valid value:
例子:<UsecaseName>UsecasePreview</UsecaseName>
<TargetName> : 该字串用于映射流的输出缓冲区到拓扑中节点的输出端口,其中<TargetName>包含的字符串用于关联节点的输出端口与此流的输出缓冲区。
Valid value:
Any string that is a valid C variable, however, there must be a matching string name in the <PortName> of any <DstPort> which is a sink port.
此标签用于指定流的方向(或流类型),即它是否是输入流、输出流或双向流。
六、自定义Use case示例
CHI override模块为实现HAL-ZSL接口以支持ZSL快照扩展了HAL3功能。此外,CHI override也提供了诸多的API,以实现像MFNR(多帧降噪)这样的多帧处理功能。
例如:override模块实现ZSL MFNR快照use case时有如下功能需求:
多帧降噪涉及到相机硬件对帧处理的复杂排序。有不同的处理阶段,即mfnn - prefilter,MFNR-Blend和MFNR-Postfilter,它们调用不同的pipeline,其中Node会在处理阶段被实例化具有不同的功能。例如,基于IPE硬件的
1初始化
override module在摄像机服务器进程启动期间初始化,此时会加载com.qti.chi.override.so,该库提供了由QTI实现的CHI override接口,所需的函数指针由CHI override module加载,同时会遍历该平台上支持的以及包括自定义nodes的vendor tags。
入口函数的伪代码:
void chi_hal_override_entry ( const chi_hal_ops_t *ops, ///< [in] chi_hal_callback_ops_t *callbacks) ///< [in | out] { ... // Store the hal ops QtiChiOverride::m_chiOps = *ops; ... // Export the callback function pointers callbacks->chi_initialize_override_session = QtiChiOverride::InitializeOverrideSession; callbacks->chi_finalize_override_session = QtiChiOverride::QtiChiExtFinalizeOverrideSession; ... // Optional callback function pointers can be NULL }
2创建会话
驱动程序中的HAL3模块将chi_initialize_override_session传递到CHIoverride module,以便override module用于检查流的配置,以及决定是否重写或忽略use case。override module通过stream的格式、类型和使用标准等配置表明是否需要HAL-ZSL,如果满足ZSL use case的条件,它就将创建realtime和offline pipeline,具体会根据拓扑中对real-time pipeline的描述调用chi_create_pipeline来创建。real-time pipeline除了PREVIEW_TARGET外,还有一个RAW_TARGET来接收原始ZSL buffer的数据。override module会将新创建的real-time pipeline句柄返回给驱动程序。
Google Framework -> HAL3 -> CHI -> QTI CHI Extension
ZSL 预览
驱动程序将framework层发出的每个请求通过chi_override_process_request接口传递给override module。如果应用程序只需要预览,则为raw target分配内部缓冲区并进行管理。修改后的请求调用chi_submit_pipeline_request利用real-time session句柄传递给驱动程序。
在目标缓冲区fences都被标记后才会收到驱动返回的结果,module获取结果。override module提取raw target buffer元数据并推入ZSL队列。framework每次发出的预览请求都会重复这个过程。
(后面时序图画一下。。。。。)
ZSL 拍照(常规请求和结果)
用户通过应用程序发送拍照请求,override module通过chi_override_process_request接口获取request来解析对ZSL帧的需要。MFNR缺失vendor tags则会使override module做一个常规的ZSL快照,以及在这样标记有助于在之后生成内部请求到pipeline。override module可以在不同的会话中配置访问缓冲区和相关的栅栏,它将会话间缓冲区和栅栏联系起来,以实现无缝控制。
对于常规的ZSL快照,在它提交完实时请求之后,override module就从ZSL,当override module接收到offline session_1的结果时,它将根据之前为offline session_2所做的标记生成一个内部请求,此请求将从offline session_1获取YUV输出,以生成一个JPEG编码照片,override module配置offline session_2来使用framework提供的拍照目标缓冲区。
(后面画下时序图。。。)
应用程序使用特殊的vendor tags请求ZSL MFNR拍照。当带有vendor tags的请求被override module获取时,它首先扫描可用的ZSL队列,以寻找合适的锚帧,以为raw target创建新的内部请求。
假设ZSL深度为4,MFNR深度为8,则实时请求的数量是:
情形1:对于ZSL队列的顶部帧,则MFNR_DEPTH (8) - (ZSL_DEPTH (4)) - AnchorIndex(0) = 4。
情形2:对于ZSL队列中的最后一帧,然后是MFNR_DEPTH (8) - (ZSL_DEPTH) (4) - 锚定指数(3)= 7。
情形3:对于ZSL队列中没有帧,则MFNR_DEPTH(8)。
除了实时请求外,还为不同的对象生成多个offline pipeline对应MFNR处理的不同阶段:MFNR共混物= N-2 = (8) -2 = 6;MFNR后置滤波器= 1。
每一个请求都是交错的,并提交给相应的pipeline,这些请求在real-time 和 offline sessions之间ID相同。
以上大致介绍了 CHI API 框架和一些关键术语的理解,后面可以根据文档各API的说明结合代码进行流程分析。
六、CHI interface
CHI向谷歌HAL3接口添加了几个关键元素,以支持更细粒度的控制。下面的函数用于访问CHI驱动程序,它们对Camera2/HAL3没有任何依赖。
CHI驱动程序的入口点,该函数用指向CHI函数的指针填充pContextOps,在使用CHI的每个进程中,必须至少调用该函数一次。
此函数用于创建ChiContext的唯一实例。ChiContext是state在ChiSessions中传输的上下文。每个进程至少有1个ChiContext。chicontext之间没有通信信息的机制,ChiSessions和ChiPipelines只能在创造它们的ChiContex中t所使用。返回值:新创建的
3.VOID (*PFNCHICLOSECONTEXT)(CHIHANDLE hChiContext);函数用于关闭ChiContext,将终止任何未完成的请求,并释放此上下文声明的所有资源。用户应该在调用PFNCHICLOSECONTEXT之前销毁所有未完成的ChiSessions 和 ChiPipeline,否则可能导致资源泄漏。
此函数用于确定平台上可用的相机传感器的数量,可以在创建ChiContext之前调用。如果一个平台有多个相机组合成一个物理通道(例如,在桥接芯片后面),这些相机被认为是一个物理传感器。如果ChiContext句柄无效,则此函数返回-1。
该函数用于获取特定camera的详细信息,可以在创建ChiContext之前调用。如果将传感器信息写入pCameraInfo,则返回一个错误代码。
该函数用于获取特定sensor模组的详细信息
该函数用于读取pipeline描述符,确认描述符中包含的拓扑,然后返回pipeline内部驱动程序表示的句柄。在代码的时间敏感部分不应调用此函数。这个函数可以在HAL设备创建之后的任何时候调用。调用者负责使用后销毁pipeline。pipeline可以在会话之间共享,不包含对缓冲区格式或大小的任何绑定。PFNCHICREATEPIPELINEDESCRIPTOR申请输出缓冲区,并确定拓扑中每个节点的入口的大小要求。返回的默认配置保证对给定的输出缓冲区集有效。对于使用传感器作为输入的管道,将返回有效的传感器模式列表,以便用户进行微调。对于使用内存作为输入的管道,将返回大小需求列表。
销毁一个pipeline描述符,pipeline不维护任何状态(这是session的职责),因此可以在任何时候销毁它们。
此函数创建包含一个或多个pipeline的独立处理单元会话。会话是在“停用”状态下创建的,只有在试图激活会话时才会获得资源。会话是不可变的,并且拥有pipeline的所有资源。互斥会话在pipeline之间共享资源。非互斥会话为所有pipeline保留了足够的资源,pipeline可以从内存(offline)或通过流(realtime)获取输入数据。
销毁一个camera session。
此函数激活会话中的pipeline,然后可以开始向pipeline提交请求。
禁用pipeline,释放会话的资源。驱动程序会试图保留尽可能多的资源,以实现快速地重新激活会话。
在会话中向特定pipeline提交请求。
14.
15.
16.
17.
18.
19.
20.
参考文档:80-pc212-1_b_qualcomm_spectra_camera_chi_api_reference.pdf
来源:博客园
作者:KKKEr
链接:https://www.cnblogs.com/KKKEr/p/11489237.html