Depuração de áudio

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

Lavatório

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. Isso permite a comparação entre o que foi realmente tocado ou gravado versus o que era esperado.

Para privacidade, o tee sink é desabilitado por padrão, tanto em tempo de compilação quanto em tempo de execução. Para usar o tee sink, você precisará habilitá-lo recompilando e também definindo uma propriedade. Certifique-se de desabilitar esse recurso depois de terminar a depuração; o tee sink não deve ser deixado habilitado em builds 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 veracidade 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. 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 a 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 faixa

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

Testar e adquirir 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 .wav PCM.
  4. Então adb pull quaisquer arquivos /data/misc/audioserver/*.wav de interesse; observe que os nomes de arquivos dump 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. Revise os arquivos de despejo quanto a questões de privacidade antes de compartilhar com outras pessoas.

Sugestões

Experimente estas ideias para obter resultados mais úteis:

  • Desative os sons de toque e cliques de teclas para reduzir as interrupções na saída de teste.
  • Maximize todos os volumes.
  • Desative os aplicativos que emitem som ou gravam do microfone, caso não sejam do interesse do seu teste.
  • Os dumps específicos da trilha são salvos apenas 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 garantir que você não perca seus arquivos de despejo, carregue-os em seu host periodicamente. Apenas 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. libaudioflinger.so .
  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 Android SDK é android.util.Log .

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

Dentro da parte nativa da estrutura do Android, preferimos macros chamadas ALOGE , ALOGW , ALOGI , ALOGV , etc. Elas são declaradas em <utils/Log.h> e, para os propósitos 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 difundidas em toda a plataforma Android. Em particular, o processo mediaserver , que inclui o servidor de som AudioFlinger, usa extensivamente o ALOGx .

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

  • Eles são suscetíveis a "log spam": o buffer de log é um recurso compartilhado para que possa transbordar facilmente devido a entradas de log não relacionadas, resultando em informações perdidas. A variante ALOGV é 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 subjacentes podem ser bloqueadas, possivelmente resultando em inversão de prioridade e, consequentemente, em distúrbios e imprecisões de medição. Isso é uma preocupação especial para threads de tempo crítico, como FastMixer e FastCapture .
  • Se um log específico for desabilitado para reduzir o spam de log, todas as informações que seriam capturadas por esse log serão perdidas. Não é possível habilitar um log específico retroativamente, depois que fica claro que o log 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 log 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 log C++, media.log é um nome de processo Linux e MediaLogService é um serviço de ligação do Android para examinar os logs.

Uma "linha do tempo" do 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 e até que seja necessário.
  • Pode ser examinado mesmo quando mediaserver trava ou trava.
  • Não é bloqueante 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 o processo init , antes media.log seja introduzido:

Arquitetura antes do media.log

Figura 1. Arquitetura antes do media.log

Pontos notáveis:

  • init forks e execs mediaserver .
  • init detecta a morte do mediaserver e re-forks conforme necessário.
  • O registro ALOGx não é mostrado.

O diagrama abaixo mostra o novo relacionamento dos componentes, após o media.log ser adicionado à arquitetura:

Arquitetura depois do 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 tal forma que qualquer corrupção da memória compartilhada não MediaLogService e ainda poderá despejar o máximo do buffer que não for afetado pela corrupção.
  • O buffer circular é sem bloqueio e sem bloqueio tanto para escrever novas entradas quanto para ler entradas existentes.
  • Nenhuma chamada de sistema do kernel é necessária para gravar ou ler do buffer circular (além de timestamps opcionais).

Onde usar

A partir do Android 4.4, existem apenas alguns pontos de log 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. Nós encorajamos você a aprender o novo sistema de registro para aquelas ocasiões em que é indispensável. Em particular, é recomendado para encadeamentos AudioFlinger que devem ser executados com frequência, periodicamente e sem bloqueio, como os FastMixer e FastCapture .

Como usar

Adicionar registros

Primeiro, você precisa adicionar logs ao seu código.

Nos threads FastMixer e FastCapture , use um código como este:

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

Como essa linha do tempo do NBLog é usada apenas pelos threads FastMixer e FastCapture , não há necessidade de exclusão mútua.

Em outros threads AudioFlinger, use mNBLogWriter :

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

Para encadeamentos diferentes de FastMixer e FastCapture , a linha do tempo do NBLog do encadeamento pode ser usada pelo próprio encadeamento e pelas operações do 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 em um contexto em que o mutex mLock do thread seja mantido.

Depois de adicionar os logs, reconstrua o AudioFlinger.

Cuidado: Uma linha de tempo NBLog::Writer separada é necessária por encadeamento, para garantir a segurança do encadeamento, pois as linhas de tempo omitem mutexes por design. Se você 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 nega um benefício principal dessa API: seu comportamento sem bloqueio.

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

Ativar media.log

media.log está desabilitado por padrão. Está ativo somente quando a propriedade ro.test_harness é 1 . Você pode habilitá-lo por:

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:
  • media.log
  • servidor de mídia

Anote o ID do processo do mediaserver para mais tarde.

Exibindo as linhas do tempo

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 as limpa:

dumpsys media.log

Observe que, por design, as linhas de tempo são independentes e não há recurso para mesclar linhas de tempo.

Recuperando logs após a morte do mediaserver

Agora tente matar o processo do mediaserver : kill -9 # , onde # é o ID do processo que você anotou anteriormente. Você deve ver um dump de media.log no logcat principal, mostrando todos os logs que levaram ao travamento.

dumpsys media.log