AAudio и MMAP

AAudio - это аудио API, представленный в версии Android 8.0. В выпуске Android 8.1 есть улучшения для уменьшения задержки при использовании вместе с HAL и драйвером, поддерживающим MMAP. В этом документе описывается уровень абстракции оборудования (HAL) и изменения драйверов, необходимые для поддержки функции MMAP AAudio в Android.

Для поддержки AAudio MMAP требуется:

  • отчет о возможностях MMAP HAL
  • внедрение новых функций в HAL
  • при желании реализовать собственный ioctl () для буфера режима EXCLUSIVE
  • предоставление дополнительного аппаратного пути к данным
  • установка свойств системы, которые включают функцию MMAP

Архитектура AAudio

AAudio - это новый собственный API C, который предоставляет альтернативу Open SL ES. Он использует шаблон проектирования Builder для создания аудиопотоков.

AAudio обеспечивает путь передачи данных с малой задержкой. В режиме EXCLUSIVE эта функция позволяет коду клиентского приложения записывать непосредственно в буфер с отображением памяти, который используется совместно с драйвером ALSA. В режиме SHARED буфер MMAP используется микшером, работающим в AudioServer. В режиме EXCLUSIVE задержка значительно меньше, так как данные обходит микшер.

В режиме EXCLUSIVE служба запрашивает буфер MMAP у HAL и управляет ресурсами. Буфер MMAP работает в режиме NOIRQ, поэтому нет общих счетчиков чтения / записи для управления доступом к буферу. Вместо этого клиент поддерживает временную модель оборудования и предсказывает, когда будет прочитан буфер.

На диаграмме ниже мы можем видеть, как данные импульсно-кодовой модуляции (PCM) проходят через MMAP FIFO в драйвер ALSA. Временные метки периодически запрашиваются службой AAudio, а затем передаются модели синхронизации клиента через атомарную очередь сообщений.

Схема потока данных PCM.
Рисунок 1. Поток данных PCM через FIFO в ALSA.

В режиме 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);

Изменения ядра

Включите в драйвере режим MMAP / NOIRQ.

Обращение к общей памяти осуществляется с помощью файлового дескриптора, который создается драйвером ALSA. Если файловый дескриптор напрямую связан с файлом /dev/snd/ driver, то он может использоваться службой AAudio в режиме SHARED. Но дескриптор не может быть передан клиентскому коду для ИСКЛЮЧИТЕЛЬНОГО режима. Дескриптор /dev/snd/ file предоставит клиенту слишком широкий доступ, поэтому он блокируется SELinux.

Для поддержки режима EXCLUSIVE необходимо преобразовать дескриптор /dev/snd/ в файловый дескриптор anon_inode:dmabuf . SELinux позволяет передать этот файловый дескриптор клиенту. Его также может использовать AAudioService.

anon_inode:dmabuf файла anon_inode:dmabuf можно создать с помощью библиотеки памяти Android Ion.

Для получения дополнительной информации см. Следующие внешние ресурсы:

  1. «Распределитель памяти Android ION» https://lwn.net/Articles/480055/
  2. «Обзор Android ION» https://wiki.linaro.org/BenjaminGaignard/ion
  3. «Интеграция распределителя памяти ION» https://lwn.net/Articles/565469/

Сервису AAudio необходимо знать, поддерживается ли этот anon_inode:dmabuf . В настоящее время единственный способ сделать это - передать размер буфера MMAP как отрицательное число, например. -2048 вместо 2048, если поддерживается. Планируется лучший способ сообщить об этом, не открывая поток.

Изменения аудиоподсистемы

AAudio требует дополнительного тракта данных во внешнем интерфейсе аудио подсистемы аудио, чтобы он мог работать параллельно с исходным трактом AudioFlinger. Этот устаревший путь используется для всех остальных системных звуков и звуков приложений. Эта функциональность может быть обеспечена программным микшером в DSP или аппаратным микшером в SOC.

Включение пути к данным MMAP AAudio

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