AAudio – это API для работы со звуком, представленный в Android 8.0. В Android 8.1 задержка уменьшена за счет использования HAL и драйвера с поддержкой MMAP. В этом документе описаны изменения, которые необходимо внести в уровень аппаратных абстракций (HAL) и драйверы, чтобы поддерживать функцию MMAP AAudio в Android.
Для поддержки AAudio MMAP требуется:
- сообщать о возможностях MMAP в HAL;
- реализация новых функций в HAL;
- при необходимости реализовать специальный ioctl() для буфера в эксклюзивном режиме;
- предоставление дополнительного пути передачи данных об аппаратном обеспечении
- настройка системных свойств, которые включают функцию MMAP;
Архитектура AAudio
AAudio – это новый собственный API на языке C, который является альтернативой Open SL ES. Для создания аудиопотоков используется шаблон проектирования Builder.
AAudio обеспечивает низкую задержку при передаче данных. В режиме EXCLUSIVE код клиентского приложения может записывать данные непосредственно в буфер, сопоставленный с памятью и используемый совместно с драйвером ALSA. В режиме SHARED буфер MMAP используется микшером, работающим в AudioServer. В эксклюзивном режиме задержка значительно меньше, поскольку данные не проходят через микшер.
В режиме EXCLUSIVE сервис запрашивает буфер MMAP у HAL и управляет ресурсами. Буфер MMAP работает в режиме NOIRQ, поэтому общих счетчиков чтения/записи для управления доступом к буферу нет. Вместо этого клиент поддерживает модель времени работы оборудования и прогнозирует, когда буфер будет прочитан.
На приведенной ниже схеме показано, как данные PCM передаются через FIFO MMAP в драйвер ALSA. Временные метки периодически запрашиваются сервисом AAudio, а затем передаются в модель синхронизации клиента через атомарную очередь сообщений.
В режиме SHARED также используется модель синхронизации, но она находится в AAudioService.
Для записи звука используется аналогичная модель, но данные PCM передаются в противоположном направлении.
Изменения HAL
Дополнительную информацию о tinyALSA можно найти на следующих страницах:
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);
Для устаревшей версии HAL:
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);
Для 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
Системное свойство aaudio.mmap_policy должно иметь значение 2 (AAUDIO_POLICY_AUTO), чтобы аудиофреймворк знал, что режим MMAP поддерживается HAL аудио. (см. раздел "Как включить путь передачи данных AAudio MMAP" ниже).
Файл audio_policy_configuration.xml также должен содержать профиль вывода и ввода, относящийся к режиму MMAP/NO IRQ, чтобы менеджер правил аудио знал, какой поток открыть при создании клиентов 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>Как открыть и закрыть поток MMAP
createMmapBuffer(int32_t minSizeFrames)
generates (Result retval, MmapBufferInfo info);Поток MMAP можно открыть и закрыть, вызвав функции Tinyalsa.
Позиция MMAP-запроса
Временная метка, возвращаемая в модель времени, содержит позицию кадра и монотонное время в наносекундах:
getMmapPosition()
generates (Result retval, MmapPosition position);HAL может получить эту информацию от драйвера ALSA, вызвав новую функцию Tinyalsa:
int pcm_mmap_get_hw_ptr(struct pcm* pcm,
unsigned int *hw_ptr,
struct timespec *tstamp);Дескрипторы файлов для общей памяти
Путь передачи данных AAudio MMAP использует область памяти, которая совместно используется оборудованием и аудиосервисом. Ссылка на общую память создается с помощью дескриптора файла, который генерируется драйвером ALSA.
Изменения ядра
Если дескриптор файла напрямую связан с файлом драйвера /dev/snd/, его можно использовать в сервисе AAudio в режиме SHARED. Однако дескриптор нельзя передать клиентскому коду для режима EXCLUSIVE. Дескриптор файла /dev/snd/ предоставляет клиенту слишком широкий доступ, поэтому он заблокирован SELinux.
Чтобы поддерживать эксклюзивный режим, необходимо преобразовать дескриптор /dev/snd/ в дескриптор файла anon_inode:dmabuf. SELinux позволяет передать этот дескриптор файла клиенту. Его также может использовать AAudioService.
Дескриптор файла anon_inode:dmabuf можно создать с помощью библиотеки памяти Android Ion.
Дополнительную информацию можно найти на следующих внешних ресурсах:
- "The Android ION memory allocator" https://lwn.net/Articles/480055/
- "Обзор Android ION" https://wiki.linaro.org/BenjaminGaignard/ion
- Integrating the ION memory allocator (Интеграция распределителя памяти ION): https://lwn.net/Articles/565469/
Изменения в HAL
Сервису AAudio нужно знать, поддерживается ли этот параметр anon_inode:dmabuf. До версии Android 10.0 размер буфера MMAP можно было передать только в виде отрицательного числа, например: -2048 вместо 2048, если поддерживается. В Android 10.0 и более поздних версий можно задать флаг AUDIO_MMAP_APPLICATION_SHAREABLE.
mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;
Изменения в аудиоподсистеме
Для работы AAudio требуется дополнительный путь передачи данных в интерфейсе пользователя аудиоподсистемы, чтобы он мог работать параллельно с исходным путем AudioFlinger. Этот устаревший путь используется для всех остальных системных и прикладных звуков. Эта функция может быть реализована с помощью программного микшера в процессоре цифровой обработки сигналов или аппаратного микшера в системе на кристалле.
Как включить путь передачи данных AAudio MMAP
AAudio будет использовать устаревший путь передачи данных AudioFlinger, если MMAP не поддерживается или не удается открыть поток. Таким образом, AAudio будет работать с аудиоустройством, которое не поддерживает путь MMAP/NOIRQ.
При тестировании поддержки MMAP для AAudio важно знать, тестируете ли вы путь передачи данных MMAP или устаревший путь передачи данных. Ниже описано, как включить или принудительно задать определенные пути передачи данных, а также как запросить путь, используемый потоком.
Свойства системы
Правила MMAP можно настроить с помощью системных свойств:
- 1 = AAUDIO_POLICY_NEVER – использовать только устаревший путь. Не пытайтесь использовать MMAP.
- 2 = AAUDIO_POLICY_AUTO – попытаться использовать MMAP. Если это не удастся или функция недоступна, используйте устаревший путь.
- 3 = AAUDIO_POLICY_ALWAYS – использовать только путь MMAP. Не возвращаться к устаревшему пути.
Их можно задать в файле Makefile устройства, как показано ниже:
# 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
Вы также можете переопределить эти значения после загрузки устройства. Чтобы изменения вступили в силу, вам нужно будет перезапустить аудиосервер. Например, чтобы включить режим AUTO для MMAP:
adb root
adb shell setprop aaudio.mmap_policy 2
adb shell killall audioserver
В ndk/sysroot/usr/include/aaudio/AAudioTesting.h есть функции, которые позволяют переопределить правила использования пути MMAP:
aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);
Чтобы узнать, использует ли поток путь MMAP, вызовите:
bool AAudioStream_isMMapUsed(AAudioStream* stream);