AAudio es una API de audio que se introdujo en la versión de Android 8.0. La versión de Android 8.1 tiene mejoras para reducir la latencia cuando se usa junto con un HAL y un controlador que admiten MMAP. En este documento, se describe la capa de abstracción de hardware (HAL) y los cambios de controladores necesarios para admitir la función MMAP de AAudio en Android.
La compatibilidad con MMAP de AAudio requiere lo siguiente:
- informar las capacidades de MMAP del HAL
- Implementar funciones nuevas en el HAL
- Implementa de forma opcional un ioctl() personalizado para el búfer de modo EXCLUSIVO
- proporciona una ruta de acceso de datos de hardware adicional
- configuración de propiedades del sistema 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. Usa un patrón de diseño de generador para crear transmisiones de audio.
AAudio proporciona una ruta de datos de baja latencia. En el modo EXCLUSIVE, la función permite que el código de la aplicación cliente escriba directamente en un búfer asignado a memoria que se comparte con el controlador ALSA. En el modo SHARED, un mezclador que se ejecuta en AudioServer usa el búfer MMAP. En el modo EXCLUSIVE, la latencia es mucho menor porque los datos omiten el mezclador.
En el modo EXCLUSIVE, el servicio solicita el búfer MMAP del HAL y administra los recursos. El búfer de MMAP se ejecuta en modo NOIRQ, por lo que no hay contadores de lectura/escritura compartidos para administrar el acceso al búfer. En su lugar, el cliente mantiene un modelo de tiempo del hardware y predice cuándo se leerá el búfer.
En el siguiente diagrama, podemos ver los datos de modulación por pulsos (PCM) que fluyen a través del FIFO de MMAP hacia el controlador de ALSA. El servicio de AAudio solicita marcas de tiempo de forma periódica y, luego, las pasa al modelo de temporización del cliente a través de una cola de mensajes atómicos.

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 de PCM fluyen en la dirección opposite.
Cambios en HAL
Para tinyALSA, consulta lo siguiente:
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 el HAL heredado, 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);
Informa la compatibilidad con MMAP
La propiedad del sistema "aaudio.mmap_policy" debe establecerse en 2 (AAUDIO_POLICY_AUTO) para que el framework de audio sepa que el modo MMAP es compatible con el HAL de audio. (consulta la sección "Cómo habilitar la ruta de acceso a los datos de MMAP de AAudio" a continuación).
El archivo audio_policy_configuration.xml también debe contener un perfil de entrada y salida específico para el modo MMAP/NO IRQ para que el Administrador de políticas de audio sepa qué transmisión abrir cuando se crean clientes de 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 MMAP
createMmapBuffer(int32_t minSizeFrames) generates (Result retval, MmapBufferInfo info);
Se puede abrir y cerrar el flujo de MMAP llamando a las funciones de Tinyalsa.
Cómo consultar la posición de MMAP
La marca de tiempo que se pasa al modelo de tiempo contiene una posición de fotograma y un tiempo MONOTONIC en nanosegundos:
getMmapPosition() generates (Result retval, MmapPosition position);
El sistema HAL puede obtener esta información del controlador de ALSA llamando a una nueva función de Tinyalsa:
int pcm_mmap_get_hw_ptr(struct pcm* pcm, unsigned int *hw_ptr, struct timespec *tstamp);
Descriptores de archivos para memoria compartida
La ruta de datos MMAP de AAudio usa una región de memoria que se comparte entre el hardware y el servicio de audio. Se hace referencia a la memoria compartida con un descriptor de archivo que genera el controlador ALSA.
Cambios en el kernel
Si el descriptor de archivo está asociado directamente con un archivo de controlador /dev/snd/
, el servicio de AAudio puede usarlo en modo COMPARTIDO. Sin embargo, el descriptor no se puede pasar al código del cliente para el modo EXCLUSIVE. El descriptor de archivo /dev/snd/
proporcionaría un acceso demasiado amplio al cliente, por lo que SELinux lo bloquea.
Para admitir el modo EXCLUSIVE, es necesario convertir el descriptor /dev/snd/
en un descriptor de archivo anon_inode:dmabuf
. SELinux permite que ese descriptor de archivo se pase al cliente. También puede ser usado por AAudioService.
Se puede generar un descriptor de archivo anon_inode:dmabuf
con la biblioteca de memoria de Ion de Android.
Para obtener más información, consulta estos recursos externos:
- “El asignador de memoria ION de Android” https://lwn.net/Articles/480055/
- “Descripción general de ION de Android” https://wiki.linaro.org/BenjaminGaignard/ion
- "Integración del asignador de memoria ION" https://lwn.net/Articles/565469/
Cambios en HAL
El servicio de AAudio necesita saber si se admite este anon_inode:dmabuf
. Antes de Android 10.0, la única forma de hacerlo era pasar el tamaño del búfer de MMAP como un número negativo, p. ej., -2048 en lugar de 2048, si es compatible. En Android 10.0 y versiones posteriores, puedes establecer la marca AUDIO_MMAP_APPLICATION_SHAREABLE
.
mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;
Cambios en el subsistema de audio
AAudio requiere una ruta de datos adicional en el frontend de audio del subsistema de audio para que pueda funcionar en paralelo con la ruta de AudioFlinger original. Esa ruta de acceso heredada se usa para todos los demás sonidos del sistema y de la aplicación. Un mezclador de software en un DSP o un mezclador de hardware en el SOC podría proporcionar esta funcionalidad.
Habilita la ruta de datos de MMAP de AAudio
AAudio usará la ruta de acceso de datos heredada de AudioFlinger si MMAP no es compatible o no se puede abrir una transmisión. Por lo tanto, AAudio funcionará con un dispositivo de audio que no admita la ruta de acceso MMAP/NOIRQ.
Cuando pruebes la compatibilidad con MMAP para AAudio, es importante saber si estás probando la ruta de acceso a los datos de MMAP o solo la ruta de acceso a los datos heredados. A continuación, se describe cómo habilitar o forzar rutas de datos específicas y cómo consultar la ruta que usa una transmisión.
Propiedades del sistema
Puedes configurar la política de MMAP a través de las propiedades del sistema:
- 1 = AAUDIO_POLICY_NEVER: Solo usa la ruta de acceso heredada. Ni siquiera intentes usar MMAP.
- 2 = AAUDIO_POLICY_AUTO: Intenta usar MMAP. Si eso falla o no está disponible, usa la ruta de acceso heredada.
- 3 = AAUDIO_POLICY_ALWAYS: Solo usa la ruta de acceso de MMAP. No recurras a la ruta de acceso heredada.
Se pueden establecer 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 inicie el dispositivo. Deberás reiniciar el servidor de audio para que se aplique el cambio. Por ejemplo, para habilitar el modo AUTO para MMAP, haz lo siguiente:
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
anular la política para usar la ruta de acceso MMAP:
aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);
Para averiguar si una transmisión usa la ruta de acceso MMAP, llama a lo siguiente:
bool AAudioStream_isMMapUsed(AAudioStream* stream);