相機 HAL3 緩衝區管理 API

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。

9 或更低的緩衝區管理

圖 1. Android 9 及更低版本中的相機 HAL 接口

Android 10 中的緩衝區管理

圖 2. Android 10 中使用緩衝區管理 API 的相機 HAL 接口

實作緩衝區管理 API

要實作緩衝區管理 API,相機 HAL 必須:

相機 HAL 使用ICameraDeviceCallback.hal中的requestStreamBuffersreturnStreamBuffers方法來請求和返回緩衝區。 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方法呼叫失敗,相機框架可以在乾淨的狀態下繼續工作。然後,相機框架呼叫configureStreamsprocessCaptureRequest方法。如果相機框架呼叫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++ 程式碼實現了緩衝區管理策略中提到的最大化記憶體節省策略。