Android 10 introduce las APIs de administración de búfer de la HAL3 opcionales que permiten implementar la lógica de administración de búferes para lograr diferentes compensaciones de memoria y latencia de captura en las implementaciones de la HAL de la cámara.
El HAL de la cámara requiere que se pongan en cola N solicitudes (donde N es igual a la profundidad de la canalización) en su canalización, pero, a menudo, no requiere todos los conjuntos de búferes de salida al mismo tiempo.
Por ejemplo, el HAL podría tener ocho solicitudes en cola en la canalización, pero solo requiere búferes de salida para las dos solicitudes en las últimas etapas de la canalización. En los dispositivos que ejecutan Android 9 y versiones anteriores, el framework de la cámara asigna búferes cuando la solicitud se pone en cola en el HAL, por lo que podría haber seis conjuntos de búferes en el HAL que no se estén usando. En Android 10, las APIs de administración de búfer de la HAL3 de la cámara permiten desacoplar los búferes de salida para liberar los seis conjuntos de búferes. Esto puede generar un ahorro de cientos de megabytes de memoria en dispositivos de alta gama y también puede ser beneficioso para dispositivos con poca memoria.
En la Figura 1, se muestra un diagrama de la interfaz de HAL de la cámara para dispositivos que ejecutan Android 9 y versiones anteriores. En la figura 2, se muestra la interfaz de la HAL de la cámara en Android 10 con las APIs de administración de búfer de la HAL3 de la cámara implementadas.
Figura 1: Interfaz de la HAL de la cámara en Android 9 y versiones anteriores
Figura 2: Interfaz de la HAL de la cámara en Android 10 con las APIs de administración de búferes
Implementa las APIs de administración de búfer
Para implementar las APIs de administración de búferes, la HAL de la cámara debe hacer lo siguiente:
- Implementa
ICameraDevice@3.5
de HIDL. - Establece la clave de características de la cámara
android.info.supportedBufferManagementVersion
enHIDL_DEVICE_3_5
.
El HAL de la cámara usa los métodos requestStreamBuffers
y returnStreamBuffers
en ICameraDeviceCallback.hal
para solicitar y devolver búferes. La HAL también debe implementar el método signalStreamFlush
en ICameraDeviceSession.hal
para indicarle a la HAL de la cámara que devuelva búferes.
requestStreamBuffers
Usa el método requestStreamBuffers
para solicitar búferes del framework de la cámara. Cuando se usan las APIs de administración de búfer de la HAL3 de la cámara, las solicitudes de captura del framework de la cámara no contienen búferes de salida, es decir, el campo bufferId
en StreamBuffer
es 0
. Por lo tanto, el HAL de la cámara debe usar requestStreamBuffers
para solicitar búferes del framework de la cámara.
El método requestStreamBuffers
permite que el llamador solicite varios búferes de varios flujos de salida en una sola llamada, lo que permite menos llamadas de IPC de HIDL. Sin embargo, las llamadas tardan más cuando se solicitan más búferes al mismo tiempo, lo que podría afectar negativamente la latencia total de la solicitud al resultado.
Además, dado que las llamadas a requestStreamBuffers
se serializan en el servicio de cámara, se recomienda que el HAL de la cámara use un subproceso dedicado de alta prioridad para solicitar búferes.
Si falla una solicitud de búfer, el HAL de la cámara debe poder controlar correctamente los errores no fatales. En la siguiente lista, se describen los motivos comunes por los que fallan las solicitudes de búfer y cómo el HAL de la cámara debe controlarlas.
- La app se desconecta del flujo de salida:
Este es un error recuperable. La HAL de la cámara debe enviar
ERROR_REQUEST
para cualquier solicitud de captura que tenga como objetivo un flujo desconectado y debe estar lista para procesar las solicitudes posteriores con normalidad. - Tiempo de espera agotado: Esto puede ocurrir cuando una app está ocupada realizando un procesamiento intensivo mientras retiene algunos búferes. El HAL de la cámara debe enviar
ERROR_REQUEST
para las solicitudes de captura que no se puedan completar debido a un error de tiempo de espera y debe estar listo para procesar las solicitudes posteriores con normalidad. - El framework de la cámara está preparando una nueva configuración de transmisión:
La HAL de la cámara debe esperar a que se complete la siguiente llamada a
configureStreams
antes de volver a llamar arequestStreamBuffers
. - La HAL de la cámara alcanzó su límite de búfer (el campo
maxBuffers
): La HAL de la cámara debe esperar hasta que devuelva al menos un búfer de la transmisión antes de volver a llamar arequestStreamBuffers
.
returnStreamBuffers
Usa el método returnStreamBuffers
para devolver búferes adicionales al framework de la cámara. Normalmente, la HAL de la cámara devuelve búferes al framework de la cámara a través del método processCaptureResult
, pero solo puede tener en cuenta las solicitudes de captura que se enviaron a la HAL de la cámara. Con el método requestStreamBuffers
, es posible que la implementación de la HAL de la cámara retenga más búferes de los que solicitó el framework de la cámara. Es en este momento cuando se debe usar el método returnStreamBuffers
. Si la implementación del HAL nunca contiene más búferes de los solicitados, no es necesario que la implementación del HAL de la cámara llame al método returnStreamBuffers
.
signalStreamFlush
El framework de la cámara llama al método signalStreamFlush
para notificar a la HAL de la cámara que devuelva todos los búferes disponibles. Normalmente, se llama a este método cuando el framework de la cámara está a punto de llamar a configureStreams
y debe agotar el canal de captura de la cámara. De manera similar al método returnStreamBuffers
, si una implementación del HAL de cámara no contiene más búferes de los solicitados, es posible tener una implementación vacía de este método.
Después de que el framework de la cámara llama a signalStreamFlush
, deja de enviar solicitudes de captura nuevas a la HAL de la cámara hasta que todos los búferes se hayan devuelto al framework de la cámara. Cuando se devuelven todos los búferes, fallan las llamadas al método requestStreamBuffers
y el framework de la cámara puede continuar su trabajo en un estado limpio. Luego, el framework de la cámara llama al método configureStreams
o al método processCaptureRequest
. Si el framework de la cámara llama al método configureStreams
, el HAL de la cámara puede comenzar a solicitar búferes nuevamente después de que la llamada a configureStreams
se complete correctamente. Si el framework de la cámara llama al método processCaptureRequest
, la HAL de la cámara puede comenzar a solicitar búferes durante la llamada a processCaptureRequest
.
La semántica es diferente para el método signalStreamFlush
y el método flush
. Cuando se llama al método flush
, el HAL puede anular las solicitudes de captura pendientes con ERROR_REQUEST
para vaciar la canalización lo antes posible. Cuando se llama al método signalStreamFlush
, el HAL debe finalizar todas las solicitudes de captura pendientes de forma normal y devolver todos los búferes al framework de la cámara.
Otra diferencia entre el método signalStreamFlush
y otros métodos es que signalStreamFlush
es un método HIDL unidireccional, lo que significa que el framework de la cámara puede llamar a otras APIs de bloqueo antes de que la HAL reciba la llamada a signalStreamFlush
. Esto significa que el método signalStreamFlush
y otros métodos (específicamente el método configureStreams
) pueden llegar a la HAL de la cámara en un orden diferente al orden en que se llamaron en el framework de la cámara. Para abordar este problema de asincronía, se agregó el campo streamConfigCounter
a StreamConfiguration
y se agregó como argumento al método signalStreamFlush
. La implementación del HAL de la cámara debe usar el argumento streamConfigCounter
para determinar si una llamada a signalStreamFlush
llega más tarde que su llamada a configureStreams
correspondiente. Consulta la Figura 3 para ver un ejemplo.
Figura 3: Cómo la HAL de la cámara debe detectar y controlar las llamadas a signalStreamFlush que llegan tarde
Cambios de comportamiento cuando se implementan las APIs de administración de búfer
Cuando uses las APIs de administración de búferes para implementar la lógica de administración de búferes, ten en cuenta los siguientes posibles cambios de comportamiento en la cámara y la implementación de la HAL de la cámara:
Las solicitudes de captura llegan a la HAL de la cámara más rápido y con mayor frecuencia: Sin las APIs de administración de búferes, el framework de la cámara solicita búferes de salida para cada solicitud de captura antes de enviar una solicitud de captura a la HAL de la cámara. Cuando se usan las APIs de administración de búfer, el framework de la cámara ya no necesita esperar los búferes y, por lo tanto, puede enviar solicitudes de captura a la HAL de la cámara antes.
Además, sin las APIs de administración de búferes, el framework de la cámara deja de enviar solicitudes de captura si uno de los flujos de salida de la solicitud de captura alcanzó la cantidad máxima de búferes que el HAL puede contener al mismo tiempo (este valor lo designa el HAL de la cámara en el campo
HalStream::maxBuffers
del valor de devolución de una llamada aconfigureStreams
). Con las APIs de administración de búferes, este comportamiento de limitación ya no existe y la implementación de la HAL de la cámara no debe aceptar llamadas aprocessCaptureRequest
cuando la HAL tiene demasiadas solicitudes de captura en cola.La latencia de la llamada
requestStreamBuffers
varía significativamente: Hay muchos motivos por los que una llamadarequestStreamBuffers
puede tardar más de lo normal. Por ejemplo:- En los primeros búferes de un flujo recién creado, las llamadas pueden tardar más porque el dispositivo necesita asignar memoria.
- La latencia esperada aumenta en proporción a la cantidad de búferes solicitados en cada llamada.
- La app está reteniendo búferes y está ocupada procesando. Esto puede hacer que las solicitudes de búfer se ralenticen o alcancen el tiempo de espera debido a la falta de búferes o a una CPU ocupada.
Estrategias de administración de búfer
Las APIs de administración de búferes permiten implementar diferentes tipos de estrategias de administración de búferes. Por ejemplo:
- Retrocompatibilidad: El HAL solicita búferes para una solicitud de captura durante la llamada a
processCaptureRequest
. Esta estrategia no proporciona ningún ahorro de memoria, pero puede servir como la primera implementación de las APIs de administración de búferes, ya que requiere muy pocos cambios de código en el HAL de la cámara existente. - Ahorro de memoria maximizado: La HAL de la cámara solo solicita búferes de salida inmediatamente antes de que se necesite llenar uno. Esta estrategia permite maximizar el ahorro de memoria. El posible inconveniente es que el canal de procesamiento de la cámara puede tener más tirones cuando las solicitudes de búfer tardan demasiado en completarse.
- Almacenamiento en caché: La HAL de la cámara almacena en caché algunos búferes para que sea menos probable que se vea afectada por una solicitud de búfer lenta ocasional.
El HAL de la cámara puede adoptar diferentes estrategias para casos de uso particulares, por ejemplo, usar la estrategia de ahorro de memoria maximizado para los casos de uso que utilizan mucha memoria y usar la estrategia compatible con versiones anteriores para otros casos de uso.
Ejemplo de implementación en la HAL de la cámara externa
La HAL de cámara externa se introdujo en Android 9 y se puede encontrar en el árbol de fuentes en hardware/interfaces/camera/device/3.5/
.
En Android 10, se actualizó para incluir ExternalCameraDeviceSession.cpp
, una implementación de la API de administración de búferes. Esta HAL externa de la cámara implementa la estrategia de maximización del ahorro de memoria que se menciona en Estrategias de administración de búferes en unos cientos de líneas de código C++.