AAudio e MMAP

A AAudio é uma API de áudio introduzida na versão do Android 8.0. O Android 8.1 tem melhorias para reduzir a latência quando usada com uma HAL e drivers compatíveis com MMAP. Este documento descreve a abstração de hardware (HAL) e alterações de driver necessárias para oferecer suporte ao recurso MMAP da AAudio em Android

A compatibilidade com o MMAP da AAudio requer:

  • informar os recursos de MMAP da HAL
  • implementar novas funções na HAL
  • a implementação de um ioctl() personalizado para o buffer de modo EXCLUSIVO
  • fornecendo um caminho de dados de hardware adicional
  • definir propriedades do sistema que ativam o recurso MMAP
.

Arquitetura da AAudio

AAudio (link em inglês) é uma nova API em C nativa que fornece uma alternativa ao Open SL ES. Ele usa uma Builder para criar streams de áudio.

A AAudio oferece um caminho de dados de baixa latência. No modo EXCLUSIVO, o recurso permite que o código do aplicativo cliente grave diretamente em um buffer mapeado de memória compartilhada com o driver do ALSA. No modo SHARED, o buffer MMAP é usado pelo um mixer em execução no AudioServer. No modo EXCLUSIVO, a latência é de significativamente menos, porque os dados ignoram o misturador.

No modo EXCLUSIVO, o serviço solicita o buffer MMAP da HAL e gerencia os recursos. Como o buffer MMAP está sendo executado no modo NOIRQ, não há arquivos contadores de leitura/gravação para gerenciar o acesso ao buffer. Em vez disso, o cliente mantém um modelo de tempo do hardware e prevê quando o buffer será ler.

No diagrama abaixo, podemos ver os dados de modulação de código de pulso (PCM) fluindo do MMAP FIFO para o driver ALSA. As marcações de tempo são periódicas solicitado pelo serviço da AAudio e depois passado ao modelo de marcação de tempo do cliente por uma fila atômica de mensagens.

Diagrama de fluxo de dados do PCM.
Figura 1. Fluxo de dados PCM pelo FIFO para o ALSA

No modo SHARED, um modelo de tempo também é usado, mas fica no AAudioService.

Para a captura de áudio, é usado um modelo semelhante, mas os dados em PCM fluem na direção oposta.

Mudanças na HAL

Para tinyALSA consulte:

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

Para a HAL legada, consulte:

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

Para HAL de áudio 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);

Informar suporte ao MMAP

Propriedade do sistema "aaudio.mmap_policy" deve ser definido como 2 (AAUDIO_POLICY_AUTO), portanto, a estrutura de áudio sabe que o modo MMAP é compatível com a HAL de áudio. (consulte "Ativar o caminho de dados MMAP da AAudio" abaixo.

O arquivo audio_policy_configuration.xml também precisa conter uma saída e uma entrada específico para o modo MMAP/NO IRQ para que o Gerenciador de políticas de áudio saiba qual fluxo deve ser aberto quando os clientes MMAP são criados:

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

Abrir e fechar um stream MMAP

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

O fluxo MMAP pode ser aberto e fechado chamando funções Tinyalsa.

Posição do MMAP da consulta

O carimbo de data/hora transmitido ao modelo de velocidade contém uma posição de frame e uma Tempo MONOTONIC em nanossegundos:

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

A HAL pode obter essas informações do driver ALSA chamando um novo Função Tinyalsa:

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

Descritores de arquivos para memória compartilhada

O caminho dos dados MMAP da AAudio usa uma região de memória compartilhada entre as o hardware e o serviço de áudio. A memória compartilhada é referenciada por um descritor de arquivo gerado pelo driver do ALSA.

Mudanças do kernel

Se o descritor do arquivo estiver diretamente associado a uma /dev/snd/, ele poderá ser usado pelo serviço AAudio em Modo COMPARTILHADO. Mas o descritor não pode ser passado ao código do cliente para Modo EXCLUSIVO. O descritor de arquivo /dev/snd/ também forneceria amplo de acesso ao cliente, portanto, é bloqueado pelo SELinux.

Para oferecer suporte ao modo EXCLUSIVO, é necessário converter o Descritor /dev/snd/ para um arquivo anon_inode:dmabuf descritor. O SELinux permite que esse descritor de arquivo seja passado para o cliente. Ela também podem ser usados pelo AAudioService.

Um descritor de arquivo anon_inode:dmabuf pode ser gerado usando a biblioteca de memória do Android Ion.

Para mais informações, acesse estes recursos externos:

  1. "O alocador de memória do Android ION" https://lwn.net/Articles/480055/ (link em inglês)
  2. "Visão geral do Android ION" https://wiki.linaro.org/BenjaminGaignard/ion (em inglês)
  3. "Como integrar o alocador de memória ION" https://lwn.net/Articles/565469/ (link em inglês)

Mudanças na HAL

O serviço da AAudio precisa saber se o anon_inode:dmabuf está suporte. Antes do Android 10.0, a única maneira de fazer isso era transmitir o tamanho do MMAP buffer como um número negativo, por exemplo, -2048 em vez de 2048, se compatível. No Android 10.0 e mais recentes defina a sinalização AUDIO_MMAP_APPLICATION_SHAREABLE.

mmapBufferInfo |= AUDIO_MMAP_APPLICATION_SHAREABLE;

Mudanças no subsistema de áudio

A AAudio exige um caminho de dados adicional no front-end do áudio para que possa operar em paralelo com o caminho original do AudioFlinger. Esse caminho legado é usado para todos os outros sons do sistema e do aplicativo. Essa funcionalidade pode ser fornecida por um mixer de software em uma DSP ou um no SOC.

Ativar caminho de dados MMAP da AAudio

A AAudio usará o caminho de dados legado do AudioFlinger se o MMAP não for compatível ou não consegue abrir um stream. Portanto, a AAudio funciona com um dispositivo de áudio que não suporte ao caminho MMAP/NOIRQ.

Ao testar o suporte a MMAP para AAudio, é importante saber se você está testar o caminho dos dados MMAP ou simplesmente testar o caminho dos dados legados. A a seguir, descrevemos como ativar ou forçar caminhos de dados específicos e como o caminho usado por um stream.

Propriedades do sistema

É possível definir a política MMAP por meio das propriedades do sistema:

  • 1 = AAUDIO_POLICY_NEVER - Usar apenas o caminho legado. Nem tente usar o MMAP.
  • 2 = AAUDIO_POLICY_AUTO - Tente usar MMAP. Se isso falhar ou não estiver disponível, use o caminho legado.
  • 3 = AAUDIO_POLICY_ALWAYS - usar apenas o caminho MMAP. Não volte para a versão legada caminho.

Eles podem ser definidos no Makefile dos dispositivos da seguinte forma:

# 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

Também é possível substituir esses valores após a inicialização do dispositivo. É necessário reiniciar o audioserver para que a alteração entre em vigor. Por exemplo, para ativar o modo AUTOMÁTICO para MMAP:

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

Há funções fornecidas ndk/sysroot/usr/include/aaudio/AAudioTesting.h, que permitem que você substitua a política por usar o caminho MMAP:

aaudio_result_t AAudio_setMMapPolicy(aaudio_policy_t policy);

Para descobrir se um stream está usando o caminho MMAP, chame:

bool AAudioStream_isMMapUsed(AAudioStream* stream);