Audio i MMAP

AAudio to interfejs API audio wprowadzony w wersji Androida 8.0. Wersja Androida 8.1 zawiera ulepszenia zmniejszające opóźnienia w przypadku używania w połączeniu z warstwą HAL i sterownikiem obsługującym MMAP. W tym dokumencie opisano warstwę abstrakcji sprzętu (HAL) i zmiany sterowników potrzebne do obsługi funkcji MMAP AAudio w systemie Android.

Obsługa AAudio MMAP wymaga:

  • raportowanie możliwości MMAP warstwy HAL
  • wdrażanie nowych funkcji w HAL
  • opcjonalnie implementując niestandardową funkcję ioctl() dla bufora trybu EXCLUSIVE
  • zapewniając dodatkową sprzętową ścieżkę danych
  • ustawianie właściwości systemu, które włączają funkcję MMAP

ARarchitektura audio

AAudio to nowy natywny interfejs API w języku C, który stanowi alternatywę dla Open SL ES. Wykorzystuje wzorzec projektowy Buildera do tworzenia strumieni audio.

AAudio zapewnia ścieżkę danych o niskim opóźnieniu. W trybie EXCLUSIVE funkcja umożliwia zapisywanie kodu aplikacji klienckiej bezpośrednio w buforze odwzorowanym w pamięci, który jest współdzielony ze sterownikiem ALSA. W trybie SHARED bufor MMAP wykorzystywany jest przez mikser działający w AudioServerze. W trybie EXCLUSIVE opóźnienie jest znacznie mniejsze, ponieważ dane omijają mikser.

W trybie EXCLUSIVE usługa żąda bufora MMAP od HAL i zarządza zasobami. Bufor MMAP działa w trybie NOIRQ, więc nie ma wspólnych liczników odczytu/zapisu zarządzających dostępem do bufora. Zamiast tego klient utrzymuje model taktowania sprzętu i przewiduje, kiedy bufor zostanie odczytany.

Na poniższym schemacie możemy zobaczyć dane modulacji impulsowo-kodowej (PCM) przepływające przez MMAP FIFO do sterownika ALSA. Znaczniki czasu są okresowo żądane przez usługę AAudio, a następnie przekazywane do modelu synchronizacji klienta za pośrednictwem niepodzielnej kolejki komunikatów.

Schemat przepływu danych PCM.
Rysunek 1. Przepływ danych PCM przez FIFO do ALSA

W trybie SHARED używany jest również model taktowania, ale znajduje się on w AAudioService.

Do przechwytywania dźwięku używany jest podobny model, ale dane PCM przepływają w przeciwnym kierunku.

HAL się zmienia

Dla tinyALSA zobacz:

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

Aby zapoznać się ze starszą wersją HAL, zobacz:

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

Dla dźwięku 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);

Zgłoś wsparcie MMAP

Właściwość systemowa „aaudio.mmap_policy” powinna być ustawiona na 2 (AAUDIO_POLICY_AUTO), aby struktura audio wiedziała, że ​​tryb MMAP jest obsługiwany przez warstwę audio HAL. (patrz „Włączanie ścieżki danych AAudio MMAP” poniżej.)

Plik audio_policy_configuration.xml musi także zawierać profil wyjściowy i wejściowy specyficzny dla trybu MMAP/NO IRQ, aby Menedżer zasad audio wiedział, który strumień otworzyć podczas tworzenia klientów 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>

Otwórz i zamknij strumień MMAP

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

Strumień MMAP można otwierać i zamykać, wywołując funkcje Tinyalsa.

Zapytanie o pozycję MMAP

Znacznik czasu przekazywany z powrotem do modelu synchronizacji zawiera pozycję ramki i czas MONOTONICZNY w nanosekundach:

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

HAL może uzyskać te informacje od sterownika ALSA, wywołując nową funkcję Tinyalsa:

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

Deskryptory plików dla pamięci współdzielonej

Ścieżka danych AAudio MMAP wykorzystuje obszar pamięci współdzielony przez sprzęt i usługę audio. Do pamięci współdzielonej odwołuje się deskryptor pliku generowany przez sterownik ALSA.

Zmiany jądra

Jeśli deskryptor pliku jest bezpośrednio powiązany z plikiem sterownika /dev/snd/ , wówczas może być używany przez usługę AAudio w trybie SHARED. Jednak deskryptora nie można przekazać do kodu klienta w trybie EXCLUSIVE. Deskryptor pliku /dev/snd/ zapewniałby zbyt szeroki dostęp do klienta, więc jest blokowany przez SELinux.

Aby obsłużyć tryb EXCLUSIVE, konieczna jest konwersja deskryptora /dev/snd/ na deskryptor pliku anon_inode:dmabuf . SELinux pozwala na przekazanie tego deskryptora pliku do klienta. Może być również używany przez AAudioService.

Deskryptor pliku anon_inode:dmabuf można wygenerować przy użyciu biblioteki pamięci Android Ion.

Dodatkowe informacje można znaleźć w tych zasobach zewnętrznych:

  1. „Alokator pamięci Android ION” https://lwn.net/Articles/480055/
  2. „Przegląd Androida ION” https://wiki.linaro.org/BenjaminGaignard/ion
  3. „Integracja alokatora pamięci ION” https://lwn.net/Articles/565469/

HAL się zmienia

Usługa AAudio musi wiedzieć, czy ten anon_inode:dmabuf jest obsługiwany. Przed Androidem 10.0 jedynym sposobem na to było przekazanie rozmiaru bufora MMAP jako liczby ujemnej, np. -2048 zamiast 2048, jeśli jest obsługiwane. W Androidzie 10.0 i nowszych możesz ustawić flagę AUDIO_MMAP_APPLICATION_SHAREABLE .

mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;

Zmiany w podsystemie audio

AAudio wymaga dodatkowej ścieżki danych na przednim końcu audio podsystemu audio, aby mógł działać równolegle z oryginalną ścieżką AudioFlinger. Ta starsza ścieżka jest używana dla wszystkich innych dźwięków systemowych i dźwięków aplikacji. Funkcjonalność tę można zapewnić za pomocą miksera programowego w procesorze DSP lub miksera sprzętowego w SOC.

Włącz ścieżkę danych AAudio MMAP

AAudio użyje starszej ścieżki danych AudioFlinger, jeśli MMAP nie jest obsługiwany lub nie otworzy strumienia. Zatem AAudio będzie działać z urządzeniem audio, które nie obsługuje ścieżki MMAP/NOIRQ.

Podczas testowania obsługi MMAP dla AAudio ważne jest, aby wiedzieć, czy faktycznie testujesz ścieżkę danych MMAP, czy tylko testujesz starszą ścieżkę danych. Poniżej opisano, jak włączyć lub wymusić określone ścieżki danych oraz jak wysyłać zapytania dotyczące ścieżki używanej przez strumień.

Właściwości systemu

Możesz ustawić politykę MMAP poprzez właściwości systemu:

  • 1 = AAUDIO_POLICY_NEVER — używaj tylko starszej ścieżki. Nawet nie próbuj używać MMAP.
  • 2 = AAUDIO_POLICY_AUTO - Spróbuj użyć MMAP. Jeśli to się nie powiedzie lub nie jest dostępne, użyj starszej ścieżki.
  • 3 = AAUDIO_POLICY_ALWAYS — używaj tylko ścieżki MMAP. Nie wracaj na starą ścieżkę.

Można je ustawić w urządzeniu Makefile w następujący sposób:

# 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

Możesz także zastąpić te wartości po uruchomieniu urządzenia. Aby zmiana zaczęła obowiązywać, konieczne będzie ponowne uruchomienie serwera audio. Na przykład, aby włączyć tryb AUTO dla MMAP:

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

W ndk/sysroot/usr/include/aaudio/AAudioTesting.h znajdują się funkcje, które umożliwiają obejście zasad używania ścieżki MMAP:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

Aby dowiedzieć się, czy strumień korzysta ze ścieżki MMAP, zadzwoń:

bool AAudioStream_isMMapUsed(AAudioStream* stream);