本文介紹了調試 Android 音頻的一些提示和技巧。
三通水槽
“tee sink”是一個 AudioFlinger 調試功能,僅在自定義版本中可用,用於保留最近音頻的一小段以供以後分析。這允許在實際播放或錄製的內容與預期內容之間進行比較。
出於隱私考慮,默認情況下,在編譯時和運行時禁用 tee sink。要使用 tee sink,您需要通過重新編譯以及設置屬性來啟用它。請務必在完成調試後禁用此功能;不應在生產版本中啟用 tee sink。
本節中的說明適用於 Android 7.x 及更高版本。對於 Android 5.x 和 6.x,將/data/misc/audioserver替換為/data/misc/media 。此外,您必須使用 userdebug 或 eng build。如果您使用 userdebug 構建,則使用以下命令禁用驗證:
adb root && adb disable-verity && adb reboot
編譯時設置
cd frameworks/av/services/audioflinger- 編輯
Configuration.h。 - 取消註釋
#define TEE_SINK。 - 重新構建
libaudioflinger.so。 -
adb root -
adb remount - 將新的
libaudioflinger.so推送或同步到設備的/system/lib。
運行時設置
adb shell getprop | grep ro.debuggable
確認輸出為:[ro.debuggable]: [1]-
adb shell -
ls -ld /data/misc/audioserver確認輸出為:
drwx------ media media ... media
如果該目錄不存在,請按如下方式創建:
mkdir /data/misc/audioserverchown media:media /data/misc/audioserver -
echo af.tee=# > /data/local.prop
其中af.tee值是下面描述的數字。 -
chmod 644 /data/local.prop -
reboot
af.tee屬性的值
af.tee的值是一個介於 0 和 7 之間的數字,表示幾個位的總和,每個特徵一個。請參閱 AudioFlinger.cpp 中AudioFlinger.cpp AudioFlinger::AudioFlinger()中的代碼以了解每個位的說明,但簡要說明:
- 1 = 輸入
- 2 = FastMixer 輸出
- 4 = 每軌 AudioRecord 和 AudioTrack
目前還沒有深度緩衝區或普通混合器的位,但您可以使用“4”獲得類似的結果。
測試和獲取數據
- 運行您的音頻測試。
-
adb shell dumpsys media.audio_flinger - 在
dumpsys輸出中查找一行,如下所示:
tee copied to /data/misc/audioserver/20131010101147_2.wav
這是一個 PCM .wav 文件。 - 然後
adb pull任何感興趣的/data/misc/audioserver/*.wav文件;請注意,特定於軌道的轉儲文件名不會出現在dumpsys輸出中,但在軌道關閉時仍會保存到/data/misc/audioserver。 - 在與他人共享之前,請查看轉儲文件是否存在隱私問題。
建議
嘗試這些想法以獲得更有用的結果:
- 禁用觸摸聲音和按鍵點擊以減少測試輸出的中斷。
- 最大化所有捲。
- 如果您的測試不感興趣,請禁用通過麥克風發出聲音或錄音的應用程序。
- 特定於軌道的轉儲僅在軌道關閉時保存;您可能需要強制關閉應用程序以轉儲其特定於軌道的數據
- 測試後立即執行
dumpsys;可用的記錄空間有限。 - 為確保您不會丟失轉儲文件,請定期將它們上傳到您的主機。只保留有限數量的轉儲文件;達到該限制後,舊轉儲將被刪除。
恢復
如上所述,不應啟用 tee sink 功能。按如下方式恢復您的構建和設備:
- 將源代碼更改還原為
Configuration.h。 - 重新構建
libaudioflinger.so。 - 將恢復的
libaudioflinger.so推送或同步到設備的/system/lib。 -
adb shell -
rm /data/local.prop -
rm /data/misc/audioserver/*.wav -
reboot
媒體日誌
ALOGx 宏
Android SDK 中的標準 Java 語言日誌記錄 API 是android.util.Log 。
Android NDK 中對應的 C 語言 API 是<android/log.h>中聲明的__android_log_print 。
在 Android 框架的原生部分中,我們更喜歡名為ALOGE 、 ALOGW 、 ALOGI 、 ALOGV等的宏。它們在<utils/Log.h>中聲明,為了本文的目的,我們將它們統稱為ALOGx .
所有這些 API 都易於使用且易於理解,因此它們在整個 Android 平台中無處不在。特別是mediaserver進程,包括 AudioFlinger 聲音服務器,廣泛使用ALOGx 。
然而, ALOGx和朋友有一些限制:
- 它們容易受到“日誌垃圾郵件”的影響:日誌緩衝區是共享資源,因此很容易由於不相關的日誌條目而溢出,從而導致信息丟失。默認情況下,
ALOGV變體在編譯時被禁用。但是當然,如果啟用它,它也會導致日誌垃圾郵件。 - 底層內核系統調用可能會阻塞,可能導致優先級反轉,從而導致測量干擾和不准確。這對於諸如
FastMixer和FastCapture等時間要求嚴格的線程來說尤其重要。 - 如果禁用特定日誌以減少日誌垃圾郵件,則該日誌將捕獲的任何信息都將丟失。在很明顯該日誌會很有趣之後,無法追溯啟用特定日誌。
NBLOG、media.log 和 MediaLogService
NBLOG API 和相關的media.log進程和MediaLogService服務共同構成了一個更新的媒體日誌系統,專門用於解決上述問題。我們將鬆散地使用術語“media.log”來指代所有這三個,但嚴格來說, NBLOG是 C++ 日誌記錄 API, media.log是 Linux 進程名稱, MediaLogService是用於檢查日誌的 Android 綁定服務。
media.log “時間線”是一系列日誌條目,其相對順序被保留。按照慣例,每個線程都應該使用自己的時間線。
好處
media.log系統的好處是:
- 除非需要,否則不會向主日誌發送垃圾郵件。
- 即使在
mediaserver崩潰或掛起時也可以檢查。 - 每個時間線都是非阻塞的。
- 對性能的干擾較小。 (當然,沒有任何形式的日誌記錄是完全非侵入性的。)
建築學
下圖展示了mediaserver進程和init進程的關係,在引入media.log之前:

圖 1. media.log 之前的架構
值得注意的點:
-
init分叉並執行mediaserver。 -
init檢測到mediaserver的死亡,並根據需要重新分叉。 - 未顯示
ALOGx日誌記錄。
下圖顯示了將media.log添加到架構後的新組件關係:

圖 2. media.log 之後的架構
重要變化:
- 客戶端使用
NBLOGAPI 構建日誌條目並將它們附加到共享內存中的循環緩衝區。 -
MediaLogService可以隨時轉儲循環緩衝區的內容。 - 循環緩衝區的設計方式使得共享內存的任何損壞都不會導致
MediaLogService崩潰,並且它仍然能夠轉儲盡可能多的不受損壞影響的緩衝區。 - 循環緩衝區對於寫入新條目和讀取現有條目都是非阻塞和無鎖的。
- 不需要內核系統調用來寫入或讀取循環緩衝區(可選的時間戳除外)。
在哪裡使用
從 Android 4.4 開始,AudioFlinger 中只有少數幾個日誌點使用了media.log系統。儘管新的 API 不像ALOGx那樣容易使用,但它們也不是特別難。我們鼓勵您在必不可少的情況下學習新的日誌記錄系統。特別是,推薦用於必須頻繁、定期且無阻塞地運行的 AudioFlinger 線程,例如FastMixer和FastCapture線程。
如何使用
添加日誌
首先,您需要將日誌添加到您的代碼中。
在FastMixer和FastCapture線程中,使用如下代碼:
logWriter->log("string");
logWriter->logf("format", parameters);
logWriter->logTimestamp();
由於此NBLog時間線僅由FastMixer和FastCapture線程使用,因此不需要互斥。
在其他 AudioFlinger 線程中,使用mNBLogWriter :
mNBLogWriter->log("string");
mNBLogWriter->logf("format", parameters);
mNBLogWriter->logTimestamp();
對於FastMixer和FastCapture以外的線程,線程本身和活頁夾操作都可以使用線程的NBLog時間線。 NBLog::Writer不為每個時間線提供任何隱式互斥,因此請確保所有日誌都發生在線程的互斥體mLock所在的上下文中。
添加日誌後,重新構建 AudioFlinger。
注意:每個線程都需要一個單獨的NBLog::Writer時間線,以確保線程安全,因為時間線在設計上忽略了互斥鎖。如果您希望多個線程使用相同的時間線,則可以使用現有的互斥鎖進行保護(如上述mLock所述)。或者您可以使用NBLog::LockedWriter包裝器而不是NBLog::Writer 。然而,這否定了這個 API 的一個主要好處:它的非阻塞行為。
完整的NBLog API 位於frameworks/av/include/media/nbaio/NBLog.h 。
啟用 media.log
media.log默認禁用。僅當屬性ro.test_harness為1時才有效。您可以通過以下方式啟用它:
adb rootadb shellecho ro.test_harness=1 > /data/local.propchmod 644 /data/local.propreboot
重新啟動期間連接丟失,因此:
adb shell命令
ps media現在將顯示兩個進程:- 媒體日誌
- 媒體服務器
記下mediaserver的進程 ID 以備後用。
顯示時間線
您可以隨時手動請求日誌轉儲。此命令顯示所有活動和最近時間線的日誌,然後清除它們:
dumpsys media.log
請注意,按照設計時間線是獨立的,並且沒有用於合併時間線的工具。
媒體服務器死亡後恢復日誌
現在嘗試殺死mediaserver進程: kill -9 # ,其中 # 是您之前記下的進程 ID。您應該在主logcat中看到來自media.log的轉儲,顯示導致崩潰的所有日誌。
dumpsys media.log