Débogage audio

Cet article décrit quelques trucs et astuces pour déboguer l’audio Android.

Évier en T

Le « tee Sink » est une fonctionnalité de débogage d'AudioFlinger, disponible uniquement dans les versions personnalisées, permettant de conserver un court fragment d'audio récent pour une analyse ultérieure. Cela permet de comparer ce qui a été réellement joué ou enregistré et ce qui était attendu.

Pour des raisons de confidentialité, le récepteur de tee est désactivé par défaut, à la fois au moment de la compilation et de l'exécution. Pour utiliser le tee-évier, vous devrez l'activer en recompilant, et également en définissant une propriété. Assurez-vous de désactiver cette fonctionnalité une fois le débogage terminé ; l'évier en T ne doit pas rester activé dans les versions de production.

Les instructions de cette section concernent Android 7.x et versions ultérieures. Pour Android 5.x et 6.x, remplacez /data/misc/audioserver par /data/misc/media . De plus, vous devez utiliser une version userdebug ou eng. Si vous utilisez une version userdebug, désactivez Verity avec :

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

Configuration au moment de la compilation

  1. cd frameworks/av/services/audioflinger
  2. Modifiez Configuration.h .
  3. Décommentez #define TEE_SINK .
  4. Reconstruisez libaudioflinger.so .
  5. adb root
  6. adb remount
  7. Poussez ou synchronisez le nouveau libaudioflinger.so avec le /system/lib de l'appareil.

Configuration d'exécution

  1. adb shell getprop | grep ro.debuggable
    Confirmez que le résultat est : [ro.debuggable]: [1]
  2. adb shell
  3. ls -ld /data/misc/audioserver

    Confirmez que le résultat est :

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

    Si le répertoire n'existe pas, créez-le comme suit :

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    Où la valeur af.tee est un nombre décrit ci-dessous.
  5. chmod 644 /data/local.prop
  6. reboot

Valeurs de la propriété af.tee

La valeur de af.tee est un nombre compris entre 0 et 7, exprimant la somme de plusieurs bits, un par fonctionnalité. Voir le code sur AudioFlinger::AudioFlinger() dans AudioFlinger.cpp pour une explication de chaque bit, mais brièvement :

  • 1 = entrée
  • 2 = sortie FastMixer
  • 4 = AudioRecord et AudioTrack par piste

Il n'y a pas encore de bit pour le tampon profond ou le mélangeur normal, mais vous pouvez obtenir des résultats similaires en utilisant "4".

Tester et acquérir des données

  1. Exécutez votre test audio.
  2. adb shell dumpsys media.audio_flinger
  3. Recherchez une ligne dans la sortie dumpsys telle que celle-ci :
    tee copied to /data/misc/audioserver/20131010101147_2.wav
    Il s'agit d'un fichier PCM .wav.
  4. Ensuite, adb pull tous les fichiers /data/misc/audioserver/*.wav qui vous intéressent ; notez que les noms de fichiers de dump spécifiques aux pistes n'apparaissent pas dans la sortie dumpsys , mais sont toujours enregistrés dans /data/misc/audioserver à la fermeture de la piste.
  5. Examinez les fichiers de vidage pour des raisons de confidentialité avant de les partager avec d'autres.

Suggestions

Essayez ces idées pour des résultats plus utiles :

  • Désactivez les sons tactiles et les clics de touches pour réduire les interruptions dans la sortie des tests.
  • Maximisez tous les volumes.
  • Désactivez les applications qui émettent du son ou enregistrent à partir du microphone si elles ne présentent pas d'intérêt pour votre test.
  • Les dumps spécifiques à une piste ne sont enregistrés que lorsque la piste est fermée ; vous devrez peut-être forcer la fermeture d'une application afin de vider ses données spécifiques à la piste
  • Faites les dumpsys immédiatement après le test ; la quantité d'espace d'enregistrement disponible est limitée.
  • Pour vous assurer de ne pas perdre vos fichiers de vidage, téléchargez-les périodiquement sur votre hébergeur. Seul un nombre limité de fichiers de vidage est conservé ; les anciens dumps sont supprimés une fois cette limite atteinte.

Restaurer

Comme indiqué ci-dessus, la fonction d’évier en T ne doit pas rester activée. Restaurez votre build et votre appareil comme suit :

  1. Annulez les modifications du code source dans Configuration.h .
  2. Reconstruisez libaudioflinger.so .
  3. Poussez ou synchronisez le libaudioflinger.so restauré avec le /system/lib de l'appareil.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.log

Macros ALOGx

L'API de journalisation en langage Java standard dans le SDK Android est android.util.Log .

L'API du langage C correspondante dans Android NDK est __android_log_print déclarée dans <android/log.h> .

Dans la partie native du framework Android, nous préférons les macros nommées ALOGE , ALOGW , ALOGI , ALOGV , etc. Elles sont déclarées dans <utils/Log.h> , et pour les besoins de cet article, nous les appellerons collectivement ALOGx .

Toutes ces API sont faciles à utiliser et bien comprises, elles sont donc omniprésentes sur toute la plate-forme Android. En particulier, le processus mediaserver , qui inclut le serveur de son AudioFlinger, utilise largement ALOGx .

Néanmoins, il existe certaines limites à ALOGx et ses amis :

  • Ils sont sensibles au « spam de journal » : le tampon de journal est une ressource partagée, il peut donc facilement déborder en raison d'entrées de journal non liées, entraînant des informations manquées. La variante ALOGV est désactivée par défaut au moment de la compilation. Mais bien sûr, même cela peut entraîner du spam dans les journaux s'il est activé.
  • Les appels système du noyau sous-jacent pourraient se bloquer, entraînant éventuellement une inversion des priorités et, par conséquent, des perturbations et des inexactitudes de mesure. Ceci est particulièrement préoccupant pour les threads à temps critique tels que FastMixer et FastCapture .
  • Si un journal particulier est désactivé pour réduire le spam des journaux, toutes les informations qui auraient été capturées par ce journal sont perdues. Il n'est pas possible d'activer rétroactivement un journal spécifique, une fois qu'il devient clair que ce journal aurait été intéressant.

NBLOG, media.log et MediaLogService

Les API NBLOG , le processus media.log associé et le service MediaLogService forment ensemble un système de journalisation plus récent pour les médias et sont spécifiquement conçus pour résoudre les problèmes ci-dessus. Nous utiliserons vaguement le terme "media.log" pour désigner les trois, mais à proprement parler, NBLOG est l'API de journalisation C++, media.log est un nom de processus Linux et MediaLogService est un service de classeur Android permettant d'examiner les journaux.

Une « chronologie » media.log est une série d’entrées de journal dont l’ordre relatif est préservé. Par convention, chaque fil de discussion doit utiliser sa propre timeline.

Avantages

Les avantages du système media.log sont les suivants :

  • Ne spamme pas le journal principal sauf si et jusqu'à ce que cela soit nécessaire.
  • Peut être examiné même lorsque mediaserver tombe en panne ou se bloque.
  • N'est pas bloquant par chronologie.
  • Offre moins de perturbations des performances. (Bien entendu, aucune forme de journalisation n’est totalement non intrusive.)

Architecture

Le diagramme ci-dessous montre la relation entre le processus mediaserver et le processus init , avant l'introduction media.log :

Architecture avant media.log

Figure 1. Architecture avant media.log

Points notables :

  • init forks et execs mediaserver .
  • init détecte la mort de mediaserver et relance si nécessaire.
  • La journalisation ALOGx n’est pas affichée.

Le diagramme ci-dessous montre la nouvelle relation entre les composants, une fois media.log ajouté à l'architecture :

Architecture après media.log

Figure 2. Architecture après media.log

Changements importants :

  • Les clients utilisent l'API NBLOG pour créer des entrées de journal et les ajouter à un tampon circulaire dans la mémoire partagée.
  • MediaLogService peut vider le contenu du tampon circulaire à tout moment.
  • Le tampon circulaire est conçu de telle manière que toute corruption de la mémoire partagée ne fera pas planter MediaLogService , et il pourra toujours vider autant de tampon que celui qui n'est pas affecté par la corruption.
  • Le tampon circulaire est non bloquant et sans verrouillage pour l'écriture de nouvelles entrées et la lecture des entrées existantes.
  • Aucun appel système du noyau n'est requis pour écrire ou lire dans le tampon circulaire (autre que les horodatages facultatifs).

Où utiliser

Depuis Android 4.4, seuls quelques points de journalisation dans AudioFlinger utilisent le système media.log . Bien que les nouvelles API ne soient pas aussi simples à utiliser ALOGx , elles ne sont pas non plus extrêmement difficiles. Nous vous encourageons à apprendre le nouveau système de journalisation pour les occasions où il est indispensable. En particulier, il est recommandé pour les threads AudioFlinger qui doivent s'exécuter fréquemment, périodiquement et sans blocage, comme les threads FastMixer et FastCapture .

Comment utiliser

Ajouter des journaux

Tout d’abord, vous devez ajouter des journaux à votre code.

Dans les threads FastMixer et FastCapture , utilisez un code tel que celui-ci :

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

Comme cette chronologie NBLog est utilisée uniquement par les threads FastMixer et FastCapture , aucune exclusion mutuelle n'est nécessaire.

Dans d'autres threads AudioFlinger, utilisez mNBLogWriter :

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

Pour les threads autres que FastMixer et FastCapture , la chronologie NBLog du thread peut être utilisée à la fois par le thread lui-même et par les opérations de liaison. NBLog::Writer ne fournit aucune exclusion mutuelle implicite par chronologie, alors assurez-vous que tous les journaux se produisent dans un contexte où le mutex mLock du thread est conservé.

Après avoir ajouté les journaux, reconstruisez AudioFlinger.

Attention : Une chronologie NBLog::Writer distincte est requise par thread, pour garantir la sécurité des threads, car les chronologies omettent les mutex par conception. Si vous souhaitez que plusieurs threads utilisent la même chronologie, vous pouvez protéger avec un mutex existant (comme décrit ci-dessus pour mLock ). Ou vous pouvez utiliser le wrapper NBLog::LockedWriter au lieu de NBLog::Writer . Cependant, cela annule un avantage majeur de cette API : son comportement non bloquant.

L'API NBLog complète se trouve sur frameworks/av/include/media/nbaio/NBLog.h .

Activer media.log

media.log est désactivé par défaut. Il n'est actif que lorsque la propriété ro.test_harness vaut 1 . Vous pouvez l'activer en :

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

La connexion est perdue lors du redémarrage, donc :

adb shell
La commande ps media affichera désormais deux processus :
  • media.log
  • Serveur multimédia

Notez l'ID de processus du mediaserver pour plus tard.

Afficher les chronologies

Vous pouvez demander manuellement un vidage du journal à tout moment. Cette commande affiche les journaux de toutes les chronologies actives et récentes, puis les efface :

dumpsys media.log

Notez que, de par leur conception, les chronologies sont indépendantes et qu’il n’existe aucune possibilité de fusionner les chronologies.

Récupérer les journaux après la mort du serveur multimédia

Essayez maintenant de supprimer le processus mediaserver : kill -9 # , où # est l'ID de processus que vous avez noté plus tôt. Vous devriez voir un vidage de media.log dans le logcat principal, affichant tous les journaux ayant précédé le crash.

dumpsys media.log