API di gestione del buffer HAL3 della videocamera

Android 10 introduce API facoltative di gestione del buffer HAL3 della fotocamera che consentono di implementare la logica di gestione del buffer per ottenere diversi compromessi in termini di memoria e latenza di acquisizione nelle implementazioni HAL della fotocamera.

L'HAL della fotocamera richiede N richieste (dove N è uguale alla profondità della pipeline) in coda nella pipeline, ma spesso non richiede tutti gli N set di buffer di output contemporaneamente.

Ad esempio, l'HAL potrebbe avere otto richieste in coda nella pipeline, ma richiede solo buffer di output per le due richieste nelle ultime fasi della pipeline. Sui dispositivi con Android 9 e versioni precedenti, il framework della fotocamera alloca buffer quando la richiesta è in coda nell'HAL, quindi potrebbero essere presenti sei insiemi di buffer nell'HAL non in uso. In Android 10, le API di gestione dei buffer HAL3 della fotocamera consentono di disaccoppiare i buffer di output per liberare i sei set di buffer. Ciò può portare a un risparmio di memoria di centinaia di megabyte sui dispositivi di fascia alta, oltre a essere vantaggioso per i dispositivi con memoria ridotta.

La Figura 1 mostra un diagramma dell'interfaccia HAL della fotocamera per i dispositivi con Android 9 e versioni precedenti. La Figura 2 mostra l'interfaccia HAL della fotocamera in Android 10 con le API di gestione dei buffer HAL3 della fotocamera implementate.

Gestione del buffer in 9 o versioni precedenti

Figura 1. Interfaccia HAL della fotocamera in Android 9 e versioni precedenti

Gestione del buffer in Android 10

Figura 2. Interfaccia della fotocamera HAL in Android 10 che utilizza le API di gestione del buffer

Implementa le API di gestione dei buffer

Per implementare le API di gestione dei buffer, l'HAL della fotocamera deve:

L'HAL della fotocamera utilizza i metodi requestStreamBuffers e returnStreamBuffers in ICameraDeviceCallback.hal per richiedere e restituire i buffer. L'HAL deve anche implementare il metodo signalStreamFlush in ICameraDeviceSession.hal per segnalare all'HAL della videocamera di restituire i buffer.

requestStreamBuffers

Utilizza il metodo requestStreamBuffers per richiedere i buffer dal framework della fotocamera. Quando utilizzi le API di gestione dei buffer HAL3 della fotocamera, le richieste di acquisizione dal framework della fotocamera non contengono buffer di output, ovvero il campo bufferId in StreamBuffer è 0. Pertanto, l'HAL della fotocamera deve utilizzare requestStreamBuffers per richiedere buffer dal framework della fotocamera.

Il metodo requestStreamBuffers consente all'autore della chiamata di richiedere più buffer da più stream di output in un'unica chiamata, consentendo un numero inferiore di chiamate HIDL IPC. Tuttavia, le chiamate richiedono più tempo quando vengono richiesti più buffer contemporaneamente e questo potrebbe influire negativamente sulla latenza totale dalla richiesta al risultato. Inoltre, poiché le chiamate a requestStreamBuffers sono serializzate nel servizio della fotocamera, è consigliabile che l'HAL della fotocamera utilizzi un thread dedicato ad alta priorità per richiedere i buffer.

Se una richiesta di buffer non va a buon fine, l'HAL della videocamera deve essere in grado di gestire correttamente gli errori non irreversibili. L'elenco seguente descrive i motivi comuni per cui le richieste di buffer non vanno a buon fine e come devono essere gestite dall'HAL della fotocamera.

  • L'app si scollega dallo stream di output: si tratta di un errore non irreversibile. L'HAL della videocamera deve inviare ERROR_REQUEST per qualsiasi richiesta di acquisizione che ha come target uno stream disconnesso ed essere pronta a elaborare le richieste successive in modo normale.
  • Timeout:questo può verificarsi quando un'app è impegnata a eseguire un'elaborazione intensiva e mantiene alcuni buffer. L'HAL della fotocamera deve inviare ERROR_REQUEST per le richieste di acquisizione che non possono essere soddisfatte a causa di un errore di timeout ed essere pronta per elaborare normalmente le richieste successive.
  • Il framework della videocamera sta preparando una nuova configurazione dello stream: l'HAL della videocamera deve attendere il completamento della chiamata configureStreams successiva prima di chiamare di nuovo requestStreamBuffers.
  • L'HAL della videocamera ha raggiunto il suo limite di buffer (campo maxBuffers): l'HAL della videocamera deve attendere fino a quando non restituisce almeno un buffer dello stream prima di chiamare nuovamente requestStreamBuffers.

returnStreamBuffers

Utilizza il metodo returnStreamBuffers per restituire buffer aggiuntivi al framework della fotocamera. Normalmente, l'HAL della fotocamera restituisce i buffer al framework della fotocamera tramite il metodo processCaptureResult, ma può tenere conto solo delle richieste di acquisizione inviate all'HAL della fotocamera. Con il metodo requestStreamBuffers, è possibile che l'implementazione dell'HAL della fotocamera conservi più buffer di quelli richiesti dal framework della fotocamera. Questo è il momento in cui deve essere utilizzato il metodo returnStreamBuffers. Se l'implementazione HAL non contiene mai più buffer di quelli richiesti, l'implementazione HAL della fotocamera non deve chiamare il metodo returnStreamBuffers.

signalStreamFlush

Il metodo signalStreamFlush viene chiamato dal framework della videocamera per comunicare all'HAL della fotocamera di restituire tutti i buffer a disposizione. Questo metodo viene chiamato normalmente quando il framework della fotocamera sta per chiamare configureStreams e deve svuotare la pipeline di acquisizione della fotocamera. Analogamente al metodo returnStreamBuffers, se un'implementazione HAL della fotocamera non contiene più buffer di quelli richiesti, è possibile avere un'implementazione vuota di questo metodo.

Dopo che il framework della videocamera chiama signalStreamFlush, il framework smette di inviare nuove richieste di acquisizione all'HAL della videocamera finché tutti gli buffer non sono stati restituiti al framework della videocamera. Quando tutti i buffer vengono restituiti, le chiamate al metodo requestStreamBuffers non vanno a buon fine e il framework della videocamera può continuare a lavorare in uno stato pulito. Il framework della videocamera chiama quindi il metodo configureStreams o processCaptureRequest. Se il framework della fotocamera chiama il metodo configureStreams, l'HAL della fotocamera può iniziare di nuovo a richiedere i buffer dopo che la chiamata a configureStreams è stata restituita. Se il framework della fotocamera chiama il metodo processCaptureRequest, l'HAL della fotocamera può iniziare a richiedere buffer durante la chiamataprocessCaptureRequest.

La semantica è diversa per il metodo signalStreamFlush e per il metodo flush. Quando viene chiamato il metodo flush, l'HAL può annullare le richieste di acquisizione in attesa con ERROR_REQUEST per svuotare la pipeline il prima possibile. Quando viene chiamato il metodo signalStreamFlush, l'HAL deve completare normalmente tutte le richieste di acquisizione in attesa e restituire tutti i buffer al framework della fotocamera.

Un'altra differenza tra il metodo signalStreamFlush e gli altri metodi è che signalStreamFlush è un metodo HIDL unidirezionale, il che significa che il framework della fotocamera potrebbe chiamare altre API bloccanti prima che l'HAL riceva la chiamata signalStreamFlush. Ciò significa che il metodo signalStreamFlush e altri metodi (in particolare il metodo configureStreams) potrebbero arrivare all'HAL della fotocamera in un ordine diverso rispetto a quello in cui sono stati chiamati nel framework della fotocamera. Per risolvere questo problema di accoppiamento, il campo streamConfigCounter è stato aggiunto a StreamConfiguration e come argomento al metodo signalStreamFlush. L'implementazione dell'HAL della fotocamera deve utilizzare l'argomento streamConfigCounter per determinare se una chiamata signalStreamFlush arriva dopo la chiamata configureStreams corrispondente. Vedi la Figura 3 per un esempio.

Gestire le chiamate che arrivano in ritardo

Figura 3. In che modo l'HAL della videocamera deve rilevare e gestire le chiamate signalStreamFlush che arrivano in ritardo

Il comportamento cambia quando vengono implementate le API di gestione dei buffer

Quando utilizzi le API di gestione del buffer per implementare la logica di gestione del buffer, tieni conto delle seguenti possibili modifiche al comportamento della fotocamera e dell'implementazione dell'HAL della fotocamera:

  • Le richieste di acquisizione arrivano all'HAL della fotocamera più rapidamente e più spesso: senza le API di gestione dei buffer, il framework della fotocamera richiede buffer di output per ogni richiesta di acquisizione prima di inviare una richiesta di acquisizione all'HAL della fotocamera. Quando si utilizzano le API di gestione dei buffer, il framework della fotocamera non deve più attendere i buffer e può quindi inviare richieste di acquisizione all'HAL della fotocamera in precedenza.

    Inoltre, senza le API di gestione dei buffer, il framework della fotocamera smette di inviare richieste di acquisizione se uno degli stream di output della richiesta di acquisizione ha raggiunto il numero massimo di buffer che l'HAL può contenere contemporaneamente (questo valore è designato dall'HAL della fotocamera nel campo HalStream::maxBuffers nel valore restituito di una chiamata configureStreams). Con le API di gestione del buffer, questo comportamento di throttling non esiste più e l'implementazione dell'HAL della fotocamera non deve accettare chiamate processCaptureRequest quando l'HAL ha troppe richieste di acquisizione in coda.

  • La latenza di chiamata requestStreamBuffers varia in modo significativo:esistono molti motivi per cui una chiamata requestStreamBuffers potrebbe richiedere più tempo rispetto alla media. Ad esempio:

    • Per i primi buffer di uno stream appena creato, le chiamate possono richiedere più tempo perché il dispositivo deve allocare memoria.
    • La latenza prevista aumenta in proporzione al numero di buffer richiesti in ogni chiamata.
    • L'app sta trattenendo i buffer ed è in fase di elaborazione. Ciò può causare il rallentamento delle richieste di buffer o il raggiungimento di un timeout a causa della mancanza di buffer o di una CPU occupata.

Strategie di gestione del buffer

Le API di gestione dei buffer consentono di implementare diversi tipi di strategie di gestione dei buffer. Ecco alcuni esempi:

  • Compatibile con le versioni precedenti: l'HAL richiede il buffer per una richiesta di acquisizione durante la chiamata processCaptureRequest. Questa strategia non consente di risparmiare memoria, ma può essere utilizzata come prima implementazione delle API di gestione del buffer, richiedendo pochissime modifiche al codice dell'HAL della fotocamera esistente.
  • Risparmio di memoria massimizzato:l'HAL della fotocamera richiede i buffer di output solo immediatamente prima che sia necessario riempirne uno. Questa strategia consente di massimizzare il risparmio di memoria. Il potenziale svantaggio è un maggiore ritardo nella pipeline della fotocamera quando il completamento delle richieste di buffer richiede un tempo insolitamente lungo.
  • Memorizzata nella cache:l'HAL della videocamera memorizza nella cache alcuni buffer in modo che sia meno probabile che venga interessata da una richiesta occasionale di buffer lento.

L'HAL della fotocamera può adottare strategie diverse per casi d'uso specifici, ad esempio utilizzare la strategia di risparmio di memoria massima per i casi d'uso che utilizzano molta memoria e la strategia di compatibilità con le versioni precedenti per altri casi d'uso.

Esempio di implementazione nella videocamera HAL esterna

L'HAL della fotocamera esterna è stato introdotto in Android 9 e si trova nella struttura ad albero delle sorgenti all'indirizzo hardware/interfaces/camera/device/3.5/. In Android 10 è stato aggiornato per includere ExternalCameraDeviceSession.cpp, un'implementazione dell'API di gestione dei buffer. Questo HAL della fotocamera esterna implementa la strategia di risparmio di memoria massimizzata descritta in Strategie di gestione dei buffer in poche centinaia di righe di codice C++.