APIs de administración de búfer de la HAL3 de la cámara

Android 10 introduce las APIs opcionales de administración de búfer de la HAL3 de la cámara que te 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 N solicitudes (donde N es igual a la profundidad de la canalización) en cola en su canalización, pero a menudo no requiere todos los N conjuntos de búferes de salida al mismo tiempo.

Por ejemplo, el sistema 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 están en uso. 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 cientos de megabytes de ahorro 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.

Administración de búferes en 9 o versiones anteriores

Figura 1: Interfaz de la HAL de la cámara en Android 9 y versiones anteriores

Administración de búferes en Android 10

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úfer, la HAL de la cámara debe hacer lo siguiente:

El sistema HAL de la cámara usa los métodos requestStreamBuffers y returnStreamBuffers en ICameraDeviceCallback.hal para solicitar y mostrar búferes. El HAL también debe implementar el método signalStreamFlush en ICameraDeviceSession.hal para indicarle al HAL de la cámara que muestre los 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úferes 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, la 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 varias transmisiones de salida en una sola llamada, lo que permite menos llamadas a HIDL IPC. Sin embargo, las llamadas tardan más tiempo cuando se solicitan más búferes al mismo tiempo, lo que podría afectar negativamente la latencia total de solicitud a resultado. Además, como las llamadas a requestStreamBuffers se serializan en el servicio de la 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, la HAL de la cámara debe poder administrar correctamente los errores recuperables. En la siguiente lista, se describen los motivos habituales por los que fallan las solicitudes de búfer y cómo debe controlarlas el HAL de la cámara.

  • 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 segmentada a una transmisión desconectada y estar lista para procesar las solicitudes posteriores de forma normal.
  • Tiempo de espera: Esto puede ocurrir cuando una app está ocupada realizando un procesamiento intensivo mientras mantiene algunos búferes. La HAL de la cámara debe enviar ERROR_REQUEST para las solicitudes de captura que no se pueden completar debido a un error de tiempo de espera y estar lista para procesar las solicitudes posteriores de manera normal.
  • El framework de la cámara está preparando una nueva configuración de transmisión: El HAL de la cámara debe esperar hasta que se complete la próxima llamada a configureStreams antes de volver a llamar a requestStreamBuffers.
  • 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 muestre al menos un búfer de la transmisión antes de volver a llamar a requestStreamBuffers.

returnStreamBuffers

Usa el método returnStreamBuffers para mostrar búferes adicionales al framework de la cámara. Por lo general, la HAL de la cámara muestra búferes al framework de la cámara a través del método processCaptureResult, pero solo puede registrar 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 que los solicitados por el framework de la cámara. En ese momento, se debe usar el método returnStreamBuffers. Si la implementación de HAL nunca contiene más búferes de los solicitados, la implementación de HAL de la cámara no necesita llamar 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. Por lo general, se llama a esta función cuando el framework de la cámara está a punto de llamar a configureStreams y debe agotar el flujo de captura de la cámara. Al igual que con el método returnStreamBuffers, si una implementación de HAL de la 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, el framework deja de enviar solicitudes de captura nuevas al HAL de la cámara hasta que se devuelven todos los búferes al framework de la cámara. Cuando se muestran todos los búferes, las llamadas al método requestStreamBuffers fallan 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 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 devuelva correctamente. Si el framework de la cámara llama al método processCaptureRequest, el HAL de la cámara puede comenzar a solicitar búferes durante la llamada a processCaptureRequest.

La semántica es diferente para los métodos signalStreamFlush y flush. Cuando se llama al método flush, el HAL puede abortar las solicitudes de captura pendientes con ERROR_REQUEST para agotar la canalización lo antes posible. Cuando se llama al método signalStreamFlush, el sistema 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 podría llamar a otras APIs de bloqueo antes de que el HAL reciba la llamada a signalStreamFlush. Esto significa que el método signalStreamFlush y otros métodos (en particular, el método configureStreams) pueden llegar a la HAL de la cámara en un orden diferente al que se les llamó en el framework de la cámara. Para abordar este problema de asincronía, se agregó el campo streamConfigCounter a StreamConfiguration y como argumento al método signalStreamFlush. La implementación de la 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.

Cómo manejar las llamadas que llegan tarde

Figura 3: Cómo debe detectar y controlar las llamadas de signalStreamFlush que llegan tarde la HAL de la cámara

Cambios de comportamiento cuando se implementan las APIs de administración de búferes

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 cambios de comportamiento posibles en la implementación de la cámara y 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 una de las transmisiones de salida de la solicitud de captura alcanzó la cantidad máxima de búferes que el HAL puede contener a la vez (este valor lo designa el HAL de la cámara en el campo HalStream::maxBuffers en el valor que se muestra de una llamada configureStreams). Con las APIs de administración de búfer, este comportamiento de limitación ya no existe y la implementación de la HAL de la cámara no debe aceptar llamadas a processCaptureRequest cuando la HAL tiene demasiadas solicitudes de captura en cola.

  • La latencia de llamadas de requestStreamBuffers varía significativamente: Hay muchos motivos por los que una llamada a requestStreamBuffers puede tardar más tiempo que el promedio. Por ejemplo:

    • En los primeros búferes de una transmisión recién creada, 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 tiene búferes y está ocupado procesando. Esto puede hacer que las solicitudes de búfer se ralenticen o se agote el tiempo de espera debido a la falta de búferes o a una CPU ocupada.

Estrategias de administración del búfer

Las APIs de administración de búferes permiten implementar diferentes tipos de estrategias de administración de búferes. Por ejemplo:

  • Retrocompatible: La 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, lo que requiere muy pocos cambios de código en el HAL de la cámara existente.
  • Ahorro de memoria maximizado: El HAL de la cámara solo solicita búferes de salida inmediatamente antes de que se necesite uno para llenarse. Esta estrategia permite maximizar los ahorros de memoria. La posible desventaja es que se produce un mayor bloqueo en la canalización de la cámara cuando las solicitudes de búfer tardan más tiempo en completarse.
  • Almacenados en caché: El HAL de la cámara almacena en caché algunos búferes para que sea menos probable que se vea afectado 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 maximizada para casos de uso que usan mucha memoria y usar la estrategia retrocompatible para otros casos de uso.

Ejemplo de implementación en la HAL de la cámara externa

La HAL de la 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 de cámara externa implementa la estrategia de ahorro de memoria maximizada que se menciona en Estrategias de administración de búferes en unas pocas cientos de líneas de código C++.