AAudio und MMAP

AAudio ist eine Audio-API, die mit der Android 8.0-Version eingeführt wurde. Die Android 8.1-Version verfügt über Verbesserungen zur Reduzierung der Latenz bei Verwendung in Verbindung mit einem HAL und Treiber, die MMAP unterstützen. In diesem Dokument werden die Hardware-Abstraktionsschicht (HAL) und Treiberänderungen beschrieben, die zur Unterstützung der MMAP-Funktion von AAudio in Android erforderlich sind.

Für die Unterstützung von AAudio MMAP ist Folgendes erforderlich:

  • Berichterstattung über die MMAP-Funktionen des HAL
  • Implementierung neuer Funktionen im HAL
  • optional Implementierung eines benutzerdefinierten ioctl() für den EXKLUSIV-Moduspuffer
  • Bereitstellung 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 geringer Latenz. Im EXKLUSIV-Modus ermöglicht die Funktion dem Client-Anwendungscode, direkt in einen speicherzugeordneten Puffer zu schreiben, der mit dem ALSA-Treiber gemeinsam genutzt wird. Im SHARED-Modus wird der MMAP-Puffer von einem Mixer verwendet, der im AudioServer läuft. Im EXKLUSIV-Modus ist die Latenz deutlich geringer, da die Daten den Mixer umgehen.

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

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

PCM-Datenflussdiagramm.
Abbildung 1. PCM-Datenfluss über FIFO zu ALSA

Im SHARED-Modus wird ebenfalls ein Timing-Modell verwendet, das sich jedoch im AAudioService befindet.

Für die Audioaufnahme wird ein ähnliches Modell verwendet, die PCM-Daten fließen jedoch 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);

Informationen zum alten HAL finden Sie unter:

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 „aaudio.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“ unten.)

Die Datei „audio_policy_configuration.xml“ muss außerdem ein für den MMAP/NO IRQ-Modus spezifisches Ausgabe- und Eingabeprofil 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 Sie einen MMAP-Stream

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

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

MMAP-Position abfragen

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

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

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

Dateideskriptoren für gemeinsam genutzten Speicher

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. Für den EXKLUSIV-Modus kann der Deskriptor jedoch nicht an den Clientcode übergeben werden. Der Dateideskriptor /dev/snd/ würde einen zu umfassenden Zugriff auf den Client ermöglichen und wird daher von SELinux blockiert.

Um den EXKLUSIV-Modus zu unterstützen, ist es notwendig, den /dev/snd/ -Deskriptor in einen anon_inode:dmabuf Dateideskriptor zu konvertieren. SELinux ermöglicht die Übergabe dieses Dateideskriptors an den Client. Es kann auch vom AAudioService verwendet werden.

Mit der Android Ion-Speicherbibliothek kann ein anon_inode:dmabuf Dateideskriptor generiert werden.

Weitere Informationen finden Sie in diesen externen Ressourcen:

  1. „Der Android ION-Speicherzuweiser“ https://lwn.net/Articles/480055/
  2. „Android ION-Übersicht“ https://wiki.linaro.org/BenjaminGaignard/ion
  3. „Integration des ION-Speicherzuweisers“ 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 dazu darin, die Größe des MMAP-Puffers als negative Zahl zu übergeben, z. B. -2048 statt 2048, sofern 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 erfordert einen zusätzlichen Datenpfad am Audio-Frontend des Audio-Subsystems, damit es parallel zum ursprünglichen AudioFlinger-Pfad arbeiten kann. Dieser Legacy-Pfad wird für alle anderen Systemsounds und Anwendungssounds verwendet. Diese Funktionalität könnte von einem Software-Mixer in einem DSP oder einem Hardware-Mixer im SOC bereitgestellt werden.

Aktivieren Sie den AAudio MMAP-Datenpfad

AAudio verwendet den alten AudioFlinger-Datenpfad, wenn MMAP nicht unterstützt wird oder kein Stream geöffnet werden 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 testen oder lediglich den alten 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 alten Weg zurück.

Diese können im Makefile des Geräts festgelegt werden, etwa so:

# 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 nach dem Hochfahren des Geräts überschreiben. 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

In ndk/sysroot/usr/include/aaudio/AAudioTesting.h stehen Funktionen zur Verfügung, 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);