Debugowanie dźwięku

W tym artykule znajdziesz kilka wskazówek dotyczących debugowania dźwięku na Androidzie.

Umywalka z blatem

„Tee sink” to funkcja debugowania AudioFlinger, dostępna tylko w niestandardowych wersjach, która służy do przechowywania krótkiego fragmentu ostatniego dźwięku na potrzeby późniejszej analizy. Umożliwia to porównanie tego, co zostało faktycznie odtworzone lub nagrane, z tym, co było oczekiwane.

Ze względów bezpieczeństwa tee sink jest domyślnie wyłączony zarówno w czasie kompilacji, jak i w czasie wykonywania. Aby korzystać z tee sink, musisz go włączyć, ponownie kompilując kod i ustawiając właściwość. Po zakończeniu debugowania wyłącz tę funkcję. W kompilacji produkcyjnej nie należy pozostawiać włączonego tee sink.

Instrukcje w tej sekcji dotyczą systemu Android 7.x i nowszych. W przypadku Androida 5.x i 6.x zastąp /data/misc/audioserver tekstem /data/misc/media. Dodatkowo musisz użyć wersji userdebug lub eng. Jeśli używasz kompilacji userdebug, wyłącz weryfikację:

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

Konfiguracja w czasie kompilacji

  1. cd frameworks/av/services/audioflinger
  2. Edytuj Configuration.h.
  3. Odkomentuj #define TEE_SINK.
  4. Zbuduj ponownie libaudioflinger.so.
  5. adb root
  6. adb remount
  7. Prześlij nowe libaudioflinger.so lub zsynchronizuj je z /system/lib urządzenia.

Konfiguracja środowiska wykonawczego

  1. adb shell getprop | grep ro.debuggable
    Sprawdź, czy dane wyjściowe mają postać: [ro.debuggable]: [1]
  2. adb shell
  3. ls -ld /data/misc/audioserver

    Sprawdź, czy dane wyjściowe:

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

    Jeśli katalog nie istnieje, utwórz go w ten sposób:

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    gdzie wartość af.tee to liczba opisana poniżej.
  5. chmod 644 /data/local.prop
  6. reboot

Wartości właściwości af.tee

Wartość af.tee to liczba z zakresu od 0 do 7, która jest sumą kilku bitów (po jednym na każdą cechę). Aby poznać krótkie wyjaśnienie każdego elementu, zobacz kod w pliku AudioFlinger::AudioFlinger() w folderze AudioFlinger.cpp:

  • 1 = dane wejściowe
  • 2 = wyjście FastMixer
  • 4 = AudioRecord i AudioTrack na ścieżkę

Nie ma jeszcze bitu dla głębokiego bufora ani normalnego miksera, ale podobne wyniki można uzyskać, używając opcji „4”.

Testowanie i pozyskiwanie danych

  1. Uruchom test dźwięku.
  2. adb shell dumpsys media.audio_flinger
  3. W wyjściu dumpsys poszukaj wiersza takiego jak ten:
    tee copied to /data/misc/audioserver/20131010101147_2.wav
    To jest plik PCM .wav.
  4. Następnie adb pulldowolne pliki /data/misc/audioserver/*.wav, które Cię interesują; pamiętaj, że nazwy plików z dumpem poszczególnych ścieżek nie pojawiają się w wyjściu dumpsys, ale są nadal zapisywane w pliku /data/misc/audioserver po zamknięciu ścieżki.
  5. Zanim udostępnisz pliki z danymi, sprawdź, czy nie zawierają one informacji, które mogą naruszać prywatność.

Sugestie

Aby uzyskać bardziej przydatne wyniki, spróbuj:

  • Wyłącz dźwięki dotykowe i kliknięcia klawiszy, aby zmniejszyć przerwy w wyświetlanych wynikach.
  • Maksymalizacja wszystkich wolumenów.
  • Wyłącz aplikacje, które emitują dźwięk lub nagrywają dźwięk z mikrofonu, jeśli nie są potrzebne do testu.
  • Dane z konkretnej ścieżki są zapisywane tylko wtedy, gdy ścieżka jest zamknięta. Aby zapisać dane z konkretnej ścieżki, może być konieczne przymusowe zamknięcie aplikacji.
  • Wykonaj dumpsys bezpośrednio po teście. Dostępna jest ograniczona ilość miejsca na nagrania.
  • Aby mieć pewność, że nie stracisz plików dump, okresowo przesyłaj je do hosta. Zachowuje się tylko ograniczoną liczbę plików z danymi. Po osiągnięciu tego limitu starsze pliki z danymi są usuwane.

Przywróć

Jak już wspomnieliśmy, funkcja ujścia nie powinna być włączona. Aby przywrócić wersję i urządzenie:

  1. Cofnij zmiany w kodzie źródłowym do wersji Configuration.h.
  2. Zbuduj ponownie libaudioflinger.so.
  3. Prześlij przywrócone dane libaudioflinger.so do /system/lib na urządzeniu za pomocą protokołu Push lub synchronizacji.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.log

Makra ALOGx

Standardowym interfejsem API do rejestrowania w języku Java w pakiecie SDK Androida jest android.util.Log.

Odpowiadający interfejs API języka C w Android NDK to __android_log_print, który jest deklarowany w pliku <android/log.h>.

W rodzimej części platformy Android preferujemy makra o nazwach ALOGE, ALOGW, ALOGI, ALOGV itp. Są one deklarowane w <utils/Log.h> i w celu tego artykułu będziemy się do nich odnosić zbiorczo jako ALOGx.

Wszystkie te interfejsy API są łatwe w użyciu i dobrze rozumiane, dlatego są powszechnie stosowane na platformie Android. W szczególności proces mediaserver, który obejmuje serwer dźwiękowy AudioFlinger, intensywnie korzysta z ALOGx.

Istnieją jednak pewne ograniczenia dotyczące ALOGx i znajomych:

  • Są one podatne na „spam w logach”: bufor logów jest zasobem współdzielonym, więc może łatwo przepełnić się z powodu niezwiązanych wpisów w logach, co spowoduje utratę informacji. Wariant ALOGV jest domyślnie wyłączony w czasie kompilacji. Ale oczywiście nawet to może powodować spam w logach, jeśli jest włączone.
  • Podstawowe wywołania systemu jądra mogą być blokowane, co może spowodować odwrócenie priorytetów i w konsekwencji zakłócenia i nieścisłości pomiarów. Jest to szczególnie ważne w przypadku wątków, w których liczy się czas, takich jak FastMixerFastCapture.
  • Jeśli konkretny dziennik jest wyłączony, aby ograniczyć spam w dziennikach, wszystkie informacje, które zostałyby w nim zarejestrowane, są tracone. Nie można włączyć określonego dziennika wstecznie, po tym, jak okaże się, że był on interesujący.

NBLOG, media.log i MediaLogService

Interfejsy API NBLOG i powiązany proces media.log oraz usługa MediaLogService tworzą nowszy system rejestrowania multimediów i są specjalnie zaprojektowane, aby rozwiązywać wymienione powyżej problemy. W dalszej części tekstu będziemy używać terminu „media.log” w przypadku wszystkich trzech plików, ale ściśle mówiąc NBLOG to interfejs API do rejestrowania w C++, media.log to nazwa procesu Linuxa, a MediaLogService to usługa bindera Androida do sprawdzania dzienników.

„Oś czasu” media.log to seria wpisów w logu, których względna kolejność jest zachowana. Zgodnie z zasadą każdy wątek powinien mieć własną oś czasu.

Zalety

Zalety systemu media.log:

  • Nie zaśmieca głównego dziennika, chyba że jest to konieczne.
  • Może być analizowany nawet wtedy, gdy mediaserver ulega awarii lub zawiesza się.
  • nie blokuje osi czasu;
  • zapewnia mniejsze zakłócenia skuteczności; (Oczywiście żadna forma rejestrowania nie jest całkowicie nieinwazyjna).

Architektura

Diagram poniżej pokazuje związek procesu mediaserver z procesem init przed wprowadzeniem procesu media.log:

Architektura przed wprowadzeniem media.log

Rysunek 1. Architektura przed wprowadzeniem media.log

Ważne informacje:

  • initforks i execsmediaserver.
  • init wykrywa, że mediaserver nie działa, i w razie potrzeby tworzy nową gałąź.
  • ALOGx logowanie się nie wyświetla.

Schemat poniżej pokazuje nowe relacje między komponentami po dodaniu do architektury elementu media.log:

Architektura po media.log

Rysunek 2. Architektura po media.log

Ważne zmiany:

  • Klienci używają interfejsu API NBLOG do tworzenia wpisów dziennika i dołączania ich do pętli w pamięci współdzielonej.
  • MediaLogService może w każdej chwili zrzucić zawartość pętli.
  • Bufor cykliczny jest zaprojektowany w taki sposób, że jakakolwiek usterka w wspólnej pamięci nie spowoduje awarii MediaLogService i nadal będzie można z niego odczytać tyle danych z bufora, na które nie wpłynęła usterka.
  • Pętla buforowa jest nieblokująca i nieblokująca blokady zarówno w przypadku zapisywania nowych wpisów, jak i odczytywania istniejących wpisów.
  • Do zapisywania i odczytywania z bufora pętli nie są wymagane żadne wywołania systemowe jądra (inne niż opcjonalne sygnatury czasowe).

Gdzie można płacić

W Androidzie 4.4 jest tylko kilka punktów logowania w AudioFlinger, które korzystają z systemu media.log. Chociaż nowe interfejsy API nie są tak łatwe w użyciu jak ALOGx, nie są też bardzo skomplikowane. Zachęcamy do zapoznania się z nowym systemem rejestrowania danych na wypadek, gdyby okazał się on niezbędny. Jest to szczególnie zalecane w przypadku wątków AudioFlinger, które muszą być uruchamiane często, okresowo i bez blokowania, takich jak wątki FastMixerFastCapture.

Instrukcje korzystania

Dodawanie logów

Najpierw musisz dodać do kodu logi.

W wątkach FastMixer i FastCapture używaj kodu takiego jak ten:

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

Ta linia czasu NBLog jest używana tylko przez wątki FastMixerFastCapture, więc nie ma potrzeby wzajemnego wykluczania.

W innych wątkach AudioFlinger używaj mNBLogWriter:

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

W przypadku wątków innych niż FastMixerFastCapture osi czasu wątku NBLog mogą używać zarówno sam wątek, jak i operacje bindera. NBLog::Writer nie zapewnia żadnej niejawnej wzajemnej wykluczalności na osi czasu, dlatego upewnij się, że wszystkie logi występują w kontekście, w którym jest zablokowany semafor wątku mLock.

Po dodaniu dzienników ponownie skompiluj AudioFlinger.

Uwaga: aby zapewnić bezpieczeństwo wątku, wymagana jest osobna oś czasu NBLog::Writer na każdy wątek, ponieważ osi czasu nie uwzględniają narzuconych blokad. Jeśli chcesz, aby więcej niż 1 wątek używało tej samej osi czasu, możesz zabezpieczyć kod za pomocą istniejącego semaforu (jak opisano powyżej w przypadku funkcji mLock). Zamiast funkcji NBLog::Writer możesz też użyć funkcji opakowującej NBLog::LockedWriter. Jednak powoduje to utratę głównej zalety tego interfejsu API, czyli braku blokowania.

Pełny interfejs API NBLog znajdziesz na stronie frameworks/av/include/media/nbaio/NBLog.h.

Włączanie pliku media.log

media.log jest domyślnie wyłączona. Jest aktywna tylko wtedy, gdy wartość właściwości ro.test_harness to 1. Aby go włączyć:

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

Połączenie zostaje utracone podczas ponownego uruchamiania:

adb shell
Polecenie ps media będzie teraz wyświetlać 2 procesy:
  • media.log
  • mediaserver

Zapisz sobie identyfikator procesu mediaserver.

Wyświetlanie osi czasu

W każdej chwili możesz ręcznie poprosić o zrzut dzienników. To polecenie wyświetla dzienniki ze wszystkich aktywnych i ostatnich osi czasu, a potem je czyści:

dumpsys media.log

Pamiętaj, że osi czasu są niezależne od siebie i nie ma możliwości ich łączenia.

Przywracanie logów po wyłączeniu mediaserver

Teraz spróbuj zatrzymać proces mediaserver: kill -9 #, gdzie # to identyfikator procesu zapisany wcześniej. W głównym pliku media.log powinieneś zobaczyć zrzut z pliku media.log, który zawiera wszystkie logi prowadzące do awarii.logcat

dumpsys media.log