AAudio et MMAP

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

La prise en charge du MMAP AAudio nécessite:

  • la création de rapports sur les fonctionnalités MMAP du HAL
  • l'implémentation de nouvelles fonctions dans le HAL ;
  • éventuellement implémenter un ioctl() personnalisé pour le tampon du mode EXCLUSIVE
  • en fournissant un chemin d'accès aux données matérielles supplémentaire
  • Définir les propriétés système qui activent la fonctionnalité MMAP

Architecture AAudio

AAudio est une nouvelle API C native qui offre une alternative à Open SL ES. Elle utilise un Compilateur de modèles de conception permettant de créer des flux audio.

AAudio fournit un chemin d'accès aux données à faible latence. En mode EXCLUSIF, la fonctionnalité permet au code de l'application cliente d'écrire directement dans un tampon mappé en mémoire partagé avec le pilote ALSA. En mode PARTAGÉ, le tampon MMAP est utilisé par un mélangeur en cours d'exécution dans le serveur audio. En mode EXCLUSIF, la latence est beaucoup moins car les données contournent le mélangeur.

En mode EXCLUSIF, le service demande le tampon MMAP au HAL et gère les ressources. Le tampon MMAP s'exécute en mode NOIQ, il n'y a donc pas de partage compteurs de lecture/écriture pour gérer l'accès au tampon. Au lieu de cela, le client Il maintient un modèle temporel du matériel et prédit quand le tampon sera lire.

Le schéma ci-dessous montre le flux de données de modulation par impulsion et codage (PCM, Pulse-Code modulation). vers le bas dans le MMAP FIFO dans le pilote ALSA. Les horodatages sont périodiquement demandé par le service AAudio, puis transmis au modèle de temps du client via une file d'attente de messages atomique.

Schéma du flux de données PCM.
Figure 1. Flux de données PCM via FIFO vers ALSA

En mode PARTAGÉ, un modèle temporel est également utilisé, mais il se trouve dans l'AAudioService.

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

Modifications HAL

Pour tinyALSA, consultez les pages suivantes:

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'ancienne HAL, consultez les ressources suivantes:

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 les fichiers audio HIDL 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);

Signaler la prise en charge du MMAP

Propriété système "aaudio.mmap_policy" doit être défini sur 2 (AAUDIO_POLICY_AUTO). le framework audio sait que le mode MMAP est compatible avec le HAL audio. (voir "Activer le chemin de données MMAP AAudio" ci-dessous.)

Le fichier audio_policy_configuration.xml doit également contenir une sortie et une entrée spécifique au mode MMAP/NO IQ, afin que le gestionnaire des règles audio sache 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>

Ouvrir et fermer un flux MMAP

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

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

Interroger la position MMAP

Le code temporel renvoyé au modèle de durée contient une position de frame et un Durée MONOTONIC en nanosecondes:

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

Le HAL peut obtenir ces informations auprès du pilote ALSA en appelant un nouveau Fonction Tinyalsa:

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

Descripteurs de fichiers pour la mémoire partagée

Le chemin d'accès aux données MMAP AAudio utilise une région de mémoire partagée entre les le matériel et le service audio. La mémoire partagée est référencée à l'aide d'un descripteur de fichier généré par le pilote ALSA.

Modifications du noyau

Si le descripteur de fichier est directement associé à un /dev/snd/, il peut alors être utilisé par le service AAudio dans mode PARTAGÉ. Mais le descripteur ne peut pas être transmis au code client pour mode EXCLUSIF. Le descripteur de fichier /dev/snd/ fournirait aussi un accès étendu au client, il est donc bloqué par SELinux.

Pour prendre en charge le mode EXCLUSIF, vous devez convertir le Descripteur /dev/snd/ à un fichier anon_inode:dmabuf du descripteur. SELinux permet de transmettre ce descripteur de fichier au client. Il peut également être utilisée par l'AAudioService.

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

Pour en savoir plus, consultez les ressources externes suivantes:

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

Modifications HAL

Le service AAudio doit savoir si ce anon_inode:dmabuf est compatibles. Avant Android 10.0, le seul moyen de le faire était de transmettre la taille du MMAP tampon en tant que nombre négatif, par exemple -2048 au lieu de 2048, si compatible. Sur Android 10.0 et versions ultérieures vous pouvez définir l'option AUDIO_MMAP_APPLICATION_SHAREABLE.

mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;

Modifications du sous-système audio

AAudio nécessite un chemin de données supplémentaire au niveau de l'interface audio de l'audio. afin qu'il puisse fonctionner en parallèle du chemin AudioFlinger d'origine. L'ancien chemin d'accès est utilisé pour tous les autres sons système et d'application. Cette fonctionnalité peut être fournie par un mélangeur logiciel dans une DSP ou un dans le SOC.

Activer le chemin d'accès aux données MMAP AAudio

AAudio utilisera l'ancien chemin d'accès aux données AudioFlinger si le MMAP n'est pas compatible ou échoue à ouvrir un flux. Ainsi, AAudio fonctionnera avec un appareil audio qui n'est pas prend en charge le chemin d'accès MMAP/NOIR.

Lorsque vous testez la prise en charge de MMAP pour AAudio, il est important de savoir si vous êtes en testant réellement le chemin de données MMAP ou simplement en testant le chemin de données ancien. La ce qui suit explique comment activer ou forcer des chemins de données spécifiques, et comment interroger le chemin d'accès utilisé par un flux.

Propriétés système

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

  • 1 = AAUDIO_POLICY_NEVER - Utiliser uniquement l'ancien chemin d'accès N'essayez même pas d'utiliser le MMAP.
  • 2 = AAUDIO_POLICY_AUTO - Essayez d'utiliser MMAP. Si elle échoue ou n'est pas disponible, puis utilisez l'ancien chemin d'accès.
  • 3 = AAUDIO_POLICY_ALWAYS - N'utilisez que le chemin d'accès MMAP. Ne pas revenir à l'ancienne version chemin d'accès.

Ceux-ci peuvent être définis dans le Makefile de l'appareil, 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 devez redémarrer le serveur audio pour que la modification soit prise en compte. Par exemple, pour activer le mode AUTO pour MMAP:

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

Des fonctions sont fournies dans ndk/sysroot/usr/include/aaudio/AAudioTesting.h qui vous permettent d'effectuer les actions suivantes : ignorez la règle d'utilisation du chemin d'accès MMAP:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

Pour savoir si un flux utilise un chemin d'accès MMAP, appelez:

bool AAudioStream_isMMapUsed(AAudioStream* stream);