Android 10 推出了可選的 camera 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 以下版本的相機 HAL 介面
圖 2. 使用緩衝區管理 API 的 Android 10 中相機 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
使用 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
使用 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 的順序,可能與在相機架構中呼叫的順序不同。為解決這個非同步問題,我們將 streamConfigCounter
欄位新增至 StreamConfiguration
,並將其做為引數新增至 signalStreamFlush
方法。相機 HAL 實作項目應使用 streamConfigCounter
引數,判斷 signalStreamFlush
呼叫是否比對應的 configureStreams
呼叫晚到達。請參閱圖 3 的範例。
圖 3. 相機 HAL 應如何偵測及處理延遲傳送的 signalStreamFlush 呼叫
實作緩衝區管理 API 時的行為變更
使用緩衝區管理 API 實作緩衝區管理邏輯時,請考量以下可能對相機和相機 HAL 實作造成的行為變更:
擷取要求更快且更頻繁地傳送至相機 HAL:如果沒有緩衝區管理 API,相機架構會先為每個擷取要求要求輸出緩衝區,然後再將擷取要求傳送至相機 HAL。使用緩衝區管理 API 時,相機架構不再需要等待緩衝區,因此可以提早將擷取要求傳送至相機 HAL。
此外,如果沒有緩衝區管理 API,當擷取要求的其中一個輸出串流達到 HAL 一次可容納的緩衝區數量上限時,相機架構就會停止傳送擷取要求 (這個值是由
configureStreams
呼叫的傳回值中HalStream::maxBuffers
欄位中的相機 HAL 指定)。有了緩衝區管理 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++ 程式碼中實作「緩衝區管理策略」一文中提及的記憶體節省策略。