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
cd frameworks/av/services/audioflinger
- Edytuj
Configuration.h
. - Odkomentuj
#define TEE_SINK
. - Zbuduj ponownie
libaudioflinger.so
. adb root
adb remount
- Prześlij nowe
libaudioflinger.so
lub zsynchronizuj je z/system/lib
urządzenia.
Konfiguracja środowiska wykonawczego
adb shell getprop | grep ro.debuggable
Sprawdź, czy dane wyjściowe mają postać:[ro.debuggable]: [1]
adb shell
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
echo af.tee=# > /data/local.prop
gdzie wartośćaf.tee
to liczba opisana poniżej.chmod 644 /data/local.prop
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
- Uruchom test dźwięku.
adb shell dumpsys media.audio_flinger
- W wyjściu
dumpsys
poszukaj wiersza takiego jak ten:
tee copied to /data/misc/audioserver/20131010101147_2.wav
To jest plik PCM .wav. - Następnie
adb pull
dowolne 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ściudumpsys
, ale są nadal zapisywane w pliku/data/misc/audioserver
po zamknięciu ścieżki. - 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:
- Cofnij zmiany w kodzie źródłowym do wersji
Configuration.h
. - Zbuduj ponownie
libaudioflinger.so
. - Prześlij przywrócone dane
libaudioflinger.so
do/system/lib
na urządzeniu za pomocą protokołu Push lub synchronizacji. adb shell
rm /data/local.prop
rm /data/misc/audioserver/*.wav
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
FastMixer
iFastCapture
. - 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
:

Rysunek 1. Architektura przed wprowadzeniem media.log
Ważne informacje:
init
forks i execsmediaserver
.init
wykrywa, żemediaserver
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
:

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 FastMixer
i FastCapture
.
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 FastMixer
i FastCapture
, 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ż FastMixer
i FastCapture
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
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