深入理解高通Camx Hal
和你一起終身學(xué)習(xí),這里是程序員Android
經(jīng)典好文推薦,通過(guò)閱讀本文,您將收獲以下知識(shí)點(diǎn):
一、概覽
二、基本組件概念
三、組件結(jié)構(gòu)關(guān)系
四、關(guān)鍵流程詳解
一、概覽
回顧高通平臺(tái)Camera HAL歷史,之前高通采用的是QCamera & MM-Camera架構(gòu),但是為了更精細(xì)化控制底層硬件(Sensor/ISP等關(guān)鍵硬件),同時(shí)方便手機(jī)廠商自定義一些功能,現(xiàn)在提出了CamX-CHI架構(gòu),由于在CamX-CHI中完全看不到之前老架構(gòu)的影子,所以它完全是一個(gè)全新的架構(gòu),它將一些高度統(tǒng)一的功能性接口抽離出來(lái)放到CamX中,將可定制化的部分放在CHI中供不同廠商進(jìn)行修改,實(shí)現(xiàn)各自獨(dú)有的特色功能,這樣設(shè)計(jì)的好處顯而易見(jiàn),那便是即便開(kāi)發(fā)者對(duì)于CamX并不是很了解,但是依然可以很方便的加入自定義的功能,從而降低了開(kāi)發(fā)者在高通平臺(tái)的開(kāi)發(fā)門(mén)檻。
接下來(lái)我們以最直觀的目錄結(jié)構(gòu)入手對(duì)該架構(gòu)做一個(gè)簡(jiǎn)單的認(rèn)識(shí),以下便是CamX-CHI基本目錄結(jié)構(gòu):

image
該部分代碼主要位于 vendor/qcom/proprietary/ 目錄下:
其中 camx 代表了通用功能性接口的代碼實(shí)現(xiàn)集合(CamX),chi-cdk代表了可定制化需求的代碼實(shí)現(xiàn)集合(CHI),從圖中可以看出Camx部分對(duì)上作為HAL3接口的實(shí)現(xiàn),對(duì)下
通過(guò)v4l2框架與Kernel保持通訊,中間通過(guò)互相dlopen so庫(kù)并獲取對(duì)方操作接口的方式保持著與CHI的交互。
camx/中有如下幾個(gè)主要目錄:
core/ :用于存放camx的核心實(shí)現(xiàn)模塊,其中還包含了主要用于實(shí)現(xiàn)hal3接口的hal/目錄,以及負(fù)責(zé)與CHI進(jìn)行交互的chi/目錄
csl/:用于存放主要負(fù)責(zé)camx與camera driver的通訊模塊,為camx提供了統(tǒng)一的Camera driver控制接口
hwl/: 用于存放自身具有獨(dú)立運(yùn)算能力的硬件node,該部分node受csl管理
swl/: 用于存放自身并不具有獨(dú)立運(yùn)算能力,必須依靠CPU才能實(shí)現(xiàn)的node
chi-cdk/中有如下幾個(gè)主要目錄:
chioverride/: 用于存放CHI實(shí)現(xiàn)的核心模塊,負(fù)責(zé)與camx進(jìn)行交互并且實(shí)現(xiàn)了CHI的總體框架以及具體的業(yè)務(wù)處理。
bin/: 用于存放平臺(tái)相關(guān)的配置項(xiàng)
topology/: 用于存放用戶自定的Usecase xml配置文件
node/: 用于存放用戶自定義功能的node
module/: 用于存放不同sensor的配置文件,該部分在初始化sensor的時(shí)候需要用到
tuning/: 用于存放不同場(chǎng)景下的效果參數(shù)的配置文件
sensor/: 用于存放不同sensor的私有信息以及寄存器配置參數(shù)
actuator/: 用于存放不同對(duì)焦模塊的配置信息
ois/:用于存放防抖模塊的配置信息
flash/:存放著閃光燈模塊的配置信息
eeprom/: 存放著eeprom外部存儲(chǔ)模塊的配置信息
fd/: 存放了人臉識(shí)別模塊的配置信息
二、基本組件概念
1. Usecase
作為CamX-CHI中最大的抽象概念,其中包含了多條實(shí)現(xiàn)特定功能的Pipeline,具體實(shí)現(xiàn)是在CHI中通過(guò)Usecase類完成的,該類主要負(fù)責(zé)了其中的業(yè)務(wù)處理以及資源的管理。
Usecase類,提供了一系列通用接口,作為現(xiàn)有的所有Usecase的基類,其中,AdvancedCameraUsecase又繼承于CameraUsecaseBase,相機(jī)中絕大部分場(chǎng)景會(huì)通過(guò)實(shí)例化AdvancedCameraUsecase來(lái)完成,它包括了幾個(gè)主要接口:
Create(): 該方法是靜態(tài)方法,用于創(chuàng)建一個(gè)AdvancedCameraUsecase實(shí)例,在其構(gòu)造方法中會(huì)去獲取XML中的相應(yīng)的Usecase配置信息。
ExecuteCaptureRequest(): 該方法用于下發(fā)一次Request請(qǐng)求。
ProcessResultCb(): 該方法會(huì)在創(chuàng)建Session的過(guò)程中,作為回調(diào)方法注冊(cè)到其中,一旦Session數(shù)據(jù)處理完成的時(shí)候便會(huì)調(diào)用該方法將結(jié)果發(fā)送到AdvancedCameraUsecase中。
ProcessDriverPartialCaptureResult(): 該方法會(huì)在創(chuàng)建Session的過(guò)程中,作為回調(diào)方法注冊(cè)到其中,一旦Session中產(chǎn)生了partial meta data的時(shí)候,便會(huì)調(diào)用該方法將其發(fā)送至AdvancedCameraUsecase中。
ProcessMessageCb(): 該方法會(huì)在創(chuàng)建Session的過(guò)程中,作為回調(diào)方法注冊(cè)到其中,一旦Session產(chǎn)生任何事件,便會(huì)調(diào)用該方法通知到AdvancedCameraUsecase中。
ExecuteFlush(): 該方法用于刷新AdvancedCameraUsecase。
Destroy(): 該方法用于安全銷毀AdvancedCameraUsecase。
Usecase的可定制化部分被抽象出來(lái)放在了common_usecase.xml文件中,這里簡(jiǎn)單介紹其中的幾個(gè)主要的標(biāo)簽含義:
Usecase
UsecaseName: 代表了該Usecase的名字,后期根據(jù)這個(gè)名字找到這個(gè)Usecase的定義。
Targets: 用于表示用于輸出的數(shù)據(jù)流的集合,其中包括了數(shù)據(jù)流的格式,輸出Size的范圍等。
Pipeline: 用于定義該Usecase可以是使用的所有Pipeline,這里必須至少定義一條Pipeline。
2. Feature
代表了一個(gè)特定的功能,該功能需要多條Pipeline組合起來(lái)實(shí)現(xiàn),受Usecase統(tǒng)一管理,在CHI中通過(guò)Feature類進(jìn)行實(shí)現(xiàn),在XML中沒(méi)有對(duì)應(yīng)的定義,具體的Feature選取工作是在Usecase中完成的,通過(guò)在創(chuàng)建Feature的時(shí)候,傳入U(xiǎn)secase的實(shí)例的方式,來(lái)和Usecase進(jìn)行相互訪問(wèn)各自的資源。
以下是現(xiàn)有的Feature,其中Feature作為基類存在,定義了一系列通用方法。

image
幾個(gè)常用的Feature:
FeatureHDR: 用于實(shí)現(xiàn)HDR功能,它負(fù)責(zé)管理內(nèi)部的一條或者幾條pipeline的資源以及它們的流轉(zhuǎn),最終輸出具有HDR效果的圖像。
FeatureMFNR: 用于實(shí)現(xiàn)MFNR功能,內(nèi)部分為幾個(gè)大的流程,分別包括Prefiltering、Blending、Postfilter以及最終的OfflineNoiseReproces(這一個(gè)是可選擇使能的),每一個(gè)小功能中包含了各自的pipeline。
FeatureASD: 用于AI功能的實(shí)現(xiàn),在預(yù)覽的時(shí)候,接收每一幀數(shù)據(jù),并且進(jìn)行分析當(dāng)前場(chǎng)景的AI識(shí)別輸出結(jié)果,并其通過(guò)諸如到metadata方式給到上層,進(jìn)行后續(xù)的處理。
3. Session
用于管理pipeline的抽象控制單元,一個(gè)Session中至少擁有一個(gè)pipeine,并且控制著所有的硬件資源,管控著每一個(gè)內(nèi)部pipeline的request的流轉(zhuǎn)以及數(shù)據(jù)的輸入輸出,它沒(méi)有可定制化的部分,所以在CHI中的XML文件中并沒(méi)有將Session作為一個(gè)獨(dú)立的單元進(jìn)行定義。
Session的實(shí)現(xiàn)主要通過(guò)CamX中的Session類,其主要接口如下:
Initialize(): 根據(jù)傳入的參數(shù)SessionCreateData進(jìn)行Session的初始化工作。
NotifyResult(): 內(nèi)部的Pipeline通過(guò)該接口將結(jié)果發(fā)送到Session中。
ProcessCaptureRequest(): 該方法用于用戶決定發(fā)送一個(gè)Request到Session中的時(shí)候調(diào)用。
StreamOn(): 通過(guò)傳入的Pipeline句柄,開(kāi)始硬件的數(shù)據(jù)傳輸。
StreamOff(): 通過(guò)傳入的Pipeline句柄,停止硬件的數(shù)據(jù)傳輸。
4. Pipeline
作為提供單一特定功能的所有資源的集合,維護(hù)著所有硬件資源以及數(shù)據(jù)的流轉(zhuǎn),每一個(gè)Pipeline包括了其中的Node/Link,在CamX中通過(guò)Pipeline類進(jìn)行實(shí)現(xiàn),負(fù)責(zé)整條Pipeline的軟硬件資源的維護(hù)以及業(yè)務(wù)邏輯的處理,接下來(lái)我們簡(jiǎn)單看下該類的幾個(gè)主要接口:
Create(): 該方法是一個(gè)靜態(tài)方法,根據(jù)傳入的PipelineCreateInputData信息來(lái)實(shí)例化一個(gè)Pipeline對(duì)象。
StreamOn(): 通知Pipeline開(kāi)始硬件的數(shù)據(jù)傳輸
StreamOff(): 通知Pipeline停止硬件的數(shù)據(jù)傳輸
FinalizePipeline(): 用于完成Pipeline的設(shè)置工作
OpenRequest(): open一個(gè)CSL用于流轉(zhuǎn)的Request
ProcessRequest(): 開(kāi)始下發(fā)Request
NotifyNodeMetadataDone(): 該方法是Pipeline提供給Node,當(dāng)Node內(nèi)部生成了metadata,便會(huì)調(diào)用該方法來(lái)通知metadata已經(jīng)完成,最后當(dāng)所有Node都通知Pipeline metadata已經(jīng)完成,Pipeline 便會(huì)調(diào)用ProcessMetadataRequestIdDone通知Session。
NotifyNodePartialMetadataDone(): 該方法是Pipeline提供給Node,當(dāng)Node內(nèi)部生成了partial metadata,便會(huì)調(diào)用該方法來(lái)通知metadata已經(jīng)完成,最后當(dāng)所有Node都通知Pipeline metadata已經(jīng)完成,Pipeline 便會(huì)調(diào)用ProcessPartialMetadataRequestIdDone通知Session。
SinkPortFenceSignaled(): 用來(lái)通知Session 某個(gè)sink port的fence處于被觸發(fā)的狀態(tài)。
NonSinkPortFenceSignaled(): 用來(lái)通知Session 某個(gè)non sink port的fence處于被觸發(fā)的狀態(tài)。
Pipeline中的Node以及連接方式都在XML中被定義,其主要包含了以下幾個(gè)標(biāo)簽定義:
PipelineName: 用來(lái)定義該條Pipeline的名稱
NodeList: 該標(biāo)簽中定義了該條Pipeline的所有的Node
PortLinkages: 該標(biāo)簽定義了Node上不同端口之間的連接關(guān)系
5. Node
作為單個(gè)具有獨(dú)立處理功能的抽象模塊,可以是硬件單元也可以是軟件單元,關(guān)于Node的具體實(shí)現(xiàn)是CamX中的Node類來(lái)完成的,其中CamX-CHI中主要分為兩個(gè)大類,一個(gè)是高通自己實(shí)現(xiàn)的Node包括硬件Node,一個(gè)是CHI中提供給用戶進(jìn)行實(shí)現(xiàn)的Node,其主要方法如下:
Create(): 該方法是靜態(tài)方法,用于實(shí)例化一個(gè)Node對(duì)象。
ExecuteProcessRequest(): 該方法用于針對(duì)hwl node下發(fā)request的操作。
ProcessRequestIdDone(): 一旦該Node當(dāng)前request已經(jīng)處理完成,便會(huì)通過(guò)調(diào)用該方法通知Pipeline。
ProcessMetadataDone(): 一旦該Node的當(dāng)前request的metadata已經(jīng)生成,便會(huì)通過(guò)調(diào)用該方法通知到Pipeline。
ProcessPartialMetadataDone(): 一旦該Node的當(dāng)前request的partial metadata已經(jīng)生成,便會(huì)通過(guò)調(diào)用該方法通知到Pipeline。
CreateImageBufferManager(): 創(chuàng)建ImageBufferManager
其可定制化的部分作為標(biāo)簽在XML中進(jìn)行定義:
NodeName:用來(lái)定義該Node的名稱
NodeId: 用來(lái)指定該Node的ID,其中IPE NodeId為65538,IFE NodeId為65536,用戶自定義的NodeId為255。
NodeInstance: 用于定義該Node的當(dāng)前實(shí)例的名稱。
NodeInstanceId: 用于指定該Node實(shí)例的Id。
6. Link
用于定義不同Port的連接,一個(gè)Port可以根據(jù)需要建立多條與其它從屬于不同Node的Port的連接,它通過(guò)標(biāo)簽來(lái)進(jìn)行定義,其中包括了作為輸入端口,作為輸出端口。
一個(gè)Link中包含了一個(gè)SrcPort和一個(gè)DstPort,分別代表了輸入端口和輸出端口,然后BufferProperties用于表示兩個(gè)端口之間的buffer配置。
7. Port
作為Node的輸入輸出的端口,在XML文件中,標(biāo)簽用來(lái)定義一個(gè)輸入端口,標(biāo)簽用來(lái)定義輸出端口,每一個(gè)Node都可以根據(jù)需要使用一個(gè)或者多個(gè)輸入輸出端口,使用OutputPort以及InputPort結(jié)構(gòu)體來(lái)進(jìn)行在代碼中定義。
Port
PortId: 該端口的Id: 該端口的名稱
NodeName: 該端口從屬的Node名稱
NodeId: 該端口從屬的Node的Id
NodeInstance: 該端口從屬的Node的實(shí)例名稱
NodeInstanceId: 該端口從屬的Node的實(shí)例的Id
三、組件結(jié)構(gòu)關(guān)系
通過(guò)之前的介紹,我們對(duì)于幾個(gè)基本組件有了一個(gè)比較清晰地認(rèn)識(shí),但是任何一個(gè)框架體系并不是僅靠組件胡亂堆砌而成的,相反,它們都必須基于各自的定位,按照各自所獨(dú)有的行為模式,同時(shí)按照約定俗稱的一系列規(guī)則組合起來(lái),共同完成整個(gè)框架某一特定的功能。所以這里不得不產(chǎn)生一個(gè)疑問(wèn),在該框架中它們到底是如何組織起來(lái)的呢?它們之間的關(guān)系又是如何的呢?接下來(lái)我們以下圖入手開(kāi)始進(jìn)行分析:

image
由上圖可以看到,幾者是通過(guò)包含關(guān)系組合起來(lái)的,Usecase 包含F(xiàn)eature,而Feature包含了Session,Session又維護(hù)了內(nèi)部的Pipeline的流轉(zhuǎn),而每一條pipeline中又通過(guò)Link將所有Node都連接了起來(lái),接下我們就這幾種關(guān)系詳細(xì)講解下:
首先,一個(gè)Usecase代表了某個(gè)特定的圖像采集場(chǎng)景,比如人像場(chǎng)景,后置拍照?qǐng)鼍暗鹊龋诔跏蓟臅r(shí)候通過(guò)根據(jù)上層傳入的一些具體信息來(lái)進(jìn)行創(chuàng)建,這個(gè)過(guò)程中,一方面實(shí)例化了特定的Usecase,這個(gè)實(shí)例是用來(lái)管理整個(gè)場(chǎng)景的所有資源,同時(shí)也負(fù)責(zé)了其中的業(yè)務(wù)處理邏輯,另一方面,獲取了定義在XML中的特定Usecase,獲取了用于實(shí)現(xiàn)某些特定功能的pipeline。
其次,在Usecase中,F(xiàn)eature是一個(gè)可選項(xiàng),如果當(dāng)前用戶選擇了HDR模式或者需要在Zoom下進(jìn)行拍照等特殊功能的話,在Usecase創(chuàng)建過(guò)程中,便會(huì)根據(jù)需要?jiǎng)?chuàng)建一個(gè)或者多個(gè)Feature,一般一個(gè)Feature對(duì)應(yīng)著一個(gè)特定的功能,如果場(chǎng)景中并不需要任何特定的功能,則也完全可以不使用也不創(chuàng)建任何Feature。
然后,每一個(gè)Usecase或者Feature都可以包含一個(gè)或者多個(gè)Session,每一個(gè)Session都是直接管理并負(fù)責(zé)了內(nèi)部的Pipeline的數(shù)據(jù)流轉(zhuǎn),其中每一次的Request都是Usecase或者Featuret通過(guò)Session下發(fā)到內(nèi)部的Pipeline進(jìn)行處理,數(shù)據(jù)處理完成之后也是通過(guò)Session的方法將結(jié)果給到CHI中,之后是直接給到上層還是將數(shù)據(jù)封裝下再次下發(fā)到另一個(gè)Session中進(jìn)行后處理,這都交由CHI來(lái)決定。
其中,Session和Pipeline是一對(duì)多的關(guān)系,通常一個(gè)Session只包含了一條Pipeline,用于某個(gè)特定圖像處理功能的實(shí)現(xiàn),但是也不絕對(duì),比如FeatureMFNR中包含的Session就包括了三條pipeline,又比如后置人像預(yù)覽,也是用一個(gè)Session包含了兩條分別用于主副雙攝預(yù)覽的Pipeline,主要是要看當(dāng)前功能需要的pipeline數(shù)量以及它們之間是否存在一定關(guān)聯(lián)。
同時(shí),根據(jù)上面關(guān)于Pipeline的定義,它內(nèi)部包含了一定數(shù)量的Node,并且實(shí)現(xiàn)的功能越復(fù)雜,所包含的Node也就越多,同時(shí)Node之間的連接也就越錯(cuò)綜復(fù)雜,比如后置人像預(yù)覽虛化效果的實(shí)現(xiàn)就是將拿到的主副雙攝的圖像通過(guò)RTBOfflinePreview這一條Pipeline將兩幀圖像合成一幀具有虛化效果的圖像,從而完成了虛化功能。
最后Pipeline中的Node的連接方式是通過(guò)XML文件中的Link來(lái)進(jìn)行描述的,每一個(gè)Link定義了一個(gè)輸入端和輸出端分別對(duì)應(yīng)著不同Node上面的輸入輸出端口,通過(guò)這種方式就將其中的一個(gè)Node的輸出端與另外一個(gè)Node的輸入端,一個(gè)一個(gè)串聯(lián)起來(lái),等到圖像數(shù)據(jù)從Pipeline的起始端開(kāi)始輸入的時(shí)候,便可以按照這種定義好的軌跡在一個(gè)一個(gè)Node之間進(jìn)行流轉(zhuǎn),而在流轉(zhuǎn)的過(guò)程中每經(jīng)過(guò)一個(gè)Node都會(huì)在內(nèi)部對(duì)數(shù)據(jù)進(jìn)行處理,這樣等到數(shù)據(jù)從起始端一直流轉(zhuǎn)到最后一個(gè)Node的輸出端的時(shí)候,數(shù)據(jù)就經(jīng)過(guò)了很多次處理,這些處理效果最后疊加在一起便是該P(yáng)ipeline所要實(shí)現(xiàn)的功能,比如降噪、虛化等等。
四、關(guān)鍵流程詳解
1. Camera Provider 啟動(dòng)初始化
當(dāng)系統(tǒng)啟動(dòng)的時(shí)候,Camera Provider主程序會(huì)被運(yùn)行,在整個(gè)程序初始化的過(guò)程中會(huì)通過(guò)獲取到的camera_module_t調(diào)用其get_number_of_camera接口獲取底層支持的camera數(shù)量,由于是第一次獲取,所以在CamX-CHI中會(huì)伴隨著很多初始化動(dòng)作,具體操作見(jiàn)下圖:

在這里插入圖片描述
主要流程如下:
通過(guò)HAL3Module::GetInstance()靜態(tài)方法實(shí)例化了HAL3Module對(duì)象,在其構(gòu)造方法里面通過(guò)HwEnvironment::GetInstance()靜態(tài)方法又實(shí)例化了HwEnvironment對(duì)象,在其構(gòu)造方法中,實(shí)例化了SettingsManager對(duì)象,然后又在它構(gòu)造方法中通過(guò)OverrideSettingsFile對(duì)象獲取了位于/vendor/etc/camera/camoverridesettings.txt文件中的平臺(tái)相關(guān)的配置信息(通過(guò)這種Override機(jī)制方便平臺(tái)廠商加入自定義配置),該配置文件中,可以加入平臺(tái)特定的配置項(xiàng),比如可以通過(guò)設(shè)置multiCameraEnable的值來(lái)表示當(dāng)前平臺(tái)是否支持多攝,或者通過(guò)設(shè)置overrideLogLevels設(shè)置項(xiàng)來(lái)配置CamX-CHI部分的Log輸出等級(jí)等等。
同時(shí)在HwEnvironment構(gòu)造方法中會(huì)調(diào)用其Initialize方法,在該方法中實(shí)例化了CSLModeManager對(duì)象,并通過(guò)CSLModeManager提供的接口,獲取了所有底層支持的硬件設(shè)備信息,其中包括了Camera Request Manager、CAPS模塊(該驅(qū)動(dòng)模塊主要用于CSL獲取Camera平臺(tái)驅(qū)動(dòng)信息,以及IPE/BPS模塊的電源控制)以及Sensor/IPE/Flash等硬件模塊,并且通過(guò)調(diào)用CSLHwInternalProbeSensorHW方法獲取了當(dāng)前設(shè)備安裝的Sensor模組信息,并且將獲取的信息暫存起來(lái),等待后續(xù)階段使用,總得來(lái)說(shuō)在HwEnvironment初始化的過(guò)程中,通過(guò)探測(cè)方法獲取了所有底層的硬件驅(qū)動(dòng)模塊,并將其信息存儲(chǔ)下來(lái)供后續(xù)階段使用。
之后通過(guò)調(diào)用HwEnvironment對(duì)象中的ProbeChiCompoents方法在/vendor/lib64/camera/components路徑下找尋各個(gè)Node生成的So庫(kù),并獲取Node提供的標(biāo)準(zhǔn)對(duì)外接口,這些Node不但包括CHI部分用戶自定義的模塊,還包括了CamX部分實(shí)現(xiàn)的硬件模塊,并最后都將其都存入ExternalComponentInfo對(duì)象中,等待后續(xù)階段使用。
另外在初始化階段還有一個(gè)比較重要的操作就是CamX 與CHI是通過(guò)互相dlopen對(duì)方的So庫(kù),獲取了對(duì)方的入口方法,最后通過(guò)彼此的入口方法獲取了對(duì)方操作方法集合,之后再通過(guò)這些操作方法與對(duì)方進(jìn)行通訊,其主要流程見(jiàn)下圖:

image
從上圖不難看出,在HAL3Module構(gòu)造方法中會(huì)去通過(guò)dlopen方法加載com.qti.chi.override.so庫(kù),并通過(guò)dlsym映射出CHI部分的入口方法chi_hal_override_entry,并調(diào)用該方法將HAL3Module對(duì)像中的成員變量m_ChiAppCallbacks(CHIAppCallbacks)傳入CHI中,其中包含了很多函數(shù)指針,這些函數(shù)指針?lè)謩e對(duì)應(yīng)著CHI部分的操作方法集中的方法,一旦進(jìn)入到CHI中,就會(huì)將CHI本地的操作方法集合中的函數(shù)地址依次賦值給m_ChiAppCallbacks,這樣CamX后續(xù)就可以通過(guò)這個(gè)成員變量調(diào)用到CHI中方法,從而保持了與CHI的通訊。
同樣地,CHI中的ExtensionModule在初始化的時(shí)候,其構(gòu)造方法中也會(huì)通過(guò)調(diào)用dlopen方法加載camera.qcom.so庫(kù),并將其入口方法ChiEntry通過(guò)dlsym映射出來(lái),之后調(diào)用該方法,將g_chiContextOps(ChiContextOps,該結(jié)構(gòu)體中定義了很多指針函數(shù))作為參數(shù)傳入CamX中,一旦進(jìn)入CamX中,便會(huì)將本地的操作方法地址依次賦值給g_chiContextOps中的每一個(gè)函數(shù)指針,這樣CHI之后就可以通過(guò)g_chiContextOps訪問(wèn)到CamX方法。
2. 打開(kāi)相機(jī)設(shè)備/初始化相機(jī)設(shè)備
一旦用戶打開(kāi)了相機(jī)應(yīng)用,App中便會(huì)去調(diào)用CameraManager的openCamera方法,該方法之后會(huì)最終調(diào)用到Camera Service中的CameraService::connectDevice方法,然后通過(guò)ICameraDevice::open()這一個(gè)HIDL接口通知Provider,然后在Provider內(nèi)部又通過(guò)調(diào)用之前獲取的camera_module_t中methods的open方法來(lái)獲取一個(gè)Camera 設(shè)備,對(duì)應(yīng)于HAL中的camera3_device_t結(jié)構(gòu)體,緊接著,在Provider中會(huì)繼續(xù)調(diào)用獲取到的camera3_device_t的initialize方法進(jìn)行初始化動(dòng)作。接下來(lái)我們便來(lái)詳細(xì)分析下CamX-CHI對(duì)于open以及initialize的具體實(shí)現(xiàn)流程:
a) open
該方法是camera_module_t的標(biāo)準(zhǔn)方法,主要用來(lái)獲取camera3_device_t設(shè)備結(jié)構(gòu)體的,CamX-CHI對(duì)其進(jìn)行了實(shí)現(xiàn),open方法中完成的工作主要有以下幾個(gè):
將當(dāng)前camera id傳入CHI中進(jìn)行remap操作,當(dāng)然這個(gè)remap操作邏輯完全是根據(jù)CHI中用戶需求來(lái)的,用戶可以根據(jù)自己的需要在CHI中加入自定義remap邏輯。
實(shí)例化HALDevice對(duì)象,其構(gòu)造函數(shù)中調(diào)用Initialize方法,該方法會(huì)填充CamX中自定義的Camera3Device結(jié)構(gòu)體。
將m_HALCallbacks.process_capture_result指向了本地方法ProcessCaptureResult以及m_HALCallbacks.notify_result指向了本地方法Notify(之后會(huì)在配置數(shù)據(jù)流的過(guò)程中,將m_HALCallbacks注冊(cè)到CHI中, 一旦當(dāng)CHI數(shù)據(jù)處理完成之后,便會(huì)通過(guò)這兩個(gè)回調(diào)方法將數(shù)據(jù)或者事件回傳給CamX)。
最后將HALDevice 中的Camera3Device成員變量作為返回值給到Provider中的CameraCaptureSession中。
Camera3Device 其實(shí)重定義了camera3_device_t,其中HwDevice對(duì)應(yīng)于camera3_device_t中的hw_device_t,Camera3DeviceOps對(duì)應(yīng)于camera3_device_ops_t,而在HALDevice的初始化過(guò)程中,會(huì)將CamX實(shí)現(xiàn)的HAL3接口的結(jié)構(gòu)體g_camera3DeviceOps賦值給Camera3DeviceOps中。
b) initialize
該方法在調(diào)用open后緊接著被調(diào)用,主要用于將上層的回調(diào)接口傳入HAL中,一旦有數(shù)據(jù)或者事件產(chǎn)生,CamX便會(huì)通過(guò)這些回調(diào)接口將數(shù)據(jù)或者事件上傳至調(diào)用者,其內(nèi)部的實(shí)現(xiàn)較為簡(jiǎn)單。
initialize方法中有兩個(gè)參數(shù),分別是之前通過(guò)open方法獲取的camera3_device_t結(jié)構(gòu)體和實(shí)現(xiàn)了camera3_callback_ops_t的CameraDevice,很顯然camera3_device_t結(jié)構(gòu)體并不是重點(diǎn),所以該方法的主要工作是將camera3_callback_ops_t與CamX關(guān)聯(lián)上,一旦數(shù)據(jù)準(zhǔn)備完成便通過(guò)這里camera3_callback_ops_t中回調(diào)方法將數(shù)據(jù)回傳到Camera Provider中的CameraDevice中,基本流程可以總結(jié)為以下幾點(diǎn):
實(shí)例化了一個(gè)Camera3CbOpsRedirect對(duì)象并將其加入了g_HAL3Entry.m_cbOpsList隊(duì)列中,這樣方便之后需要的時(shí)候能夠順利拿到該對(duì)象。
將本地的process_capture_result以及notify方法地址分別賦值給Camera3CbOpsRedirect.cbOps中的process_capture_result以及notify函數(shù)指針。
將上層傳入的回調(diào)方法結(jié)構(gòu)體指針pCamera3CbOpsAPI賦值給Camera3CbOpsRedirect.pCbOpsAPI,并將Camera3CbOpsRedirect.cbOps賦值給pCamera3CbOpsAPI,通過(guò)JumpTableHal3的initialize方法將pCamera3CbOpsAPI傳給HALDevice中的m_pCamera3CbOps成員變量,這樣HALDevice中的m_pCamera3CbOps就指向了CamX中本地方法process_capture_result以及notify。
經(jīng)過(guò)這樣的一番操作之后,一旦CHI有數(shù)據(jù)傳入便會(huì)首先進(jìn)入到本地方法ProcessCaptureResult,然后在該方法中獲取到HALDevice的成員變量m_pCamera3CbOps,進(jìn)而調(diào)用m_pCamera3CbOps中的process_capture_result方法,即camxhal3entry.cpp中定義的process_capture_result方法,然后這個(gè)方法中會(huì)去調(diào)用JumpTableHAL3.process_capture_result方法,該方法最終會(huì)去調(diào)用Camera3CbOpsRedirect.pCbOpsAPI中的process_capture_result方法,這樣就調(diào)到從Provider傳入的回調(diào)方法,將數(shù)據(jù)順利給到了CameraCaptureSession中。
3. 配置相機(jī)設(shè)備數(shù)據(jù)流
在打開(kāi)相機(jī)應(yīng)用過(guò)程中,App在獲取并打開(kāi)相機(jī)設(shè)備之后,會(huì)調(diào)用CameraDevice.createCaptureSession來(lái)獲取CameraDeviceSession,并且通過(guò)Camera api v2標(biāo)準(zhǔn)接口,通知Camera Service,調(diào)用其CameraDeviceClient.endConfigure方法,在該方法內(nèi)部又會(huì)去通過(guò)HIDL接口ICameraDeviceSession::configureStreams_3_4通知Provider開(kāi)始處理此次配置需求,在Provider內(nèi)部,會(huì)去通過(guò)在調(diào)用open流程中獲取的camera3_device_t結(jié)構(gòu)體的configure_streams方法來(lái)將數(shù)據(jù)流的配置傳入CamX-CHI中,之后由CamX-CHI完成對(duì)數(shù)據(jù)流的配置工作,接下來(lái)我們來(lái)詳細(xì)分析下CamX-CHI對(duì)于該標(biāo)準(zhǔn)HAL3接口 configure_streams的具體實(shí)現(xiàn):
配置數(shù)據(jù)流是整個(gè)CamX-CHI流程比較重要的一環(huán),其中主要包括兩個(gè)階段:
選擇UsecaseId
根據(jù)選擇的UsecaseId創(chuàng)建Usecase
接下來(lái)我們就這兩個(gè)階段分別進(jìn)行詳細(xì)介紹:
① 選擇UsecaseId
不同的UsecaseId分別對(duì)應(yīng)的不同的應(yīng)用場(chǎng)景,該階段是通過(guò)調(diào)用UsecaseSelector::GetMatchingUsecase()方法來(lái)實(shí)現(xiàn)的,該函數(shù)中通過(guò)傳入的operation_mode、num_streams配置數(shù)據(jù)流數(shù)量以及當(dāng)前使用的Sensor個(gè)數(shù)來(lái)選擇相應(yīng)的UsecaseId,比如當(dāng)numPhysicalCameras值大于1同時(shí)配置的數(shù)據(jù)流數(shù)量num_streams大于1時(shí)選擇的就是UsecaseId::MultiCamera,表示當(dāng)前采用的是雙攝場(chǎng)景。
② 創(chuàng)建Usecase
根據(jù)之前選擇的UsecaseId,通過(guò)UsecaseFactory來(lái)創(chuàng)建相應(yīng)的Usecase,
其中Class Usecase是所有Usecase的基類,其中定義并實(shí)現(xiàn)了一些通用接口,CameraUsecaseBase繼承于Usecase,并擴(kuò)展了部分功能。AdvancedCameraUsecase又繼承于CameraUsecaseBase,作為主要負(fù)責(zé)大部分場(chǎng)景的Usecase實(shí)現(xiàn)類,另外對(duì)于多攝場(chǎng)景,現(xiàn)提供了繼承于AdvancedCameraUsecase的UsecaseMultiCamera來(lái)負(fù)責(zé)實(shí)現(xiàn)。
除了雙攝場(chǎng)景,其它大部分場(chǎng)景使用的都是AdvancedCameraUsecase類來(lái)管理各項(xiàng)資源的,接下來(lái)我們重點(diǎn)梳理下AdvancedCameraUsecase::Create()方法。
在AdvancedCameraUsecase::Create方法中做了很多初始化操作,其中包括了以下幾個(gè)階段:
獲取XML文件中Usecase配置信息
創(chuàng)建Feature
保存數(shù)據(jù)流,重建Usecase的配置信息
調(diào)用父類CameraUsecaseBase的initialize方法,進(jìn)行一些常規(guī)初始化工作
接下來(lái)我們就這幾個(gè)階段逐一進(jìn)行分析:
1. 獲取XML文件中Usecase配置信息
這一部分主要通過(guò)調(diào)用CameraUsecaseBase::GetXMLUsecaseByName方法進(jìn)行實(shí)現(xiàn)。
該方法的主要操作是從PerNumTargetUsecases數(shù)組中找到匹配到給定的usecaseName的Usecase,并作為返回值返回給調(diào)用者,其中這里我們以"UsecaseZSL“為例進(jìn)行分析,PerNumTargetUsecases的定義是在g_pipeline.h中,該文件是在編譯過(guò)程中通過(guò)usecaseconverter.pl腳本將定義在個(gè)平臺(tái)目錄下的common_usecase.xml中的內(nèi)容轉(zhuǎn)換生成g_pipeline.h。
2.創(chuàng)建Feature
如果當(dāng)前場(chǎng)景選取了Feature,則調(diào)用FeatureSetup來(lái)完成創(chuàng)建工作。
該方法主要是通過(guò)諸如operation_mode、camera數(shù)量以及UsecaseId等信息來(lái)決定需要選擇哪些Feature,具體邏輯比較清晰,一旦決定需要使用哪一個(gè)Feature之后,便調(diào)用相應(yīng)的Feature的Create()方法進(jìn)行初始化操作。
3.保存數(shù)據(jù)流,重建Usecase的配置信息
從Camera Service 傳入的數(shù)據(jù)流,需要將其存儲(chǔ)下來(lái),供后續(xù)使用,同時(shí)高通針對(duì)Usecase也加入了Override機(jī)制,根據(jù)需要可以選擇性地?cái)U(kuò)展Usecase,這兩個(gè)步驟的實(shí)現(xiàn)主要是通過(guò)SelectUsecaseConfig方法來(lái)實(shí)現(xiàn)。
其中主要是調(diào)用以下兩個(gè)方法來(lái)實(shí)現(xiàn)的:
ConfigureStream:該方法將從上層配置的數(shù)據(jù)流指針存入AdvancedCameraUsecase中,其中包括了用于預(yù)覽的m_pPreviewStream以及用于拍照的m_pSnapshotStream。
BuildUsecase:這個(gè)方法用來(lái)重新在原有的Usecase上面加入了Feature中所需要的pipeline,并創(chuàng)建了一個(gè)新的Usecase,并將其存入AdvancedCameraUsecase中的m_pChiUsecase成員變量中,緊接著通過(guò)SetPipelineToSessionMapping方法將pipeline與Session進(jìn)行關(guān)聯(lián)。
4.調(diào)用父類CameraUsecaseBase的initialize方法,進(jìn)行一些常規(guī)初始化工作
該方法中的操作主要有以下三個(gè):
設(shè)置Session回調(diào)
創(chuàng)建Pipeline
創(chuàng)建Session
設(shè)置Session回調(diào)
該方法有兩個(gè)參數(shù),第二個(gè)是缺省的,第一個(gè)是ChiCallBacks,該參數(shù)是作為創(chuàng)建的每一條Session的回調(diào)方法,當(dāng)Session中的pipeline全部跑完之后,會(huì)回調(diào)該方法將數(shù)據(jù)投遞到CHI中。
創(chuàng)建Pipeline
根據(jù)之前獲取的pipeline信息開(kāi)始創(chuàng)建每一條pipeline,通過(guò)調(diào)用CreatePipeline()方法實(shí)現(xiàn)。
創(chuàng)建Session
創(chuàng)建Session,通過(guò)CreateSession()方法實(shí)現(xiàn),此時(shí)會(huì)將AdvancedCameraUsecase端的回調(diào)函數(shù)注冊(cè)到Session中,一旦Session中數(shù)據(jù)處理完成,便會(huì)調(diào)用回調(diào)將數(shù)據(jù)回傳給AdvancedCameraUsecase。
綜上,整個(gè)configure_stream過(guò)程,基本可以概括為以下幾點(diǎn):
根據(jù)operation_mode、camera 個(gè)數(shù)以及stream的配置信息選取了對(duì)應(yīng)的UsecaseId
根據(jù)所選取的UsecaseId,使用UsecaseFactory簡(jiǎn)單工廠類創(chuàng)建了用于管理整個(gè)場(chǎng)景下所有資源的AdvancedCameraUsecase對(duì)象。
創(chuàng)建AdvancedCameraUsecase對(duì)象是通過(guò)調(diào)用其Create()方法完成,該方法中獲取了common_usecase.xml定義的關(guān)于Usecase的配置信息,之后又根據(jù)需要?jiǎng)?chuàng)建了Feature并選取了Feature所需的pipeline,并通過(guò)Override機(jī)制將Feature中所需要的Pipeline加入重建后的Usecase中。
最后通過(guò)調(diào)用CameraUsecaseBaese的initialize方法依次創(chuàng)建了各個(gè)pipeline以及Session,并且將AdvancedCameraUsecase的成員方法注冊(cè)到Session,用于Session將數(shù)據(jù)返回給Usecase中
4. 處理拍照請(qǐng)求
當(dāng)用戶打開(kāi)相機(jī)應(yīng)用進(jìn)行預(yù)覽或者點(diǎn)擊一次拍照操作的時(shí)候,便觸發(fā)了一次拍照請(qǐng)求,該動(dòng)作首先通過(guò)CameraDeviceSession的capture或者setRepeatingRequest方法將請(qǐng)求通過(guò)Camera api v2接口下發(fā)到Camera Service中,然后在Camera Service內(nèi)部將此次請(qǐng)求發(fā)送到CameraDevice::RequestThread線程中進(jìn)行處理,一旦進(jìn)入到該線程之后,便會(huì)最終通過(guò)HIDL接口ICameraCaptureSession:processCaptureRequest_3_4將請(qǐng)求發(fā)送至Provider中,之后當(dāng)Provider收到請(qǐng)求之后,會(huì)調(diào)用camera3_device_t結(jié)構(gòu)體的process_capture_request開(kāi)始了HAL針對(duì)此次Request的處理,而該處理是由CamX-CHI來(lái)負(fù)責(zé)實(shí)現(xiàn),現(xiàn)在我們就來(lái)看下CamX-CHI是如何實(shí)現(xiàn)該方法的:
首先CamX中會(huì)將此次request轉(zhuǎn)發(fā)到HALDevice中,再通過(guò)HALDevice對(duì)象調(diào)用之前初始化的時(shí)候獲取的CHI部分的回調(diào)接口m_ChiAppCallbacks.chi_override_process_request方法(chi_override_process_request方法的定義位于chxextensioninterface.cpp中)將request發(fā)送到CHI部分。
在chi_override_process_request方法中會(huì)去獲取ExtensionModule對(duì)象,并將request發(fā)送到ExtensionModule對(duì)象中,該對(duì)象中存儲(chǔ)了之前創(chuàng)建的Usecase對(duì)象,然后經(jīng)過(guò)層層調(diào)用,最終會(huì)調(diào)用AdvancedCameraUsecase的ExecuteCaptureRequest方法,該方法負(fù)責(zé)處理此次Request,具體流程如下:
在AdvancedCameraUsecase的ExecuteCaptureRequest中會(huì)有兩個(gè)主要的分支來(lái)分別處理:
如果當(dāng)前并沒(méi)有任何Feature需要實(shí)現(xiàn),此時(shí)便會(huì)走默認(rèn)流程,根據(jù)上面的流程圖所示,這里會(huì)調(diào)用CameraUsecaseBase::ExecuteCaptureRequest方法,在該方法中,首先會(huì)將request取出,重新封裝成CHICAPTUREREQUEST,然后調(diào)用CheckAndActivatePipeline方法喚醒pipeline,這一操作到最后會(huì)調(diào)到Session的StreamOn方法,在喚醒了pipeline之后,繼續(xù)往下執(zhí)行,再將封裝后的Request發(fā)送到CamX中,最終調(diào)用到相應(yīng)的Session::ProcessCaptureRequest方法,此時(shí)Request就進(jìn)入到了Session內(nèi)部進(jìn)行流轉(zhuǎn)了。
如果當(dāng)前場(chǎng)景需要實(shí)現(xiàn)某個(gè)Feature,則直接調(diào)用Feature的ExecuteProcessRequest方法將此次request送入Feature中處理,最后依然會(huì)調(diào)用到Session::StreamOn以及Session::ProcessCaptureRequest方法來(lái)分別完成喚醒pipeline以及下發(fā)request的到Session的操作。
該流程最終都會(huì)調(diào)用到兩個(gè)比較關(guān)鍵的方法Session::StreamOn以及Session::ProcessCaptureRequest,接下來(lái)針對(duì)這兩個(gè)方法重點(diǎn)介紹下:
Session::StreamOn
從方法名稱基本可以知道該方法主要用于開(kāi)始硬件的數(shù)據(jù)輸出,具體點(diǎn)兒就是進(jìn)行配置Sensor寄存器,讓其開(kāi)始出圖,并且將當(dāng)前的Session的狀態(tài)告知每一Node,讓它們?cè)谧约簝?nèi)部也做好處理數(shù)據(jù)的準(zhǔn)備,所以之后的相關(guān)Request的流轉(zhuǎn)都是以該方法為前提進(jìn)行的,所以該方法重要性可見(jiàn)一斑,其操作流程見(jiàn)下圖:
Session的StreamOn方法中主要做了如下兩個(gè)工作:
調(diào)用FinalizeDeferPipeline()方法,如果當(dāng)前pipeline并未初始化,則會(huì)調(diào)用pipeline的FinalizePipeline方法,這里方法里面會(huì)去針對(duì)每一個(gè)從屬于當(dāng)前pipeline的Node依次做FinalizeInitialization、CreateBufferManager、NotifyPipelineCreated以及PrepareNodeStreamOn操作,F(xiàn)inalizeInitialization用于完成Node的初始化動(dòng)作,NotifyPipelineCreated用于通知Node當(dāng)前Pipeline的狀態(tài),此時(shí)Node內(nèi)部可以根據(jù)自身的需要作相應(yīng)的操作,PrepareNodeStreamOn方法的主要是完成Sensor以及IFE等Node的控制硬件模塊出圖前的配置,其中包括了曝光的參數(shù)的設(shè)置,CreateBufferManagers方法涉及到CamX-CHI中的一個(gè)非常重要的Buffer管理機(jī)制,用于Node的ImageBufferManager的創(chuàng)建,而該類用于管理Node中的output port的buffer申請(qǐng)/流轉(zhuǎn)/釋放等操作。
調(diào)用Pipeline的StreamOn方法,里面會(huì)進(jìn)一步通知CSL部分開(kāi)啟數(shù)據(jù)流,并且調(diào)用每一個(gè)Node的OnNodeStreamOn方法,該方法會(huì)去調(diào)用ImageBufferManager的Activate(),該方法里面會(huì)去真正分配用于裝載圖像數(shù)據(jù)的buffer,之后會(huì)去調(diào)用CHI部分實(shí)現(xiàn)的用戶自定義的Nod的pOnStreamOn方法,用戶可以在該方法中做一些自定義的操作。
Session::ProcessCaptureRequest
針對(duì)每一次的Request的流轉(zhuǎn),都是以該方法為入口開(kāi)始的,具體流程見(jiàn)下圖:

在這里插入圖片描述
上述流程可以總結(jié)為以下幾個(gè)步驟:
通過(guò)調(diào)用Session的ProcessCaptureRequest方法進(jìn)入到Session,然后調(diào)用Pipeline中的ProcessRequest方法通知Pipeline開(kāi)始處理此次Request。
在Pipeline中,會(huì)先去調(diào)用內(nèi)部的每一個(gè)Node的SetupRequest方法分別設(shè)置該Node的Output Port以及Input Port,之后通過(guò)調(diào)用DRQ(DeferredRequestQueue)的AddDeferredNode方法將所有的Node加入到DRQ中,其中DRQ中有兩個(gè)隊(duì)列分別是用于保存沒(méi)有依賴項(xiàng)的Node的m_readyNodes以及保存處于等待依賴關(guān)系滿足的Node的m_deferredNodes,當(dāng)調(diào)用DRQ的DispatchReadyNodes方法后,會(huì)開(kāi)始從m_readyNodes隊(duì)列中取出Node調(diào)用其ProcessRequest開(kāi)始進(jìn)入Node內(nèi)部處理本次request,在處理過(guò)程中會(huì)更新meta data數(shù)據(jù),并更新至DRQ中,當(dāng)該Node處理完成之后,會(huì)將處于m_deferredNodes中的已無(wú)依賴關(guān)系的Node移到m_readyNodes中,并再次調(diào)用DispatchReadyNodes方法從m_readyNodes取出Node進(jìn)行處理。
與此過(guò)程中,當(dāng)Node的數(shù)據(jù)處理完成之后會(huì)通過(guò)CSLFenceCallback通知到Pipeline,此時(shí)Pipeline會(huì)判斷當(dāng)前Node的Output port 是否是Sink Port(輸出到CHI),如果不是,則會(huì)更新依賴項(xiàng)到DRQ中,并且將不存在依賴項(xiàng)的Node移到m_readyNodes隊(duì)列中,然后調(diào)用DispatchReadyNdoes繼續(xù)進(jìn)入到DRQ中流轉(zhuǎn),如果是Sink Port,則表示此Node是整個(gè)Pipeline的最末端,調(diào)用sinkPortFenceSignaled將數(shù)據(jù)給到Session中,最后通過(guò)調(diào)用Session中的NotifyResult將結(jié)果發(fā)送到CHI中。
DeferredRequestQueue
上述流程里面中涉及到DeferredRequestQueue這個(gè)概念,這里簡(jiǎn)單介紹下:
DeferredRequestQueue繼承于IPropertyPoolObserver,實(shí)現(xiàn)了OnPropertyUpdate/OnMetadataUpdate/OnPropertyFailure/OnMetadataFailure接口,這幾個(gè)接口用于接收Meta Data以及Property的更新,另外,DRQ主要包含了以下幾個(gè)主要方法:
Create()
該方法用于創(chuàng)建DRQ,其中創(chuàng)建了用于存儲(chǔ)依賴信息的m_pDependencyMap,并將自己注冊(cè)到MetadataPool中,一旦有meta data或者property更新便會(huì)通過(guò)類中實(shí)現(xiàn)的幾個(gè)接口通知到DRQ。DispatchReadyNodes()
該方法主要用于將處于m_readyNodes隊(duì)列的Node取出,將其投遞到m_hDeferredWorker線程中進(jìn)行處理。AddDeferredNode()
該方法主要用于添加依賴項(xiàng)到m_pDependencyMap中。FenceSignaledCallback()
當(dāng)Node內(nèi)部針對(duì)某次request處理完成之后,會(huì)通過(guò)一系列回調(diào)通知到DRQ,而其調(diào)用的方法便是該方法,在該方法中,會(huì)首先調(diào)用UpdateDependency更新依賴項(xiàng),然后調(diào)用DispatchReadyNodes觸發(fā)開(kāi)始對(duì)處于ready狀態(tài)的Node開(kāi)始進(jìn)行處理OnPropertyUpdate()
該方法是定義于IPropertyPoolObserver接口,DRQ實(shí)現(xiàn)了它,主要用于接收Property更新的通知,并在內(nèi)部調(diào)用UpdateDependency更新依賴項(xiàng)。OnMetadataUpdate()
該方法是定義于IPropertyPoolObserver接口,DRQ實(shí)現(xiàn)了它,主要用于接收Meta data更新的通知,并在內(nèi)部調(diào)用UpdateDependency更新依賴項(xiàng)。UpdateDependency()
該方法用于更新Node的依賴項(xiàng)信息,并且將沒(méi)有依賴的Node從m_deferredNodes隊(duì)列中移到m_readyNodes,這樣該Node就可以在之后的某次DispatchReadyNodes調(diào)用之后投入運(yùn)行。DeferredWorkerWrapper()
該方法是m_hDeferredWorker線程的處理函數(shù),主要用于處理需要下發(fā)request的Node,同時(shí)再次更新依賴項(xiàng),最后會(huì)再次調(diào)用DispatchReadyNodes開(kāi)始處理。
其中需要注意的是,Pipeline首次針對(duì)每一個(gè)Node通過(guò)調(diào)用AddDeferredNode方法加入到DRQ中,此時(shí)所有的Node都會(huì)加入到m_readyNodes中,然后通過(guò)調(diào)用dispatchReadyNodes方法,觸發(fā)DRQ開(kāi)始進(jìn)行整個(gè)內(nèi)部處理流程,基本流程可以參見(jiàn)下圖,接下來(lái)就以該圖進(jìn)行深入梳理下:

在這里插入圖片描述
當(dāng)調(diào)用了DRQ的dispatchReadyNodes方法后,會(huì)從m_readyNodes鏈表里面依次取出Dependency,將其投遞到DeferredWorkerWrapper線程中,在該線程會(huì)從Dependency取出Node調(diào)用其ProcessRequest方法開(kāi)始在Node內(nèi)部處理本次request,處理完成之后如果當(dāng)前Node依然存在依賴項(xiàng),則調(diào)用AddDeferredNode方法將Node再次加入到m_deferredNodes鏈表中,并且加入新的依賴項(xiàng),存入m_pDependencyMap hash表中。
在Node處理request的過(guò)程中,會(huì)持續(xù)更新meta data以及property,此時(shí)會(huì)通過(guò)調(diào)用MetadataSlot的PublishMetadata方法更新到MetadataPool中,此時(shí)MetadataPool會(huì)調(diào)用之前在DRQ初始化時(shí)候注冊(cè)的幾個(gè)回調(diào)方法OnPropertyUpdate以及OnMetadataUpdate方法通知DRQ,此時(shí)有新的meta data 和property更新,接下來(lái)會(huì)在這兩個(gè)方法中調(diào)用UpdateDependency方法,去更新meta data 和property到m_pDependencyMap中,并且將沒(méi)有任何依賴項(xiàng)的Node從m_deferredNodes取出加入到m_readyNodes,等待處理。
與此同時(shí),Node的處理結(jié)果也會(huì)通過(guò)ProcessFenceCallback方法通知pipeline,并且調(diào)用pipeline的NonSinkPortFenceSignaled方法,在該方法內(nèi)部又會(huì)去調(diào)用DRQ的FenceSignaledCallback方法,而該方法又會(huì)調(diào)用UpdateDependency更新依賴,并將依賴項(xiàng)都滿足的Node從m_deferredNodes取出加入到m_readyNodes,然后調(diào)用dispatchReadyNodes繼續(xù)進(jìn)行處理。
5. 上傳拍照結(jié)果
在用戶開(kāi)啟了相機(jī)應(yīng)用,相機(jī)框架收到某次Request請(qǐng)求之后會(huì)開(kāi)始對(duì)其進(jìn)行處理,一旦有圖像數(shù)據(jù)產(chǎn)生便會(huì)通過(guò)層層回調(diào)最終返回到應(yīng)用層進(jìn)行顯示,這里我們針對(duì)CamX-CHI部分對(duì)于拍照結(jié)果的上傳流程進(jìn)行一個(gè)簡(jiǎn)單的梳理:
每一個(gè)Request對(duì)應(yīng)了三個(gè)Result,分別是partial metadata、metadata以及image data,對(duì)于每一個(gè)Result,上傳過(guò)程可以大致分為以下兩個(gè)階段:
Session內(nèi)部完成圖像數(shù)據(jù)的處理,將結(jié)果發(fā)送至Usecase中
Usecase接收到來(lái)自Session的數(shù)據(jù),并將其上傳至Provider
首先來(lái)看下Session內(nèi)部完成圖像數(shù)據(jù)的處理后是如何將結(jié)果發(fā)送至Usecase的:

在這里插入圖片描述
在整個(gè)requets流轉(zhuǎn)的過(guò)程中,一旦Node中有Partial Meta Data產(chǎn)生,便會(huì)調(diào)用Node的ProcessPartialMetadataDone方法去通知從屬的Pipeline,其內(nèi)部又調(diào)用了pipeline的NotifyNodePartialMetadataDone方法。每次調(diào)用Pipeline的NotifyNodePartialMetadataDone方法都會(huì)去將pPerRequestInfo→numNodesPartialMetadataDone加一并且判斷當(dāng)前值是否等于pipeline中的Node數(shù)量,一旦相等,便說(shuō)明當(dāng)前所有的Node都完成了partial meta data的更新動(dòng)作,此時(shí),便會(huì)調(diào)用ProcessPartialMetadataRequestIdDone方法,里面會(huì)去取出partial meta data,并且重新封裝成ResultsData結(jié)構(gòu)體,將其作為參數(shù)通過(guò)Session的NotifyResult方法傳入Session中,之后在Session中經(jīng)過(guò)層層調(diào)用最終會(huì)調(diào)用到內(nèi)部成員變量m_chiCallBacks的ChiProcessPartialCaptureResult方法,該方法正是創(chuàng)建Session的時(shí)候,傳入Session中的Usecase的方法(AdvancedCameraUsecase::ProcessDriverPartialCaptureResultCb),通過(guò)該方法就將meta data返回到了CHI中。
同樣地,Meta data的邏輯和Partial Meta Data很相似,每個(gè)Node在處理request的過(guò)程中,會(huì)調(diào)用ProcessMetadataDone方法將數(shù)據(jù)發(fā)送到Pipeline中,一旦所有的Node的meta data否發(fā)送完成了,pipeline會(huì)調(diào)用NotifyNodeMetadataDone方法,將最終的結(jié)果發(fā)送至Session中,最后經(jīng)過(guò)層層調(diào)用,會(huì)調(diào)用Session 中成員變量m_chiCallBacks的ChiProcessCaptureResult方法,將結(jié)果發(fā)送到CHI中Usecase中。
圖像數(shù)據(jù)的流轉(zhuǎn)和前兩個(gè)meta data的流轉(zhuǎn)有點(diǎn)兒差異,一旦Node內(nèi)部圖像數(shù)據(jù)處理完成后便會(huì)調(diào)用其ProcessFenceCallback方法,在該方法中會(huì)去檢查當(dāng)前輸出是否是SInk Buffer,如果是則會(huì)調(diào)用Pipeline的SinkPortFenceSignaled方法將數(shù)據(jù)發(fā)送到Pipeline中,在該方法中Pipeline又會(huì)將數(shù)據(jù)發(fā)送至Session中,最后經(jīng)過(guò)層層調(diào)用,會(huì)調(diào)用Session 中成員變量m_chiCallBacks的ChiProcessCaptureResult方法,將結(jié)果發(fā)送到CHI中Usecase中。
接下來(lái)我們來(lái)看下一旦Usecase接收到Session的數(shù)據(jù),是如何發(fā)送至Provider的:
我們以常用的AdvancedCameraUsecase為例進(jìn)行代碼的梳理:

在這里插入圖片描述
如上圖所示,整個(gè)result的流轉(zhuǎn)邏輯還是比較清晰的,CamX通過(guò)回調(diào)方法將結(jié)果回傳給CHI中,而在CHI中,首先判斷是否需要發(fā)送到具體的Feature的, 如果需要,則調(diào)用相應(yīng)Feature的ProcessDriverPartialCaptureResult或者ProcessResult方法將結(jié)果發(fā)送到具體的Feature中,一旦處理完成,便會(huì)調(diào)用調(diào)用CameraUsecaseBase的ProcessAndReturnPartialMetadataFinishedResults以及ProcessAndReturnFinishedResults方法將結(jié)果發(fā)送到Usecase中,如果當(dāng)前不需要發(fā)送到Feature進(jìn)行處理,就在AdvancedCameraUsecase中調(diào)用CameraUsecaseBase的SessionCbPartialCaptureResult以及SessionCbCaptureResult方法,然后通過(guò)Usecase::ReturnFrameResult方法將結(jié)果發(fā)送到ExtensionModule中,之后調(diào)用ExtensionModule中存儲(chǔ)的CamX中的回調(diào)函數(shù)process_capture_result將結(jié)果發(fā)送到CamX中的HALDevice中,之后HALDevice又通過(guò)之前存儲(chǔ)的上層傳入的回調(diào)方法,將結(jié)果最終發(fā)送到CameraDeviceSession中。
通過(guò)以上的梳理,可以發(fā)現(xiàn),整個(gè)CamX-CHI框架設(shè)計(jì)的很不錯(cuò),目錄結(jié)構(gòu)清晰明確,框架簡(jiǎn)單高效,流程控制邏輯分明,比如針對(duì)某一圖像請(qǐng)求,整個(gè)流程經(jīng)過(guò)Usecase、Feature、Session、Pipeline并且給到具體的Node中進(jìn)行處理,最終輸出結(jié)果。另外,相比較之前的QCamera & Mm-Camera框架的針對(duì)某個(gè)算法的擴(kuò)展需要在整個(gè)流程代碼中嵌入自定義的修改做法而言,CamX-CHI通過(guò)將自定義實(shí)現(xiàn)的放入CHI中,提高了其擴(kuò)展性,降低了開(kāi)發(fā)門(mén)檻,使得平臺(tái)廠商在并不是很熟悉CamX框架的情況下也可以通過(guò)小規(guī)模的修改成功添加新功能。但是人無(wú)完人,框架也是一樣,該框架異步化處理太多,加大了定位問(wèn)題以及解決問(wèn)題的難度,給開(kāi)發(fā)者帶來(lái)了不小的壓力。另外,框架對(duì)于內(nèi)存的要求較高,所以在一些低端機(jī)型尤其是低內(nèi)存機(jī)型上,整個(gè)框架的運(yùn)行效率可能會(huì)受到一定的限制,進(jìn)而導(dǎo)致相機(jī)效率低于預(yù)期
原文鏈接:https://blog.csdn.net/u012596975/article/details/107138576
相關(guān)文章友情推薦?
1. Android開(kāi)發(fā)干貨分享
至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺(jué)得很優(yōu)秀,歡迎點(diǎn)擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請(qǐng)聯(lián)系小編刪除,歡迎您的建議與指正。同時(shí)期待您的關(guān)注,感謝您的閱讀,謝謝!
點(diǎn)個(gè)在看,方便您使用時(shí)快速查看!
