AAudio y MMAP

AAudio es una API de audio que se introdujo en la versión de Android 8.0. La versión de Android 8.1 incluye mejoras para reducir la latencia cuando se usa junto con un HAL y controladores compatibles con MMAP. En este documento, se describe la abstracción de hardware capa (HAL) y controlador necesarios para admitir la función MMAP de AAudio en Android

La compatibilidad con MMAP de AAudio requiere lo siguiente:

  • informar las capacidades MMAP de la HAL
  • Cómo implementar funciones nuevas en la HAL
  • De manera opcional, implementa un ioctl() personalizado para el búfer de modo EXCLUSIVO
  • lo que proporciona una ruta de acceso adicional a los datos de hardware
  • propiedades del sistema de configuración que habilitan la función MMAP

Arquitectura de AAudio

AAudio es una nueva API nativa de C que proporciona una alternativa a Open SL ES. Utiliza un Patrón de diseño de compilador para crear transmisiones de audio.

AAudio proporciona una ruta de acceso de datos de baja latencia. En el modo EXCLUSIVO, la función permite que el código de la aplicación cliente escriba directamente en un búfer asignado a la memoria que se comparte con el controlador de ALSA. En el modo SHARED, el búfer MMAP es utilizado por un mezclador que se ejecuta en el servidor de audio. En el modo EXCLUSIVO, la latencia se mucho menos porque los datos omiten el mezclador.

En el modo EXCLUSIVO, el servicio solicita el búfer MMAP a la HAL y administra los recursos. El búfer MMAP se ejecuta en modo NOIRQ, por lo que no se comparten de lectura/escritura para administrar el acceso al búfer. En cambio, el cliente mantiene un modelo de sincronización del hardware y predice cuándo se leer.

En el siguiente diagrama, podemos ver que los datos de modulación por pulso (PCM) fluyen a través del MMAP FIFO hasta el controlador ALSA. Las marcas de tiempo se muestran periódicamente solicitadas por el servicio de AAudio y, luego, pasan al modelo de tiempo del cliente a través de una cola de mensajes atómicos.

Diagrama de flujo de datos PCM
Figura 1: Flujo de datos PCM a través de FIFO a ALSA

En el modo SHARED, también se usa un modelo de tiempo, pero se encuentra en AAudioService.

Para la captura de audio, se usa un modelo similar, pero los datos PCM fluyen en la dirección opuesta.

Cambios en la HAL

Para tinyALSA, consulta:

external/tinyalsa/include/tinyalsa/asoundlib.h
external/tinyalsa/include/tinyalsa/pcm.c
int pcm_start(struct pcm *pcm);
int pcm_stop(struct pcm *pcm);
int pcm_mmap_begin(struct pcm *pcm, void **areas,
           unsigned int *offset,
           unsigned int *frames);
int pcm_get_poll_fd(struct pcm *pcm);
int pcm_mmap_commit(struct pcm *pcm, unsigned int offset,
           unsigned int frames);
int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr,
           struct timespec *tstamp);

Para obtener información sobre la HAL heredada, consulta lo siguiente:

hardware/libhardware/include/hardware/audio.h
hardware/qcom/audio/hal/audio_hw.c
int start(const struct audio_stream_out* stream);
int stop(const struct audio_stream_out* stream);
int create_mmap_buffer(const struct audio_stream_out *stream,
                        int32_t min_size_frames,
                        struct audio_mmap_buffer_info *info);
int get_mmap_position(const struct audio_stream_out *stream,
                        struct audio_mmap_position *position);

Para la HAL de audio HIDL:

hardware/interfaces/audio/2.0/IStream.hal
hardware/interfaces/audio/2.0/types.hal
hardware/interfaces/audio/2.0/default/Stream.h
start() generates (Result retval);
stop() generates (Result retval) ;
createMmapBuffer(int32_t minSizeFrames)
       generates (Result retval, MmapBufferInfo info);
getMmapPosition()
       generates (Result retval, MmapPosition position);

Informar compatibilidad con MMAP

Propiedad del sistema "aaudio.mmap_policy" debe establecerse en 2 (AAUDIO_POLICY_AUTO), por lo que el framework de audio sabrá que la HAL de audio admite el modo MMAP. (consulta "Habilitación de la ruta de datos MMAP de AAudio" a continuación).

El archivo audio_policy_configuration.xml también debe contener una salida y una entrada perfil específico para el modo MMAP/NO IRQ para que el Administrador de políticas de audio sepa qué transmisión abrir al crear los clientes MMAP:

<mixPort name="mmap_no_irq_out" role="source"
            flags="AUDIO_OUTPUT_FLAG_DIRECT|AUDIO_OUTPUT_FLAG_MMAP_NOIRQ">
            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                                samplingRates="48000"
                                channelMasks="AUDIO_CHANNEL_OUT_STEREO"/>
</mixPort>

<mixPort name="mmap_no_irq_in" role="sink" flags="AUDIO_INPUT_FLAG_MMAP_NOIRQ">
            <profile name="" format="AUDIO_FORMAT_PCM_16_BIT"
                                samplingRates="48000"
                                channelMasks="AUDIO_CHANNEL_IN_STEREO"/>
</mixPort>

Cómo abrir y cerrar una transmisión de MMAP

createMmapBuffer(int32_t minSizeFrames)
            generates (Result retval, MmapBufferInfo info);

La transmisión de MMAP se puede abrir y cerrar llamando a las funciones de Timinialsa.

Posición de MMAP de consulta

La marca de tiempo que se devuelve al modelo de tiempo contiene una posición de fotograma y una Tiempo MONOTONIC en nanosegundos:

getMmapPosition()
        generates (Result retval, MmapPosition position);

La HAL puede obtener esta información del controlador de ALSA llamando a un nuevo Función Smallalsa:

int pcm_mmap_get_hw_ptr(struct pcm* pcm,
                        unsigned int *hw_ptr,
                        struct timespec *tstamp);

Descriptores de archivos para la memoria compartida

La ruta de acceso a los datos MMAP de AAudio utiliza una región de memoria compartida entre los hardware y el servicio de audio. Se hace referencia a la memoria compartida usando un descriptor de archivos generada por el controlador ALSA.

Cambios en el kernel

Si el descriptor de archivo está directamente asociado con una /dev/snd/, el servicio de AAudio puede usarlo en modo SHARED. Pero el descriptor no se puede pasar al código de cliente para modo EXCLUSIVE. El descriptor de archivo /dev/snd/ también proporcionaría amplio de acceso al cliente, por lo que SELinux lo bloquea.

Para admitir el modo EXCLUSIVO, es necesario convertir el el descriptor /dev/snd/ a un archivo anon_inode:dmabuf descriptor de la aplicación. SELinux permite que el descriptor de archivos se pase al cliente. Integra y AAudioService también las puede usar.

Se puede generar un descriptor de archivo anon_inode:dmabuf con el Biblioteca de memoria de Android Ion

Para obtener información adicional, consulta estos recursos externos:

  1. "El asignador de memoria ION de Android" https://lwn.net/Articles/480055/
  2. "Descripción general de Android ION" https://wiki.linaro.org/BenjaminGaignard/ion
  3. "Integra el asignador de memoria ION" https://lwn.net/Articles/565469/

Cambios en la HAL

El servicio de AAudio debe saber si este anon_inode:dmabuf es no es compatible. En versiones anteriores a Android 10.0, la única forma de hacerlo era pasar el tamaño del MMAP búfer como un número negativo, p. ej., -2,048 en lugar de 2,048, si es compatible. En Android 10.0 y versiones posteriores puedes configurar la marca AUDIO_MMAP_APPLICATION_SHAREABLE.

mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;

Cambios en el subsistema de audio

AAudio requiere una ruta de acceso de datos adicional en el frontend de audio para que pueda operar en paralelo con la ruta de acceso de AudioFlinger original. Esa ruta heredada se usa para todos los demás sonidos del sistema y de la aplicación. Esta funcionalidad se puede proporcionar mediante un mezclador de software en una DSP o un hardware. mezclador en el SOC.

Habilitar la ruta de acceso a los datos MMAP de AAudio

AAudio utilizará la ruta de acceso de datos heredada de AudioFlinger si MMAP no es compatible o no puede abrir una transmisión. Por eso, AAudio funciona con dispositivos de audio que no admite la ruta MMAP/NOIRQ.

Cuando pruebes la compatibilidad de MMAP para AAudio, es importante que sepas si estás prueba la ruta de datos MMAP o simplemente prueba la ruta de datos heredada. El se describe cómo habilitar o forzar rutas de acceso a datos específicas y cómo consultar la ruta de acceso que usa una transmisión.

Propiedades del sistema

Puedes establecer la política MMAP a través de las propiedades del sistema:

  • 1 = AAUDIO_POLICY_NEVER - Usar solo la ruta heredada Ni siquiera intentes usar MMAP.
  • 2 = AAUDIO_POLICY_AUTO: Intenta usar MMAP. Si eso falla o no está disponible, y, luego, usar la ruta heredada.
  • 3 = AAUDIO_POLICY_ALWAYS - Usar solo la ruta MMAP No recurrir a la versión heredada ruta de acceso.

Estos se pueden configurar en el archivo makefile de los dispositivos, de la siguiente manera:

# Enable AAudio MMAP/NOIRQ data path.
# 2 is AAUDIO_POLICY_AUTO so it will try MMAP then fallback to Legacy path.
PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_policy=2
# Allow EXCLUSIVE then fall back to SHARED.
PRODUCT_PROPERTY_OVERRIDES += aaudio.mmap_exclusive_policy=2

También puedes anular estos valores después de que se haya iniciado el dispositivo. Deberás reiniciar el audioserver para que se aplique el cambio. Por ejemplo, para habilitar el modo AUTOMÁTICO para MMAP, sigue estos pasos:

adb root
adb shell setprop aaudio.mmap_policy 2
adb shell killall audioserver

Hay funciones proporcionadas en ndk/sysroot/usr/include/aaudio/AAudioTesting.h que te permiten hacer lo siguiente: anular la política para usar la ruta MMAP:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

Para saber si una transmisión usa la ruta de acceso MMAP, haz la siguiente llamada:

bool AAudioStream_isMMapUsed(AAudioStream* stream);