AAudio et MMAP

AAudio est une API audio introduite dans la version Android 8.0. La version Android 8.1 a des améliorations pour réduire la latence lorsqu'elle est utilisée en conjonction avec un HAL et un pilote prenant en charge MMAP. Ce document décrit la couche d'abstraction matérielle (HAL) et les changements de pilote nécessaires pour prendre en charge la fonctionnalité MMAP d'AAudio dans Android.

La prise en charge de AAudio MMAP nécessite:

  • rapporter les capacités MMAP de la HAL
  • implémentation de nouvelles fonctions dans la HAL
  • implémentation facultative d'un ioctl () personnalisé pour le tampon du mode EXCLUSIVE
  • fournir un chemin de données matériel supplémentaire
  • définition des propriétés système qui activent la fonction MMAP

Architecture audio

AAudio est une nouvelle API C native qui offre une alternative à Open SL ES. Il utilise un modèle de conception Builder pour créer des flux audio.

AAudio fournit un chemin de données à faible latence. En mode EXCLUSIF, la fonction permet au code d'application client d'écrire directement dans une mémoire tampon mappée en mémoire partagée avec le pilote ALSA. En mode SHARED, le tampon MMAP est utilisé par un mélangeur fonctionnant dans l'AudioServer. En mode EXCLUSIF, la latence est nettement moindre car les données contournent la table de mixage.

En mode EXCLUSIF, le service demande le tampon MMAP à la HAL et gère les ressources. Le tampon MMAP fonctionne en mode NOIRQ, il n'y a donc pas de compteurs de lecture / écriture partagés pour gérer l'accès au tampon. Au lieu de cela, le client maintient un modèle de synchronisation du matériel et prédit quand le tampon sera lu.

Dans le diagramme ci-dessous, nous pouvons voir les données de modulation d'impulsions en code (PCM) circulant à travers la FIFO MMAP dans le pilote ALSA. Les horodatages sont périodiquement demandés par le service AAudio, puis transmis au modèle de synchronisation du client via une file d'attente de messages atomiques.

Diagramme de flux de données PCM.
Figure 1. Flux de données PCM via FIFO vers ALSA

En mode SHARED, un modèle de synchronisation est également utilisé, mais il vit dans AAudioService.

Pour la capture audio, un modèle similaire est utilisé, mais les données PCM circulent dans la direction opposée.

Changements HAL

Pour tinyALSA voir:

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);

Pour l'héritage HAL, voir:

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);

Pour HIDL audio HAL:

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);

Prise en charge des rapports MMAP

La propriété système "aaudio.mmap_policy" doit être définie sur 2 (AAUDIO_POLICY_AUTO) afin que le framework audio sache que le mode MMAP est pris en charge par le HAL audio. (voir «Activation du chemin de données AAudio MMAP» ci-dessous.)

Le fichier audio_policy_configuration.xml doit également contenir un profil de sortie et d'entrée spécifique au mode MMAP / NO IRQ afin que le gestionnaire de stratégie audio sache quel flux ouvrir lors de la création de clients 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>

Ouverture et fermeture d'un flux MMAP

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

Le flux MMAP peut être ouvert et fermé en appelant les fonctions Tinyalsa.

Interroger la position MMAP

L'horodatage renvoyé au modèle de synchronisation contient une position d'image et une heure MONOTONIQUE en nanosecondes:

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

Le HAL peut obtenir ces informations du pilote ALSA en appelant une nouvelle fonction Tinyalsa:

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

Modifications du noyau

Activez le mode MMAP / NOIRQ dans le pilote.

La mémoire partagée est référencée à l'aide d'un descripteur de fichier généré par le pilote ALSA. Si le descripteur de fichier est directement associé à un fichier /dev/snd/ driver, il peut être utilisé par le service AAudio en mode SHARED. Mais le descripteur ne peut pas être passé au code client pour le mode EXCLUSIF. Le descripteur de fichier /dev/snd/ fournirait un accès trop large au client, il est donc bloqué par SELinux.

Afin de prendre en charge le mode EXCLUSIF, il est nécessaire de convertir le descripteur /dev/snd/ en un descripteur de fichier anon_inode:dmabuf . SELinux permet à ce descripteur de fichier d'être transmis au client. Il peut également être utilisé par AAudioService.

Un descripteur de fichier anon_inode:dmabuf peut être généré à l'aide de la bibliothèque de mémoire Android Ion.

Pour plus d'informations, consultez ces ressources externes:

  1. "L'allocateur de mémoire Android ION" https://lwn.net/Articles/480055/
  2. "Présentation d'Android ION" https://wiki.linaro.org/BenjaminGaignard/ion
  3. "Intégration de l'allocateur de mémoire ION" https://lwn.net/Articles/565469/

Le service AAudio a besoin de savoir si cet anon_inode:dmabuf est pris en charge. Actuellement, le seul moyen de le faire est de transmettre la taille du tampon MMAP sous la forme d'un nombre négatif, par exemple. -2048 au lieu de 2048, si pris en charge. Une meilleure façon de signaler cela sans avoir à ouvrir le flux est prévue.

Modifications du sous-système audio

AAudio nécessite un chemin de données supplémentaire à l'extrémité avant audio du sous-système audio afin qu'il puisse fonctionner en parallèle avec le chemin AudioFlinger d'origine. Ce chemin hérité est utilisé pour tous les autres sons système et sons d'application. Cette fonctionnalité pourrait être fournie par un mélangeur logiciel dans un DSP ou un mélangeur matériel dans le SOC.

Activation du chemin de données AAudio MMAP

AAudio utilisera l'ancien chemin de données AudioFlinger si MMAP n'est pas pris en charge ou ne parvient pas à ouvrir un flux. Ainsi, AAudio fonctionnera avec un périphérique audio qui ne prend pas en charge le chemin MMAP / NOIRQ.

Lors du test de la prise en charge MMAP pour AAudio, il est important de savoir si vous testez réellement le chemin de données MMAP ou si vous testez simplement le chemin de données hérité. Ce qui suit décrit comment activer ou forcer des chemins de données spécifiques et comment interroger le chemin utilisé par un flux.

Propriétés du système

Vous pouvez définir la stratégie MMAP via les propriétés système:

  • 1 = AAUDIO_POLICY_NEVER - Utilisez uniquement le chemin hérité. N'essayez même pas d'utiliser MMAP.
  • 2 = AAUDIO_POLICY_AUTO - Essayez d'utiliser MMAP. Si cela échoue ou n'est pas disponible, utilisez le chemin hérité.
  • 3 = AAUDIO_POLICY_ALWAYS - Utilisez uniquement le chemin MMAP. Ne revenez pas au chemin hérité.

Ceux-ci peuvent être définis dans le Makefile des appareils, comme ceci:

# 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

Vous pouvez également remplacer ces valeurs après le démarrage de l'appareil. Vous devrez redémarrer le serveur audio pour que la modification prenne effet. Par exemple, pour activer le mode AUTO pour MMAP:

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

Il existe des fonctions fournies dans ndk/sysroot/usr/include/aaudio/AAudioTesting.h qui vous permettent de remplacer la politique d'utilisation du chemin MMAP:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

Pour savoir si un flux utilise le chemin MMAP, appelez:

bool AAudioStream_isMMapUsed(AAudioStream* stream);