USB音效卡解碼器連接Android手機時問題的出現和分析[三] 從Android源碼找出采樣率鎖定的原因
農步祥 于 2017.09.28 02:05:36 | 源自:www.soomal.com | 版權:原創
平均/總評分:09.92/129

在前兩篇文章中,我們通過偶然機會發現了Android智能手機在搭配外接USB音效卡、解碼器等音頻裝置時存在的一些兼容性問題。除了OTG模式不穩定、供電不佳、設定無效、經常需要重啟外,多數Android裝置的音樂播放器會將外接USB音效卡的播放采樣率鎖定在一個較高值上[通常是192kHz],而主流的44.1kHz音樂源均會SRC至192kHz播放,對音質體驗帶來了負面影響。

Android裝置連接USB音效卡時的工作流程分析

雖然從Android 5.0版本以來,許多Android裝置即使存在SRC現象,也比更早時期的Android SRC對音質的負面影響改善不少,處于可以接受的程度。但對于對音質要求更高的使用場合來說,尤其是欣賞無損和高清音樂時,其聲音相對應有的正常音質水準還是明顯粗糙。那么這個問題從何而來?那么首先應該簡單了解一下Android的USB音頻架構的大致工作流程。

在《Qualcomm 高通晶片組與Android音頻系統缺陷測評分析 》[作者:趙宇為 ] 一文中,我們已經簡單介紹了Android音頻架構的工作流程,雖然這個流程圖是用于解釋高通手機在Android系統下的音頻架構,而在連接USB裝置時,其工作原理是完全一致的,只不過硬體驅動由手機內置音頻CODEC晶片或SoC廠商的驅動改成了通用的UAC[USB Audio Class]驅動。另外在我們翻譯自海外網站的一篇文章《Android的10毫秒問題 解讀Android系統音頻通道延遲缺陷》[作者:Gabor、Patrick ] 中,里面有一張動態gif圖也直觀地體現了Android音頻系統的工作流程,將兩者結合,我們可以得出一個新的圖表。

和當前多數Android裝置的內置音頻系統SRC一樣,在連接USB2.0音效卡時,系統的采樣率會鎖定在192kHz采樣率上,在使用系統內置的音樂播放器或網易云等應用時,也會統一SRC至192kHz播放。

Android系統外接USB音頻裝置時采樣率鎖定深度分析

雖然明白了Android裝置SRC的工作流程,但這并不能解釋外接USB音效卡時會SRC至192而不是Android手機更常見的48kHz,一開始我個人初步判斷為Android系統去傾向于設定USB音效卡或UAC驅動所支援的最高采樣率值。然而蛋爺表示,許多USB音效卡所支援的最高采樣率已經達到384kHz,為何卻偏偏是鎖定192呢?這里便涉及到一個有關Android系統底層的問題:Android系統是如何獲得和設定USB音效卡的采樣率的?

為了獲得答案,我們首先要確認Android的UAC驅動能支援多高的采樣率,和桌面Linux系統一樣,Android的內核音效卡驅動主要來源自開源的ALSA。其音效卡資訊會在系統的/proc/asound/目錄下顯示,我們在接上樂之邦或XMOS音效卡后,可以從/proc/asound/card1目錄中獲得外接USB音效卡的資訊。

從顯示資訊來看,數字時代2在Android下最高支援至384kHz,看來鎖定192并非來自硬體驅動的限制。看來這個問題是源自Android系統自身了,很大概率問題是出現在Android的硬體抽象管理層HAL驅動上,那么我們所獲得的資訊是否就到此為止了呢?答案當然是否定的,由于Android的絕大部分代碼是開源的,任何人都能在網上通過網頁瀏覽器和git等開發工具軟體隨意檢視。本著還殘留一些碼農的編程基礎和Linux系統知識,在花費了兩小時后,找到了鎖定采樣率的真正原因,由于涉及編程知識,對這方面缺乏了解的讀者如果覺得內容實在太硬,可以直接跳過看本文結尾的總結。

Android的HAL部分源代碼基本公開,其USB音頻管理部分源碼位于/hal/audio_extn/usb.c中。從代碼片段中,我們可以輕易找到涉及采樣率的幾個關鍵點:

這兩段代碼片段包含的內容是:定義了一個數組,里面包含了USB音頻裝置支援的采樣率集合supported_sample_rates{44100, 48000, 64000, 88200, 96000, 176400, 192000, 384000}。還定義了一個整數為MAX_SAMPLE_RATE_SIZE,也就是這個集合的最高值,根據算式可以得出結果為八[即384000,384kHz,在編程語言中數組集合從0開始,實際結果是7]。而在初始化usb_card_config中,系統設定的采樣率正是最高的MAX_SAMPLE_RATE_SIZE。按照這段源代碼的邏輯,在正常的情況下,Android系統的USB外接音頻裝置初始化采樣率應該是裝置所支援的最大值[數字時代2或XMOS都支援至384kHz]而非192kHz。

根據這三段代碼線索,我們在同一個代碼檔案中找到了HAL獲取USB裝置采樣率的方式函數usb_get_sample_rates。毫不意外的是,HAL正是讀取/proc/asound目錄中的音效卡資訊獲得了采樣率列表,然而在判斷音效卡最大采樣率上,源代碼是這么寫的:

由于在這段遍歷循環代碼中,由于需要判斷音效卡實際支援的采樣率和supported_sample_rates中的數字對應,也就是合集,由于剔除了許多USB音效卡不支援的64000采樣率,以及supported_sample_rates中沒有的352800,HAL自己應獲得的裝置采樣率應該是合集{44100, 48000, 88200, 96000, 176400, 192000, 384000},然而系統日志中最終輸出顯示,系統最終獲得的采樣率合集是{44100, 48000, 88200, 96000, 176400, 192000},384000則無故“消失”了。

這也正是初始化USB音效卡usb_card_config時所獲得的最大值,如果是SA9027或PCM270X等采樣率低于192kHz的USB音頻方案,則采用音效卡硬體驅動所支援的采樣率最大值,例如48kHz、96kHz等,這也就是Android系統裝置在連接樂之邦、XMOS等USB2.0高性能異步音頻方案時鎖定192kHz的“罪魁禍首”。

總結

造成如此奇妙的SRC問題,原因卻如此的簡單:只是由于Android HAL以及AudioFlinger源代碼開發者的錯誤,就造成了USB音頻裝置在Android系統下采樣率鎖定在192kHz的問題。為何至今卻沒有被谷歌旗下任何高級攻城獅和碼農們發現和解決?最直接的原因自然是Android源代碼的擁有者谷歌對于這部分應用從來沒有真正上心過,Android手機是目前普及度最高的個人終端裝置,音頻愛好者自然會對音質有更高的要求,那谷歌在Android 6.0開始就著力在PDF上宣傳的所謂專業Android音頻系統,到底是對誰說的?

盡管外接USB音頻裝置時,Android用戶的確還有海貝音樂應用這一選擇,也有類似OPPO R11等系統自帶USB音頻采樣率自動識別切換的手機個例,但海貝畢竟無法支援云音樂應用,而OPPO則是由于自身也是高階音頻裝置的廠商,自然需要讓HA-2等便攜式或臺式解碼器在OPPO手機下正常工作。對于廣大Android手機用戶群體以及忠實的云音樂用戶來說,是否就只能等待谷歌或者手機廠商自己去良心發現或利益需求后才能解決呢?

轉發到新浪微博 轉發到騰訊微博 RSS訂閱 收藏本文 本文代碼
請您評分 1 2 3 4 5 6 7 8 9 10
123.121.005.***
123.121.005.***
發表于2017.10.07 17:31:29
62
049.130.131.***
049.130.131.***
發表于2017.10.06 13:18:43
61
國慶節前升級了Flyme7.0結果外接裝置各種bug都出現了。一會無聲,一會卡頓破音。真是走倒退路了!
發表于2017.10.06 13:06:44
60
222.188.148.***
222.188.148.***
發表于2017.10.05 01:04:17
59
從14年開始用這個組合一直到現在,云音樂什么的壓根不理會,裝一個QQ音樂有時候搜需要的歌就行
此帖使用Win10提交
發表于2017.10.04 00:15:00
58
122.059.077.***
122.059.077.***
發表于2017.10.03 12:42:24
57
117.136.070.***
117.136.070.***
發表于2017.10.02 11:53:31
56
112.193.093.***
112.193.093.***
發表于2017.10.01 07:45:09
55
125.068.011.***
125.068.011.***
發表于2017.09.30 07:21:10
54
發表于2017.09.29 18:12:07
53
384000確實丟失了,但看樣子問題不在這段代碼。編輯親自編譯源碼驗證,贊
發表于2017.09.29 18:06:15
52
111.076.***.***
111.076.***.***
一看就知道了啊,'i<MAX_SAMPLE_RATE_SIZE',MAX_SAMPLE_RATE_SIZE=7,length(support_sample_rates)=8
所以384000漏掉了
此帖使用Win10提交
發表于2017.09.29 17:16:54
51
next_sr_string是用strtok获取的,strtok 并不是线程安全的函数,会不会是这个问题?

next_sr_string是用strtok获取的,strtok 并不是线程安全的函数,会不会是这个问题?
如果能改代码,把next_sr_string和sr打印一下,就很清楚了。
發表于2017.09.29 16:02:30
50
看不懂
此帖使用Lumia 950提交
發表于2017.09.29 13:32:14
49
八成ALOGI_IF這個函數里有上限。
此帖使用iPhone提交
發表于2017.09.29 10:53:05
48
036.023.***.***
036.023.***.***
就是找音效卡的采樣率和系統支援的采樣率的交集,至于為什么漏了384000,應該有別的原因。
此帖使用HTC U-3W提交
發表于2017.09.29 09:25:02
47
就這么簡單
此帖使用ZUK Z2131提交
發表于2017.09.29 06:20:01
46
122.226.185.***
122.226.185.***
發表于2017.09.28 22:46:43
45
125.073.***.***
125.073.***.***
發表于2017.09.28 20:07:48
44
123.015.***.***
123.015.***.***
(手動滑稽)
此帖使用Win10提交
發表于2017.09.28 19:26:20
43
提示本貼可以匿名回復 ,您現在正處在潛水狀態
回復
驗證碼
6459 為防止廣告機貼垃圾,不得已而為之
表情
正文