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 以下版本的相機 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 一次可容納的緩衝區數量上限時,相機架構就會停止傳送擷取要求 (這個值是由相機 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++ 程式碼中實作「緩衝區管理策略」一文中提到的記憶體節省策略。