Depuración de audio

En este artículo, se describen algunos trucos y sugerencias para depurar el audio de Android.

Lavabo de tina

El "tee sink" es una función de depuración de AudioFlinger, disponible solo en compilaciones personalizadas, para retener un fragmento corto de audio reciente para un análisis posterior. Esto permite comparar lo que se reprodujo o grabó realmente con lo que se esperaba.

Por motivos de privacidad, el sumidero de Tee está inhabilitado de forma predeterminada, tanto en el tiempo de compilación como en el tiempo de ejecución. Para usar el sumidero de tee, deberás volver a compilarlo y habilitarlo, y también configurar una propiedad. Asegúrate de inhabilitar esta función después de terminar la depuración. El sumidero de Tee no debe dejarse habilitado en las compilaciones de producción.

Las instrucciones de esta sección son para Android 7.x y versiones posteriores. Para Android 5.x y 6.x, reemplaza /data/misc/audioserver por /data/misc/media. Además, debes usar una compilación userdebug o de eng. Si usas una compilación de userdebug, inhabilita Verity con lo siguiente:

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

Configuración en tiempo de compilación

  1. cd frameworks/av/services/audioflinger
  2. Editar Configuration.h.
  3. Quita el comentario de #define TEE_SINK.
  4. Vuelve a compilar libaudioflinger.so.
  5. adb root
  6. adb remount
  7. Envía o sincroniza el nuevo libaudioflinger.so con el /system/lib del dispositivo.

Configuración del tiempo de ejecución

  1. adb shell getprop | grep ro.debuggable
    Confirma que el resultado sea: [ro.debuggable]: [1]
  2. adb shell
  3. ls -ld /data/misc/audioserver

    Confirma que el resultado sea el siguiente:

    drwx------ media media ... media
    

    Si el directorio no existe, créalo de la siguiente manera:

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    Donde el valor af.tee es un número que se describe a continuación.
  5. chmod 644 /data/local.prop
  6. reboot

Valores de la propiedad af.tee

El valor de af.tee es un número entre 0 y 7, que expresa la suma de varios bits, uno por componente. Consulta el código en AudioFlinger::AudioFlinger() en AudioFlinger.cpp para obtener una explicación de cada bit, pero de forma breve:

  • 1 = entrada
  • 2 = Salida de FastMixer
  • 4 = AudioRecord y AudioTrack por pista

Aún no hay un bit para el búfer profundo ni el mezclador normal, pero puedes obtener resultados similares con "4".

Prueba y adquiere datos

  1. Ejecuta la prueba de audio.
  2. adb shell dumpsys media.audio_flinger
  3. Busca una línea en el resultado de dumpsys como esta:
    tee copied to /data/misc/audioserver/20131010101147_2.wav
    Este es un archivo .wav PCM.
  4. Luego, adb pull cualquier archivo /data/misc/audioserver/*.wav que te interese. Ten en cuenta que los nombres de los archivos de volcado específicos de la pista no aparecen en el resultado de dumpsys, pero se guardan en /data/misc/audioserver cuando se cierra la pista.
  5. Revisa los archivos de volcado para detectar problemas de privacidad antes de compartirlos con otras personas.

Sugerencias

Prueba estas ideas para obtener resultados más útiles:

  • Inhabilita los sonidos táctiles y los clics de teclas para reducir las interrupciones en el resultado de la prueba.
  • Maximizar todos los volúmenes
  • Inhabilita las apps que emitan sonido o que graban desde el micrófono si no son de interés para tu prueba.
  • Los volcados específicos de la pista solo se guardan cuando se cierra la pista. Es posible que debas forzar el cierre de una app para volcar sus datos específicos de la pista.
  • Realiza el dumpsys inmediatamente después de la prueba. Hay una cantidad limitada de espacio de grabación disponible.
  • Para asegurarte de no perder tus archivos de volcado, cárgalos en tu host de forma periódica. Solo se conserva una cantidad limitada de archivos de volcado. Los volcados más antiguos se quitan después de alcanzar ese límite.

Restablecer

Como se indicó más arriba, no se debe dejar habilitada la función de sumidero en T. Restablece la compilación y el dispositivo de la siguiente manera:

  1. Revierte los cambios en el código fuente a Configuration.h.
  2. Vuelve a compilar libaudioflinger.so.
  3. Envía o sincroniza el libaudioflinger.so restaurado con el /system/lib del dispositivo.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.log

Macros ALOGx

La API de registro estándar del lenguaje Java en el SDK de Android es android.util.Log.

La API correspondiente del lenguaje C en el NDK de Android se __android_log_print declara en <android/log.h>.

Dentro de la parte nativa del framework de Android, preferimos macros con nombres como ALOGE, ALOGW, ALOGI, ALOGV, etcétera. Se declaran en <utils/Log.h> y, a los efectos de este artículo, nos referiremos a ellas colectivamente como ALOGx.

Todas estas APIs son fáciles de usar y están bien comprendidas, por lo que están muy difundidas en la plataforma de Android. En particular, el proceso mediaserver, que incluye el servidor de sonido AudioFlinger, usa ALOGx de forma extensa.

Sin embargo, ALOGx y los amigos tienen algunas limitaciones:

  • Son susceptibles al “spam de registro”: el búfer de registro es un recurso compartido, por lo que puede desbordarse fácilmente debido a entradas de registro no relacionadas, lo que genera información perdida. La variante ALOGV está inhabilitada de forma predeterminada en el tiempo de compilación. Sin embargo, por supuesto, incluso puede generar spam de registro si está habilitado.
  • Las llamadas subyacentes del sistema del kernel podrían bloquearse, lo que podría generar una inversión de prioridad y, en consecuencia, perturbaciones y errores de medición. Esto es de especial importancia para los subprocesos urgentes, como FastMixer y FastCapture.
  • Si se inhabilita un registro en particular para reducir el spam de registro, se pierde toda la información que ese registro habría capturado. No es posible habilitar un registro específico de forma retroactiva, después de que quede claro que el registro habría sido interesante.

NBLOG, media.log y MediaLogService

Las APIs de NBLOG y el proceso media.log y el servicio MediaLogService asociados forman un sistema de registro más reciente para el contenido multimedia y están diseñados específicamente para abordar los problemas anteriores. Usaremos el término "media.log" de forma imprecisa para referirnos a los tres, pero, estrictamente hablando, NBLOG es la API de registro de C++, media.log es un nombre de proceso de Linux y MediaLogService es un servicio de Binder de Android para examinar los registros.

Una “línea de tiempo” de media.log es una serie de entradas de registro cuyo orden relativo se conserva. Por convención, cada subproceso debe usar su propio cronograma.

Beneficios

Los beneficios del sistema media.log son los siguientes:

  • No genera spam en el registro principal, a menos que sea necesario.
  • Se puede examinar incluso cuando mediaserver falla o se bloquea.
  • No genera bloqueos por cronograma.
  • Ofrece menos interrupciones en el rendimiento. (Por supuesto, ninguna forma de registro es completamente no intrusiva).

Arquitectura

En el siguiente diagrama, se muestra la relación entre el proceso mediaserver y el proceso init, antes de que se presente media.log:

Arquitectura anterior a media.log

Figura 1: Arquitectura anterior a media.log

Puntos destacados:

  • init bifurcaciones y ejecuciones mediaserver.
  • init detecta la muerte de mediaserver y vuelve a crear una bifurcación según sea necesario.
  • No se muestra el registro de ALOGx.

En el siguiente diagrama, se muestra la nueva relación de los componentes, después de agregar media.log a la arquitectura:

Arquitectura después de media.log

Figura 2: Arquitectura después de media.log

Cambios importantes:

  • Los clientes usan la API de NBLOG para crear entradas de registro y agregarlas a un búfer circular en la memoria compartida.
  • MediaLogService puede volcar el contenido del búfer circular en cualquier momento.
  • El búfer circular está diseñado de manera tal que cualquier daño en la memoria compartida no haga fallar a MediaLogService y aún pueda volcar la mayor parte del búfer que no se vea afectado por el daño.
  • El búfer circular no bloquea y no tiene bloqueo para escribir entradas nuevas y leer entradas existentes.
  • No se requieren llamadas al sistema del kernel para escribir en el búfer circular ni leerlo (excepto las marcas de tiempo opcionales).

Dónde puedes usarlo

A partir de Android 4.4, solo hay algunos puntos de registro en AudioFlinger que usan el sistema media.log. Si bien las nuevas APIs no son tan fáciles de usar como ALOGx, tampoco son muy difíciles. Te recomendamos que aprendas a usar el nuevo sistema de registro para las ocasiones en las que sea indispensable. En particular, se recomienda para los subprocesos de AudioFlinger que deben ejecutarse con frecuencia, de forma periódica y sin bloqueos, como los subprocesos FastMixer y FastCapture.

How to use

Cómo agregar registros

Primero, debes agregar registros a tu código.

En los subprocesos FastMixer y FastCapture, usa un código como este:

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

Como solo los subprocesos FastMixer y FastCapture usan este cronograma de NBLog, no es necesario realizar la exclusión mutua.

En otros subprocesos de AudioFlinger, usa mNBLogWriter:

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

En el caso de los subprocesos que no son FastMixer ni FastCapture, el subproceso en sí y las operaciones de Binder pueden usar el cronograma NBLog del subproceso. NBLog::Writer no proporciona ninguna exclusión mutua implícita por cronograma, por lo que debes asegurarte de que todos los registros se produzcan en un contexto en el que se mantenga el mutex mLock del subproceso.

Después de agregar los registros, vuelve a compilar AudioFlinger.

Precaución: Se requiere un cronograma NBLog::Writer independiente por subproceso para garantizar la seguridad de los subprocesos, ya que los cronogramas omiten los mutexes de forma predeterminada. Si quieres que más de un subproceso use el mismo cronograma, puedes protegerlo con un mutex existente (como se describió anteriormente para mLock). También puedes usar el wrapper NBLog::LockedWriter en lugar de NBLog::Writer. Sin embargo, esto anula un beneficio principal de esta API: su comportamiento no bloqueador.

La API completa de NBLog se encuentra en frameworks/av/include/media/nbaio/NBLog.h.

Habilita media.log

media.log está inhabilitado de forma predeterminada. Solo está activo cuando la propiedad ro.test_harness es 1. Para habilitarla, haz lo siguiente:

adb root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot

La conexión se pierde durante el reinicio, por lo que sucede lo siguiente:

adb shell
El comando ps media ahora mostrará dos procesos:
  • media.log
  • mediaserver

Anota el ID de proceso de mediaserver para más adelante.

Cómo mostrar las líneas de tiempo

Puedes solicitar un volcado de registro de forma manual en cualquier momento. Este comando muestra los registros de todos los cronogramas activos y recientes y, luego, los borra:

dumpsys media.log

Ten en cuenta que, por diseño, los cronogramas son independientes y no hay una función para combinarlos.

Cómo recuperar registros después de que se cierre MediaServer

Ahora, intenta finalizar el proceso mediaserver: kill -9 #, en el que # es el ID del proceso que anotaste antes. Deberías ver un volcado de media.log en el logcat principal, que muestra todos los registros previos a la falla.

dumpsys media.log