AAudio und MMAP

AAudio ist eine Audio-API, die in der Version Android 8.0 eingeführt wurde. Die Android 8.1-Version verfügt über Verbesserungen zur Verringerung der Latenz, wenn sie in Verbindung mit einem HAL und einem Treiber verwendet wird, die MMAP unterstützen. Dieses Dokument beschreibt die Hardware-Abstraktionsschicht (HAL) und Treiberänderungen, die zur Unterstützung der MMAP-Funktion von AAudio in Android erforderlich sind.

Unterstützung für AAudio MMAP erfordert:

  • Berichten der MMAP-Fähigkeiten des HAL
  • Implementieren neuer Funktionen in der HAL
  • optional ein benutzerdefiniertes ioctl() für den Puffer im EXCLUSIVE-Modus implementieren
  • Bereitstellen eines zusätzlichen Hardware-Datenpfads
  • Festlegen von Systemeigenschaften, die die MMAP-Funktion aktivieren

AAudio-Architektur

AAudio ist eine neue native C-API, die eine Alternative zu Open SL ES bietet. Es verwendet ein Builder-Entwurfsmuster, um Audiostreams zu erstellen.

AAudio bietet einen Datenpfad mit niedriger Latenz. Im EXCLUSIVE-Modus ermöglicht die Funktion dem Client-Anwendungscode, direkt in einen speicherabgebildeten Puffer zu schreiben, der mit dem ALSA-Treiber gemeinsam genutzt wird. Im SHARED-Modus wird der MMAP-Puffer von einem Mixer verwendet, der auf dem AudioServer läuft. Im EXCLUSIVE-Modus ist die Latenz deutlich geringer, da die Daten den Mixer umgehen.

Im EXCLUSIVE-Modus fordert der Dienst den MMAP-Puffer von der HAL an und verwaltet die Ressourcen. Der MMAP-Puffer läuft im NOIRQ-Modus, daher gibt es keine gemeinsam genutzten Lese-/Schreibzähler, um den Zugriff auf den Puffer zu verwalten. Stattdessen verwaltet der Client ein Zeitsteuerungsmodell der Hardware und sagt voraus, wann der Puffer gelesen wird.

Im Diagramm unten sehen wir, wie die Daten der Pulscodemodulation (PCM) durch das MMAP FIFO in den ALSA-Treiber fließen. Zeitstempel werden regelmäßig vom AAudio-Dienst angefordert und dann über eine atomare Nachrichtenwarteschlange an das Zeitsteuerungsmodell des Clients weitergegeben.

PCM-Datenflussdiagramm.
Abbildung 1. PCM-Datenfluss durch FIFO zu ALSA

Im SHARED-Modus wird auch ein Timing-Modell verwendet, aber es lebt im AAudioService.

Für die Audioaufnahme wird ein ähnliches Modell verwendet, aber die PCM-Daten fließen in die entgegengesetzte Richtung.

HAL-Änderungen

Für tinyALSA siehe:

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

Für die Legacy-HAL siehe:

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

Für 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);

MMAP-Unterstützung melden

Die Systemeigenschaft "audio.mmap_policy" sollte auf 2 (AAUDIO_POLICY_AUTO) gesetzt werden, damit das Audio-Framework weiß, dass der MMAP-Modus von der Audio-HAL unterstützt wird. (Siehe „Aktivieren des AAudio-MMAP-Datenpfads“ weiter unten.)

Die Datei audio_policy_configuration.xml muss außerdem ein spezifisches Ausgabe- und Eingabeprofil für den MMAP/NO IRQ-Modus enthalten, damit der Audio Policy Manager weiß, welcher Stream geöffnet werden muss, wenn MMAP-Clients erstellt werden:

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

Öffnen und Schließen eines MMAP-Streams

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

Der MMAP-Stream kann durch Aufrufen von Tinyalsa-Funktionen geöffnet und geschlossen werden.

Abfrage der MMAP-Position

Der an das Timing-Modell zurückgegebene Zeitstempel enthält eine Frame-Position und eine MONOTONIC-Zeit in Nanosekunden:

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

Die HAL kann diese Informationen vom ALSA-Treiber erhalten, indem sie eine neue Tinyalsa-Funktion aufruft:

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

Dateideskriptoren für Shared Memory

Der AAudio-MMAP-Datenpfad verwendet einen Speicherbereich, der von der Hardware und dem Audiodienst gemeinsam genutzt wird. Auf den gemeinsam genutzten Speicher wird mithilfe eines Dateideskriptors verwiesen, der vom ALSA-Treiber generiert wird.

Kerneländerungen

Wenn der Dateideskriptor direkt mit einer /dev/snd/ -Treiberdatei verknüpft ist, kann er vom AAudio-Dienst im SHARED-Modus verwendet werden. Der Deskriptor kann jedoch nicht an den Clientcode für den EXCLUSIVE-Modus übergeben werden. Der Dateideskriptor /dev/snd/ würde dem Client einen zu breiten Zugriff gewähren, sodass er von SELinux blockiert wird.

Um den EXCLUSIVE-Modus zu unterstützen, muss der /dev/snd/ -Deskriptor in einen anon_inode:dmabuf -Dateideskriptor konvertiert werden. SELinux ermöglicht die Weitergabe dieses Dateideskriptors an den Client. Es kann auch vom AAudioService verwendet werden.

Ein anon_inode:dmabuf -Dateideskriptor kann mit der Android-Ion-Speicherbibliothek generiert werden.

Weitere Informationen finden Sie in diesen externen Ressourcen:

  1. "Der Android ION-Speicherzuordner" https://lwn.net/Articles/480055/
  2. "Android ION-Übersicht" https://wiki.linaro.org/BenjaminGaignard/ion
  3. „Integrieren des ION-Speicherzuordners“ https://lwn.net/Articles/565469/

HAL-Änderungen

Der AAudio-Dienst muss wissen, ob dieser anon_inode:dmabuf unterstützt wird. Vor Android 10.0 bestand die einzige Möglichkeit darin, die Größe des MMAP-Puffers als negative Zahl zu übergeben, z. -2048 statt 2048, falls unterstützt. In Android 10.0 und höher können Sie das Flag AUDIO_MMAP_APPLICATION_SHAREABLE setzen.

mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;

Änderungen am Audio-Subsystem

AAudio benötigt einen zusätzlichen Datenpfad am Audio-Front-End des Audio-Subsystems, damit es parallel zum ursprünglichen AudioFlinger-Pfad betrieben werden kann. Dieser alte Pfad wird für alle anderen System- und Anwendungssounds verwendet. Diese Funktionalität könnte durch einen Software-Mischer in einem DSP oder einen Hardware-Mischer im SOC bereitgestellt werden.

Aktivieren des AAudio-MMAP-Datenpfads

AAudio verwendet den alten AudioFlinger-Datenpfad, wenn MMAP nicht unterstützt wird oder einen Stream nicht öffnen kann. AAudio funktioniert also mit einem Audiogerät, das den MMAP/NOIRQ-Pfad nicht unterstützt.

Beim Testen der MMAP-Unterstützung für AAudio ist es wichtig zu wissen, ob Sie tatsächlich den MMAP-Datenpfad oder nur den Legacy-Datenpfad testen. Im Folgenden wird beschrieben, wie bestimmte Datenpfade aktiviert oder erzwungen werden und wie der von einem Stream verwendete Pfad abgefragt wird.

Systemeigenschaften

Sie können die MMAP-Richtlinie über die Systemeigenschaften festlegen:

  • 1 = AAUDIO_POLICY_NEVER - Nur Legacy-Pfad verwenden. Versuchen Sie nicht einmal, MMAP zu verwenden.
  • 2 = AAUDIO_POLICY_AUTO - Versuchen Sie, MMAP zu verwenden. Wenn dies fehlschlägt oder nicht verfügbar ist, verwenden Sie den Legacy-Pfad.
  • 3 = AAUDIO_POLICY_ALWAYS - Nur MMAP-Pfad verwenden. Fallen Sie nicht auf den Legacy-Pfad zurück.

Diese können wie folgt im Makefile des Geräts festgelegt werden:

# 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

Sie können diese Werte auch überschreiben, nachdem das Gerät hochgefahren ist. Sie müssen den Audioserver neu starten, damit die Änderung wirksam wird. So aktivieren Sie beispielsweise den AUTO-Modus für MMAP:

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

Es gibt Funktionen in ndk/sysroot/usr/include/aaudio/AAudioTesting.h , mit denen Sie die Richtlinie für die Verwendung des MMAP-Pfads außer Kraft setzen können:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

Um herauszufinden, ob ein Stream den MMAP-Pfad verwendet, rufen Sie Folgendes auf:

bool AAudioStream_isMMapUsed(AAudioStream* stream);