AAudio dan MMAP

AAudio adalah API audio yang diperkenalkan dalam rilis Android 8.0. Rilis Android 8.1 memiliki peningkatan untuk mengurangi latensi saat digunakan bersama dengan HAL dan driver yang mendukung MMAP. Dokumen ini menjelaskan hardware abstraction layer (HAL) dan perubahan driver yang diperlukan untuk mendukung fitur MMAP AAudio di Android.

Dukungan untuk AAudio MMAP memerlukan:

  • melaporkan kemampuan MMAP HAL
  • menerapkan fungsi baru di HAL
  • secara opsional menerapkan ioctl() kustom untuk buffering mode EXCLUSIVE
  • menyediakan jalur data hardware tambahan
  • menetapkan properti sistem yang mengaktifkan fitur MMAP

Arsitektur AAudio

AAudio adalah API C native baru yang menyediakan alternatif untuk Open SL ES. Class ini menggunakan pola desain Builder untuk membuat streaming audio.

AAudio menyediakan jalur data latensi rendah. Dalam mode EXCLUSIVE, fitur ini memungkinkan kode aplikasi klien menulis langsung ke buffer yang dipetakan memori yang dibagikan dengan driver ALSA. Dalam mode SHARED, buffering MMAP digunakan oleh mixer yang berjalan di AudioServer. Dalam mode EXCLUSIVE, latensi akan jauh lebih rendah karena data mengabaikan mixer.

Dalam mode EKSKLUSIF, layanan meminta buffer MMAP dari HAL dan mengelola resource. Buffer MMAP berjalan dalam mode NOIRQ, sehingga tidak ada penghitung baca/tulis bersama untuk mengelola akses ke buffer. Sebagai gantinya, klien mempertahankan model pengaturan waktu hardware dan memprediksi kapan buffering akan dibaca.

Pada diagram di bawah, kita dapat melihat data Pulse-code modulation (PCM) yang mengalir ke bawah melalui MMAP FIFO ke dalam driver ALSA. Stempel waktu secara berkala diminta oleh layanan AAudio, lalu diteruskan ke model pengaturan waktu klien melalui antrean pesan atom.

Diagram alur data PCM.
Gambar 1. Aliran data PCM melalui FIFO ke ALSA

Dalam mode SHARED, model pengaturan waktu juga digunakan, tetapi berada di AAudioService.

Untuk perekaman audio, model serupa digunakan, tetapi data PCM mengalir dalam arah yang berlawanan.

Perubahan HAL

Untuk tinyALSA, lihat:

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

Untuk HAL lama, lihat:

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

Untuk HAL audio HIDL:

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

Melaporkan dukungan MMAP

Properti sistem "aaudio.mmap_policy" harus ditetapkan ke 2 (AAUDIO_POLICY_AUTO) sehingga framework audio mengetahui bahwa mode MMAP didukung oleh HAL audio. (lihat "Mengaktifkan Jalur Data MMAP AAudio" di bawah.)

File audio_policy_configuration.xml juga harus berisi profil output dan input yang khusus untuk mode MMAP/NO IRQ sehingga Pengelola Kebijakan Audio mengetahui streaming mana yang akan dibuka saat klien MMAP dibuat:

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

Membuka dan menutup streaming MMAP

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

Streaming MMAP dapat dibuka dan ditutup dengan memanggil fungsi Tinyalsa.

Membuat kueri posisi MMAP

Stempel waktu yang diteruskan kembali ke Model Pengaturan Waktu berisi posisi frame dan waktu MONOTONIC dalam nanodetik:

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

HAL dapat memperoleh informasi ini dari driver ALSA dengan memanggil fungsi Tinyalsa baru:

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

Deskripsi file untuk memori bersama

Jalur data MMAP AAudio menggunakan region memori yang dibagikan antara hardware dan layanan audio. Memori bersama direferensikan menggunakan deskriptor file yang dihasilkan oleh driver ALSA.

Perubahan kernel

Jika deskripsi file dikaitkan secara langsung dengan file driver /dev/snd/, file tersebut dapat digunakan oleh layanan AAudio dalam mode SHARED. Namun, deskripsi tidak dapat diteruskan ke kode klien untuk mode EKSKLUSIF. Deskripsi file /dev/snd/ akan memberikan akses yang terlalu luas ke klien, sehingga diblokir oleh SELinux.

Untuk mendukung mode EXCLUSIVE, Anda perlu mengonversi deskripsi /dev/snd/ menjadi deskripsi file anon_inode:dmabuf. SELinux memungkinkan deskripsi file tersebut diteruskan ke klien. AAudioService juga dapat digunakan.

Deskripsi file anon_inode:dmabuf dapat dibuat menggunakan library memori Android Ion.

Untuk informasi tambahan, lihat referensi eksternal berikut:

  1. "The Android ION memory allocator" https://lwn.net/Articles/480055/
  2. "Ringkasan ION Android" https://wiki.linaro.org/BenjaminGaignard/ion
  3. "Integrating the ION memory allocator" https://lwn.net/Articles/565469/

Perubahan HAL

Layanan AAudio perlu mengetahui apakah anon_inode:dmabuf ini didukung. Sebelum Android 10.0, satu-satunya cara untuk melakukannya adalah dengan meneruskan ukuran buffer MMAP sebagai bilangan negatif, misalnya. -2048, bukan 2048, jika didukung. Di Android 10.0 dan yang lebih baru, Anda dapat menetapkan tanda AUDIO_MMAP_APPLICATION_SHAREABLE.

mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;

Perubahan subsistem audio

AAudio memerlukan jalur data tambahan di frontend audio subsistem audio sehingga dapat beroperasi secara paralel dengan jalur AudioFlinger asli. Jalur lama tersebut digunakan untuk semua suara sistem dan suara aplikasi lainnya. Fungsi ini dapat disediakan oleh mixer software di DSP atau mixer hardware di SOC.

Mengaktifkan jalur data MMAP AAudio

AAudio akan menggunakan jalur data AudioFlinger lama jika MMAP tidak didukung atau gagal membuka streaming. Jadi, AAudio akan berfungsi dengan perangkat audio yang tidak mendukung jalur MMAP/NOIRQ.

Saat menguji dukungan MMAP untuk AAudio, penting untuk mengetahui apakah Anda sebenarnya menguji jalur data MMAP atau hanya menguji jalur data lama. Berikut ini penjelasan cara mengaktifkan atau memaksa jalur data tertentu, dan cara membuat kueri jalur yang digunakan oleh streaming.

Properti sistem

Anda dapat menetapkan kebijakan MMAP melalui properti sistem:

  • 1 = AAUDIO_POLICY_NEVER - Hanya gunakan jalur lama. Jangan coba gunakan MMAP.
  • 2 = AAUDIO_POLICY_AUTO - Mencoba menggunakan MMAP. Jika gagal atau tidak tersedia, gunakan jalur lama.
  • 3 = AAUDIO_POLICY_ALWAYS - Hanya gunakan jalur MMAP. Jangan kembali ke jalur lama.

Ini dapat ditetapkan di Makefile perangkat, seperti ini:

# 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

Anda juga dapat mengganti nilai ini setelah perangkat melakukan booting. Anda harus memulai ulang audioserver agar perubahan diterapkan. Misalnya, untuk mengaktifkan mode AUTO untuk MMAP:

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

Ada fungsi yang disediakan di ndk/sysroot/usr/include/aaudio/AAudioTesting.h yang memungkinkan Anda mengganti kebijakan untuk menggunakan jalur MMAP:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

Untuk mengetahui apakah streaming menggunakan jalur MMAP, panggil:

bool AAudioStream_isMMapUsed(AAudioStream* stream);