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

本篇文章次要介绍 Android 开发中的局部知识点,通过浏览本篇文章,您将播种以下内容:

一、概览
二、根本组件概念
三、组件构造关系
四、要害流程详解

一、概览

回顾高通平台Camera HAL历史,之前高通采纳的是QCamera & MM-Camera架构,然而为了更精细化管制底层硬件(Sensor/ISP等要害硬件),同时不便手机厂商自定义一些性能,当初提出了CamX-CHI架构,因为在CamX-CHI中齐全看不到之前老架构的影子,所以它齐全是一个全新的架构,它将一些高度对立的功能性接口抽离进去放到CamX中,将可定制化的局部放在CHI中供不同厂商进行批改,实现各自独有的特色性能,这样设计的益处不言而喻,那便是即使开发者对于CamX并不是很理解,然而仍然能够很不便的退出自定义的性能,从而升高了开发者在高通平台的开发门槛。

接下来咱们以最直观的目录构造动手对该架构做一个简略的意识,以下便是CamX-CHI根本目录构造:

该局部代码次要位于 vendor/qcom/proprietary/ 目录下:

其中 camx 代表了通用功能性接口的代码实现汇合(CamX),chi-cdk代表了可定制化需要的代码实现汇合(CHI),从图中能够看出Camx局部对上作为HAL3接口的实现,对下

通过v4l2框架与Kernel放弃通信,两头通过相互dlopen so库并获取对方操作接口的形式放弃着与CHI的交互。

camx/中有如下几个次要目录:

  • core/ : 用于寄存camx的外围实现模块,其中还蕴含了次要用于实现hal3接口的hal/目录,以及负责与CHI进行交互的chi/目录
  • csl/: 用于寄存次要负责camx与camera driver的通信模块,为camx提供了对立的Camera driver管制接口
  • hwl/: 用于寄存本身具备独立运算能力的硬件node,该局部node受csl治理
  • swl/: 用于寄存本身并不具备独立运算能力,必须依附CPU能力实现的node

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

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

二、根本组件概念

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文件中,这里简略介绍其中的几个次要的标签含意:
Usecase

  • UsecaseName: 代表了该Usecase的名字,前期依据这个名字找到这个Usecase的定义。
  • Targets: 用于示意用于输入的数据流的汇合,其中包含了数据流的格局,输入Size的范畴等。
  • Pipeline: 用于定义该Usecase能够是应用的所有Pipeline,这里必须至多定义一条Pipeline。

2. Feature

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

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

几个罕用的Feature:

  • FeatureHDR: 用于实现HDR性能,它负责管理外部的一条或者几条pipeline的资源以及它们的流转,最终输入具备HDR成果的图像。
  • FeatureMFNR: 用于实现MFNR性能,外部分为几个大的流程,别离包含Prefiltering、Blending、Postfilter以及最终的OfflineNoiseReproces(这一个是可抉择使能的),每一个小性能中蕴含了各自的pipeline。
  • FeatureASD: 用于AI性能的实现,在预览的时候,接管每一帧数据,并且进行剖析以后场景的AI辨认输入后果,并其通过诸如到metadata形式给到下层,进行后续的解决。

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. 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上不同端口之间的连贯关系

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。

6. Link

用于定义不同Port的连贯,一个Port能够依据须要建设多条与其它从属于不同Node的Port的连贯,它通过标签来进行定义,其中包含了作为输出端口,作为输入端口。
一个Link中蕴含了一个SrcPort和一个DstPort,别离代表了输出端口和输入端口,而后BufferProperties用于示意两个端口之间的buffer配置。

7. Port

作为Node的输入输出的端口,在XML文件中,标签用来定义一个输出端口,标签用来定义输入端口,每一个Node都能够依据须要应用一个或者多个输入输出端口,应用OutputPort以及InputPort构造体来进行在代码中定义。
Port

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

三、组件构造关系

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

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

四、要害流程详解

1. Camera Provider 启动初始化

当系统启动的时候,Camera Provider主程序会被运行,在整个程序初始化的过程中会通过获取到的camera_module_t调用其get_number_of_camera接口获取底层反对的camera数量,因为是第一次获取,所以在CamX-CHI中会随同着很多初始化动作,具体操作见下图:

次要流程如下:

  1. 通过HAL3Module::GetInstance()静态方法实例化了HAL3Module对象,在其构造方法外面通过HwEnvironment::GetInstance()静态方法又实例化了HwEnvironment对象,在其构造方法中,实例化了SettingsManager对象,而后又在它构造方法中通过OverrideSettingsFile对象获取了位于/vendor/etc/camera/camoverridesettings.txt文件中的平台相干的配置信息(通过这种Override机制不便平台厂商退出自定义配置),该配置文件中,能够退出平台特定的配置项,比方能够通过设置multiCameraEnable的值来示意以后平台是否反对多摄,或者通过设置overrideLogLevels设置项来配置CamX-CHI局部的Log输入等级等等。
  2. 同时在HwEnvironment构造方法中会调用其Initialize办法,在该办法中实例化了CSLModeManager对象,并通过CSLModeManager提供的接口,获取了所有底层反对的硬件设施信息,其中包含了Camera Request Manager、CAPS模块(该驱动模块次要用于CSL获取Camera平台驱动信息,以及IPE/BPS模块的电源管制)以及Sensor/IPE/Flash等硬件模块,并且通过调用CSLHwInternalProbeSensorHW办法获取了以后设施装置的Sensor模组信息,并且将获取的信息暂存起来,期待后续阶段应用,总得来说在HwEnvironment初始化的过程中,通过探测办法获取了所有底层的硬件驱动模块,并将其信息存储下来供后续阶段应用。
  3. 之后通过调用HwEnvironment对象中的ProbeChiCompoents办法在/vendor/lib64/camera/components门路下找寻各个Node生成的So库,并获取Node提供的规范对外接口,这些Node岂但包含CHI局部用户自定义的模块,还包含了CamX局部实现的硬件模块,并最初都将其都存入ExternalComponentInfo对象中,期待后续阶段应用。

另外在初始化阶段还有一个比拟重要的操作就是CamX 与CHI是通过相互dlopen对方的So库,获取了对方的入口办法,最初通过彼此的入口办法获取了对方操作方法汇合,之后再通过这些操作方法与对方进行通信,其次要流程见下图:

从上图不难看出,在HAL3Module构造方法中会去通过dlopen办法加载com.qti.chi.override.so库,并通过dlsym映射出CHI局部的入口办法chi_hal_override_entry,并调用该办法将HAL3Module对像中的成员变量m_ChiAppCallbacks(CHIAppCallbacks)传入CHI中,其中蕴含了很多函数指针,这些函数指针别离对应着CHI局部的操作方法集中的办法,一旦进入到CHI中,就会将CHI本地的操作方法汇合中的函数地址顺次赋值给m_ChiAppCallbacks,这样CamX后续就能够通过这个成员变量调用到CHI中办法,从而放弃了与CHI的通信。

同样地,CHI中的ExtensionModule在初始化的时候,其构造方法中也会通过调用dlopen办法加载camera.qcom.so库,并将其入口办法ChiEntry通过dlsym映射进去,之后调用该办法,将g_chiContextOps(ChiContextOps,该构造体中定义了很多指针函数)作为参数传入CamX中,一旦进入CamX中,便会将本地的操作方法地址顺次赋值给g_chiContextOps中的每一个函数指针,这样CHI之后就能够通过g_chiContextOps拜访到CamX办法。

2. 关上相机设施/初始化相机设施

一旦用户关上了相机利用,App中便会去调用CameraManager的openCamera办法,该办法之后会最终调用到Camera Service中的CameraService::connectDevice办法,而后通过ICameraDevice::open()这一个HIDL接口告诉Provider,而后在Provider外部又通过调用之前获取的camera_module_t中methods的open办法来获取一个Camera 设施,对应于HAL中的camera3_device_t构造体,紧接着,在Provider中会持续调用获取到的camera3_device_t的initialize办法进行初始化动作。接下来咱们便来详细分析下CamX-CHI对于open以及initialize的具体实现流程:

a) open

该办法是camera_module_t的规范办法,次要用来获取camera3_device_t设施构造体的,CamX-CHI对其进行了实现,open办法中实现的工作次要有以下几个:

  1. 将以后camera id传入CHI中进行remap操作,当然这个remap操作逻辑齐全是依据CHI中用户需要来的,用户能够依据本人的须要在CHI中退出自定义remap逻辑。
  2. 实例化HALDevice对象,其构造函数中调用Initialize办法,该办法会填充CamX中自定义的Camera3Device构造体。
  3. 将m_HALCallbacks.process_capture_result指向了本地办法ProcessCaptureResult以及m_HALCallbacks.notify_result指向了本地办法Notify(之后会在配置数据流的过程中,将m_HALCallbacks注册到CHI中, 一旦当CHI数据处理实现之后,便会通过这两个回调办法将数据或者事件回传给CamX)。
  4. 最初将HALDevice 中的Camera3Device成员变量作为返回值给到Provider中的CameraCaptureSession中。

Camera3Device 其实重定义了camera3_device_t,其中HwDevice对应于camera3_device_t中的hw_device_t,Camera3DeviceOps对应于camera3_device_ops_t,而在HALDevice的初始化过程中,会将CamX实现的HAL3接口的构造体g_camera3DeviceOps赋值给Camera3DeviceOps中。

b) initialize

该办法在调用open后紧接着被调用,次要用于将下层的回调接口传入HAL中,一旦有数据或者事件产生,CamX便会通过这些回调接口将数据或者事件上传至调用者,其外部的实现较为简单。

initialize办法中有两个参数,别离是之前通过open办法获取的camera3_device_t构造体和实现了camera3_callback_ops_t的CameraDevice,很显然camera3_device_t构造体并不是重点,所以该办法的次要工作是将camera3_callback_ops_t与CamX关联上,一旦数据筹备实现便通过这里camera3_callback_ops_t中回调办法将数据回传到Camera Provider中的CameraDevice中,根本流程能够总结为以下几点:

  1. 实例化了一个Camera3CbOpsRedirect对象并将其退出了g_HAL3Entry.m_cbOpsList队列中,这样不便之后须要的时候可能顺利拿到该对象。
  2. 将本地的process_capture_result以及notify办法地址别离赋值给Camera3CbOpsRedirect.cbOps中的process_capture_result以及notify函数指针。
  3. 将下层传入的回调办法构造体指针pCamera3CbOpsAPI赋值给Camera3CbOpsRedirect.pCbOpsAPI,并将Camera3CbOpsRedirect.cbOps赋值给pCamera3CbOpsAPI,通过JumpTableHal3的initialize办法将pCamera3CbOpsAPI传给HALDevice中的m_pCamera3CbOps成员变量,这样HALDevice中的m_pCamera3CbOps就指向了CamX中本地办法process_capture_result以及notify。

通过这样的一番操作之后,一旦CHI有数据传入便会首先进入到本地办法ProcessCaptureResult,而后在该办法中获取到HALDevice的成员变量m_pCamera3CbOps,进而调用m_pCamera3CbOps中的process_capture_result办法,即camxhal3entry.cpp中定义的process_capture_result办法,而后这个办法中会去调用JumpTableHAL3.process_capture_result办法,该办法最终会去调用Camera3CbOpsRedirect.pCbOpsAPI中的process_capture_result办法,这样就调到从Provider传入的回调办法,将数据顺利给到了CameraCaptureSession中。

3. 配置相机设施数据流

在关上相机利用过程中,App在获取并关上相机设施之后,会调用CameraDevice.createCaptureSession来获取CameraDeviceSession,并且通过Camera api v2标准接口,告诉Camera Service,调用其CameraDeviceClient.endConfigure办法,在该办法外部又会去通过HIDL接口ICameraDeviceSession::configureStreams_3_4告诉Provider开始解决此次配置需要,在Provider外部,会去通过在调用open流程中获取的camera3_device_t构造体的configure_streams办法来将数据流的配置传入CamX-CHI中,之后由CamX-CHI实现对数据流的配置工作,接下来咱们来详细分析下CamX-CHI对于该规范HAL3接口 configure_streams的具体实现:

配置数据流是整个CamX-CHI流程比拟重要的一环,其中次要包含两个阶段

  1. 抉择UsecaseId
  2. 依据抉择的UsecaseId创立Usecase

接下来咱们就这两个阶段别离进行具体介绍:

① 抉择UsecaseId

不同的UsecaseId别离对应的不同的利用场景,该阶段是通过调用UsecaseSelector::GetMatchingUsecase()办法来实现的,该函数中通过传入的operation_mode、num_streams配置数据流数量以及以后应用的Sensor个数来抉择相应的UsecaseId,比方当numPhysicalCameras值大于1同时配置的数据流数量num_streams大于1时抉择的就是UsecaseId::MultiCamera,示意以后采纳的是双摄场景。

② 创立Usecase
依据之前抉择的UsecaseId,通过UsecaseFactory来创立相应的Usecase,
其中Class Usecase是所有Usecase的基类,其中定义并实现了一些通用接口,CameraUsecaseBase继承于Usecase,并扩大了局部性能。AdvancedCameraUsecase又继承于CameraUsecaseBase,作为次要负责大部分场景的Usecase实现类,另外对于多摄场景,现提供了继承于AdvancedCameraUsecase的UsecaseMultiCamera来负责实现。

除了双摄场景,其它大部分场景应用的都是AdvancedCameraUsecase类来治理各项资源的,接下来咱们重点梳理下AdvancedCameraUsecase::Create()办法。
在AdvancedCameraUsecase::Create办法中做了很多初始化操作,其中包含了以下几个阶段:

  1. 获取XML文件中Usecase配置信息
  2. 创立Feature
  3. 保留数据流,重建Usecase的配置信息
  4. 调用父类CameraUsecaseBase的initialize办法,进行一些惯例初始化工作

接下来咱们就这几个阶段逐个进行剖析:
1. 获取XML文件中Usecase配置信息
这一部分次要通过调用CameraUsecaseBase::GetXMLUsecaseByName办法进行实现。
该办法的次要操作是从PerNumTargetUsecases数组中找到匹配到给定的usecaseName的Usecase,并作为返回值返回给调用者,其中这里咱们以"UsecaseZSL“为例进行剖析,PerNumTargetUsecases的定义是在g_pipeline.h中,该文件是在编译过程中通过usecaseconverter.pl脚本将定义在个平台目录下的common_usecase.xml中的内容转换生成g_pipeline.h。

2.创立Feature
如果以后场景选取了Feature,则调用FeatureSetup来实现创立工作。
该办法次要是通过诸如operation_mode、camera数量以及UsecaseId等信息来决定须要抉择哪些Feature,具体逻辑比拟清晰,一旦决定须要应用哪一个Feature之后,便调用相应的Feature的Create()办法进行初始化操作。

3.保留数据流,重建Usecase的配置信息
从Camera Service 传入的数据流,须要将其存储下来,供后续应用,同时高通针对Usecase也退出了Override机制,依据须要能够选择性地扩大Usecase,这两个步骤的实现次要是通过SelectUsecaseConfig办法来实现。

其中次要是调用以下两个办法来实现的:

  • ConfigureStream: 该办法将从下层配置的数据流指针存入AdvancedCameraUsecase中,其中包含了用于预览的m_pPreviewStream以及用于拍照的m_pSnapshotStream。
  • BuildUsecase: 这个办法用来从新在原有的Usecase下面退出了Feature中所须要的pipeline,并创立了一个新的Usecase,并将其存入AdvancedCameraUsecase中的m_pChiUsecase成员变量中,紧接着通过SetPipelineToSessionMapping办法将pipeline与Session进行关联。

4.调用父类CameraUsecaseBase的initialize办法,进行一些惯例初始化工作

该办法中的操作次要有以下三个:

  • 设置Session回调
  • 创立Pipeline
  • 创立Session

设置Session回调

该办法有两个参数,第二个是缺省的,第一个是ChiCallBacks,该参数是作为创立的每一条Session的回调办法,当Session中的pipeline全副跑完之后,会回调该办法将数据投递到CHI中。

创立Pipeline
依据之前获取的pipeline信息开始创立每一条pipeline,通过调用CreatePipeline()办法实现。

创立Session
创立Session,通过CreateSession()办法实现,此时会将AdvancedCameraUsecase端的回调函数注册到Session中,一旦Session中数据处理实现,便会调用回调将数据回传给AdvancedCameraUsecase。

综上,整个configure_stream过程,根本能够概括为以下几点:

  1. 依据operation_mode、camera 个数以及stream的配置信息选取了对应的UsecaseId
  2. 依据所选取的UsecaseId,应用UsecaseFactory简略工厂类创立了用于治理整个场景下所有资源的AdvancedCameraUsecase对象。
  3. 创立AdvancedCameraUsecase对象是通过调用其Create()办法实现,该办法中获取了common_usecase.xml定义的对于Usecase的配置信息,之后又依据须要创立了Feature并选取了Feature所需的pipeline,并通过Override机制将Feature中所须要的Pipeline退出重建后的Usecase中。
  4. 最初通过调用CameraUsecaseBaese的initialize办法顺次创立了各个pipeline以及Session,并且将AdvancedCameraUsecase的成员办法注册到Session,用于Session将数据返回给Usecase中

4. 解决拍照申请

当用户关上相机利用进行预览或者点击一次拍照操作的时候,便触发了一次拍照申请,该动作首先通过CameraDeviceSession的capture或者setRepeatingRequest办法将申请通过Camera api v2接口下发到Camera Service中,而后在Camera Service外部将此次申请发送到CameraDevice::RequestThread线程中进行解决,一旦进入到该线程之后,便会最终通过HIDL接口ICameraCaptureSession:processCaptureRequest_3_4将申请发送至Provider中,之后当Provider收到申请之后,会调用camera3_device_t构造体的process_capture_request开始了HAL针对此次Request的解决,而该解决是由CamX-CHI来负责实现,当初咱们就来看下CamX-CHI是如何实现该办法的:

首先CamX中会将此次request转发到HALDevice中,再通过HALDevice对象调用之前初始化的时候获取的CHI局部的回调接口m_ChiAppCallbacks.chi_override_process_request办法(chi_override_process_request办法的定义位于chxextensioninterface.cpp中)将request发送到CHI局部。

在chi_override_process_request办法中会去获取ExtensionModule对象,并将request发送到ExtensionModule对象中,该对象中存储了之前创立的Usecase对象,而后通过层层调用,最终会调用AdvancedCameraUsecase的ExecuteCaptureRequest办法,该办法负责解决此次Request,具体流程如下:

在AdvancedCameraUsecase的ExecuteCaptureRequest中会有两个次要的分支来别离解决:

  • 如果以后并没有任何Feature须要实现,此时便会走默认流程,依据下面的流程图所示,这里会调用CameraUsecaseBase::ExecuteCaptureRequest办法,在该办法中,首先会将request取出,从新封装成CHICAPTUREREQUEST,而后调用CheckAndActivatePipeline办法唤醒pipeline,这一操作到最初会调到Session的StreamOn办法,在唤醒了pipeline之后,持续往下执行,再将封装后的Request发送到CamX中,最终调用到相应的Session::ProcessCaptureRequest办法,此时Request就进入到了Session外部进行流转了。
  • 如果以后场景须要实现某个Feature,则间接调用Feature的ExecuteProcessRequest办法将此次request送入Feature中解决,最初仍然会调用到Session::StreamOn以及Session::ProcessCaptureRequest办法来别离实现唤醒pipeline以及下发request的到Session的操作。

该流程最终都会调用到两个比拟要害的办法Session::StreamOn以及Session::ProcessCaptureRequest,接下来针对这两个办法重点介绍下:

Session::StreamOn

从办法名称根本能够晓得该办法次要用于开始硬件的数据输入,具体点儿就是进行配置Sensor寄存器,让其开始出图,并且将以后的Session的状态告知每一Node,让它们在本人外部也做好解决数据的筹备,所以之后的相干Request的流转都是以该办法为前提进行的,所以该办法重要性可见一斑,其操作流程见下图:

Session的StreamOn办法中次要做了如下两个工作:

  • 调用FinalizeDeferPipeline()办法,如果以后pipeline并未初始化,则会调用pipeline的FinalizePipeline办法,这里办法外面会去针对每一个从属于以后pipeline的Node顺次做FinalizeInitialization、CreateBufferManager、NotifyPipelineCreated以及PrepareNodeStreamOn操作,FinalizeInitialization用于实现Node的初始化动作,NotifyPipelineCreated用于告诉Node以后Pipeline的状态,此时Node外部能够依据本身的须要作相应的操作,PrepareNodeStreamOn办法的次要是实现Sensor以及IFE等Node的管制硬件模块出图前的配置,其中包含了曝光的参数的设置,CreateBufferManagers办法波及到CamX-CHI中的一个十分重要的Buffer管理机制,用于Node的ImageBufferManager的创立,而该类用于治理Node中的output port的buffer申请/流转/开释等操作。
  • 调用Pipeline的StreamOn办法,外面会进一步告诉CSL局部开启数据流,并且调用每一个Node的OnNodeStreamOn办法,该办法会去调用ImageBufferManager的Activate(),该办法外面会去真正调配用于装载图像数据的buffer,之后会去调用CHI局部实现的用户自定义的Nod的pOnStreamOn办法,用户能够在该办法中做一些自定义的操作。

Session::ProcessCaptureRequest
针对每一次的Request的流转,都是以该办法为入口开始的,具体流程见下图:

上述流程能够总结为以下几个步骤:

  1. 通过调用Session的ProcessCaptureRequest办法进入到Session,而后调用Pipeline中的ProcessRequest办法告诉Pipeline开始解决此次Request。
  2. 在Pipeline中,会先去调用外部的每一个Node的SetupRequest办法别离设置该Node的Output Port以及Input Port,之后通过调用DRQ(DeferredRequestQueue)的AddDeferredNode办法将所有的Node退出到DRQ中,其中DRQ中有两个队列别离是用于保留没有依赖项的Node的m_readyNodes以及保留处于期待依赖关系满足的Node的m_deferredNodes,当调用DRQ的DispatchReadyNodes办法后,会开始从m_readyNodes队列中取出Node调用其ProcessRequest开始进入Node外部解决本次request,在处理过程中会更新meta data数据,并更新至DRQ中,当该Node解决实现之后,会将处于m_deferredNodes中的已无依赖关系的Node移到m_readyNodes中,并再次调用DispatchReadyNodes办法从m_readyNodes取出Node进行解决。
  3. 与此过程中,当Node的数据处理实现之后会通过CSLFenceCallback告诉到Pipeline,此时Pipeline会判断以后Node的Output port 是否是Sink Port(输入到CHI),如果不是,则会更新依赖项到DRQ中,并且将不存在依赖项的Node移到m_readyNodes队列中,而后调用DispatchReadyNdoes持续进入到DRQ中流转,如果是Sink Port,则示意此Node是整个Pipeline的最末端,调用sinkPortFenceSignaled将数据给到Session中,最初通过调用Session中的NotifyResult将后果发送到CHI中。

DeferredRequestQueue

上述流程外面中波及到DeferredRequestQueue这个概念,这里简略介绍下:
DeferredRequestQueue继承于IPropertyPoolObserver,实现了OnPropertyUpdate/OnMetadataUpdate/OnPropertyFailure/OnMetadataFailure接口,这几个接口用于接管Meta Data以及Property的更新,另外,DRQ次要蕴含了以下几个次要办法:

  • Create()
    该办法用于创立DRQ,其中创立了用于存储依赖信息的m_pDependencyMap,并将本人注册到MetadataPool中,一旦有meta data或者property更新便会通过类中实现的几个接口告诉到DRQ。
  • DispatchReadyNodes()
    该办法次要用于将处于m_readyNodes队列的Node取出,将其投递到m_hDeferredWorker线程中进行解决。
  • AddDeferredNode()
    该办法次要用于增加依赖项到m_pDependencyMap中。
  • FenceSignaledCallback()
    当Node外部针对某次request解决实现之后,会通过一系列回调告诉到DRQ,而其调用的办法便是该办法,在该办法中,会首先调用UpdateDependency更新依赖项,而后调用DispatchReadyNodes触发开始对处于ready状态的Node开始进行解决
  • OnPropertyUpdate()
    该办法是定义于IPropertyPoolObserver接口,DRQ实现了它,次要用于接管Property更新的告诉,并在外部调用UpdateDependency更新依赖项。
  • OnMetadataUpdate()
    该办法是定义于IPropertyPoolObserver接口,DRQ实现了它,次要用于接管Meta data更新的告诉,并在外部调用UpdateDependency更新依赖项。
  • UpdateDependency()
    该办法用于更新Node的依赖项信息,并且将没有依赖的Node从m_deferredNodes队列中移到m_readyNodes,这样该Node就能够在之后的某次DispatchReadyNodes调用之后投入运行。
  • DeferredWorkerWrapper()
    该办法是m_hDeferredWorker线程的处理函数,次要用于解决须要下发request的Node,同时再次更新依赖项,最初会再次调用DispatchReadyNodes开始解决。

其中须要留神的是,Pipeline首次针对每一个Node通过调用AddDeferredNode办法退出到DRQ中,此时所有的Node都会退出到m_readyNodes中,而后通过调用dispatchReadyNodes办法,触发DRQ开始进行整个外部解决流程,根本流程能够参见下图,接下来就以该图进行深刻梳理下:

  1. 当调用了DRQ的dispatchReadyNodes办法后,会从m_readyNodes链表外面顺次取出Dependency,将其投递到DeferredWorkerWrapper线程中,在该线程会从Dependency取出Node调用其ProcessRequest办法开始在Node外部解决本次request,解决实现之后如果以后Node仍然存在依赖项,则调用AddDeferredNode办法将Node再次退出到m_deferredNodes链表中,并且退出新的依赖项,存入m_pDependencyMap hash表中。
  2. 在Node解决request的过程中,会继续更新meta data以及property,此时会通过调用MetadataSlot的PublishMetadata办法更新到MetadataPool中,此时MetadataPool会调用之前在DRQ初始化时候注册的几个回调办法OnPropertyUpdate以及OnMetadataUpdate办法告诉DRQ,此时有新的meta data 和property更新,接下来会在这两个办法中调用UpdateDependency办法,去更新meta data 和property到m_pDependencyMap中,并且将没有任何依赖项的Node从m_deferredNodes取出退出到m_readyNodes,期待解决。
  3. 与此同时,Node的处理结果也会通过ProcessFenceCallback办法告诉pipeline,并且调用pipeline的NonSinkPortFenceSignaled办法,在该办法外部又会去调用DRQ的FenceSignaledCallback办法,而该办法又会调用UpdateDependency更新依赖,并将依赖项都满足的Node从m_deferredNodes取出退出到m_readyNodes,而后调用dispatchReadyNodes持续进行解决。

5. 上传拍照后果

在用户开启了相机利用,相机框架收到某次Request申请之后会开始对其进行解决,一旦有图像数据产生便会通过层层回调最终返回到应用层进行显示,这里咱们针对CamX-CHI局部对于拍照后果的上传流程进行一个简略的梳理:

每一个Request对应了三个Result,别离是partial metadata、metadata以及image data,对于每一个Result,上传过程能够大抵分为以下两个阶段:

  • Session外部实现图像数据的解决,将后果发送至Usecase中
  • Usecase接管到来自Session的数据,并将其上传至Provider

首先来看下Session外部实现图像数据的解决后是如何将后果发送至Usecase的:

在整个requets流转的过程中,一旦Node中有Partial Meta Data产生,便会调用Node的ProcessPartialMetadataDone办法去告诉隶属的Pipeline,其外部又调用了pipeline的NotifyNodePartialMetadataDone办法。每次调用Pipeline的NotifyNodePartialMetadataDone办法都会去将pPerRequestInfo→numNodesPartialMetadataDone加一并且判断以后值是否等于pipeline中的Node数量,一旦相等,便阐明以后所有的Node都实现了partial meta data的更新动作,此时,便会调用ProcessPartialMetadataRequestIdDone办法,外面会去取出partial meta data,并且从新封装成ResultsData构造体,将其作为参数通过Session的NotifyResult办法传入Session中,之后在Session中通过层层调用最终会调用到外部成员变量m_chiCallBacks的ChiProcessPartialCaptureResult办法,该办法正是创立Session的时候,传入Session中的Usecase的办法(AdvancedCameraUsecase::ProcessDriverPartialCaptureResultCb),通过该办法就将meta data返回到了CHI中。

同样地,Meta data的逻辑和Partial Meta Data很类似,每个Node在解决request的过程中,会调用ProcessMetadataDone办法将数据发送到Pipeline中,一旦所有的Node的meta data否发送实现了,pipeline会调用NotifyNodeMetadataDone办法,将最终的后果发送至Session中,最初通过层层调用,会调用Session 中成员变量m_chiCallBacks的ChiProcessCaptureResult办法,将后果发送到CHI中Usecase中。

图像数据的流转和前两个meta data的流转有点儿差别,一旦Node外部图像数据处理实现后便会调用其ProcessFenceCallback办法,在该办法中会去查看以后输入是否是SInk Buffer,如果是则会调用Pipeline的SinkPortFenceSignaled办法将数据发送到Pipeline中,在该办法中Pipeline又会将数据发送至Session中,最初通过层层调用,会调用Session 中成员变量m_chiCallBacks的ChiProcessCaptureResult办法,将后果发送到CHI中Usecase中。

接下来咱们来看下一旦Usecase接管到Session的数据,是如何发送至Provider的:

咱们以罕用的AdvancedCameraUsecase为例进行代码的梳理:

如上图所示,整个result的流转逻辑还是比拟清晰的,CamX通过回调办法将后果回传给CHI中,而在CHI中,首先判断是否须要发送到具体的Feature的, 如果须要,则调用相应Feature的ProcessDriverPartialCaptureResult或者ProcessResult办法将后果发送到具体的Feature中,一旦解决实现,便会调用调用CameraUsecaseBase的ProcessAndReturnPartialMetadataFinishedResults以及ProcessAndReturnFinishedResults办法将后果发送到Usecase中,如果以后不须要发送到Feature进行解决,就在AdvancedCameraUsecase中调用CameraUsecaseBase的SessionCbPartialCaptureResult以及SessionCbCaptureResult办法,而后通过Usecase::ReturnFrameResult办法将后果发送到ExtensionModule中,之后调用ExtensionModule中存储的CamX中的回调函数process_capture_result将后果发送到CamX中的HALDevice中,之后HALDevice又通过之前存储的下层传入的回调办法,将后果最终发送到CameraDeviceSession中。

通过以上的梳理,能够发现,整个CamX-CHI框架设计的很不错,目录构造清晰明确,框架简略高效,流程管制逻辑明显,比方针对某一图像申请,整个流程通过Usecase、Feature、Session、Pipeline并且给到具体的Node中进行解决,最终输入后果。另外,相比拟之前的QCamera & Mm-Camera框架的针对某个算法的扩大须要在整个流程代码中嵌入自定义的批改做法而言,CamX-CHI通过将自定义实现的放入CHI中,进步了其扩展性,升高了开发门槛,使得平台厂商在并不是很相熟CamX框架的状况下也能够通过小规模的批改胜利增加新性能。然而人无完人,框架也是一样,该框架异步化解决太多,加大了定位问题以及解决问题的难度,给开发者带来了不小的压力。另外,框架对于内存的要求较高,所以在一些低端机型尤其是低内存机型上,整个框架的运行效率可能会受到肯定的限度,进而导致相机效率低于预期

原文链接:https://blog.csdn.net/u012596...

情谊举荐:
Android 干货分享

至此,本篇已完结,如有不对的中央,欢迎您的倡议与斧正。同时期待您的关注,感谢您的浏览,谢谢!