Android 10 推出選用的相機 HAL3 緩衝區管理 API,可讓您實作緩衝區管理邏輯,在相機 HAL 實作中達成不同的記憶體和擷取延遲時間取捨。
攝影機 HAL 需要在管道中排入 N 個要求 (其中 N 等於管道深度),但通常不需要同時提供所有 N 組輸出緩衝區。
舉例來說,HAL 可能在管道中排隊了八項要求,但只需要管道最後階段的兩項要求輸出緩衝區。在搭載 Android 9 以下版本的裝置上,當要求在 HAL 中排入佇列時,相機架構會分配緩衝區,因此 HAL 中可能有六組未使用的緩衝區。在 Android 10 中,相機 HAL3 緩衝區管理 API 可讓輸出緩衝區解除耦合,釋出六組緩衝區。這項做法可為高階裝置節省數百 MB 的記憶體,對記憶體不足的裝置也有幫助。
圖 1 顯示搭載 Android 9 以下版本的裝置所用攝影機 HAL 介面的圖表。圖 2 顯示 Android 10 中的相機 HAL 介面,並實作相機 HAL3 緩衝區管理 API。
圖 1. Android 9 以下版本的 Camera HAL 介面
圖 2. Android 10 中的 Camera HAL 介面,使用緩衝區管理 API
實作緩衝區管理 API
如要實作緩衝區管理 API,相機 HAL 必須:
- 實作 HIDL
ICameraDevice@3.5
。 - 將攝影機特徵碼鍵
android.info.supportedBufferManagementVersion
設為HIDL_DEVICE_3_5
。
相機 HAL 會使用 requestStreamBuffers
和 returnStreamBuffers
方法 (位於 ICameraDeviceCallback.hal
中) 要求及傳回緩衝區。HAL 也必須在 ICameraDeviceSession.hal
中實作 signalStreamFlush
方法,向攝影機 HAL 發出訊號,要求傳回緩衝區。
requestStreamBuffers
使用 requestStreamBuffers
方法,向攝影機架構要求緩衝區。使用 Camera HAL3 緩衝區管理 API 時,相機架構的擷取要求不會包含輸出緩衝區,也就是 StreamBuffer
中的 bufferId
欄位為 0
。因此,相機 HAL 必須使用 requestStreamBuffers
向相機架構要求緩衝區。
呼叫端可透過 requestStreamBuffers
方法,在單一呼叫中從多個輸出串流要求多個緩衝區,減少 HIDL IPC 呼叫次數。不過,如果同時要求多個緩衝區,通話時間會變長,可能會對要求到結果的總延遲時間造成負面影響。此外,由於對 requestStreamBuffers
的呼叫會在攝影機服務中序列化,因此建議攝影機 HAL 使用專屬的高優先權執行緒來要求緩衝區。
如果緩衝區要求失敗,相機 HAL 必須能夠正確處理非嚴重錯誤。以下列出緩衝區要求失敗的常見原因,以及攝影機 HAL 應如何處理。
- 應用程式與輸出串流中斷連線:
這是非致命錯誤。攝影機 HAL 應針對以已中斷串流為目標的任何擷取要求,傳送
ERROR_REQUEST
,並準備好正常處理後續要求。 - 逾時:如果應用程式忙於執行大量處理作業,同時保留某些緩衝區,就可能發生這種情況。如果因逾時錯誤而無法滿足擷取要求,相機 HAL 應傳送
ERROR_REQUEST
,並準備好正常處理後續要求。 - 相機架構正在準備新的串流設定:
相機 HAL 應等待下一個
configureStreams
呼叫完成,再呼叫requestStreamBuffers
。 - 相機 HAL 已達到緩衝區限制 (
maxBuffers
欄位):相機 HAL 應等待,直到傳回至少一個串流緩衝區,再呼叫requestStreamBuffers
。
returnStreamBuffers
使用 returnStreamBuffers
方法將額外緩衝區傳回給攝影機架構。相機 HAL 通常會透過 processCaptureResult
方法將緩衝區傳回相機架構,但只能處理已傳送至相機 HAL 的擷取要求。使用 requestStreamBuffers
方法時,相機 HAL 實作項目可能會保留比相機架構要求更多的緩衝區。此時應使用 returnStreamBuffers
方法。如果 HAL 實作項目從未保留超過要求數量的緩衝區,相機 HAL 實作項目就不需要呼叫 returnStreamBuffers
方法。
signalStreamFlush
相機架構會呼叫 signalStreamFlush
方法,通知相機 HAL 傳回所有可用的緩衝區。相機架構即將呼叫 configureStreams
時,通常會呼叫這個函式,且必須排空相機擷取管道。與 returnStreamBuffers
方法類似,如果攝影機 HAL 實作項目持有的緩衝區數量未超過要求,這個方法可能會是空實作項目。
相機架構呼叫 signalStreamFlush
後,架構會停止將新的擷取要求傳送至相機 HAL,直到所有緩衝區都傳回相機架構為止。所有緩衝區傳回後,requestStreamBuffers
方法呼叫會失敗,而攝影機架構可在乾淨的狀態下繼續運作。攝影機架構隨後會呼叫 configureStreams
或 processCaptureRequest
方法。如果攝影機架構呼叫 configureStreams
方法,攝影機 HAL 可以在 configureStreams
呼叫成功傳回後,再次開始要求緩衝區。如果攝影機架構呼叫 processCaptureRequest
方法,攝影機 HAL 可以在 processCaptureRequest
呼叫期間開始要求緩衝區。
signalStreamFlush
方法和 flush
方法的語意不同。呼叫 flush
方法時,HAL 可以使用 ERROR_REQUEST
中止待處理的擷取要求,盡快排空管道。呼叫 signalStreamFlush
方法時,HAL 必須正常完成所有待處理的擷取要求,並將所有緩衝區傳回相機架構。
signalStreamFlush
方法與其他方法的另一個差異是,signalStreamFlush
是單向 HIDL 方法,也就是說,在 HAL 收到 signalStreamFlush
呼叫之前,相機架構可能會呼叫其他封鎖 API。也就是說,signalStreamFlush
方法和其他方法 (具體來說是 configureStreams
方法) 抵達相機 HAL 的順序,可能與在相機架構中呼叫這些方法的順序不同。為解決這個非同步問題,我們在 StreamConfiguration
中新增了 streamConfigCounter
欄位,並將其做為 signalStreamFlush
方法的引數。相機 HAL 實作應使用 streamConfigCounter
引數,判斷 signalStreamFlush
呼叫是否晚於對應的 configureStreams
呼叫。請參閱圖 3 瞭解範例。
圖 3. 相機 HAL 應如何偵測及處理延遲抵達的 signalStreamFlush 呼叫
實作緩衝區管理 API 時的行為變化
使用緩衝區管理 API 實作緩衝區管理邏輯時,請注意相機和相機 HAL 實作可能出現的下列行為變化:
擷取要求會更快且更頻繁地傳送至相機 HAL:如果沒有緩衝區管理 API,相機架構會在將擷取要求傳送至相機 HAL 之前,為每個擷取要求要求輸出緩衝區。使用緩衝區管理 API 時,相機架構不再需要等待緩衝區,因此可以更早將擷取要求傳送至相機 HAL。
此外,如果沒有緩衝區管理 API,當擷取要求其中一個輸出串流的緩衝區數量達到 HAL 一次可保留的緩衝區數量上限時,相機架構就會停止傳送擷取要求 (這個值是由相機 HAL 在
configureStreams
呼叫的回傳值中,於HalStream::maxBuffers
欄位指定)。有了緩衝區管理 API,就不會再發生這種限制行為,且當 HAL 佇列中的擷取要求過多時,相機 HAL 實作不得接受processCaptureRequest
呼叫。requestStreamBuffers
呼叫延遲時間差異很大:requestStreamBuffers
呼叫時間可能比平均時間長,原因有很多。例如:- 對於新建立串流的前幾個緩衝區,由於裝置需要配置記憶體,因此呼叫時間可能會較長。
- 預期延遲時間會隨著每次呼叫中要求的緩衝區數量而增加。
- 應用程式正在保留緩衝區,並忙於處理作業。這可能會導致緩衝區要求速度變慢,或因緩衝區不足或 CPU 忙碌而逾時。
緩衝區管理策略
緩衝區管理 API 可讓您實作各種緩衝區管理策略。例如:
- 回溯相容:HAL 會在
processCaptureRequest
呼叫期間,要求擷取要求的緩衝區。這項策略無法節省任何記憶體,但可做為緩衝區管理 API 的首次實作,只需要對現有的攝影機 HAL 進行極少的程式碼變更。 - 記憶體用量降到最低:相機 HAL 只會在需要填滿輸出緩衝區的前一刻,才要求輸出緩衝區。這項策略可盡量節省記憶體。潛在缺點是緩衝區要求完成時間異常長時,相機管道可能會發生更多延遲。
- 已快取:攝影機 HAL 會快取幾個緩衝區,因此較不容易受到偶爾緩慢的緩衝區要求影響。
相機 HAL 可針對特定用途採用不同策略,例如針對使用大量記憶體的用途採用記憶體節省最大化策略,並針對其他用途採用回溯相容策略。
外部攝影機 HAL 中的實作範例
Android 9 導入了外部攝影機 HAL,可在 hardware/interfaces/camera/device/3.5/
的原始碼樹狀結構中找到。在 Android 10 中,這項功能已更新為包含 ExternalCameraDeviceSession.cpp
,也就是緩衝區管理 API 的實作項目。這個外部攝影機 HAL 會以幾百行的 C++ 程式碼,實作「緩衝區管理策略」中提及的記憶體節省策略。