Este artigo descreve algumas dicas e truques para depurar áudio do Android.
Pia
O "tee sink" é um recurso de depuração do AudioFlinger, disponível apenas em compilações personalizadas, para reter um pequeno fragmento de áudio recente para análise posterior. Isto permite a comparação entre o que foi realmente reproduzido ou gravado e o que era esperado.
Para privacidade, o tee sink está desabilitado por padrão, tanto em tempo de compilação quanto em tempo de execução. Para usar o coletor tee, você precisará habilitá-lo recompilando e também definindo uma propriedade. Certifique-se de desabilitar esse recurso após concluir a depuração; o coletor tee não deve ser deixado ativado em compilações de produção.
As instruções nesta seção são para Android 7.xe superior. Para Android 5.xe 6.x, substitua /data/misc/audioserver
por /data/misc/media
. Além disso, você deve usar um userdebug ou eng build. Se você usar uma compilação userdebug, desative a verificação com:
adb root && adb disable-verity && adb reboot
Configuração em tempo de compilação
-
cd frameworks/av/services/audioflinger
- Editar
Configuration.h
. - Remova o comentário
#define TEE_SINK
. - Reconstrua
libaudioflinger.so
. -
adb root
-
adb remount
- Envie ou sincronize o novo
libaudioflinger.so
com o/system/lib
do dispositivo.
Configuração em tempo de execução
-
adb shell getprop | grep ro.debuggable
Confirme se a saída é:[ro.debuggable]: [1]
-
adb shell
-
ls -ld /data/misc/audioserver
Confirme se a saída é:
drwx------ media media ... media
Se o diretório não existir, crie-o da seguinte forma:
mkdir /data/misc/audioserver
chown media:media /data/misc/audioserver
-
echo af.tee=# > /data/local.prop
Onde o valoraf.tee
é um número descrito abaixo. -
chmod 644 /data/local.prop
-
reboot
Valores para propriedade af.tee
O valor de af.tee
é um número entre 0 e 7, expressando a soma de vários bits, um por recurso. Veja o código em AudioFlinger::AudioFlinger()
em AudioFlinger.cpp
para uma explicação de cada bit, mas brevemente:
- 1 = entrada
- 2 = Saída FastMixer
- 4 = AudioRecord e AudioTrack por trilha
Ainda não há espaço para buffer profundo ou mixer normal, mas você pode obter resultados semelhantes usando "4".
Teste e adquira dados
- Execute seu teste de áudio.
-
adb shell dumpsys media.audio_flinger
- Procure uma linha na saída
dumpsys
como esta:
tee copied to /data/misc/audioserver/20131010101147_2.wav
Este é um arquivo PCM .wav. - Em seguida,
adb pull
quaisquer arquivos/data/misc/audioserver/*.wav
de interesse; observe que os nomes de arquivos de despejo específicos da trilha não aparecem na saídadumpsys
, mas ainda são salvos em/data/misc/audioserver
após o fechamento da trilha. - Revise os arquivos de despejo em busca de questões de privacidade antes de compartilhá-los com outras pessoas.
Sugestões
Experimente estas ideias para obter resultados mais úteis:
- Desative sons de toque e cliques de teclas para reduzir interrupções na saída do teste.
- Maximize todos os volumes.
- Desative aplicativos que emitem som ou gravam do microfone, caso não sejam do interesse do seu teste.
- Os dumps específicos da trilha só são salvos quando a trilha é fechada; pode ser necessário forçar o fechamento de um aplicativo para despejar seus dados específicos da faixa
- Faça o
dumpsys
imediatamente após o teste; há uma quantidade limitada de espaço de gravação disponível. - Para ter certeza de não perder seus arquivos de despejo, carregue-os periodicamente em seu host. Somente um número limitado de arquivos de despejo é preservado; dumps mais antigos são removidos depois que esse limite é atingido.
Restaurar
Conforme observado acima, o recurso tee sink não deve ser deixado ativado. Restaure sua compilação e dispositivo da seguinte maneira:
- Reverta as alterações do código-fonte para
Configuration.h
. - Reconstrua
libaudioflinger.so
. - Envie ou sincronize o
libaudioflinger.so
restaurado com o/system/lib
do dispositivo. -
adb shell
-
rm /data/local.prop
-
rm /data/misc/audioserver/*.wav
-
reboot
mídia.log
Macros ALOGx
A API de registro da linguagem Java padrão no Android SDK é android.util.Log .
A API de linguagem C correspondente no Android NDK é __android_log_print
declarada em <android/log.h>
.
Na parte nativa da estrutura Android, preferimos macros chamadas ALOGE
, ALOGW
, ALOGI
, ALOGV
, etc. Elas são declaradas em <utils/Log.h>
e, para os fins deste artigo, iremos nos referir a elas coletivamente como ALOGx
.
Todas essas APIs são fáceis de usar e bem compreendidas, por isso estão presentes em toda a plataforma Android. Em particular, o processo mediaserver
, que inclui o servidor de som AudioFlinger, utiliza ALOGx
extensivamente.
No entanto, existem algumas limitações para ALOGx
e amigos:
- Eles são suscetíveis a "spam de log": o buffer de log é um recurso compartilhado, portanto pode facilmente transbordar devido a entradas de log não relacionadas, resultando em informações perdidas. A variante
ALOGV
está desabilitada em tempo de compilação por padrão. Mas é claro que mesmo isso pode resultar em spam de log se estiver ativado. - As chamadas de sistema do kernel subjacente podem ser bloqueadas, possivelmente resultando em inversão de prioridade e, consequentemente, em distúrbios e imprecisões de medição. Isso é especialmente preocupante para threads de tempo crítico, como
FastMixer
eFastCapture
. - Se um log específico for desativado para reduzir o spam de log, qualquer informação que teria sido capturada por esse log será perdida. Não é possível habilitar retroativamente um log específico, após ficar claro que o log teria sido interessante.
NBLOG, media.log e MediaLogService
As APIs NBLOG
e o processo media.log
associado e o serviço MediaLogService
juntos formam um sistema de registro mais recente para mídia e são projetados especificamente para resolver os problemas acima. Usaremos vagamente o termo "media.log" para nos referirmos a todos os três, mas estritamente falando, NBLOG
é a API de registro em C++, media.log
é um nome de processo do Linux e MediaLogService
é um serviço de ligação do Android para examinar os logs.
Uma "linha do tempo" media.log
é uma série de entradas de log cuja ordem relativa é preservada. Por convenção, cada thread deve usar sua própria linha do tempo.
Benefícios
Os benefícios do sistema media.log
são:
- Não envia spam para o log principal, a menos que seja necessário.
- Pode ser examinado mesmo quando
mediaserver
trava ou trava. - Não bloqueia por linha do tempo.
- Oferece menos perturbação ao desempenho. (É claro que nenhuma forma de registro é completamente não intrusiva.)
Arquitetura
O diagrama abaixo mostra o relacionamento do processo mediaserver
e do processo init
, antes da introdução media.log
:
Pontos notáveis:
-
init
forks e execsmediaserver
. -
init
detecta a morte demediaserver
e re-forca conforme necessário. - O registro
ALOGx
não é mostrado.
O diagrama abaixo mostra o novo relacionamento dos componentes, após media.log
ser adicionado à arquitetura:
Mudanças importantes:
- Os clientes usam a API
NBLOG
para construir entradas de log e anexá-las a um buffer circular na memória compartilhada. -
MediaLogService
pode despejar o conteúdo do buffer circular a qualquer momento. - O buffer circular é projetado de forma que qualquer corrupção da memória compartilhada não trave
MediaLogService
e ainda será capaz de despejar o máximo do buffer que não é afetado pela corrupção. - O buffer circular não bloqueia nem bloqueia tanto para escrever novas entradas quanto para ler entradas existentes.
- Nenhuma chamada de sistema do kernel é necessária para gravar ou ler no buffer circular (exceto carimbos de data e hora opcionais).
Onde usar
A partir do Android 4.4, existem apenas alguns pontos de registro no AudioFlinger que usam o sistema media.log
. Embora as novas APIs não sejam tão fáceis de usar quanto ALOGx
, elas também não são extremamente difíceis. Incentivamos você a aprender o novo sistema de registro nas ocasiões em que ele for indispensável. Em particular, é recomendado para threads AudioFlinger que devem ser executados com frequência, periodicamente e sem bloqueios, como os threads FastMixer
e FastCapture
.
Como usar
Adicionar registros
Primeiro, você precisa adicionar logs ao seu código.
Nos threads FastMixer
e FastCapture
, use código como este:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
Como esta linha do tempo NBLog
é usada apenas pelos threads FastMixer
e FastCapture
, não há necessidade de exclusão mútua.
Em outros threads do AudioFlinger, use mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
Para threads diferentes de FastMixer
e FastCapture
, a linha do tempo NBLog
do thread pode ser usada pelo próprio thread e por operações de binder. NBLog::Writer
não fornece nenhuma exclusão mútua implícita por linha do tempo, portanto, certifique-se de que todos os logs ocorram dentro de um contexto onde o mutex mLock
do thread é mantido.
Depois de adicionar os logs, reconstrua o AudioFlinger.
Cuidado: Uma linha do tempo NBLog::Writer
separada é necessária por thread, para garantir a segurança do thread, uma vez que as linhas do tempo omitem mutexes por design. Se quiser que mais de um thread use a mesma linha do tempo, você pode proteger com um mutex existente (conforme descrito acima para mLock
). Ou você pode usar o wrapper NBLog::LockedWriter
em vez de NBLog::Writer
. No entanto, isso anula um benefício principal desta API: seu comportamento sem bloqueio.
A API NBLog
completa está em frameworks/av/include/media/nbaio/NBLog.h
.
Habilitar media.log
media.log
está desabilitado por padrão. Está ativo apenas quando a propriedade ro.test_harness
é 1
. Você pode habilitá-lo:
adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
A conexão é perdida durante a reinicialização, então:
adb shellO comando
ps media
agora mostrará dois processos:- mídia.log
- servidor de mídia
Anote o ID do processo do mediaserver
para uso posterior.
Exibir os cronogramas
Você pode solicitar manualmente um dump de log a qualquer momento. Este comando mostra logs de todas as linhas do tempo ativas e recentes e depois os limpa:
dumpsys media.log
Observe que, por design, os cronogramas são independentes e não há recurso para mesclar cronogramas.
Recuperar logs após a morte do mediaserver
Agora tente encerrar o processo mediaserver
: kill -9 #
, onde # é o ID do processo que você anotou anteriormente. Você deverá ver um dump de media.log
no logcat
principal, mostrando todos os logs que levaram à falha.
dumpsys media.log