AAudio — это аудио API, представленный в версии Android 8.0. В версии Android 8.1 есть улучшения, позволяющие уменьшить задержку при использовании в сочетании с HAL и драйвером, поддерживающим MMAP. В этом документе описываются уровень абстракции оборудования (HAL) и изменения драйверов, необходимые для поддержки функции MMAP AAudio в Android.
Для поддержки AAudio MMAP требуется:
- отчет о возможностях MMAP HAL
- реализация новых функций в HAL
- опционально реализация пользовательского ioctl() для буфера режима EXCLUSIVE
- предоставление дополнительного аппаратного пути к данным
- настройка системных свойств, которые включают функцию MMAP
Аудио архитектура
AAudio — это новый собственный API C, предоставляющий альтернативу Open SL ES. Для создания аудиопотоков он использует шаблон проектирования Builder.
AAudio обеспечивает путь передачи данных с малой задержкой. В режиме EXCLUSIVE эта функция позволяет коду клиентского приложения записывать непосредственно в буфер, отображаемый в памяти, который используется совместно с драйвером ALSA. В режиме SHARED буфер MMAP используется микшером, работающим на AudioServer. В режиме EXCLUSIVE задержка значительно меньше, поскольку данные обходят микшер.
В режиме EXCLUSIVE служба запрашивает буфер MMAP у HAL и управляет ресурсами. Буфер MMAP работает в режиме NOIRQ, поэтому общие счетчики чтения/записи для управления доступом к буферу отсутствуют. Вместо этого клиент поддерживает временную модель оборудования и прогнозирует, когда будет прочитан буфер.
На диаграмме ниже мы видим, как данные импульсно-кодовой модуляции (PCM) проходят через MMAP FIFO в драйвер 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-аудио 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.
Для поддержки режима EXCLUSIVE необходимо преобразовать дескриптор /dev/snd/
в файловый дескриптор anon_inode:dmabuf
. SELinux позволяет передавать этот файловый дескриптор клиенту. Его также может использовать AAudioService.
Дескриптор файла anon_inode:dmabuf
можно создать с помощью библиотеки памяти Android Ion.
Дополнительную информацию см. на следующих внешних ресурсах:
- «Распределитель памяти Android ION» https://lwn.net/Articles/480055/
- «Обзор Android ION» https://wiki.linaro.org/BenjaminGaignard/ion
- «Интеграция распределителя памяти 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. Этот устаревший путь используется для всех остальных системных звуков и звуков приложений. Эта функциональность может быть обеспечена программным микшером в DSP или аппаратным микшером в SOC.
Включить путь к данным 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
Вы также можете переопределить эти значения после загрузки устройства. Вам нужно будет перезапустить аудиосервер, чтобы изменения вступили в силу. Например, чтобы включить режим АВТО для 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);