相機 HAL3 緩衝區管理 API

Android 10 引入了可選的相機 HAL3緩衝區管理 API,允許您實現緩衝區管理邏輯,以在相機 HAL 實現中實現不同的內存和捕獲延遲權衡。

相機 HAL 需要在其管道中排隊的 N 個請求(其中 N 等於管道深度),但它通常不需要同時所有 N 組輸出緩衝區。

例如,HAL 可能有 8 個請求在管道中排隊,但它只需要管道最後階段的兩個請求的輸出緩衝區。在運行 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方法,則在configureStreams調用成功返回後,相機 HAL 可以再次開始請求緩衝區。如果相機框架調用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++ 代碼實現了緩衝區管理策略中提到的最大化內存節省策略。