Android Camera HAL3 - 框架流程預覽

前面說了 HAL3 是一個總線型的設計結構,本文就先對 HAL3 的控制流進行一個提綱挈領式的概述,主要理解整個 HAL3 的主幹框架,以便對後續深入各個細節。

主幹流程

以下全部都是摘抄 Google 官方網站上面介紹的 Camera 使用流程。

  • 枚舉、打開相機設備並創建有效的會話(session)
    1. 在初始化完成之後(這個初始化我覺得是 framework 層的服務初始化完成),framework 就會監聽多有的 camera provider,camera provider 是在 ICameraProvider 接口實現的(AIDL 類型的接口),如果發現系統裏面有的話就會建立一個連接。
    2. framework 通過 ICameraProvider::getCameraIdList 接口來進行設備的枚舉,其實這裏並沒有調用到具體設備相關的 HAL3 內部,因爲在 provider 初始化的時候就已經調用過了,這裏只是把 provider 裏面保存下來的 camera list 返回給上層的 framework。
    3. framework 實例化一個新的 ICameraDevice,實例化過程是 ICameraProvider::getCameraDeviceInterface_VX_X() 來完成的。
    4. framework 調用 ICameraDevice::open() 接口來打開並且創建一個新的 session 來完成後續的控制過程。
  • 使用剛剛創建好的 session 進行具體的業務邏輯處理
    1. framework 通過 ICameraDeviceSession::configureStreams() 接口來向設備相關的 HAL 層傳遞用戶所需的 stream 配置,同時 HAL 內部會根據傳遞下來的 stream 來選擇具體的業務進行配置。
    2. framework 通過 ICameraDeviceSession::constructDefaultRequestSettings() 接口來獲取一個默認的具體業務場景配置,這個操作可以在 ICameraDevice::open() 之後的任何時刻執行,後面說下這個 constructDefaultRequestSettings() 接口的內容。
    3. framework 使用上面構建的默認 setting,再佐以自己需要的特殊配置,然後發送第一個 request 請求到 HAL 裏面去。這個 request 裏面必須包含至少一個之前配置好的 stream(沒錯,它們的地址必須相同,不能夠使用 copy 的方式重新新建一個 stream,並且這個 stream 地址與使用由 framework&app 去維護),因爲要輸出 buffer 給 framework 那邊。上面的 request 使用 ICameraDeviceSession::processCaptureRequest() 進行傳遞,HAL 內部需要阻塞本次 request 請求,直到準備好接受下一個 request 的時候才返回。
    4. framework 持續調用 ICameraDeviceSession::processCaptureRequest() 進行 request 的下發,如果需要切換其它場景的話就再次調用 ICameraDeviceSession::constructDefaultRequestSettings() 進行具體場景的默認 setting 構建。
    5. 當本次 request 開始被 HAL 正式執行的時候,也就是 HAL 底下的硬件 sensor 開始曝光本幀的時候(sensor starts exposing for capture),HAL 需要調用 framework 實現的 ICameraDeviceCallback::notify() 函數進行 SHUTTER 消息回調,這個消息裏面包含了時間戳數據和 framenumber。這個是 HAL 層主動發起的回調,但是不需要再第一個 request 下發下去之前就開始回調,並且一個 request 的 notify 完成之前,不要返回任何 result 給到 framework。
    6. 經過幾個 pipeline 延遲之後,HAL 開始向 framework 返回已經完成的結果,返回過程通過 ICameraDeviceCallback::processCaptureResult() 函數完成。返回的順序得是按照提交的 request 請求的順序來,request 也可以一次性下發多個,數量取決於 request 的隊列深度,一般 6~8 個吧,也取決於具體的平臺和其代碼實現。
  • 上面的過程會不斷地進行循環,等到一定時刻,會發生下面的事件
    1. framework 停止下發 request 請求,然後等待所有的 buffer 被填充完畢並且所有的 result 全部返回,這個時候再次調用 ICameraDeviceSession::configureStreams() 接口重新配置業務流,這在 HAL 內部會導致整個的硬件被重置一遍,然後用新的 stream 重新配置,接下來繼續上面的因果循環。這說白了就是場景的切換,比如從拍照切換到錄像(不關閉 camera app)。
    2. framework 可能會調用 ICameraDeviceSession::close() 接口來結束整個會話,該函數可以在任何時候進行調用,但是 framework 在調用該函數的時候不可以繼續下發 request,並且所有其它的函數調用都不能夠進行下去,在 close 函數返回之後,HAL 層不得再回調 framework 的函數。其實在 close 之前,通常會有 flush 函數調用,這個函數的調用要求與 close 差不多,close 可以認爲就是個收尾動作,flush 會等待所有的 request 完成並且所有的 result 全部返回到 framework。
    3. 當發生錯誤或者其它異步事件的時候,HAL 必須調用 ICameraDeviceCallback::notify() 函數來返回錯誤消息給 framework。如果 HAL 內部發生了嚴重的設備錯誤,HAL 內部在回覆完錯誤消息之後需要按照 close 函數被調用時候的動作來清理案發現場。同時,在調用 notify 之前,也應該停止所有的尚在隊列中的 request 請求響應,嚴重錯誤傳遞給 framework 的時候,HAL 不再返回任何結果給到 framework,並且在 framework 嘗試調用 close 的時候需要返回 -ENODEV 或者 NULL。

上面就是整體上站在 framework 角度上來描述一個 camera 業務邏輯的生命週期完整的過程,從上面可以知道比較關鍵的就是下面幾個:

  1. 設備枚舉
  2. session 創建
  3. stream 配置,request 構建與下發
  4. result 返回和 notify 事件回調
  5. 現場清理

下面會從這些關鍵點來稍微概述下它們的工作以及基本的實現原理。依然是從大體上進行一次概述,先基本瞭解下整個的 Camera 的工作流程是什麼樣子的。

設備枚舉

設備的枚舉有兩個層面意義上的枚舉,第一個是在 provider 初始化的時候向 HAL 層內部的枚舉,另一個是 framework 向 provider 索要的設備枚舉過程,兩者是不太一樣的,至少其發生的時間是不一樣的。

在 provider 創建的時候,也就是 CameraProviderImpl,這裏有一個點,就是舊的 Android P 的代碼,只有一個 Provider 的實現,但是新的代碼裏面擴展了兩個,這個以後再說。創建的時候會去獲取 HAL 內部定義的 camera_module_t 結構體,然後創建一個新的 Google HAL 層的 CameraModule 抽象,以後的很多配置獲取相關的操作都是使用這個 CameraModule 進行中轉。

在 CameraModule 創建之後,Provider 就會繼續獲取設備上面枚舉出來的 Camera 數量,然後再遍歷所有的 Camera,調用其 get_camera_info 回調函數把 camera 的信息給保存起來,注意這個時候 Camera 設備名的枚舉還沒有送往 framework 層,而是保存在了 Provider 的一個 map 數組裏面,這裏只是檢查下時候 Camera 設備是否兼容,把兼容的設備放在 map 數組裏面去。

然後在 framework 想去獲取設備列表的時候,就調用 Provider 的 getCameraIdList 進行設備名列表的獲取,這個調用過程依然是通過 HIDL 完成的,媒介就是 Binder 這個跨進程通信工具。

在枚舉過程完成之後,就會調用 getCameraDeviceInterface_VX_X 進行設備的創建,這個函數的實現也是在 CameraProviderImpl 裏面實現的,之後這個設備的構造函數被調用,正式創建一個與 Camera device id 相關聯的 CameraDevice 實體類,最後 framework 會調用這個類的 open 接口,在 open 函數裏面會調用到 HAL 底層實現的 open,並且會創建一個 Google HAL 層的 CameraDeviceSession。

然後呢,這個 Session 就是後續進行各種騷操作的基礎類了,非常重要,等於說是 framework 通過 Provider 搭建了一個平臺,然後平臺之上催生了 CameraDeviceSession 這個交通樞紐,所有後期與業務邏輯緊密相關的調用操作都是通過這個交通樞紐來完成的,比如 configure_streams,processCaptureRequest,flush,close 等等。

session 的創建

session 附屬於 device 下面,很多重要的操作全部都是使用 session 來完成的。

struct camera3_device_ops_t {
  1. initialize
  2. configure_streams
  3. construct_default_request_settings
  4. process_capture_request
  5. dump
  6. flush  
}

另外在 session 裏面還附加了一些回調函數,比如 notify、processCaptureResult 這樣的回調,這些回調函數的實現是從 HAL 內層往 Google HAL 以至於 framework 層進行回調的。session 裏面還簡單的對 request 進行了隊列的創建與管理,還對 stream 與 buffer 進行了簡單的構建與管理。

總之 session 的角色就是重要交通樞紐,我個人覺得,很多的重要操作以及數據資源都需要從它這裏經過。

另外,上面討論的 Provider 是 2.4 版本,CameraModule 是 1.0 版本,CameraDevice、CameraDeviceSession 都是基於 3.2 這個版本進行的,其它的版本是有不少改變的,尤其是 3.4 版本的 Device 和 3.2 就有很多的不同。這個在之後的文章裏面也會稍稍進行說明。

constructDefaultRequestSettings

這個函數我覺得有必要在這裏拎出來說下,這個函數的主要功通過我縝密的推理與驗證,結論是它就是創建默認的 request 模板之用的(廢話,名字都能看出來了)。

說,在 Camera 的世界裏面,它有很多種模式,這個在 Google 的官方文檔裏面有去定義,比如下面的幾種類型:

  1. TEMPLATE_MANUAL
  2. TEMPLATE_PREVIEW
  3. TEMPLATE_RECORD

以及其它的多種模板枚舉定義,從它們的名字就可以看出來其基本的使用場景了,這裏就說下手機上面的經典使用模式吧,比如普通錄像模式就對應的是 TEMPLATE_RECORD,還有一種模式是在錄像的時候還可以實時地進行拍照操作,這個在最新的手機上面大多都是有這樣的功能的,就算是我的小米6上面也都有這個功能。TEMPLATE_ZERO_SHUTTER_LAG 就是所謂的零延時拍照,這個就是說你按下拍照鍵之後就立刻給你返回了一個照片,而不是等待那麼一會兒延遲,可以知道,這個時候的照片其實並不是你按下拍照鍵那一刻的照片,而是稍前面一點的,所謂零延時拍照。

底層要構建好這樣一個以 CameraMetadata 爲載體的默認設置集,該 CameraMetadata 由 CameraDeviceSession 和 HAL 底層各執一份,當然源頭還是在 HAL 底層,上面的都是其拷貝體,裏面包含的內容就是 Google 規定的 CameraMetadata 的內容項了,當然,更加具體的還是取決於 HAL 底層的實現以及部分 Google 規定好的套路化的東西。

本操作在第一次真正的 request 下發之前就已經應該執行完了,在具體的業務邏輯場景開始的時候,framework 使用默認的 CameraMetadata 進行每一幀的 request 具體參數的構建操作,這樣就省去了很多的麻煩並且也能夠適配好底層 HAL 的實現。

End

本文主要就是說下對於 HAL 來說,在一個具體的業務邏輯下大致的流程,應該先要怎麼樣,後來怎麼樣,然後結果怎樣返回等等,主要是對整體流程有一個概念性的瞭解,之後的文章會盡量的從上到下對 Camera 的初始化和使用進行說明,當然也不一定,有可能會穿插着底層 HAL 的實現來說明。

另外,我覺得此處以及那處以及這裏那裏都應該加上一張圖,一圖勝千言,但是無奈畫圖太費勁了,我這麼懶,畫一張圖半個周就過去了,後面有緣的話就畫點圖,沒有就是正常的。


想做的事情就去做吧
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章