Depuração de áudio

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

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

Configuração em 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 forma:

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    Onde o valor af.tee é um número descrito abaixo.
  5. chmod 644 /data/local.prop
  6. 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

  1. Execute seu 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 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ída dumpsys , mas ainda são salvos em /data/misc/audioserver após o fechamento da trilha.
  5. 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:

  1. Reverta as alterações do código-fonte para Configuration.h .
  2. Reconstrua libaudioflinger.so .
  3. Envie ou sincronize o libaudioflinger.so restaurado com o /system/lib do dispositivo.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. 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 e FastCapture .
  • 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 :

Arquitetura antes de media.log

Figura 1. Arquitetura antes de media.log

Pontos notáveis:

  • init forks e execs mediaserver .
  • init detecta a morte de mediaserver 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:

Arquitetura após media.log

Figura 2. Arquitetura após media.log

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