相機 HAL3 緩衝區管理 API

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。

9 以下版本的緩衝區管理

圖 1. Android 9 以下版本的相機 HAL 介面

Android 10 中的緩衝區管理

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

實作緩衝區管理 API

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

相機 HAL 會使用 ICameraDeviceCallback.hal 中的 requestStreamBuffersreturnStreamBuffers 方法,要求及傳回緩衝區。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 方法呼叫會失敗,而相機架構可在清除狀態下繼續執行工作。接著,相機架構會呼叫 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++ 程式碼中實作「緩衝區管理策略」一文中提到的記憶體節省策略。