Debugowanie dźwięku

W tym artykule opisano kilka wskazówek i wskazówek dotyczących debugowania dźwięku w systemie Android.

Zlew trójnikowy

„Tee umywalka” to funkcja debugowania AudioFlingera, dostępna tylko w niestandardowych kompilacjach, służąca do zachowywania krótkiego fragmentu najnowszego dźwięku do późniejszej analizy. Pozwala to na porównanie tego, co faktycznie zostało odtworzone lub nagrane, z tym, czego oczekiwano.

Ze względu na prywatność, tee-sink jest domyślnie wyłączony zarówno w czasie kompilacji, jak i w czasie wykonywania. Aby skorzystać z tee-sink, musisz go włączyć poprzez ponowną kompilację, a także ustawienie właściwości. Pamiętaj o wyłączeniu tej funkcji po zakończeniu debugowania; nie należy pozostawiać włączonego zlewu tee w kompilacjach produkcyjnych.

Instrukcje zawarte w tej sekcji dotyczą systemu Android 7.x i nowszych wersji. W przypadku Androida 5.x i 6.x zamień /data/misc/audioserver na /data/misc/media . Dodatkowo musisz użyć debugowania użytkownika lub kompilacji eng. Jeśli używasz kompilacji userdebug, wyłącz prawdziwość za pomocą:

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 plik libaudioflinger.so .
  5. adb root
  6. adb remount
  7. Wciśnij lub zsynchronizuj nowy libaudioflinger.so z plikiem /system/lib urządzenia.

Konfiguracja w czasie wykonywania

  1. adb shell getprop | grep ro.debuggable
    Potwierdź, że wynik to: [ro.debuggable]: [1]
  2. adb shell
  3. ls -ld /data/misc/audioserver

    Potwierdź, że dane wyjściowe to:

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

    Jeśli katalog nie istnieje, utwórz go w następujący sposób:

    mkdir /data/misc/audioserver
    chown media:media /data/misc/audioserver
    
  4. echo af.tee=# > /data/local.prop
    Gdzie wartość af.tee jest liczbą opisaną 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, wyrażająca sumę kilku bitów, po jednym na funkcję. Zobacz kod w AudioFlinger::AudioFlinger() w AudioFlinger.cpp , aby uzyskać wyjaśnienie każdego bitu, ale krótko:

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

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

Testuj i zbieraj dane

  1. Uruchom test dźwięku.
  2. adb shell dumpsys media.audio_flinger
  3. Poszukaj linii na wyjściu dumpsys , takiej jak ta:
    tee copied to /data/misc/audioserver/20131010101147_2.wav
    To jest plik .wav PCM.
  4. Następnie adb pull wszystkie interesujące Cię pliki /data/misc/audioserver/*.wav ; zwróć uwagę, że nazwy plików zrzutu specyficzne dla ścieżki nie pojawiają się w wynikach dumpsys , ale nadal są zapisywane w /data/misc/audioserver po zamknięciu ścieżki.
  5. Przed udostępnieniem plików zrzutów innym osobom przejrzyj pliki zrzutu pod kątem ochrony prywatności.

Propozycje

Wypróbuj te pomysły, aby uzyskać bardziej przydatne wyniki:

  • Wyłącz dźwięki dotyku i kliknięcia klawiszy, aby ograniczyć przerwy w wynikach testu.
  • Maksymalizuj wszystkie woluminy.
  • Wyłącz aplikacje, które wydają dźwięk lub nagrywają z mikrofonu, jeśli nie są interesujące dla Twojego testu.
  • Zrzuty specyficzne dla toru są zapisywane tylko wtedy, gdy tor jest zamknięty; może być konieczne wymuszenie zamknięcia aplikacji, aby zrzucić dane dotyczące utworu
  • Wykonaj dumpsys natychmiast po teście; dostępna jest ograniczona ilość miejsca na nagrania.
  • Aby mieć pewność, że nie stracisz plików zrzutów, przesyłaj je okresowo do swojego hosta. Zachowana zostanie tylko ograniczona liczba plików zrzutów; starsze zrzuty są usuwane po osiągnięciu tego limitu.

Przywrócić

Jak wspomniano powyżej, funkcja zlewu tee nie powinna być włączona. Przywróć kompilację i urządzenie w następujący sposób:

  1. Przywróć zmiany w kodzie źródłowym do Configuration.h .
  2. Zbuduj ponownie plik libaudioflinger.so .
  3. Prześlij lub zsynchronizuj przywrócony plik libaudioflinger.so z plikiem /system/lib urządzenia.
  4. adb shell
  5. rm /data/local.prop
  6. rm /data/misc/audioserver/*.wav
  7. reboot

media.log

Makra ALOGx

Standardowym interfejsem API rejestrowania w języku Java w zestawie SDK systemu Android jest android.util.Log .

Odpowiedni interfejs API języka C w systemie Android NDK to __android_log_print zadeklarowany w <android/log.h> .

W natywnej części Androida preferujemy makra o nazwach ALOGE , ALOGW , ALOGI , ALOGV itp. Są one zadeklarowane w <utils/Log.h> i na potrzeby tego artykułu będziemy je zbiorczo nazywać ALOGx .

Wszystkie te interfejsy API są łatwe w obsłudze i dobrze zrozumiałe, dzięki czemu są wszechobecne na platformie Android. W szczególności proces mediaserver , który obejmuje serwer dźwięku AudioFlinger, intensywnie wykorzystuje ALOGx .

Niemniej jednak istnieją pewne ograniczenia dotyczące ALOGx i znajomych:

  • Są podatne na „spam w dziennikach”: bufor dziennika jest zasobem współdzielonym, więc może łatwo zostać przepełniony z powodu niepowiązanych wpisów w dzienniku, co skutkuje pominięciem informacji. Wariant ALOGV jest domyślnie wyłączony w czasie kompilacji. Ale oczywiście nawet to może skutkować spamem w logach, jeśli jest włączone.
  • Podstawowe wywołania systemowe jądra mogą się blokować, co może skutkować odwróceniem priorytetów, a w konsekwencji zakłóceniami i niedokładnościami pomiarów. Jest to szczególnie istotne w przypadku wątków, w których czas jest krytyczny, takich jak FastMixer i FastCapture .
  • Jeśli określony dziennik zostanie wyłączony w celu ograniczenia spamu w dziennikach, wszelkie informacje, które zostałyby przechwycone przez ten dziennik, zostaną utracone. Nie jest możliwe włączenie konkretnego dziennika z mocą wsteczną, gdy stanie się jasne, że dziennik byłby interesujący.

NBLOG, media.log i MediaLogService

Interfejsy API NBLOG i powiązany proces media.log oraz usługa MediaLogService tworzą razem nowszy system rejestrowania multimediów i zostały zaprojektowane specjalnie z myślą o rozwiązaniu powyższych problemów. Będziemy luźno używać terminu „media.log” w odniesieniu do wszystkich trzech, ale ściśle rzecz biorąc, NBLOG to interfejs API rejestrowania w C++, media.log to nazwa procesu w systemie Linux, a MediaLogService to usługa segregatora systemu Android służąca do sprawdzania dzienników.

„Oś czasu” media.log to seria wpisów dziennika, których względna kolejność jest zachowana. Zgodnie z konwencją każdy wątek powinien używać własnej osi czasu.

Korzyści

Zaletami systemu media.log jest to, że:

  • Nie spamuje głównego dziennika, chyba że jest to potrzebne.
  • Można sprawdzić nawet w przypadku awarii lub zawieszenia mediaserver .
  • Nie blokuje na osi czasu.
  • Zapewnia mniej zakłóceń w działaniu. (Oczywiście żadna forma rejestrowania nie jest całkowicie nieinwazyjna.)

Architektura

Poniższy diagram pokazuje związek procesu mediaserver z procesem init przed wprowadzeniem media.log :

Architektura przed media.log

Rysunek 1. Architektura przed media.log

Godne uwagi punkty:

  • init fork i execs mediaserver .
  • init wykrywa śmierć mediaserver i w razie potrzeby ponownie forkuje.
  • Rejestrowanie ALOGx nie jest wyświetlane.

Poniższy diagram przedstawia nową relację komponentów po dodaniu media.log do architektury:

Architektura według media.log

Rysunek 2. Architektura po media.log

Ważne zmiany:

  • Klienci używają interfejsu NBLOG API do tworzenia wpisów dziennika i dołączania ich do bufora cyklicznego w pamięci współdzielonej.
  • MediaLogService może w dowolnym momencie zrzucić zawartość bufora cyklicznego.
  • Bufor cykliczny został zaprojektowany w taki sposób, że jakiekolwiek uszkodzenie pamięci współdzielonej nie spowoduje awarii MediaLogService i nadal będzie w stanie zrzucić taką część bufora, na którą nie ma wpływu uszkodzenie.
  • Bufor cykliczny nie blokuje się i nie blokuje zarówno zapisu nowych wpisów, jak i odczytu istniejących wpisów.
  • Do zapisu lub odczytu z bufora cyklicznego nie są wymagane żadne wywołania systemowe jądra (inne niż opcjonalne znaczniki czasu).

Gdzie używać

Począwszy od Androida 4.4, tylko kilka punktów dziennika w AudioFlingerze korzysta z systemu media.log . Chociaż nowe interfejsy API nie są tak łatwe w użyciu jak ALOGx , nie są też niezwykle trudne. Zachęcamy do zapoznania się z nowym systemem logowania, na wypadek sytuacji, gdy jest on niezbędny. W szczególności jest to zalecane w przypadku wątków AudioFlinger, które muszą działać często, okresowo i bez blokowania, takich jak wątki FastMixer i FastCapture .

Jak używać

Dodaj logi

Najpierw musisz dodać logi do swojego kodu.

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

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

Ponieważ ta oś czasu NBLog jest używana tylko przez wątki FastMixer i FastCapture , nie ma potrzeby wzajemnego wykluczania.

W innych wątkach AudioFlinger użyj mNBLogWriter :

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

W przypadku wątków innych niż FastMixer i FastCapture oś czasu NBLog wątku może być używana zarówno przez sam wątek, jak i przez operacje powiązania. NBLog::Writer nie zapewnia żadnego ukrytego wzajemnego wykluczania na osi czasu, więc upewnij się, że wszystkie dzienniki występują w kontekście, w którym przechowywany jest muteks mLock wątku.

Po dodaniu dzienników zbuduj ponownie AudioFlinger.

Przestroga: dla każdego wątku wymagana jest osobna oś czasu NBLog::Writer , aby zapewnić bezpieczeństwo wątku, ponieważ osie czasu z założenia pomijają muteksy. Jeśli chcesz, aby więcej niż jeden wątek korzystał z tej samej osi czasu, możesz zabezpieczyć go za pomocą istniejącego muteksu (jak opisano powyżej dla mLock ). Możesz też użyć opakowania NBLog::LockedWriter zamiast NBLog::Writer . Neguje to jednak główną zaletę tego interfejsu API: jego brak blokowania.

Pełne API NBLog znajduje się pod frameworks/av/include/media/nbaio/NBLog.h .

Włącz media.log

media.log jest domyślnie wyłączony. Jest aktywny tylko wtedy, gdy właściwość ro.test_harness ma wartość 1 . Możesz to włączyć poprzez:

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, więc:

adb shell
Polecenie ps media wyświetli teraz dwa procesy:
  • media.log
  • serwer mediów

Zanotuj identyfikator procesu mediaserver na później.

Wyświetl osie czasu

W dowolnym momencie możesz ręcznie zażądać zrzutu dziennika. To polecenie wyświetla logi ze wszystkich aktywnych i ostatnich osi czasu, a następnie je czyści:

dumpsys media.log

Należy pamiętać, że z założenia osie czasu są niezależne i nie ma możliwości łączenia osi czasu.

Odzyskaj logi po śmierci serwera multimediów

Teraz spróbuj zabić proces mediaserver : kill -9 # , gdzie # to identyfikator procesu, który zanotowałeś wcześniej. Powinieneś zobaczyć zrzut z media.log w głównym logcat , pokazujący wszystkie logi prowadzące do awarii.

dumpsys media.log