CamX關(guān)鍵流程

本文介紹CamX關(guān)鍵流程.

文章目錄

一始苇、Camera Provider 初始化

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

Camera Provider 啟動初始化

主要流程如下:

  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. 同時在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路徑下找尋各個Node生成的so庫窍育,并獲取Node提供的標(biāo)準(zhǔn)對外接口卡睦,這些Node不但包括CHI部分用戶自定義的模塊,還包括了CamX部分實(shí)現(xiàn)的硬件模塊漱抓,并最后都將其都存入ExternalComponentInfo對象中表锻,等待后續(xù)階段使用。

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

Camera Provider 啟動初始化

從上圖不難看出码耐,在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ù)就可以通過這個成員變量調(diào)用到CHI中方法厢绝,從而保持了與CHI的通訊契沫。

同樣地,CHI中的ExtensionModule在初始化的時候昔汉,其構(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中的每一個函數(shù)指針涕侈,這樣CHI之后就可以通過g_chiContextOps訪問到CamX方法。

二煤辨、Open Camera

一旦用戶打開了相機(jī)應(yīng)用裳涛,App中便會去調(diào)用CameraManager的openCamera方法,該方法之后會最終調(diào)用到Camera Service中的CameraService::connectDevice方法众辨,然后通過ICameraDevice::open()這一個HIDL接口通知Provider端三,然后在Provider內(nèi)部又通過調(diào)用之前獲取的camera_module_t中methods的open方法來獲取一個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)流程:

2.1open

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

  1. 將當(dāng)前camera id傳入CHI中進(jìn)行remap操作,當(dāng)然這個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ù)處理完成之后唆香,便會通過這兩個回調(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中倘待。

2.2 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方法中有兩個參數(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í)例化了一個Camera3CbOpsRedirect對象并將其加入了g_HAL3Entry.m_cbOpsList隊(duì)列中胧辽,這樣方便之后需要的時候能夠順利拿到該對象峻仇。
  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方法,然后這個方法中會去調(diào)用JumpTableHAL3.process_capture_result方法暇仲,該方法最終會去調(diào)用Camera3CbOpsRedirect.pCbOpsAPI中的process_capture_result方法步做,這樣就調(diào)到從Provider傳入的回調(diào)方法,將數(shù)據(jù)順利給到了CameraCaptureSession中奈附。

三全度、Configure Streams

在打開相機(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ù)流是整個CamX-CHI流程比較重要的一環(huán)驱还,其中主要包括兩個階段

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

3.1 選擇UsecaseId

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

3.2 創(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方法中做了很多初始化操作景东,其中包括了以下幾個階段:

  1. 獲取XML文件中Usecase配置信息
  2. 創(chuàng)建Feature
  3. 保存數(shù)據(jù)流,重建Usecase的配置信息
  4. 調(diào)用父類CameraUsecaseBase的initialize方法奔誓,進(jìn)行一些常規(guī)初始化工作

接下來我們就這幾個階段逐一進(jìn)行分析:

3.2.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腳本將定義在個平臺目錄下的common_usecase.xml中的內(nèi)容轉(zhuǎn)換生成g_pipeline.h杯聚。

3.2.2 創(chuàng)建Feature

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

3.2.3 保存數(shù)據(jù)流颁独,重建Usecase的配置信息

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

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

  • ConfigureStream: 該方法將從上層配置的數(shù)據(jù)流指針存入AdvancedCameraUsecase中寨辩,其中包括了用于預(yù)覽的m_pPreviewStream以及用于拍照的m_pSnapshotStream。
  • BuildUsecase: 這個方法用來重新在原有的Usecase上面加入了Feature中所需要的pipeline歼冰,并創(chuàng)建了一個新的Usecase靡狞,并將其存入AdvancedCameraUsecase中的m_pChiUsecase成員變量中,緊接著通過SetPipelineToSessionMapping方法將pipeline與Session進(jìn)行關(guān)聯(lián)隔嫡。
3.2.4 調(diào)用父類CameraUsecaseBase的initialize方法

調(diào)用父類CameraUsecaseBase的initialize方法甸怕,進(jìn)行一些常規(guī)初始化工作. 該方法中的操作主要有以下三個:

  • 設(shè)置Session回調(diào)
  • 創(chuàng)建Pipeline
  • 創(chuàng)建Session

設(shè)置Session回調(diào)

該方法有兩個參數(shù),第二個是缺省的腮恩,第一個是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),此時會將AdvancedCameraUsecase端的回調(diào)函數(shù)注冊到Session中内颗,一旦Session中數(shù)據(jù)處理完成钧排,便會調(diào)用回調(diào)將數(shù)據(jù)回傳給AdvancedCameraUsecase。

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

  1. 根據(jù)operation_mode、camera 個數(shù)以及stream的配置信息選取了對應(yīng)的UsecaseId
  2. 根據(jù)所選取的UsecaseId找前,使用UsecaseFactory簡單工廠類創(chuàng)建了用于管理整個場景下所有資源的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)建了各個pipeline以及Session槽惫,并且將AdvancedCameraUsecase的成員方法注冊到Session周叮,用于Session將數(shù)據(jù)返回給Usecase中

四辩撑、ProcessCaptureRequest

當(dāng)用戶打開相機(jī)應(yīng)用進(jìn)行預(yù)覽或者點(diǎn)擊一次拍照操作的時候,便觸發(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)用之前初始化的時候獲取的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中會有兩個主要的分支來分別處理:

  • 如果當(dāng)前并沒有任何Feature需要實(shí)現(xiàn),此時便會走默認(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方法百拓,此時Request就進(jìn)入到了Session內(nèi)部進(jìn)行流轉(zhuǎn)了。
  • 如果當(dāng)前場景需要實(shí)現(xiàn)某個Feature晰甚,則直接調(diào)用Feature的ExecuteProcessRequest方法將此次request送入Feature中處理衙传,最后依然會調(diào)用到Session::StreamOn以及Session::ProcessCaptureRequest方法來分別完成喚醒pipeline以及下發(fā)request的到Session的操作。

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

4.1 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方法中主要做了如下兩個工作:

  • 調(diào)用FinalizeDeferPipeline()方法绎速,如果當(dāng)前pipeline并未初始化,則會調(diào)用pipeline的FinalizePipeline方法焙蚓,這里方法里面會去針對每一個從屬于當(dāng)前pipeline的Node依次做FinalizeInitialization纹冤、CreateBufferManager、NotifyPipelineCreated以及PrepareNodeStreamOn操作购公,F(xiàn)inalizeInitialization用于完成Node的初始化動作萌京,NotifyPipelineCreated用于通知Node當(dāng)前Pipeline的狀態(tài),此時Node內(nèi)部可以根據(jù)自身的需要作相應(yīng)的操作宏浩,PrepareNodeStreamOn方法的主要是完成Sensor以及IFE等Node的控制硬件模塊出圖前的配置知残,其中包括了曝光的參數(shù)的設(shè)置,CreateBufferManagers方法涉及到CamX-CHI中的一個非常重要的Buffer管理機(jī)制比庄,用于Node的ImageBufferManager的創(chuàng)建求妹,而該類用于管理Node中的output port的buffer申請/流轉(zhuǎn)/釋放等操作。
  • 調(diào)用Pipeline的StreamOn方法佳窑,里面會進(jìn)一步通知CSL部分開啟數(shù)據(jù)流制恍,并且調(diào)用每一個Node的OnNodeStreamOn方法,該方法會去調(diào)用ImageBufferManager的Activate(),該方法里面會去真正分配用于裝載圖像數(shù)據(jù)的buffer神凑,之后會去調(diào)用CHI部分實(shí)現(xiàn)的用戶自定義的Nod的pOnStreamOn方法净神,用戶可以在該方法中做一些自定義的操作。

4.2 Session::ProcessCaptureRequest

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

Camera Provider 啟動初始化

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

  1. 通過調(diào)用Session的ProcessCaptureRequest方法進(jìn)入到Session,然后調(diào)用Pipeline中的ProcessRequest方法通知Pipeline開始處理此次Request瓣喊。
  2. 在Pipeline中坡慌,會先去調(diào)用內(nèi)部的每一個Node的SetupRequest方法分別設(shè)置該Node的Output Port以及Input Port,之后通過調(diào)用DRQ(DeferredRequestQueue)的AddDeferredNode方法將所有的Node加入到DRQ中藻三,其中DRQ中有兩個隊(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指巡,此時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是整個Pipeline的最末端指煎,調(diào)用sinkPortFenceSignaled將數(shù)據(jù)給到Session中,最后通過調(diào)用Session中的NotifyResult將結(jié)果發(fā)送到CHI中便斥。

4.3 DeferredRequestQueue

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

  • Create()
    該方法用于創(chuàng)建DRQ晋渺,其中創(chuàng)建了用于存儲依賴信息的m_pDependencyMap镰绎,并將自己注冊到MetadataPool中,一旦有meta data或者property更新便會通過類中實(shí)現(xiàn)的幾個接口通知到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占业,同時再次更新依賴項(xiàng)绒怨,最后會再次調(diào)用DispatchReadyNodes開始處理。

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

Camera Provider 啟動初始化
  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恋沃,此時會通過調(diào)用MetadataSlot的PublishMetadata方法更新到MetadataPool中必搞,此時MetadataPool會調(diào)用之前在DRQ初始化時候注冊的幾個回調(diào)方法OnPropertyUpdate以及OnMetadataUpdate方法通知DRQ,此時有新的meta data 和property更新囊咏,接下來會在這兩個方法中調(diào)用UpdateDependency方法恕洲,去更新meta data 和property到m_pDependencyMap中,并且將沒有任何依賴項(xiàng)的Node從m_deferredNodes取出加入到m_readyNodes梅割,等待處理霜第。
  3. 與此同時,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)行處理拙绊。

五双仍、ProcessCaptureResult

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

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

  • Session內(nèi)部完成圖像數(shù)據(jù)的處理让簿,將結(jié)果發(fā)送至Usecase中
  • Usecase接收到來自Session的數(shù)據(jù),并將其上傳至Provider

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

Camera Provider 啟動初始化

在整個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的更新動作田盈,此時畜号,便會調(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的時候痹升,傳入Session中的Usecase的方法(AdvancedCameraUsecase::ProcessDriverPartialCaptureResultCb),通過該方法就將meta data返回到了CHI中畦韭。

同樣地疼蛾,Meta data的邏輯和Partial Meta Data很相似,每個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)和前兩個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)行代碼的梳理:

Camera Provider 啟動初始化

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

轉(zhuǎn)載來源:https://deepinout.com/qcom-camx-chi/qcom-camx-main-scenario-flow.html

*本人從事Android Camera相關(guān)開發(fā)已有5年,
*目前在深圳上班板丽,
*小伙伴記得點(diǎn)我頭像衣式,看【個人介紹】進(jìn)行關(guān)注哦,希望和更多的小伙伴一起交流 ~

-----2021.05.02 深圳 13:56

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末檐什,一起剝皮案震驚了整個濱河市碴卧,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌乃正,老刑警劉巖住册,帶你破解...
    沈念sama閱讀 206,378評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異瓮具,居然都是意外死亡荧飞,警方通過查閱死者的電腦和手機(jī),發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,356評論 2 382
  • 文/潘曉璐 我一進(jìn)店門名党,熙熙樓的掌柜王于貴愁眉苦臉地迎上來叹阔,“玉大人,你說我怎么就攤上這事传睹《保” “怎么了?”我有些...
    開封第一講書人閱讀 152,702評論 0 342
  • 文/不壞的土叔 我叫張陵欧啤,是天一觀的道長睛藻。 經(jīng)常有香客問我,道長邢隧,這世上最難降的妖魔是什么店印? 我笑而不...
    開封第一講書人閱讀 55,259評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮倒慧,結(jié)果婚禮上按摘,老公的妹妹穿的比我還像新娘。我一直安慰自己纫谅,他們只是感情好炫贤,可當(dāng)我...
    茶點(diǎn)故事閱讀 64,263評論 5 371
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著系宜,像睡著了一般照激。 火紅的嫁衣襯著肌膚如雪发魄。 梳的紋絲不亂的頭發(fā)上盹牧,一...
    開封第一講書人閱讀 49,036評論 1 285
  • 那天俩垃,我揣著相機(jī)與錄音,去河邊找鬼汰寓。 笑死口柳,一個胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的有滑。 我是一名探鬼主播跃闹,決...
    沈念sama閱讀 38,349評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼毛好!你這毒婦竟也來了望艺?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 36,979評論 0 259
  • 序言:老撾萬榮一對情侶失蹤肌访,失蹤者是張志新(化名)和其女友劉穎找默,沒想到半個月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體吼驶,經(jīng)...
    沈念sama閱讀 43,469評論 1 300
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡惩激,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 35,938評論 2 323
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發(fā)現(xiàn)自己被綠了蟹演。 大學(xué)時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片风钻。...
    茶點(diǎn)故事閱讀 38,059評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖酒请,靈堂內(nèi)的尸體忽然破棺而出骡技,到底是詐尸還是另有隱情,我是刑警寧澤羞反,帶...
    沈念sama閱讀 33,703評論 4 323
  • 正文 年R本政府宣布哮兰,位于F島的核電站,受9級特大地震影響苟弛,放射性物質(zhì)發(fā)生泄漏喝滞。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 39,257評論 3 307
  • 文/蒙蒙 一膏秫、第九天 我趴在偏房一處隱蔽的房頂上張望右遭。 院中可真熱鬧,春花似錦缤削、人聲如沸窘哈。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,262評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽滚婉。三九已至,卻和暖如春帅刀,著一層夾襖步出監(jiān)牢的瞬間让腹,已是汗流浹背远剩。 一陣腳步聲響...
    開封第一講書人閱讀 31,485評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留骇窍,地道東北人瓜晤。 一個月前我還...
    沈念sama閱讀 45,501評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像腹纳,于是被迫代替她去往敵國和親痢掠。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 42,792評論 2 345

推薦閱讀更多精彩內(nèi)容