本文將說明一些 Android 音訊偵錯的訣竅。
Tee 接收器
「tee sink」是 AudioFlinger 偵錯功能,僅適用於自訂版本,可保留近期音訊的短片段,供日後分析。這樣一來,您就能比較實際播放或錄製的內容,與預期的內容。
為了保護隱私,TEE 接收器預設會在編譯和執行期間停用。如要使用 tee 匯出端,您必須重新編譯並設定屬性才能啟用。請務必在偵錯完成後停用這項功能;在正式版版本中,請勿讓 tee sink 保持啟用狀態。
本節的操作說明適用於 Android 7.x 以上版本。如果是 Android 5.x 和 6.x,請將 /data/misc/audioserver
替換為 /data/misc/media
。此外,您必須使用 userdebug 或 eng 版本。如果您使用的是 userdebug 版本,請使用以下指令停用 verity:
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/audioserver
chown 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::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
,因為錄製空間有限。 - 為確保不會遺失傾印檔案,請定期將這些檔案上傳至主機。系統只會保留有限數量的傾印檔案;達到該限制後,系統會移除較舊的傾印檔案。
還原
如上所述,請勿啟用 T 型接頭功能。依下列步驟還原建構和裝置:
- 將原始碼變更還原為
Configuration.h
。 - 重新建構
libaudioflinger.so
。 - 將已還原的
libaudioflinger.so
推送或同步至裝置的/system/lib
。 adb shell
rm /data/local.prop
rm /data/misc/audioserver/*.wav
reboot
media.log
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. 媒體.log 之前的架構
重要事項:
init
分支並執行mediaserver
。init
會偵測mediaserver
的死亡,並視需要重新分支。- 不會顯示
ALOGx
記錄。
下圖顯示在架構中加入 media.log
後,元件之間的新關係:

圖 2. 導入 media.log 後的架構
重要異動:
-
用戶端會使用
NBLOG
API 建構記錄項目,並將這些項目附加至共用記憶體中的循環緩衝區。 -
MediaLogService
可隨時轉儲循環緩衝區的內容。 -
環形緩衝區的設計方式可確保共用記憶體的任何毀損情形都不會導致
MediaLogService
當機,且仍可傾印未受毀損情形影響的大部分緩衝區。 - 循環緩衝區不阻斷且無鎖定,可用於寫入新項目和讀取現有項目。
- 寫入或讀取環形緩衝區時,不需要使用核心系統呼叫 (除了選用的時間戳記)。
適用商家
在 Android 4.4 中,AudioFlinger 中只有少數記錄點會使用 media.log
系統。雖然新版 API 不如 ALOGx
容易使用,但也不會太難。建議您瞭解新記錄系統,以便在必要時使用。特別是,建議將 AudioFlinger 執行緒用於必須經常執行、定期執行且不會封鎖的情況,例如 FastMixer
和 FastCapture
執行緒。
使用方法
新增記錄
首先,您需要在程式碼中加入記錄。
在 FastMixer
和 FastCapture
執行緒中,請使用類似以下的程式碼:
logWriter->log("string"); logWriter->logf("format", parameters); logWriter->logTimestamp();
由於只有 FastMixer
和 FastCapture
執行緒會使用這個 NBLog
時間軸,因此不需要互相排除。
在其他 AudioFlinger 執行緒中,請使用 mNBLogWriter
:
mNBLogWriter->log("string"); mNBLogWriter->logf("format", parameters); mNBLogWriter->logTimestamp();
對於 FastMixer
和 FastCapture
以外的執行緒,執行緒的 NBLog
時間軸可由執行緒本身和 Binder 作業使用。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 root
adb shell
echo ro.test_harness=1 > /data/local.prop
chmod 644 /data/local.prop
reboot
重新啟動時連線會中斷,因此:
adb shell
ps media
指令會顯示兩個程序:
- media.log
- mediaserver
請記下 mediaserver
的程序 ID,以便稍後使用。
顯示時間軸
您隨時可以手動要求記錄傾印。這個指令會顯示所有有效和最近時間軸的記錄,然後清除這些記錄:
dumpsys media.log
請注意,根據設計,時間軸是獨立的,沒有合併時間軸的功能。
在媒體伺服器終止後復原記錄
接著,請嘗試終止 mediaserver
程序:kill -9 #
,其中 # 是您先前記下的程序 ID。您應該會在主 logcat
中看到來自 media.log
的傾印,顯示導致當機的所有記錄。
dumpsys media.log