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 является новым родным C API , который обеспечивает альтернативу 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 AAudio использует область памяти, которая совместно используется оборудованием и аудио службой. Обращение к общей памяти осуществляется с помощью файлового дескриптора, который создается драйвером ALSA.

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

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

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

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/

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.

Включение пути к данным 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);