Depuração de áudio

Este artigo descreve algumas dicas e truques para depurar áudio no Android.

Tee sink

O "tee sink" é um recurso de depuração do AudioFlinger, disponível apenas em builds personalizados, para reter um fragmento curto de áudio recente para análise posterior. Isso permite a comparação entre o que foi realmente tocado ou gravado e o que era esperado.

Para privacidade, o coletor de tee é desativado por padrão no tempo de compilação e de execução. Para usar o coletor de tee, você precisa ativá-lo recriando-o e definindo uma propriedade. Desative esse recurso depois de terminar a depuração. O sink de tee não pode ser deixado ativado em builds de produção.

As instruções desta seção são para o Android 7.x e versões mais recentes. Para o Android 5.x e 6.x, substitua /data/misc/audioserver por /data/misc/media. Além disso, é necessário usar um build userdebug ou eng. Se você usar um build userdebug, desative o verity com:

adb root && adb disable-verity && adb reboot

Configuração no tempo de compilação

  1. cd frameworks/av/services/audioflinger
  2. Editar Configuration.h.
  3. Remova o comentário de #define TEE_SINK.
  4. Crie novamente o libaudioflinger.so.
  5. adb root
  6. adb remount
  7. Envie ou sincronize o novo libaudioflinger.so com o /system/lib do dispositivo.

Configuração do tempo de execução

  1. adb shell getprop | grep ro.debuggable
    Confirme se a saída é: [ro.debuggable]: [1]
  2. adb shell
  3. 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 maneira:

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    Em que o valor af.tee é um número descrito abaixo.
  5. chmod 644 /data/local.prop
  6. reboot

Valores para a propriedade af.tee

O valor de af.tee é um número entre 0 e 7, que expressa a soma de vários bits, um por elemento. Consulte o código em AudioFlinger::AudioFlinger() em AudioFlinger.cpp para uma explicação de cada bit, mas brevemente:

  • 1 = entrada
  • 2 = saída do FastMixer
  • 4 = AudioRecord e AudioTrack por faixa

Ainda não há um bit para buffer profundo ou mixer normal, mas você pode conseguir resultados semelhantes usando "4".

Testar e adquirir dados

  1. Execute o teste de áudio.
  2. adb shell dumpsys media.audio_flinger
  3. Procure uma linha na saída dumpsys, como esta:
    tee copied to /data/misc/audioserver/20131010101147_2.wav
    Este é um arquivo PCM .wav.
  4. Em seguida, adb pull qualquer arquivo /data/misc/audioserver/*.wav de interesse. Os nomes de arquivos de despejo específicos da faixa não aparecem na saída dumpsys, mas ainda são salvos em /data/misc/audioserver após o fechamento da faixa.
  5. Analise os arquivos de despejo para verificar se há problemas de privacidade antes de compartilhar com outras pessoas.

Sugestões

Para resultados mais úteis, tente estas ideias:

  • Desative os sons de toque e os cliques de tecla para reduzir interrupções na saída do teste.
  • Maximizar todos os volumes.
  • Desative os apps que emitem som ou gravam pelo microfone, se eles não forem de interesse para o teste.
  • Os despejos específicos da faixa são salvos apenas quando a faixa é fechada. Talvez seja necessário forçar o fechamento de um app para despejar os dados específicos da faixa.
  • Faça a dumpsys imediatamente após o teste. Há uma quantidade limitada de espaço de gravação disponível.
  • Para não perder seus arquivos de despejo, faça upload deles no host periodicamente. Apenas um número limitado de arquivos de despejo é preservado. Os despejo mais antigos são removidos quando esse limite é atingido.

Restaurar

Conforme observado acima, o recurso de coletor de tee não deve ser ativado. Restaure o build e o dispositivo da seguinte maneira:

  1. Reverta as mudanças no código-fonte para Configuration.h.
  2. Crie o libaudioflinger.so novamente.
  3. Envie ou sincronize o libaudioflinger.so restaurado para o /system/lib do dispositivo.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.log

Macros ALOGx

A API de registro de linguagem Java padrão no SDK do Android é android.util.Log.

A API da linguagem C correspondente no Android NDK é __android_log_print declarada em <android/log.h>.

Na parte nativa do framework do Android, preferimos macros com os nomes ALOGE, ALOGW, ALOGI, ALOGV etc. Elas são declaradas em <utils/Log.h> e, para os fins deste artigo, vamos nos referir a elas coletivamente como ALOGx.

Todas essas APIs são fáceis de usar e bem compreendidas, por isso são usadas em toda a Plataforma Android. Especificamente, o processo mediaserver, que inclui o servidor de som AudioFlinger, usa ALOGx extensivamente.

No entanto, há algumas limitações para ALOGx e amigos:

  • Eles são suscetíveis a "spam de registro": o buffer de registro é um recurso compartilhado que pode transbordar facilmente devido a entradas de registro não relacionadas, resultando em perda de informações. A variante ALOGV é desativada no momento da compilação por padrão. No entanto, ele pode resultar em spam de registro se estiver ativado.
  • As chamadas do sistema do kernel podem ser bloqueadas, o que pode resultar na inversão de prioridade e, consequentemente, em interferências e incorreções de medição. Isso é de preocupação especial para linhas de execução críticas para o tempo, como FastMixer e FastCapture.
  • Se um registro específico for desativado para reduzir o spam, todas as informações que seriam capturadas por ele serão perdidas. Não é possível ativar um registro específico retroativamente, depois de ficar claro que ele seria 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 foram criados especificamente para resolver os problemas acima. Usaremos o termo "media.log" de forma genérica para nos referirmos aos três, mas, estritamente falando, NBLOG é a API de geração de registros C++, media.log é um nome de processo do Linux e MediaLogService é um serviço de vinculação do Android para examinar os registros.

Uma "linha do tempo" media.log é uma série de entradas de registro com a ordem relativa preservada. Por convenção, cada linha de execução precisa usar a própria linha do tempo.

Vantagens

Os benefícios do sistema media.log são:

  • Não gera spam no registro principal, a menos que seja necessário.
  • Pode ser examinado mesmo quando mediaserver falha ou trava.
  • Não bloqueia por linha do tempo.
  • Oferece menos interferência no desempenho. Obviamente, nenhuma forma de registro é completamente não intrusiva.

Arquitetura

O diagrama abaixo mostra a relação entre o processo mediaserver e o processo init, antes da introdução do media.log:

Arquitetura antes do media.log

Figura 1. Arquitetura antes do media.log

Pontos importantes:

  • init bifurca e executa mediaserver.
  • init detecta a morte de mediaserver e faz uma nova bifurcação conforme necessário.
  • O registro de ALOGx não é mostrado.

O diagrama abaixo mostra a nova relação dos componentes, depois que media.log foi adicionado à arquitetura:

Arquitetura após o media.log

Figura 2. Arquitetura após o media.log

Mudanças importantes:

  • Os clientes usam a API NBLOG para criar entradas de registro 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 modo que qualquer corrupção da memória compartilhada não cause uma falha no MediaLogService e ainda seja capaz de despejar o máximo do buffer que não é afetado pela corrupção.
  • O buffer circular não bloqueia e não tem bloqueio para gravar novas entradas e ler entradas existentes.
  • Nenhuma chamada de sistema do kernel é necessária para gravar ou ler o buffer circular (exceto carimbos de data/hora opcionais).

Onde usar

No Android 4.4 e versões mais recentes, há 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 a ALOGx, elas também não são extremamente difíceis. Recomendamos que você aprenda a usar o novo sistema de registro para as ocasiões em que ele é indispensável. Em particular, é recomendável para linhas de execução do AudioFlinger que precisam ser executadas com frequência, periodicamente e sem bloqueio, como as linhas de execução FastMixer e FastCapture.

Como usar

Adicionar registros

Primeiro, você precisa adicionar registros ao código.

Nas linhas de execução FastMixer e FastCapture, use um código como este:

logWriter->log("string");
logWriter->logf("format", parameters);
logWriter->logTimestamp();

Como essa linha do tempo NBLog é usada apenas pelas linhas de execução FastMixer e FastCapture, não é necessário fazer a exclusão mútua.

Em outras linhas de execução do AudioFlinger, use mNBLogWriter:

mNBLogWriter->log("string");
mNBLogWriter->logf("format", parameters);
mNBLogWriter->logTimestamp();

Para linhas de execução diferentes de FastMixer e FastCapture, a linha do tempo NBLog da linha de execução pode ser usada pela própria linha de execução e por operações de vinculação. O NBLog::Writer não fornece nenhuma exclusão mútua implícita por linha do tempo. Portanto, verifique se todos os registros ocorrem em um contexto em que o mutex mLock da linha de execução é mantido.

Depois de adicionar os registros, recrie o AudioFlinger.

Atenção:uma linha do tempo NBLog::Writer separada é necessária por linha de execução para garantir a segurança da linha de execução, já que as linhas do tempo omitem mutexes por design. Se você quiser que mais de uma linha de execução use a mesma linha do tempo, proteja com um mutex existente (conforme descrito acima para mLock). Ou use o wrapper NBLog::LockedWriter em vez de NBLog::Writer. No entanto, isso anula um dos principais benefícios dessa API: o comportamento não bloqueado.

A API NBLog completa está em frameworks/av/include/media/nbaio/NBLog.h.

Ativar o media.log

media.log está desativado por padrão. Ela só fica ativa quando a propriedade ro.test_harness é 1. Para ativar esse recurso, faça o seguinte:

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. Portanto:

adb shell
O comando ps media agora vai mostrar dois processos:
  • media.log
  • mediaserver

Anote o ID do processo de mediaserver para uso posterior.

Mostrar as linhas do tempo

É possível solicitar um despejo de registro manualmente a qualquer momento. Este comando mostra os registros de todas as linhas do tempo ativas e recentes e os limpa:

dumpsys media.log

As linhas do tempo de design são independentes, e não há uma opção para mesclar linhas do tempo.

Recuperar registros após a morte do mediaserver

Agora tente encerrar o processo mediaserver: kill -9 #, em que # é o ID do processo que você anotou anteriormente. Você vai encontrar um despejo de media.log no logcat principal, mostrando todos os registros que levaram ao erro.

dumpsys media.log