在 Android TV 平台上做 ACR(Automatic Content Recognition)需要擷取裝置正在播放的音訊做指紋比對。ALSA loopback 是第一個想到的方案。在 Amlogic S905X4 上花了一天驗證,結論是:loopback 硬體沒壞,但它監聽的那條線上沒有信號。
問題出在 Amlogic Audio HAL 的路由設計。AudioFlinger 的 PCM 資料寫進 TDM-B 之後,HAL 在音訊到達硬體 I/O 腳位之前就把它攔走,送進 Dolby MS12 DSP 做混音和處理,再透過一條 ALSA 完全看不到的內部 SPDIF 路徑送到 HDMI TX。Loopback 設備指向的是 TDMOUT_B 的硬體輸出腳位——那裡什麼都沒有。
S905X4 的音訊硬體架構
7 個 PCM 裝置與 Loopback 的關係
S905X4 在一張 ALSA card(AML-AUGESOUND)上掛了 7 個 PCM 裝置:
| 裝置 | 類型 | 角色 |
|---|---|---|
| 0 | TDM-A | 未使用(dummy) |
| 1 | TDM-B | AudioFlinger 主要輸出(T9015 DAC) |
| 2 | TDM-C | I2S 到 HDMI |
| 3 | PDM | 麥克風輸入 |
| 4 | SPDIF | 數位音訊 I/O(光纖/同軸) |
| 5 | SPDIF-B | 次要數位輸出 |
| 6 | LOOPBACK-A | 僅擷取用的 loopback |
AudioFlinger 寫入裝置 1(TDM-B)。Loopback 的 mixer control 設定指向 TDMOUT_B(control #22 → TDMIN_B,control #23 → TDMOUT_B)。看起來應該能抓到音訊。
音訊實際走的路徑
從 ALSA userspace 的角度,TDM-B 看起來像一個普通的音訊輸出——/proc/asound/card0/pcm1p/info 沒有任何異常。但實際上 HAL 在音訊到達 TDM-B 的硬體腳位之前就攔截了它:
AudioFlinger
→ ALSA Device 1 (TDM-B) — HAL 接手點
→ Amlogic Audio HAL
→ Dolby MS12 DSP(混音、DRC、EQ)
→ 內部 SPDIF encoder(不暴露給 ALSA)
→ HDMI TX
Loopback 監聽的是 TDMOUT_B 的硬體 I/O 腳位。但 TDM-B 的 speaker 輸出是靜音的(mixer control #69 SPK mute = On),因為 HAL 在 PCM 到達腳位前就把它導走了。Loopback 指向的是一條空線。
關鍵的診斷 control 是 #65(HDMITX Audio Source Select),它的值是 Spdif——這直接告訴你 HDMI TX 從內部 SPDIF bus 讀音訊,不是從任何 TDM 輸出。一旦知道這個,就知道基於 TDM 的 loopback 不可能抓到 HDMI 音訊。
實測驗證
A/B 測試:繞過 HAL vs 走 AudioFlinger
為了確認是路由問題而不是 loopback 硬體壞了,做了對照測試:
| 測試 | 做了什麼 | Loopback 來源 | 結果 |
|---|---|---|---|
| 1 | tinyplay 直接寫 WAV 到 TDM-A(裝置 0) | TDMOUT_A | -18 dB(有音訊) |
| 2 | tinyplay 直接寫 WAV 到 TDM-C(裝置 2) | TDMOUT_C | -25 dB(有音訊) |
| 3 | 播放影片,擷取 AudioFlinger 輸出 | TDMOUT_B | -999 dB(靜音) |
| 4 | 取消 SPK 靜音,播放 UI 音效 | TDMOUT_B | -999 dB(靜音) |
測試 1-2 繞過 HAL——tinyplay 直接把 PCM 寫到 ALSA 裝置,音訊真的到達硬體腳位,loopback 正常擷取。測試 3-4 走 AudioFlinger → HAL → MS12 路徑,音訊在腳位之前就被轉走了。
SPDIF 內部迴路的陷阱
Mixer control #14(Audio spdifin source)有一個 spdifout 選項,聽起來可以把 SPDIF 輸出迴路到輸入。實測結果:靜音。
原因是平台上有兩個不同的「SPDIF」:
- ALSA SPDIF(裝置 4):物理 SPDIF I/O,用於外接光纖/同軸
- HAL 內部 SPDIF:MS12 DSP 處理後送往 HDMI TX 的路徑,不暴露給 ALSA
Control #14 的 spdifout 迴路的是 ALSA SPDIF 裝置自己,不是 HAL 的 HDMI 路徑。這兩條是不同的 bus。
備註:這個判斷基於 control 命名和靜音測試結果推論,未透過 kernel source 或暫存器 dump 直接確認。
Control #29 的未啟用路由選項
Control #29(Audio In Source)有一些看起來有用的選項:TDMIN_LB、LOOPBACK_A/B、SPDIFIN_LB、FRHDMIRX。這些名稱暗示 SoC 矽片可能支援 post-MS12 音訊的內部路由。但全部測試結果為 null。
可能的原因:驅動沒有接線、需要搭配其他 control 或 DTS 設定、需要特定的取樣率。沒有查看 kernel source(sound/soc/amlogic/auge/),所以無法確認。
ACR 的替代方案
硬體 loopback 不可行之後,剩下兩條路:
| 策略 | 擷取點 | 覆蓋範圍 | 限制 |
|---|---|---|---|
| AudioPlaybackCapture | AudioFlinger 層(HAL 之上) | 非 DRM 內容 | 兩個阻擋點 |
| HAL post-MS12 tap | MS12 DSP 之後(需要 vendor 修改) | 所有內容含 DRM | 需要晶片廠配合 |
AudioPlaybackCapture 的兩個阻擋點
AudioPlaybackCapture 在 AudioFlinger 層擷取,不依賴硬體,所以不受 loopback 路由問題影響。但它有兩個阻擋點:
-
allowAudioPlaybackCapture=false:Netflix 等 app 在 manifest 中明確 opt out,AudioFlinger 對這些 session 回傳靜音 buffer。 -
Tunneled Playback:Widevine L1 內容(Netflix 4K、Disney+ 等)的音訊解碼在 TEE 內部執行,透過 Android tunneled playback 路徑直接從 secure decoder 送到 HAL,音訊從頭到尾不經過 AudioFlinger。AudioPlaybackCapture 抓不到根本沒進 AudioFlinger 的東西。這個限制比 manifest flag 更根本。
實際覆蓋率取決於 app 組合——有多少觀看時間是非 DRM、非 tunneled 的內容(FAST 頻道、免費廣告支持內容等)。
HAL post-MS12 tap
要完整覆蓋(DRM + tunneled + HDMI-in),需要在 HAL 層做一個 post-MS12 的 loopback tap。Control #29 的那些 null 選項暗示矽片可能有這個能力,但需要晶片廠把它接上。
如果要向晶片廠提需求,關鍵規格:tap 必須輸出 downmixed LPCM,在解碼之後、重新編碼之前(例如在 Dolby MAT 封裝之前)。如果 tap 點在編碼之後,拿到的是壓縮 bitstream,ACR 指紋比對用不了。
總結:Debug 方法論
這次調查中最有效的 debug 方式是從輸出端往回追。Control #65(HDMITX Audio Source Select = Spdif)直接告訴你 HDMI TX 的音源是內部 SPDIF bus,不是 TDM——如果一開始就檢查這個 control,可以省下好幾個小時的 loopback 測試。
幾個具體的 takeaway:
- ALSA 裝置編號在這個平台上有誤導性。 裝置 1(TDM-B)看起來像普通音訊輸出,但 HAL 在 PCM 到達硬體腳位前就攔截了。從 userspace 看不出來。
- MS12 從 ALSA 的角度是黑盒。 音訊進了 DSP 之後,剩下的路徑完全由 HAL 控制,ALSA 沒有能見度。
- Mixer control 選項存在不代表它能用。
spdifout迴路和SPDIFIN_LB內部路由都是存在的選項,但都產生靜音。永遠用tinycap+sox實測,不要只看 control 名稱。 - 診斷 SoC 音訊路由時,先查輸出端的 source select control。 它直接告訴你音訊從哪條 bus 來,省去逐條測試的時間。
備註:測試結果基於 Amlogic S905X4 單一 SoC。其他 Amlogic 型號(S905X3、S928X 等)的音訊路由可能不同。Control #29 的內部路由選項未搭配 DTS 變更或其他 control 組合測試,不排除可啟用的可能。