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.
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:
- "O alocador de memória do Android ION" https://lwn.net/Articles/480055/ (link em inglês)
- "Visão geral do Android ION" https://wiki.linaro.org/BenjaminGaignard/ion (em inglês)
- "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);