Android 10 引入了可選的相機 HAL3緩衝區管理 API,可讓您實現緩衝區管理邏輯,以在相機 HAL 實作中實現不同的記憶體和擷取延遲權衡。
相機 HAL 需要在其管道中排隊的 N 個請求(其中 N 等於管道深度),但它通常不需要同時所有 N 組輸出緩衝區。
例如,HAL 可能有八個請求在管道中排隊,但它只需要管道最後階段的兩個請求的輸出緩衝區。在運行 Android 9 及更低版本的裝置上,相機框架會在請求在 HAL 中排隊時分配緩衝區,因此 HAL 中可能有六組緩衝區未使用。在 Android 10 中,相機 HAL3 緩衝區管理 API 允許解耦輸出緩衝區以釋放六組緩衝區。這可以在高階設備上節省數百兆位元組的內存,並且對於低內存設備也有好處。
圖 1 顯示了運行 Android 9 及更低版本的裝置的相機 HAL 介面圖。圖 2 顯示了 Android 10 中的相機 HAL 接口,並實作了相機 HAL3 緩衝區管理 API。
圖 1. Android 9 及更低版本中的相機 HAL 接口
圖 2. Android 10 中使用緩衝區管理 API 的相機 HAL 接口
實作緩衝區管理 API
要實作緩衝區管理 API,相機 HAL 必須:
- 實作 HIDL
ICameraDevice@3.5
。 - 將相機特性鍵
android.info.supportedBufferManagementVersion
設定為HIDL_DEVICE_3_5
。
相機 HAL 使用ICameraDeviceCallback.hal
中的requestStreamBuffers
和returnStreamBuffers
方法來請求和返回緩衝區。 HAL 還必須實作ICameraDeviceSession.hal
中的signalStreamFlush
方法,以通知相機 HAL 返回緩衝區。
請求流緩衝區
使用requestStreamBuffers
方法從相機框架請求緩衝區。使用相機 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
方法將額外的緩衝區返回相機框架。相機 HAL 通常透過processCaptureResult
方法將緩衝區返回到相機框架,但它只能處理已傳送至相機 HAL 的擷取要求。使用requestStreamBuffers
方法,相機 HAL 實作可以保留比相機框架請求的更多的緩衝區。這是應該使用returnStreamBuffers
方法的時候。如果 HAL 實作從未持有比請求更多的緩衝區,則相機 HAL 實作不需要呼叫returnStreamBuffers
方法。
訊號流刷新
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 的順序可能與它們在相機框架中呼叫的順序不同。為了解決這個非同步問題, streamConfigCounter
欄位被加入到StreamConfiguration
中,並作為參數加入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 中的範例實現
外部相機 HAL 是在 Android 9 中引入的,可以在hardware/interfaces/camera/device/3.5/
的源代碼樹中找到。在 Android 10 中,它已更新為包含ExternalCameraDeviceSession.cpp
,它是緩衝區管理 API 的實作。這個外部攝影機 HAL 用幾百行 C++ 程式碼實現了緩衝區管理策略中提到的最大化記憶體節省策略。