<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          深入理解相機(jī)體系 六 相機(jī)硬件抽象層實(shí)現(xiàn) HAL

          共 22231字,需瀏覽 45分鐘

           ·

          2022-04-11 23:25

          和你一起終身學(xué)習(xí),這里是程序員Android

          經(jīng)典好文推薦,通過閱讀本文,您將收獲以下知識點(diǎn):

          一、概覽
          二、基本組件概念
          三、組件結(jié)構(gòu)關(guān)系
          四、關(guān)鍵流程詳解

          相機(jī)硬件抽象層實(shí)現(xiàn)

          一、概覽

          回顧高通平臺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)一的功能性接口抽離出來放到CamX中,將可定制化的部分放在CHI中供不同廠商進(jìn)行修改,實(shí)現(xiàn)各自獨(dú)有的特色功能,這樣設(shè)計(jì)的好處顯而易見,那便是即便開發(fā)者對于CamX并不是很了解,但是依然可以很方便的加入自定義的功能,從而降低了開發(fā)者在高通平臺的開發(fā)門檻。

          接下來我們以最直觀的目錄結(jié)構(gòu)入手對該架構(gòu)做一個(gè)簡單的認(rèn)識,以下便是CamX-CHI基本目錄結(jié)構(gòu):

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          該部分代碼主要位于 vendor/qcom/proprietary/ 目錄下:

          其中 camx 代表了通用功能性接口的代碼實(shí)現(xiàn)集合(CamX),chi-cdk代表了可定制化需求的代碼實(shí)現(xiàn)集合(CHI),從圖中可以看出Camx部分對上作為HAL3接口的實(shí)現(xiàn),對下

          通過v4l2框架與Kernel保持通訊,中間通過互相dlopen so庫并獲取對方操作接口的方式保持著與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/: 用于存放平臺相關(guān)的配置項(xiàng)

          • topology/: 用于存放用戶自定的Usecase xml配置文件

          • node/: 用于存放用戶自定義功能的node

          • module/: 用于存放不同sensor的配置文件,該部分在初始化sensor的時(shí)候需要用到

          • tuning/: 用于存放不同場景下的效果參數(shù)的配置文件

          • sensor/: 用于存放不同sensor的私有信息以及寄存器配置參數(shù)

          • actuator/: 用于存放不同對焦模塊的配置信息

          • ois/:用于存放防抖模塊的配置信息

          • flash/:存放著閃光燈模塊的配置信息

          • eeprom/: 存放著eeprom外部存儲模塊的配置信息

          • fd/: 存放了人臉識別模塊的配置信息

          二、基本組件概念

          1. Usecase

          作為CamX-CHI中最大的抽象概念,其中包含了多條實(shí)現(xiàn)特定功能的Pipeline,具體實(shí)現(xiàn)是在CHI中通過Usecase類完成的,該類主要負(fù)責(zé)了其中的業(yè)務(wù)處理以及資源的管理。

          Usecase類,提供了一系列通用接口,作為現(xiàn)有的所有Usecase的基類,其中,AdvancedCameraUsecase又繼承于CameraUsecaseBase,相機(jī)中絕大部分場景會通過實(shí)例化AdvancedCameraUsecase來完成,它包括了幾個(gè)主要接口:

          • Create(): 該方法是靜態(tài)方法,用于創(chuàng)建一個(gè)AdvancedCameraUsecase實(shí)例,在其構(gòu)造方法中會去獲取XML中的相應(yīng)的Usecase配置信息。

          • ExecuteCaptureRequest(): 該方法用于下發(fā)一次Request請求。

          • ProcessResultCb(): 該方法會在創(chuàng)建Session的過程中,作為回調(diào)方法注冊到其中,一旦Session數(shù)據(jù)處理完成的時(shí)候便會調(diào)用該方法將結(jié)果發(fā)送到AdvancedCameraUsecase中。

          • ProcessDriverPartialCaptureResult(): 該方法會在創(chuàng)建Session的過程中,作為回調(diào)方法注冊到其中,一旦Session中產(chǎn)生了partial meta data的時(shí)候,便會調(diào)用該方法將其發(fā)送至AdvancedCameraUsecase中。

          • ProcessMessageCb(): 該方法會在創(chuàng)建Session的過程中,作為回調(diào)方法注冊到其中,一旦Session產(chǎn)生任何事件,便會調(diào)用該方法通知到AdvancedCameraUsecase中。

          • ExecuteFlush(): 該方法用于刷新AdvancedCameraUsecase。

          • Destroy(): 該方法用于安全銷毀AdvancedCameraUsecase。

          Usecase的可定制化部分被抽象出來放在了common_usecase.xml文件中,這里簡單介紹其中的幾個(gè)主要的標(biāo)簽含義:
          Usecase

          • UsecaseName: 代表了該Usecase的名字,后期根據(jù)這個(gè)名字找到這個(gè)Usecase的定義。

          • Targets: 用于表示用于輸出的數(shù)據(jù)流的集合,其中包括了數(shù)據(jù)流的格式,輸出Size的范圍等。

          • Pipeline: 用于定義該Usecase可以是使用的所有Pipeline,這里必須至少定義一條Pipeline。

          2. Feature

          代表了一個(gè)特定的功能,該功能需要多條Pipeline組合起來實(shí)現(xiàn),受Usecase統(tǒng)一管理,在CHI中通過Feature類進(jìn)行實(shí)現(xiàn),在XML中沒有對應(yīng)的定義,具體的Feature選取工作是在Usecase中完成的,通過在創(chuàng)建Feature的時(shí)候,傳入U(xiǎn)secase的實(shí)例的方式,來和Usecase進(jìn)行相互訪問各自的資源。

          以下是現(xiàn)有的Feature,其中Feature作為基類存在,定義了一系列通用方法。


          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          幾個(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)前場景的AI識別輸出結(jié)果,并其通過諸如到metadata方式給到上層,進(jìn)行后續(xù)的處理。

          3. Session

          用于管理pipeline的抽象控制單元,一個(gè)Session中至少擁有一個(gè)pipeine,并且控制著所有的硬件資源,管控著每一個(gè)內(nèi)部pipeline的request的流轉(zhuǎn)以及數(shù)據(jù)的輸入輸出,它沒有可定制化的部分,所以在CHI中的XML文件中并沒有將Session作為一個(gè)獨(dú)立的單元進(jìn)行定義。

          Session的實(shí)現(xiàn)主要通過CamX中的Session類,其主要接口如下:

          • Initialize(): 根據(jù)傳入的參數(shù)SessionCreateData進(jìn)行Session的初始化工作。

          • NotifyResult(): 內(nèi)部的Pipeline通過該接口將結(jié)果發(fā)送到Session中。

          • ProcessCaptureRequest(): 該方法用于用戶決定發(fā)送一個(gè)Request到Session中的時(shí)候調(diào)用。

          • StreamOn(): 通過傳入的Pipeline句柄,開始硬件的數(shù)據(jù)傳輸。

          • StreamOff(): 通過傳入的Pipeline句柄,停止硬件的數(shù)據(jù)傳輸。

          4. Pipeline

          作為提供單一特定功能的所有資源的集合,維護(hù)著所有硬件資源以及數(shù)據(jù)的流轉(zhuǎn),每一個(gè)Pipeline包括了其中的Node/Link,在CamX中通過Pipeline類進(jìn)行實(shí)現(xiàn),負(fù)責(zé)整條Pipeline的軟硬件資源的維護(hù)以及業(yè)務(wù)邏輯的處理,接下來我們簡單看下該類的幾個(gè)主要接口:

          • Create(): 該方法是一個(gè)靜態(tài)方法,根據(jù)傳入的PipelineCreateInputData信息來實(shí)例化一個(gè)Pipeline對象。

          • StreamOn(): 通知Pipeline開始硬件的數(shù)據(jù)傳輸

          • StreamOff(): 通知Pipeline停止硬件的數(shù)據(jù)傳輸

          • FinalizePipeline(): 用于完成Pipeline的設(shè)置工作

          • OpenRequest(): open一個(gè)CSL用于流轉(zhuǎn)的Request

          • ProcessRequest(): 開始下發(fā)Request

          • NotifyNodeMetadataDone(): 該方法是Pipeline提供給Node,當(dāng)Node內(nèi)部生成了metadata,便會調(diào)用該方法來通知metadata已經(jīng)完成,最后當(dāng)所有Node都通知Pipeline metadata已經(jīng)完成,Pipeline 便會調(diào)用ProcessMetadataRequestIdDone通知Session。

          • NotifyNodePartialMetadataDone(): 該方法是Pipeline提供給Node,當(dāng)Node內(nèi)部生成了partial metadata,便會調(diào)用該方法來通知metadata已經(jīng)完成,最后當(dāng)所有Node都通知Pipeline metadata已經(jīng)完成,Pipeline 便會調(diào)用ProcessPartialMetadataRequestIdDone通知Session。

          • SinkPortFenceSignaled(): 用來通知Session 某個(gè)sink port的fence處于被觸發(fā)的狀態(tài)。

          • NonSinkPortFenceSignaled(): 用來通知Session 某個(gè)non sink port的fence處于被觸發(fā)的狀態(tài)。

          Pipeline中的Node以及連接方式都在XML中被定義,其主要包含了以下幾個(gè)標(biāo)簽定義:

          • PipelineName: 用來定義該條Pipeline的名稱

          • NodeList: 該標(biāo)簽中定義了該條Pipeline的所有的Node

          • PortLinkages: 該標(biāo)簽定義了Node上不同端口之間的連接關(guān)系

          5. Node

          作為單個(gè)具有獨(dú)立處理功能的抽象模塊,可以是硬件單元也可以是軟件單元,關(guān)于Node的具體實(shí)現(xiàn)是CamX中的Node類來完成的,其中CamX-CHI中主要分為兩個(gè)大類,一個(gè)是高通自己實(shí)現(xiàn)的Node包括硬件Node,一個(gè)是CHI中提供給用戶進(jìn)行實(shí)現(xiàn)的Node,其主要方法如下:

          • Create(): 該方法是靜態(tài)方法,用于實(shí)例化一個(gè)Node對象。

          • ExecuteProcessRequest(): 該方法用于針對hwl node下發(fā)request的操作。

          • ProcessRequestIdDone(): 一旦該Node當(dāng)前request已經(jīng)處理完成,便會通過調(diào)用該方法通知Pipeline。

          • ProcessMetadataDone(): 一旦該Node的當(dāng)前request的metadata已經(jīng)生成,便會通過調(diào)用該方法通知到Pipeline。

          • ProcessPartialMetadataDone(): 一旦該Node的當(dāng)前request的partial metadata已經(jīng)生成,便會通過調(diào)用該方法通知到Pipeline。

          • CreateImageBufferManager(): 創(chuàng)建ImageBufferManager

          其可定制化的部分作為標(biāo)簽在XML中進(jìn)行定義:

          • NodeName:用來定義該Node的名稱

          • NodeId: 用來指定該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的連接,它通過標(biāo)簽來進(jìn)行定義,其中包括了作為輸入端口,作為輸出端口。
          一個(gè)Link中包含了一個(gè)SrcPort和一個(gè)DstPort,分別代表了輸入端口和輸出端口,然后BufferProperties用于表示兩個(gè)端口之間的buffer配置。

          7. Port

          作為Node的輸入輸出的端口,在XML文件中,標(biāo)簽用來定義一個(gè)輸入端口,標(biāo)簽用來定義輸出端口,每一個(gè)Node都可以根據(jù)需要使用一個(gè)或者多個(gè)輸入輸出端口,使用OutputPort以及InputPort結(jié)構(gòu)體來進(jìn)行在代碼中定義。
          Port

          • PortId: 該端口的Id: 該端口的名稱

          • NodeName: 該端口從屬的Node名稱

          • NodeId: 該端口從屬的Node的Id

          • NodeInstance: 該端口從屬的Node的實(shí)例名稱

          • NodeInstanceId: 該端口從屬的Node的實(shí)例的Id

          三、組件結(jié)構(gòu)關(guān)系

          通過之前的介紹,我們對于幾個(gè)基本組件有了一個(gè)比較清晰地認(rèn)識,但是任何一個(gè)框架體系并不是僅靠組件胡亂堆砌而成的,相反,它們都必須基于各自的定位,按照各自所獨(dú)有的行為模式,同時(shí)按照約定俗稱的一系列規(guī)則組合起來,共同完成整個(gè)框架某一特定的功能。所以這里不得不產(chǎn)生一個(gè)疑問,在該框架中它們到底是如何組織起來的呢?它們之間的關(guān)系又是如何的呢?接下來我們以下圖入手開始進(jìn)行分析:


          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          由上圖可以看到,幾者是通過包含關(guān)系組合起來的,Usecase 包含F(xiàn)eature,而Feature包含了Session,Session又維護(hù)了內(nèi)部的Pipeline的流轉(zhuǎn),而每一條pipeline中又通過Link將所有Node都連接了起來,接下我們就這幾種關(guān)系詳細(xì)講解下:

          首先,一個(gè)Usecase代表了某個(gè)特定的圖像采集場景,比如人像場景,后置拍照場景等等,在初始化的時(shí)候通過根據(jù)上層傳入的一些具體信息來進(jìn)行創(chuàng)建,這個(gè)過程中,一方面實(shí)例化了特定的Usecase,這個(gè)實(shí)例是用來管理整個(gè)場景的所有資源,同時(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)建過程中,便會根據(jù)需要創(chuàng)建一個(gè)或者多個(gè)Feature,一般一個(gè)Feature對應(yīng)著一個(gè)特定的功能,如果場景中并不需要任何特定的功能,則也完全可以不使用也不創(chuàng)建任何Feature。

          然后,每一個(gè)Usecase或者Feature都可以包含一個(gè)或者多個(gè)Session,每一個(gè)Session都是直接管理并負(fù)責(zé)了內(nèi)部的Pipeline的數(shù)據(jù)流轉(zhuǎn),其中每一次的Request都是Usecase或者Featuret通過Session下發(fā)到內(nèi)部的Pipeline進(jìn)行處理,數(shù)據(jù)處理完成之后也是通過Session的方法將結(jié)果給到CHI中,之后是直接給到上層還是將數(shù)據(jù)封裝下再次下發(fā)到另一個(gè)Session中進(jìn)行后處理,這都交由CHI來決定。

          其中,Session和Pipeline是一對多的關(guān)系,通常一個(gè)Session只包含了一條Pipeline,用于某個(gè)特定圖像處理功能的實(shí)現(xiàn),但是也不絕對,比如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)就是將拿到的主副雙攝的圖像通過RTBOfflinePreview這一條Pipeline將兩幀圖像合成一幀具有虛化效果的圖像,從而完成了虛化功能。

          最后Pipeline中的Node的連接方式是通過XML文件中的Link來進(jìn)行描述的,每一個(gè)Link定義了一個(gè)輸入端和輸出端分別對應(yīng)著不同Node上面的輸入輸出端口,通過這種方式就將其中的一個(gè)Node的輸出端與另外一個(gè)Node的輸入端,一個(gè)一個(gè)串聯(lián)起來,等到圖像數(shù)據(jù)從Pipeline的起始端開始輸入的時(shí)候,便可以按照這種定義好的軌跡在一個(gè)一個(gè)Node之間進(jìn)行流轉(zhuǎn),而在流轉(zhuǎn)的過程中每經(jīng)過一個(gè)Node都會在內(nèi)部對數(shù)據(jù)進(jìn)行處理,這樣等到數(shù)據(jù)從起始端一直流轉(zhuǎn)到最后一個(gè)Node的輸出端的時(shí)候,數(shù)據(jù)就經(jīng)過了很多次處理,這些處理效果最后疊加在一起便是該P(yáng)ipeline所要實(shí)現(xiàn)的功能,比如降噪、虛化等等。

          四、關(guān)鍵流程詳解

          1. Camera Provider 啟動初始化

          當(dāng)系統(tǒng)啟動的時(shí)候,Camera Provider主程序會被運(yùn)行,在整個(gè)程序初始化的過程中會通過獲取到的camera_module_t調(diào)用其get_number_of_camera接口獲取底層支持的camera數(shù)量,由于是第一次獲取,所以在CamX-CHI中會伴隨著很多初始化動作,具體操作見下圖:


          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          主要流程如下:

          1. 通過HAL3Module::GetInstance()靜態(tài)方法實(shí)例化了HAL3Module對象,在其構(gòu)造方法里面通過HwEnvironment::GetInstance()靜態(tài)方法又實(shí)例化了HwEnvironment對象,在其構(gòu)造方法中,實(shí)例化了SettingsManager對象,然后又在它構(gòu)造方法中通過OverrideSettingsFile對象獲取了位于/vendor/etc/camera/camoverridesettings.txt文件中的平臺相關(guān)的配置信息(通過這種Override機(jī)制方便平臺廠商加入自定義配置),該配置文件中,可以加入平臺特定的配置項(xiàng),比如可以通過設(shè)置multiCameraEnable的值來表示當(dāng)前平臺是否支持多攝,或者通過設(shè)置overrideLogLevels設(shè)置項(xiàng)來配置CamX-CHI部分的Log輸出等級等等。

          2. 同時(shí)在HwEnvironment構(gòu)造方法中會調(diào)用其Initialize方法,在該方法中實(shí)例化了CSLModeManager對象,并通過CSLModeManager提供的接口,獲取了所有底層支持的硬件設(shè)備信息,其中包括了Camera Request Manager、CAPS模塊(該驅(qū)動模塊主要用于CSL獲取Camera平臺驅(qū)動信息,以及IPE/BPS模塊的電源控制)以及Sensor/IPE/Flash等硬件模塊,并且通過調(diào)用CSLHwInternalProbeSensorHW方法獲取了當(dāng)前設(shè)備安裝的Sensor模組信息,并且將獲取的信息暫存起來,等待后續(xù)階段使用,總得來說在HwEnvironment初始化的過程中,通過探測方法獲取了所有底層的硬件驅(qū)動模塊,并將其信息存儲下來供后續(xù)階段使用。

          3. 之后通過調(diào)用HwEnvironment對象中的ProbeChiCompoents方法在/vendor/lib64/camera/components路徑下找尋各個(gè)Node生成的So庫,并獲取Node提供的標(biāo)準(zhǔn)對外接口,這些Node不但包括CHI部分用戶自定義的模塊,還包括了CamX部分實(shí)現(xiàn)的硬件模塊,并最后都將其都存入ExternalComponentInfo對象中,等待后續(xù)階段使用。

          另外在初始化階段還有一個(gè)比較重要的操作就是CamX 與CHI是通過互相dlopen對方的So庫,獲取了對方的入口方法,最后通過彼此的入口方法獲取了對方操作方法集合,之后再通過這些操作方法與對方進(jìn)行通訊,其主要流程見下圖:

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          從上圖不難看出,在HAL3Module構(gòu)造方法中會去通過dlopen方法加載com.qti.chi.override.so庫,并通過dlsym映射出CHI部分的入口方法chi_hal_override_entry,并調(diào)用該方法將HAL3Module對像中的成員變量m_ChiAppCallbacks(CHIAppCallbacks)傳入CHI中,其中包含了很多函數(shù)指針,這些函數(shù)指針分別對應(yīng)著CHI部分的操作方法集中的方法,一旦進(jìn)入到CHI中,就會將CHI本地的操作方法集合中的函數(shù)地址依次賦值給m_ChiAppCallbacks,這樣CamX后續(xù)就可以通過這個(gè)成員變量調(diào)用到CHI中方法,從而保持了與CHI的通訊。

          同樣地,CHI中的ExtensionModule在初始化的時(shí)候,其構(gòu)造方法中也會通過調(diào)用dlopen方法加載camera.qcom.so庫,并將其入口方法ChiEntry通過dlsym映射出來,之后調(diào)用該方法,將g_chiContextOps(ChiContextOps,該結(jié)構(gòu)體中定義了很多指針函數(shù))作為參數(shù)傳入CamX中,一旦進(jìn)入CamX中,便會將本地的操作方法地址依次賦值給g_chiContextOps中的每一個(gè)函數(shù)指針,這樣CHI之后就可以通過g_chiContextOps訪問到CamX方法。

          2. 打開相機(jī)設(shè)備/初始化相機(jī)設(shè)備

          一旦用戶打開了相機(jī)應(yīng)用,App中便會去調(diào)用CameraManager的openCamera方法,該方法之后會最終調(diào)用到Camera Service中的CameraService::connectDevice方法,然后通過ICameraDevice::open()這一個(gè)HIDL接口通知Provider,然后在Provider內(nèi)部又通過調(diào)用之前獲取的camera_module_t中methods的open方法來獲取一個(gè)Camera 設(shè)備,對應(yīng)于HAL中的camera3_device_t結(jié)構(gòu)體,緊接著,在Provider中會繼續(xù)調(diào)用獲取到的camera3_device_t的initialize方法進(jìn)行初始化動作。接下來我們便來詳細(xì)分析下CamX-CHI對于open以及initialize的具體實(shí)現(xiàn)流程:

          a) open

          該方法是camera_module_t的標(biāo)準(zhǔn)方法,主要用來獲取camera3_device_t設(shè)備結(jié)構(gòu)體的,CamX-CHI對其進(jìn)行了實(shí)現(xiàn),open方法中完成的工作主要有以下幾個(gè):

          1. 將當(dāng)前camera id傳入CHI中進(jìn)行remap操作,當(dāng)然這個(gè)remap操作邏輯完全是根據(jù)CHI中用戶需求來的,用戶可以根據(jù)自己的需要在CHI中加入自定義remap邏輯。

          2. 實(shí)例化HALDevice對象,其構(gòu)造函數(shù)中調(diào)用Initialize方法,該方法會填充CamX中自定義的Camera3Device結(jié)構(gòu)體。

          3. 將m_HALCallbacks.process_capture_result指向了本地方法ProcessCaptureResult以及m_HALCallbacks.notify_result指向了本地方法Notify(之后會在配置數(shù)據(jù)流的過程中,將m_HALCallbacks注冊到CHI中, 一旦當(dāng)CHI數(shù)據(jù)處理完成之后,便會通過這兩個(gè)回調(diào)方法將數(shù)據(jù)或者事件回傳給CamX)。

          4. 最后將HALDevice 中的Camera3Device成員變量作為返回值給到Provider中的CameraCaptureSession中。

          Camera3Device 其實(shí)重定義了camera3_device_t,其中HwDevice對應(yīng)于camera3_device_t中的hw_device_t,Camera3DeviceOps對應(yīng)于camera3_device_ops_t,而在HALDevice的初始化過程中,會將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便會通過這些回調(diào)接口將數(shù)據(jù)或者事件上傳至調(diào)用者,其內(nèi)部的實(shí)現(xiàn)較為簡單。

          initialize方法中有兩個(gè)參數(shù),分別是之前通過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)備完成便通過這里camera3_callback_ops_t中回調(diào)方法將數(shù)據(jù)回傳到Camera Provider中的CameraDevice中,基本流程可以總結(jié)為以下幾點(diǎn):

          1. 實(shí)例化了一個(gè)Camera3CbOpsRedirect對象并將其加入了g_HAL3Entry.m_cbOpsList隊(duì)列中,這樣方便之后需要的時(shí)候能夠順利拿到該對象。

          2. 將本地的process_capture_result以及notify方法地址分別賦值給Camera3CbOpsRedirect.cbOps中的process_capture_result以及notify函數(shù)指針。

          3. 將上層傳入的回調(diào)方法結(jié)構(gòu)體指針pCamera3CbOpsAPI賦值給Camera3CbOpsRedirect.pCbOpsAPI,并將Camera3CbOpsRedirect.cbOps賦值給pCamera3CbOpsAPI,通過JumpTableHal3的initialize方法將pCamera3CbOpsAPI傳給HALDevice中的m_pCamera3CbOps成員變量,這樣HALDevice中的m_pCamera3CbOps就指向了CamX中本地方法process_capture_result以及notify。

          經(jīng)過這樣的一番操作之后,一旦CHI有數(shù)據(jù)傳入便會首先進(jìn)入到本地方法ProcessCaptureResult,然后在該方法中獲取到HALDevice的成員變量m_pCamera3CbOps,進(jìn)而調(diào)用m_pCamera3CbOps中的process_capture_result方法,即camxhal3entry.cpp中定義的process_capture_result方法,然后這個(gè)方法中會去調(diào)用JumpTableHAL3.process_capture_result方法,該方法最終會去調(diào)用Camera3CbOpsRedirect.pCbOpsAPI中的process_capture_result方法,這樣就調(diào)到從Provider傳入的回調(diào)方法,將數(shù)據(jù)順利給到了CameraCaptureSession中。

          3. 配置相機(jī)設(shè)備數(shù)據(jù)流

          在打開相機(jī)應(yīng)用過程中,App在獲取并打開相機(jī)設(shè)備之后,會調(diào)用CameraDevice.createCaptureSession來獲取CameraDeviceSession,并且通過Camera api v2標(biāo)準(zhǔn)接口,通知Camera Service,調(diào)用其CameraDeviceClient.endConfigure方法,在該方法內(nèi)部又會去通過HIDL接口ICameraDeviceSession::configureStreams_3_4通知Provider開始處理此次配置需求,在Provider內(nèi)部,會去通過在調(diào)用open流程中獲取的camera3_device_t結(jié)構(gòu)體的configure_streams方法來將數(shù)據(jù)流的配置傳入CamX-CHI中,之后由CamX-CHI完成對數(shù)據(jù)流的配置工作,接下來我們來詳細(xì)分析下CamX-CHI對于該標(biāo)準(zhǔn)HAL3接口 configure_streams的具體實(shí)現(xiàn):

          配置數(shù)據(jù)流是整個(gè)CamX-CHI流程比較重要的一環(huán),其中主要包括兩個(gè)階段

          1. 選擇UsecaseId

          2. 根據(jù)選擇的UsecaseId創(chuàng)建Usecase

          接下來我們就這兩個(gè)階段分別進(jìn)行詳細(xì)介紹:

          ① 選擇UsecaseId

          不同的UsecaseId分別對應(yīng)的不同的應(yīng)用場景,該階段是通過調(diào)用UsecaseSelector::GetMatchingUsecase()方法來實(shí)現(xiàn)的,該函數(shù)中通過傳入的operation_mode、num_streams配置數(shù)據(jù)流數(shù)量以及當(dāng)前使用的Sensor個(gè)數(shù)來選擇相應(yīng)的UsecaseId,比如當(dāng)numPhysicalCameras值大于1同時(shí)配置的數(shù)據(jù)流數(shù)量num_streams大于1時(shí)選擇的就是UsecaseId::MultiCamera,表示當(dāng)前采用的是雙攝場景。

          ② 創(chuàng)建Usecase
          根據(jù)之前選擇的UsecaseId,通過UsecaseFactory來創(chuàng)建相應(yīng)的Usecase,
          其中Class Usecase是所有Usecase的基類,其中定義并實(shí)現(xiàn)了一些通用接口,CameraUsecaseBase繼承于Usecase,并擴(kuò)展了部分功能。AdvancedCameraUsecase又繼承于CameraUsecaseBase,作為主要負(fù)責(zé)大部分場景的Usecase實(shí)現(xiàn)類,另外對于多攝場景,現(xiàn)提供了繼承于AdvancedCameraUsecase的UsecaseMultiCamera來負(fù)責(zé)實(shí)現(xiàn)。

          除了雙攝場景,其它大部分場景使用的都是AdvancedCameraUsecase類來管理各項(xiàng)資源的,接下來我們重點(diǎn)梳理下AdvancedCameraUsecase::Create()方法。
          在AdvancedCameraUsecase::Create方法中做了很多初始化操作,其中包括了以下幾個(gè)階段:

          1. 獲取XML文件中Usecase配置信息

          2. 創(chuàng)建Feature

          3. 保存數(shù)據(jù)流,重建Usecase的配置信息

          4. 調(diào)用父類CameraUsecaseBase的initialize方法,進(jìn)行一些常規(guī)初始化工作

          接下來我們就這幾個(gè)階段逐一進(jìn)行分析:
          1. 獲取XML文件中Usecase配置信息
          這一部分主要通過調(diào)用CameraUsecaseBase::GetXMLUsecaseByName方法進(jìn)行實(shí)現(xiàn)。
          該方法的主要操作是從PerNumTargetUsecases數(shù)組中找到匹配到給定的usecaseName的Usecase,并作為返回值返回給調(diào)用者,其中這里我們以"UsecaseZSL“為例進(jìn)行分析,PerNumTargetUsecases的定義是在g_pipeline.h中,該文件是在編譯過程中通過usecaseconverter.pl腳本將定義在個(gè)平臺目錄下的common_usecase.xml中的內(nèi)容轉(zhuǎn)換生成g_pipeline.h。

          2.創(chuàng)建Feature
          如果當(dāng)前場景選取了Feature,則調(diào)用FeatureSetup來完成創(chuàng)建工作。
          該方法主要是通過諸如operation_mode、camera數(shù)量以及UsecaseId等信息來決定需要選擇哪些Feature,具體邏輯比較清晰,一旦決定需要使用哪一個(gè)Feature之后,便調(diào)用相應(yīng)的Feature的Create()方法進(jìn)行初始化操作。

          3.保存數(shù)據(jù)流,重建Usecase的配置信息
          從Camera Service 傳入的數(shù)據(jù)流,需要將其存儲下來,供后續(xù)使用,同時(shí)高通針對Usecase也加入了Override機(jī)制,根據(jù)需要可以選擇性地?cái)U(kuò)展Usecase,這兩個(gè)步驟的實(shí)現(xiàn)主要是通過SelectUsecaseConfig方法來實(shí)現(xiàn)。

          其中主要是調(diào)用以下兩個(gè)方法來實(shí)現(xiàn)的:

          • ConfigureStream:該方法將從上層配置的數(shù)據(jù)流指針存入AdvancedCameraUsecase中,其中包括了用于預(yù)覽的m_pPreviewStream以及用于拍照的m_pSnapshotStream。

          • BuildUsecase:這個(gè)方法用來重新在原有的Usecase上面加入了Feature中所需要的pipeline,并創(chuàng)建了一個(gè)新的Usecase,并將其存入AdvancedCameraUsecase中的m_pChiUsecase成員變量中,緊接著通過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全部跑完之后,會回調(diào)該方法將數(shù)據(jù)投遞到CHI中。

          創(chuàng)建Pipeline
          根據(jù)之前獲取的pipeline信息開始創(chuàng)建每一條pipeline,通過調(diào)用CreatePipeline()方法實(shí)現(xiàn)。

          創(chuàng)建Session
          創(chuàng)建Session,通過CreateSession()方法實(shí)現(xiàn),此時(shí)會將AdvancedCameraUsecase端的回調(diào)函數(shù)注冊到Session中,一旦Session中數(shù)據(jù)處理完成,便會調(diào)用回調(diào)將數(shù)據(jù)回傳給AdvancedCameraUsecase。

          綜上,整個(gè)configure_stream過程,基本可以概括為以下幾點(diǎn):

          1. 根據(jù)operation_mode、camera 個(gè)數(shù)以及stream的配置信息選取了對應(yīng)的UsecaseId

          2. 根據(jù)所選取的UsecaseId,使用UsecaseFactory簡單工廠類創(chuàng)建了用于管理整個(gè)場景下所有資源的AdvancedCameraUsecase對象。

          3. 創(chuàng)建AdvancedCameraUsecase對象是通過調(diào)用其Create()方法完成,該方法中獲取了common_usecase.xml定義的關(guān)于Usecase的配置信息,之后又根據(jù)需要創(chuàng)建了Feature并選取了Feature所需的pipeline,并通過Override機(jī)制將Feature中所需要的Pipeline加入重建后的Usecase中。

          4. 最后通過調(diào)用CameraUsecaseBaese的initialize方法依次創(chuàng)建了各個(gè)pipeline以及Session,并且將AdvancedCameraUsecase的成員方法注冊到Session,用于Session將數(shù)據(jù)返回給Usecase中

          4. 處理拍照請求

          當(dāng)用戶打開相機(jī)應(yīng)用進(jìn)行預(yù)覽或者點(diǎn)擊一次拍照操作的時(shí)候,便觸發(fā)了一次拍照請求,該動作首先通過CameraDeviceSession的capture或者setRepeatingRequest方法將請求通過Camera api v2接口下發(fā)到Camera Service中,然后在Camera Service內(nèi)部將此次請求發(fā)送到CameraDevice::RequestThread線程中進(jìn)行處理,一旦進(jìn)入到該線程之后,便會最終通過HIDL接口ICameraCaptureSession:processCaptureRequest_3_4將請求發(fā)送至Provider中,之后當(dāng)Provider收到請求之后,會調(diào)用camera3_device_t結(jié)構(gòu)體的process_capture_request開始了HAL針對此次Request的處理,而該處理是由CamX-CHI來負(fù)責(zé)實(shí)現(xiàn),現(xiàn)在我們就來看下CamX-CHI是如何實(shí)現(xiàn)該方法的:

          首先CamX中會將此次request轉(zhuǎn)發(fā)到HALDevice中,再通過HALDevice對象調(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方法中會去獲取ExtensionModule對象,并將request發(fā)送到ExtensionModule對象中,該對象中存儲了之前創(chuàng)建的Usecase對象,然后經(jīng)過層層調(diào)用,最終會調(diào)用AdvancedCameraUsecase的ExecuteCaptureRequest方法,該方法負(fù)責(zé)處理此次Request,具體流程如下:

          在AdvancedCameraUsecase的ExecuteCaptureRequest中會有兩個(gè)主要的分支來分別處理:

          • 如果當(dāng)前并沒有任何Feature需要實(shí)現(xiàn),此時(shí)便會走默認(rèn)流程,根據(jù)上面的流程圖所示,這里會調(diào)用CameraUsecaseBase::ExecuteCaptureRequest方法,在該方法中,首先會將request取出,重新封裝成CHICAPTUREREQUEST,然后調(diào)用CheckAndActivatePipeline方法喚醒pipeline,這一操作到最后會調(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)前場景需要實(shí)現(xiàn)某個(gè)Feature,則直接調(diào)用Feature的ExecuteProcessRequest方法將此次request送入Feature中處理,最后依然會調(diào)用到Session::StreamOn以及Session::ProcessCaptureRequest方法來分別完成喚醒pipeline以及下發(fā)request的到Session的操作。

          該流程最終都會調(diào)用到兩個(gè)比較關(guān)鍵的方法Session::StreamOn以及Session::ProcessCaptureRequest,接下來針對這兩個(gè)方法重點(diǎn)介紹下:

          Session::StreamOn

          從方法名稱基本可以知道該方法主要用于開始硬件的數(shù)據(jù)輸出,具體點(diǎn)兒就是進(jìn)行配置Sensor寄存器,讓其開始出圖,并且將當(dāng)前的Session的狀態(tài)告知每一Node,讓它們在自己內(nèi)部也做好處理數(shù)據(jù)的準(zhǔn)備,所以之后的相關(guān)Request的流轉(zhuǎn)都是以該方法為前提進(jìn)行的,所以該方法重要性可見一斑,其操作流程見下圖:

          Session的StreamOn方法中主要做了如下兩個(gè)工作:

          • 調(diào)用FinalizeDeferPipeline()方法,如果當(dāng)前pipeline并未初始化,則會調(diào)用pipeline的FinalizePipeline方法,這里方法里面會去針對每一個(gè)從屬于當(dāng)前pipeline的Node依次做FinalizeInitialization、CreateBufferManager、NotifyPipelineCreated以及PrepareNodeStreamOn操作,F(xiàn)inalizeInitialization用于完成Node的初始化動作,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申請/流轉(zhuǎn)/釋放等操作。

          • 調(diào)用Pipeline的StreamOn方法,里面會進(jìn)一步通知CSL部分開啟數(shù)據(jù)流,并且調(diào)用每一個(gè)Node的OnNodeStreamOn方法,該方法會去調(diào)用ImageBufferManager的Activate(),該方法里面會去真正分配用于裝載圖像數(shù)據(jù)的buffer,之后會去調(diào)用CHI部分實(shí)現(xiàn)的用戶自定義的Nod的pOnStreamOn方法,用戶可以在該方法中做一些自定義的操作。

          Session::ProcessCaptureRequest
          針對每一次的Request的流轉(zhuǎn),都是以該方法為入口開始的,具體流程見下圖:

          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          上述流程可以總結(jié)為以下幾個(gè)步驟:

          1. 通過調(diào)用Session的ProcessCaptureRequest方法進(jìn)入到Session,然后調(diào)用Pipeline中的ProcessRequest方法通知Pipeline開始處理此次Request。

          2. 在Pipeline中,會先去調(diào)用內(nèi)部的每一個(gè)Node的SetupRequest方法分別設(shè)置該Node的Output Port以及Input Port,之后通過調(diào)用DRQ(DeferredRequestQueue)的AddDeferredNode方法將所有的Node加入到DRQ中,其中DRQ中有兩個(gè)隊(duì)列分別是用于保存沒有依賴項(xiàng)的Node的m_readyNodes以及保存處于等待依賴關(guān)系滿足的Node的m_deferredNodes,當(dāng)調(diào)用DRQ的DispatchReadyNodes方法后,會開始從m_readyNodes隊(duì)列中取出Node調(diào)用其ProcessRequest開始進(jìn)入Node內(nèi)部處理本次request,在處理過程中會更新meta data數(shù)據(jù),并更新至DRQ中,當(dāng)該Node處理完成之后,會將處于m_deferredNodes中的已無依賴關(guān)系的Node移到m_readyNodes中,并再次調(diào)用DispatchReadyNodes方法從m_readyNodes取出Node進(jìn)行處理。

          3. 與此過程中,當(dāng)Node的數(shù)據(jù)處理完成之后會通過CSLFenceCallback通知到Pipeline,此時(shí)Pipeline會判斷當(dāng)前Node的Output port 是否是Sink Port(輸出到CHI),如果不是,則會更新依賴項(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中,最后通過調(diào)用Session中的NotifyResult將結(jié)果發(fā)送到CHI中。

          DeferredRequestQueue

          上述流程里面中涉及到DeferredRequestQueue這個(gè)概念,這里簡單介紹下:
          DeferredRequestQueue繼承于IPropertyPoolObserver,實(shí)現(xiàn)了OnPropertyUpdate/OnMetadataUpdate/OnPropertyFailure/OnMetadataFailure接口,這幾個(gè)接口用于接收Meta Data以及Property的更新,另外,DRQ主要包含了以下幾個(gè)主要方法:

          • Create()
            該方法用于創(chuàng)建DRQ,其中創(chuàng)建了用于存儲依賴信息的m_pDependencyMap,并將自己注冊到MetadataPool中,一旦有meta data或者property更新便會通過類中實(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)部針對某次request處理完成之后,會通過一系列回調(diào)通知到DRQ,而其調(diào)用的方法便是該方法,在該方法中,會首先調(diào)用UpdateDependency更新依賴項(xiàng),然后調(diào)用DispatchReadyNodes觸發(fā)開始對處于ready狀態(tài)的Node開始進(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)信息,并且將沒有依賴的Node從m_deferredNodes隊(duì)列中移到m_readyNodes,這樣該Node就可以在之后的某次DispatchReadyNodes調(diào)用之后投入運(yùn)行。

          • DeferredWorkerWrapper()
            該方法是m_hDeferredWorker線程的處理函數(shù),主要用于處理需要下發(fā)request的Node,同時(shí)再次更新依賴項(xiàng),最后會再次調(diào)用DispatchReadyNodes開始處理。

          其中需要注意的是,Pipeline首次針對每一個(gè)Node通過調(diào)用AddDeferredNode方法加入到DRQ中,此時(shí)所有的Node都會加入到m_readyNodes中,然后通過調(diào)用dispatchReadyNodes方法,觸發(fā)DRQ開始進(jìn)行整個(gè)內(nèi)部處理流程,基本流程可以參見下圖,接下來就以該圖進(jìn)行深入梳理下:


          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          1. 當(dāng)調(diào)用了DRQ的dispatchReadyNodes方法后,會從m_readyNodes鏈表里面依次取出Dependency,將其投遞到DeferredWorkerWrapper線程中,在該線程會從Dependency取出Node調(diào)用其ProcessRequest方法開始在Node內(nèi)部處理本次request,處理完成之后如果當(dāng)前Node依然存在依賴項(xiàng),則調(diào)用AddDeferredNode方法將Node再次加入到m_deferredNodes鏈表中,并且加入新的依賴項(xiàng),存入m_pDependencyMap hash表中。

          2. 在Node處理request的過程中,會持續(xù)更新meta data以及property,此時(shí)會通過調(diào)用MetadataSlot的PublishMetadata方法更新到MetadataPool中,此時(shí)MetadataPool會調(diào)用之前在DRQ初始化時(shí)候注冊的幾個(gè)回調(diào)方法OnPropertyUpdate以及OnMetadataUpdate方法通知DRQ,此時(shí)有新的meta data 和property更新,接下來會在這兩個(gè)方法中調(diào)用UpdateDependency方法,去更新meta data 和property到m_pDependencyMap中,并且將沒有任何依賴項(xiàng)的Node從m_deferredNodes取出加入到m_readyNodes,等待處理。

          3. 與此同時(shí),Node的處理結(jié)果也會通過ProcessFenceCallback方法通知pipeline,并且調(diào)用pipeline的NonSinkPortFenceSignaled方法,在該方法內(nèi)部又會去調(diào)用DRQ的FenceSignaledCallback方法,而該方法又會調(diào)用UpdateDependency更新依賴,并將依賴項(xiàng)都滿足的Node從m_deferredNodes取出加入到m_readyNodes,然后調(diào)用dispatchReadyNodes繼續(xù)進(jìn)行處理。

          5. 上傳拍照結(jié)果

          在用戶開啟了相機(jī)應(yīng)用,相機(jī)框架收到某次Request請求之后會開始對其進(jìn)行處理,一旦有圖像數(shù)據(jù)產(chǎn)生便會通過層層回調(diào)最終返回到應(yīng)用層進(jìn)行顯示,這里我們針對CamX-CHI部分對于拍照結(jié)果的上傳流程進(jìn)行一個(gè)簡單的梳理:

          每一個(gè)Request對應(yīng)了三個(gè)Result,分別是partial metadata、metadata以及image data,對于每一個(gè)Result,上傳過程可以大致分為以下兩個(gè)階段:

          • Session內(nèi)部完成圖像數(shù)據(jù)的處理,將結(jié)果發(fā)送至Usecase中

          • Usecase接收到來自Session的數(shù)據(jù),并將其上傳至Provider

          首先來看下Session內(nèi)部完成圖像數(shù)據(jù)的處理后是如何將結(jié)果發(fā)送至Usecase的:


          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          在整個(gè)requets流轉(zhuǎn)的過程中,一旦Node中有Partial Meta Data產(chǎn)生,便會調(diào)用Node的ProcessPartialMetadataDone方法去通知從屬的Pipeline,其內(nèi)部又調(diào)用了pipeline的NotifyNodePartialMetadataDone方法。每次調(diào)用Pipeline的NotifyNodePartialMetadataDone方法都會去將pPerRequestInfo→numNodesPartialMetadataDone加一并且判斷當(dāng)前值是否等于pipeline中的Node數(shù)量,一旦相等,便說明當(dāng)前所有的Node都完成了partial meta data的更新動作,此時(shí),便會調(diào)用ProcessPartialMetadataRequestIdDone方法,里面會去取出partial meta data,并且重新封裝成ResultsData結(jié)構(gòu)體,將其作為參數(shù)通過Session的NotifyResult方法傳入Session中,之后在Session中經(jīng)過層層調(diào)用最終會調(diào)用到內(nèi)部成員變量m_chiCallBacks的ChiProcessPartialCaptureResult方法,該方法正是創(chuàng)建Session的時(shí)候,傳入Session中的Usecase的方法(AdvancedCameraUsecase::ProcessDriverPartialCaptureResultCb),通過該方法就將meta data返回到了CHI中。

          同樣地,Meta data的邏輯和Partial Meta Data很相似,每個(gè)Node在處理request的過程中,會調(diào)用ProcessMetadataDone方法將數(shù)據(jù)發(fā)送到Pipeline中,一旦所有的Node的meta data否發(fā)送完成了,pipeline會調(diào)用NotifyNodeMetadataDone方法,將最終的結(jié)果發(fā)送至Session中,最后經(jīng)過層層調(diào)用,會調(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ù)處理完成后便會調(diào)用其ProcessFenceCallback方法,在該方法中會去檢查當(dāng)前輸出是否是SInk Buffer,如果是則會調(diào)用Pipeline的SinkPortFenceSignaled方法將數(shù)據(jù)發(fā)送到Pipeline中,在該方法中Pipeline又會將數(shù)據(jù)發(fā)送至Session中,最后經(jīng)過層層調(diào)用,會調(diào)用Session 中成員變量m_chiCallBacks的ChiProcessCaptureResult方法,將結(jié)果發(fā)送到CHI中Usecase中。

          接下來我們來看下一旦Usecase接收到Session的數(shù)據(jù),是如何發(fā)送至Provider的:

          我們以常用的AdvancedCameraUsecase為例進(jìn)行代碼的梳理:


          程序員Android轉(zhuǎn)于網(wǎng)絡(luò)

          如上圖所示,整個(gè)result的流轉(zhuǎn)邏輯還是比較清晰的,CamX通過回調(diào)方法將結(jié)果回傳給CHI中,而在CHI中,首先判斷是否需要發(fā)送到具體的Feature的, 如果需要,則調(diào)用相應(yīng)Feature的ProcessDriverPartialCaptureResult或者ProcessResult方法將結(jié)果發(fā)送到具體的Feature中,一旦處理完成,便會調(diào)用調(diào)用CameraUsecaseBase的ProcessAndReturnPartialMetadataFinishedResults以及ProcessAndReturnFinishedResults方法將結(jié)果發(fā)送到Usecase中,如果當(dāng)前不需要發(fā)送到Feature進(jìn)行處理,就在AdvancedCameraUsecase中調(diào)用CameraUsecaseBase的SessionCbPartialCaptureResult以及SessionCbCaptureResult方法,然后通過Usecase::ReturnFrameResult方法將結(jié)果發(fā)送到ExtensionModule中,之后調(diào)用ExtensionModule中存儲的CamX中的回調(diào)函數(shù)process_capture_result將結(jié)果發(fā)送到CamX中的HALDevice中,之后HALDevice又通過之前存儲的上層傳入的回調(diào)方法,將結(jié)果最終發(fā)送到CameraDeviceSession中。

          通過以上的梳理,可以發(fā)現(xiàn),整個(gè)CamX-CHI框架設(shè)計(jì)的很不錯(cuò),目錄結(jié)構(gòu)清晰明確,框架簡單高效,流程控制邏輯分明,比如針對某一圖像請求,整個(gè)流程經(jīng)過Usecase、Feature、Session、Pipeline并且給到具體的Node中進(jìn)行處理,最終輸出結(jié)果。另外,相比較之前的QCamera & Mm-Camera框架的針對某個(gè)算法的擴(kuò)展需要在整個(gè)流程代碼中嵌入自定義的修改做法而言,CamX-CHI通過將自定義實(shí)現(xiàn)的放入CHI中,提高了其擴(kuò)展性,降低了開發(fā)門檻,使得平臺廠商在并不是很熟悉CamX框架的情況下也可以通過小規(guī)模的修改成功添加新功能。但是人無完人,框架也是一樣,該框架異步化處理太多,加大了定位問題以及解決問題的難度,給開發(fā)者帶來了不小的壓力。另外,框架對于內(nèi)存的要求較高,所以在一些低端機(jī)型尤其是低內(nèi)存機(jī)型上,整個(gè)框架的運(yùn)行效率可能會受到一定的限制,進(jìn)而導(dǎo)致相機(jī)效率低于預(yù)期。

          原文鏈接:https://blog.csdn.net/u012596975/article/details/107138576

          友情推薦:

          Android 開發(fā)干貨集錦

          至此,本篇已結(jié)束。轉(zhuǎn)載網(wǎng)絡(luò)的文章,小編覺得很優(yōu)秀,歡迎點(diǎn)擊閱讀原文,支持原創(chuàng)作者,如有侵權(quán),懇請聯(lián)系小編刪除,歡迎您的建議與指正。同時(shí)期待您的關(guān)注,感謝您的閱讀,謝謝!

          點(diǎn)擊閱讀原文,為大佬點(diǎn)贊!

          瀏覽 91
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評論
          圖片
          表情
          推薦
          點(diǎn)贊
          評論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  色国产在线视频 | 国产成人a亚洲精品久久久久 | 国产男女网站 | 高清无码免费视频在线观看 | 加勒比无码综合在线 |