Audio i MMAP

AAudio to interfejs API audio wprowadzony w wersji Androida 8.0. W wersji Android 8.1 wprowadzono ulepszenia zmniejszające opóźnienia, gdy jest używane 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 HAL
  • wdrażanie nowych funkcji w HAL
  • opcjonalnie implementacja niestandardowego ioctl() dla bufora trybu EXCLUSIVE
  • zapewnienie dodatkowej ścieżki danych sprzętowych
  • ustawianie właściwości systemu, które włączają funkcję MMAP

Architektura audio

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

AAudio zapewnia ścieżkę danych o niskim opóźnieniu. W trybie EXCLUSIVE funkcja ta umożliwia bezpośredni zapis kodu aplikacji klienta w buforze mapowanym w pamięci, który jest współdzielony ze sterownikiem ALSA. W trybie SHARED bufor MMAP jest używany przez mikser działający w AudioServer. W trybie EXCLUSIVE opóźnienie jest znacznie mniejsze, ponieważ dane omijają mikser.

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

Na poniższym schemacie możemy zobaczyć dane modulacji kodu impulsowego (PCM) przepływające przez MMAP FIFO do sterownika ALSA. Sygnatury czasowe są okresowo żądane przez usługę AAudio, a następnie przekazywane do modelu czasu 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 działa on w usłudze AAudioService.

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

Zmiany HAL

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

Obsługa raportowania 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ę HAL dźwięku. (patrz „Włączanie ścieżki danych Aaudio MMAP” poniżej).

Plik audio_policy_configuration.xml musi również zawierać profil wyjściowy i wejściowy specyficzny dla trybu MMAP/NO IRQ, aby Menedżer zasad dźwiękowych 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>

Otwieranie i zamykanie strumienia MMAP

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

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

Pytanie o pozycję MMAP

Znacznik czasu przekazany z powrotem do modelu czasowego zawiera pozycję klatki i czas MONOTONIC w nanosekundach:

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

HAL może uzyskać te informacje ze 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 region 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/ , to może być używany przez usługę Aaudio w trybie SHARED. Ale 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ługiwać tryb EXCLUSIVE, konieczne jest przekonwertowanie deskryptora /dev/snd/ na deskryptor pliku anon_inode:dmabuf . SELinux umożliwia przekazanie tego deskryptora pliku do klienta. Może być również używany przez AAudioService.

Przy użyciu biblioteki pamięci systemu Android Ion można wygenerować deskryptor pliku anon_inode:dmabuf .

Aby uzyskać dodatkowe informacje, zobacz te zasoby zewnętrzne:

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

Zmiany HAL

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, dzięki czemu może 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. Ta funkcjonalność może być zapewniona przez mikser programowy w DSP lub mikser sprzętowy w SOC.

Włączanie ścieżki danych Audio MMAP

Aaudio użyje starszej ścieżki danych AudioFlinger, jeśli MMAP nie jest obsługiwany lub nie można otworzyć strumienia. Tak więc 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 wykonać zapytanie o ścieżkę używaną przez strumień.

Właściwości systemu

Zasady MMAP można ustawić za pomocą 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 do starszej ścieżki.

Można je ustawić w Makefile urządzeń, na przykład:

# 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 również zmienić 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 dostępne są funkcje, które pozwalają nadpisać zasady korzystania ze ścieżki MMAP:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

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

bool AAudioStream_isMMapUsed(AAudioStream* stream);