美文网首页
高通 Camx-CHI usecase

高通 Camx-CHI usecase

作者: 程序员Android1 | 来源:发表于2023-07-02 09:24 被阅读0次

    和你一起终身学习,这里是程序员 Android

    经典好文推荐,通过阅读本文,您将收获以下知识点:

    一、Camx ,Chi-cdk 概览
    二、Camx-CHI 组件
    三、参考文献

    一、Camx ,Chi-cdk 概览

    程序员Android 转于网络

    Camx 通过dlopen camera.qcom.so 获取CHI操作接口,CHI 通过dlopen com.qti.chi.override.so 获取Camx 操作的接口。

    1.1 Camx

    Camx 代码路径vendor/qcom/proprietary/camx/src
    主要目录如下:

    ├── chioverride 存放CHI 实现的核心模块,负责与Camx 进行交互并实现CHI的总体框架及具体业务的处理
    ├── bin  存放平台相关的配置项
    ├── core(hal/chi)  Camx 核心实现,主要用于实现hal3接口的hal/目录,以及负责与CHI进行交互的chi/目录
    ├── csl  主要负责camx与camera driver的通讯模块,为camx提供了统一的Camera driver控制接口
    ├── hwl  存放自身具有独立运算能力的硬件node,该部分node受csl管理
    └── swl  存放自身并不具有独立运算能力,必须依靠CPU才能实现的node
    
    1.2 Chi-cdk

    Chi-cdk 代码路径/vendor/qcom/proprietary/chi-cdk/oem/qcom
    主要目录如下:

    ├── actuator    对焦马达相关的配置信息
    ├── eeprom      存放着eeprom外部存储模块的配置信息
    ├── feature2    存放Feature 相关的信息,比如HDR QCFA fusion等
    ├── node    存放用户自定义功能的node
    ├── sensor  存放不同sensor的私有信息以及寄存器配置参数
    ├── tuning  效果参数
    ├── fd  人脸
    ├── flash   闪光灯
    ├── module  存放不同sensor的配置文件,该部分在初始化sensor的时候需要用到
    ├── ois 光学防抖相关的配置信息
    └── topology 存放用户自定的Usecase xml配置文件
    

    二、Camx-CHI 组件

    Camx-CHI 组件关系图如下:

    程序员Android 转于网络

    由上图可以看到,几者是通过包含关系组合起来的,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所要实现的功能,比如降噪、虚化等等。

    2.1 UseCase 类

    UseCase 类提供一系列通用接口,包含了多条实现特定功能的Pipeline(比如:ZSL 、EIS 、SAT 、DualCamera等),主要负责其中的业务处理以及资源的管理。

    程序员Android 转于网络

    UseCase 类作为现有的所有Usecase的基类,其中,AdvancedCameraUsecase(vendor/qcom/proprietary/chi-cdk/core/chiusecase/chxadvancedcamerausecase.cpp)
    又继承CameraUsecaseBase(chxadvancedcamerausecase.cpp 内部类)
    CameraUsecaseBase 的主要方法有:

    CameraUsecaseBase::Initialize(){}
    CameraUsecaseBase::CreateSession(){}
    CameraUsecaseBase::CreatePipeline(){}
    CameraUsecaseBase::ExecuteCaptureRequest(){}
    CameraUsecaseBase::SessionCbCaptureResult(){}
    CameraUsecaseBase::SessionProcessResult(){}
    

    相机中绝大部分场景会通过实例化AdvancedCameraUsecase来完成,它包括了几个主要接口

    AdvancedCameraUsecase::Create()
    创建AdvancedCameraUsecase实例,在其构造方法中会去获取XML中的相应的Usecase配置信息。

    AdvancedCameraUsecase::ExecuteCaptureRequest()
    下发一次Request请求

    AdvancedCameraUsecase::ProcessDriverPartialCaptureResult()
    该方法会在创建Session的过程中,作为回调方法注册到其中,一旦Session中产生了partial meta data的时候,便会调用该方法将其发送至AdvancedCameraUsecase中

    AdvancedCameraUsecase::ProcessMessage(){}
    该方法会在创建Session的过程中,作为回调方法注册到其中,一旦Session产生任何事件,便会调用该方法通知到AdvancedCameraUsecase中

    AdvancedCameraUsecase::ExecuteFlush(){}
    该方法用于刷新AdvancedCameraUsecase

    AdvancedCameraUsecase::Initialize(){}
    初始化AdvancedCameraUsecase ,并打印出输入的configurestream 数据流信息,比如: Usecase UsecaseZSL selected

    AdvancedCameraUsecase::SelectFeatures(){}
    AdvancedCameraUsecase::FeatureSetup(){}
    AdvancedCameraUsecase::BuildUsecase(){}
    AdvancedCameraUsecase::SelectUsecaseConfig(){}
    AdvancedCameraUsecase::PipelineCreated(){}
    AdvancedCameraUsecase::PostUsecaseCreation(){}
    AdvancedCameraUsecase::ConfigureStream(){}
    AdvancedCameraUsecase::BuildUsecase(){}
    AdvancedCameraUsecase::SetPipelineCameraId(){}
    

    UseCase 标签

    比如:UsecaseZSL 的流程

    <Usecase>
     // UsecaseName: 代表了该Usecase的名字,后期根据这个名字找到这个Usecase的定义。
        <UsecaseName>UsecaseZSL</UsecaseName>
        <!--Target Buffers-->
    //Targets: 用于表示用于输出的数据流的集合,其中包括了数据流的格式,输出Size的范围等。
        <Targets>
            <Target>
                <TargetName>TARGET_BUFFER_CUSTOM_YUV</TargetName>
                <TargetDirection>TargetOutput</TargetDirection>
                <CamxInclude target_segment="0_0_5312_3456_ChiFormatYUV420NV12_ChiFormatYUV420NV21"/>
            </Target>
    ... ...
        </Targets>
    //Pipeline: 用于定义该Usecase可以是使用的所有Pipeline,这里必须至少定义一条Pipeline
        <CamxInclude href="../../pipelines/g_camxZSLPreviewRaw.xml" />
    
    </Usecase>
    

    UsecaseName:
    代表了该Usecase的名字,后期根据这个名字找到这个Usecase的定义。

    Targets:
    用于表示用于输出的数据流的集合,其中包括了数据流的格式,输出Size的范围等。

    Pipeline:
    用于定义该Usecase可以是使用的所有Pipeline,这里必须至少定义一条Pipeline。

    2.2 Feature

    Feature 受 Usecase 统一管理,需要多条Pipeline 组合实现,没有在XML中定义,主要是在UseCase中完成AdvancedCameraUsecase::FeatureSetup()创建以及Feature的选取AdvancedCameraUsecase::SelectFeatures,进而可以和Usecase进行资源互访。
    常用的Feature 方法有多帧,多帧降噪,超级夜景,人像超分,ZSL零延时拍照,ZSD自动场景识别,HDR,QCFA 等等


    程序员Android 转于网络

    几个常用的Feature:

    FeatureHDR: 用于实现HDR功能,它负责管理内部的一条或者几条pipeline的资源以及它们的流转,最终输出具有HDR效果的图像。

    FeatureMFNR: 用于实现MFNR功能,内部分为几个大的流程,分别包括Prefiltering、Blending、Postfilter以及最终的OfflineNoiseReproces(这一个是可选择使能的),每一个小功能中包含了各自的pipeline。

    FeatureASD: 用于AI功能的实现,在预览的时候,接收每一帧数据,并且进行分析当前场景的AI识别输出结果,并其通过诸如到metadata方式给到上层,进行后续的处理

    2.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句柄,停止硬件的数据传输。

    2.4 Pipeline

    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中被定义,其主要包含了以下几个标签定义:

    <Pipeline>
      <PipelineName>ZSLPreviewRaw</PipelineName>                  //PipelineName:   用来定义该条Pipeline的名称
      
      <NodesList>                                                //NodeList: 该标签中定义了该条Pipeline的所有的Node
        <Node>
            ...  ...
        </Node>
      </NodesList>
    
      <PortLinkages> // PortLinkages: 该标签定义了Node上不同端口之间的连接关系
        <Link>
          <SrcPort> //SrcPort : 输入端口
            ...  ...
          </SrcPort>
          <DstPort> //DstPort : 输出端口
            ...  ...
          </DstPort>
        </Link>
      </PortLinkages>
    </Pipeline>
    

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

    2.5 Node

    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。

    <Node>
          <NodeName>FDManager</NodeName>                          //NodeName:   用来定义该Node的名称
          <NodeId>8</NodeId>                                      //NodeId: 用来指定该Node的ID,其中IPE NodeId为65538,IFE NodeId为65536,用户自定义的NodeId为255。
          <NodeInstance>FDManagerInstanceName0</NodeInstance>    //NodeInstance: 用于定义该Node的当前实例的名称。
          <NodeInstanceId>0</NodeInstanceId>    //NodeInstanceId: 用于指定该Node实例的Id。
    </Node>
    
    
    2.6 Link

    Link 用于定义不同Port的连接,一个Port可以根据需要建立多条与其它从属于不同Node的Port的连接,它通过标签来进行定义,其中包括了作为输入端口,作为输出端口。

    一个Link中包含了一个SrcPort和一个DstPort,分别代表了输入端口和输出端口,然后BufferProperties用于表示两个端口之间的buffer配置

        <Link>
          <SrcPort> //SrcPort : 输入端口
          </SrcPort>
          <DstPort> //DstPort : 输出端口
          ...  ...
          </DstPort>
        </Link>
    

    SrcPort : 输入端口
    DstPort : 输出端口

    2.7 Port 标签

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

    Port 标签 举例如下:

          <SrcPort> //SrcPort : 输入端口
            <PortName>FDManagerOutputPortResults</PortName>        //PortName:  该端口的名称
            <PortId>0</PortId>  //PortId:   该端口的Id:
            <NodeName>FDManager</NodeName>  //NodeName: 该端口从属的Node名称
            <NodeId>8</NodeId>
            <NodeInstance>FDManagerInstanceName0</NodeInstance> //NodeInstance: 该端口从属的Node的实例名称
            <NodeInstanceId>0</NodeInstanceId>  //NodeInstanceId: 该端口从属的Node的实例的Id
          </SrcPort>
    
          <DstPort> //DstPort : 输出端口
            <PortName>TARGET_BUFFER_DISPLAY</PortName>  //PortName: 该端口的名称
            <PortId>0</PortId>  //PortId:   该端口的Id:
            <NodeName>SinkNoBuffer</NodeName>   //NodeName: 该端口从属的Node名称
            <NodeId>3</NodeId>  //NodeId: 该端口从属的Node的Id
            <NodeInstance>SinkInstanceName4</NodeInstance>  //NodeInstance: 该端口从属的Node的实例名称
            <NodeInstanceId>4</NodeInstanceId>  //NodeInstanceId: 该端口从属的Node的实例的Id
          </DstPort>
    

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

    2.8 Pipeline Node Link Pork 举例

    Pipeline 使用到的一条Pipeline 举例如下:

    vendor\qcom\proprietary\chi-cdk\oem\qcom\topology\mimas\usecase-components\usecases\UsecaseZSL\pipelines\camxZSLPreviewRaw.xml

    <Pipeline>
      <PipelineName>ZSLPreviewRaw</PipelineName>                  //PipelineName:   用来定义该条Pipeline的名称
      <CamxInclude segment="RealTimePreviewWithStatsSegment" />
      <CamxInclude segment="VGPUFDSegment" />
      <NodesList>                                                //NodeList: 该标签中定义了该条Pipeline的所有的Node
        <Node>
          <NodeName>FDManager</NodeName>                          //NodeName:   用来定义该Node的名称
          <NodeId>8</NodeId>                                      //NodeId: 用来指定该Node的ID,其中IPE NodeId为65538,IFE NodeId为65536,用户自定义的NodeId为255。
          <NodeInstance>FDManagerInstanceName0</NodeInstance>    //NodeInstance: 用于定义该Node的当前实例的名称。
          <NodeInstanceId>0</NodeInstanceId>    //NodeInstanceId: 用于指定该Node实例的Id。
        </Node>
      </NodesList>
      <PortLinkages> // PortLinkages: 该标签定义了Node上不同端口之间的连接关系
        <Link>
          <SrcPort> //SrcPort : 输入端口
            <PortName>FDManagerOutputPortResults</PortName>        //PortName:  该端口的名称
            <PortId>0</PortId>  //PortId:   该端口的Id:
            <NodeName>FDManager</NodeName>  //NodeName: 该端口从属的Node名称
            <NodeId>8</NodeId>
            <NodeInstance>FDManagerInstanceName0</NodeInstance> //NodeInstance: 该端口从属的Node的实例名称
            <NodeInstanceId>0</NodeInstanceId>  //NodeInstanceId: 该端口从属的Node的实例的Id
          </SrcPort>
    
          <DstPort> //DstPort : 输出端口
            <PortName>TARGET_BUFFER_DISPLAY</PortName>  //PortName: 该端口的名称
            <PortId>0</PortId>  //PortId:   该端口的Id:
            <NodeName>SinkNoBuffer</NodeName>   //NodeName: 该端口从属的Node名称
            <NodeId>3</NodeId>  //NodeId: 该端口从属的Node的Id
            <NodeInstance>SinkInstanceName4</NodeInstance>  //NodeInstance: 该端口从属的Node的实例名称
            <NodeInstanceId>4</NodeInstanceId>  //NodeInstanceId: 该端口从属的Node的实例的Id
          </DstPort>
        </Link>
    ...  ...
      </PortLinkages>
    </Pipeline>
    
    
    2.9 算法对应SO 文件命名规则

    com.<厂商>.<分类>.<算法>.so

    比如:com.qti.3a.aec.so

    三、参考文献

    1.【腾讯文档】Camera学习知识库
    https://docs.qq.com/doc/DSWZ6dUlNemtUWndv

    2.https://blog.csdn.net/u012596975/article/details/107138576
    至此,本篇已结束。转载网络的文章,小编觉得很优秀,欢迎点击阅读原文,支持原创作者,如有侵权,恳请联系小编删除,欢迎您的建议与指正。同时期待您的关注,感谢您的阅读,谢谢!

    相关文章

      网友评论

          本文标题:高通 Camx-CHI usecase

          本文链接:https://www.haomeiwen.com/subject/fvwrrdtx.html