电竞比分网-中国电竞赛事及体育赛事平台

分享

深度好文 | Android高性能音頻解析

 Neal199 2024-08-06

1.引言

1.1目的和對(duì)象

該文檔用于Android音頻流暢性,聚焦優(yōu)化音頻卡頓、雜音問題。適用于Android音頻開發(fā)人員查看。

1.2背景

游戲、k歌、直播等一些使用場(chǎng)景,音頻對(duì)時(shí)延有較高的要求,保障低延遲,就需要更小的buffer,降低整個(gè)鏈路數(shù)據(jù)傳遞時(shí)延,但是抗性能抖動(dòng)能力就會(huì)下降。如果音頻線程CPU調(diào)度延遲,生產(chǎn)數(shù)據(jù)不及時(shí),系統(tǒng)就會(huì)錯(cuò)過buffer周期,產(chǎn)生補(bǔ)0噪聲。

一方面,Android音頻系統(tǒng)框架對(duì)于音頻性能策略設(shè)計(jì)偏保守,沒有提供類似IOS,Windows系統(tǒng)那樣的一套API 用于標(biāo)記音頻線程,系統(tǒng)也就不知道APP的線程哪些是音頻線程,哪些不是。無法對(duì)APP音頻線程在CPU資源上做一些適度地傾斜。

另一方面,Android生態(tài)控制力較弱,多數(shù)APP在音頻邏輯的實(shí)現(xiàn)過程中,忽略了CPU調(diào)度因素,沒有按照Google的建議設(shè)置音頻線程的優(yōu)先級(jí),也就是默認(rèn)的優(yōu)先級(jí)(Nice 120)。在整機(jī)CPU重載場(chǎng)景下,APP內(nèi)部音頻關(guān)聯(lián)線程,特別是解碼器線程CPU調(diào)度延遲,導(dǎo)致數(shù)據(jù)生產(chǎn)不及時(shí),觸發(fā)聲音卡頓、雜音問題,這樣的問題越來越突出。

一些APP在實(shí)現(xiàn)過程中,在關(guān)鍵的播放線程中,調(diào)用AudioManager耗時(shí)接口,阻塞了數(shù)據(jù)傳遞,導(dǎo)致了聲音卡頓。

還有一些應(yīng)用面在主線程/UI渲染線程中,調(diào)用Audio的耗時(shí)接口,導(dǎo)致游戲畫面渲染幀率降低。

與APP開發(fā)伙伴交流過程中,發(fā)現(xiàn)APP開發(fā)者和系統(tǒng)開發(fā)者,存在一些理解上的GAP,APP開發(fā)者對(duì)音頻系統(tǒng)不熟悉,而系統(tǒng)開發(fā)者對(duì)APP內(nèi)部業(yè)務(wù)邏輯不熟悉。遇到問題debug起來,耗時(shí)較長,不利于提升用戶體驗(yàn)。音頻流暢性體驗(yàn)是Android基礎(chǔ)體驗(yàn)的一個(gè)核心,也是一個(gè)痛點(diǎn)。本文聚焦音頻流暢性分析,希望能有助于音頻APP開發(fā)者和音頻系統(tǒng)開發(fā)者聯(lián)合調(diào)優(yōu),提升用戶音頻基礎(chǔ)體驗(yàn)。

1.3音頻格式

人耳的聽覺范圍是在20Hz到20KHz之間的頻段,不同于人眼的“暫留”生理特性,人耳鼓膜的靈敏度遠(yuǎn)高于人眼。兩耳間距產(chǎn)生的微秒級(jí)時(shí)延(700us)帶來的細(xì)微聲壓、聲相變化,可辨別遠(yuǎn)處的音源方位。普通人可清晰辨識(shí)低至10db的響度變化,經(jīng)過專業(yè)訓(xùn)練的“金耳朵”,可識(shí)別更低db的響度變化,人耳對(duì)時(shí)延敏感,對(duì)聲音卡頓敏感。這也是音頻采樣率(48000 fps)遠(yuǎn)高于畫面的幀率(60 fps)的原因。通過大幅增加音頻流采樣次數(shù),使得響度曲線變化更平滑,避免出現(xiàn)鄰幀之間有較大的db變化,避免聽覺上不適感。

  • 聲道數(shù):聲場(chǎng)還原,主要用于表達(dá)聲音方位立體感。例如 單聲道(1ch,mono),雙聲道立體聲(2ch,stereo),5.1聲道, 7.1聲道

圖片

圖1.1 室內(nèi)5.1聲道聲場(chǎng)還原

  •  位寬:也稱位深,即一個(gè)采樣點(diǎn)用幾個(gè)字節(jié)編碼

圖片

表1.1 PCM編碼類型

  • 音頻幀:即一個(gè)采樣點(diǎn),音頻幀大小計(jì)算方法:聲道數(shù)據(jù) * 位寬,例如:

16bit,2ch 音樂,它的音頻幀大小為:2 * 2 = 4 字節(jié)。

32bit,5.1ch 音樂,它的音頻幀大小為:4 * 6 = 24 字節(jié)。

  • 采樣率:也稱音頻幀率fps,每秒需要取樣多少次,一個(gè)采樣點(diǎn)就是一個(gè)音頻幀。通過采樣對(duì)模擬型號(hào)離散化成數(shù)字信號(hào),采樣率越高,聲音信息越豐富。人耳聽覺頻段范圍限制,無法聽到低頻聲音,Android支持的采樣率通常范圍在閉區(qū)間[8000HZ, 384000HZ]。

Android最常見采樣率:

通話:8k,16k, 32k

視頻及游戲:24k, 44.1k,48k

高清音樂:96k, 192k

圖片

圖1.2 采樣與量化

音頻需要足夠高的采樣率,使得數(shù)據(jù)更平滑。

  • 碼率:1秒時(shí)間播放了多少字節(jié)數(shù)據(jù)(或多少bit數(shù)據(jù))。

計(jì)算公式:bRate = 采樣率 *  位寬 * 聲道數(shù)

例如 48k,7.1聲道,16bit pcm編碼  碼率為:48000 * 8 * 2 = 768000 Bps

2.常見雜音類型

雜音是主觀體驗(yàn)的概念,技術(shù)上常稱為音頻卡頓,“聞香可以識(shí)女人,看波形也能知音”,不同原因,有不同的雜音波形特征。例如寫0,斷點(diǎn),重復(fù)數(shù)據(jù),削頂,截?cái)?,高頻/低頻截止,白噪聲,無規(guī)律斷點(diǎn)數(shù)據(jù)。

2.1pop音

常常稱為“破音”,是屬于斷音的一類。數(shù)據(jù)不連續(xù),有明顯跳變發(fā)生。聽起來,“啪!啪!啪!”的破音,耳朵有不適感。常見的有seek pop noise,在快進(jìn),快退,或者倍速播放時(shí),更容易遇到這類雜音。

圖片

2.2 補(bǔ)0

特征是,刪除重點(diǎn)的0數(shù)據(jù),數(shù)據(jù)就是連續(xù)的了,因?yàn)橛羞B續(xù)兩次跳變,聽起來雜音特征比pop音更明顯一些。

圖片

刪除0后:

圖片

2.3截?cái)嘁?/strong>

屬于斷音的一種,與pop音的區(qū)別是,啟播的第一幀,沒有做淡入,或者停播的最后一幀,沒有做淡出。

常見場(chǎng)景例如刷短視頻,切換視頻之間,有時(shí)候會(huì)聽到“啪!啪!啪!”的破音。簡單來說就是,需要做淡入淡出。FadeInFadeOut

2.3.1切換截?cái)嘁?/strong>

圖片

2.3.2起始截?cái)嘁?/strong>

開始播放階段,沒有做淡入(Fade In),產(chǎn)生的雜音。

圖片

2.3.3 結(jié)尾截?cái)嘁?/strong>

圖片

2.4 削頂

削頂,又稱為“削波”,原因是音頻信號(hào)的響度,超過了編碼范圍,即0db,并不是說音源存在問題,只是音源信號(hào),幅度太大,超過手機(jī)音頻系統(tǒng)的表達(dá)能力,無法進(jìn)行還原。超出的部分,被統(tǒng)一削減到0db。聽起來,聲音有連續(xù)卡頓的感覺,不自然。

圖片

2.5 蜂鳴音

聽感起來,像蜂鳴器,汽笛聲。波形上,4ms重復(fù)數(shù)據(jù),常見于游戲聲音卡頓。

圖片

2.6 嘯叫

2.6.1 什么是嘯叫?

嘯叫現(xiàn)象是指音頻信號(hào)通過揚(yáng)聲器播放后,經(jīng)過一定的傳播路徑,再次被麥克風(fēng)拾取,經(jīng)過放大器的處理后,最后經(jīng)由揚(yáng)聲器播放,倘若在 “揚(yáng)聲器-麥克風(fēng)-揚(yáng)聲器”的閉環(huán)電路中,存在某種正反饋導(dǎo)致某些音頻頻率發(fā)生自激振蕩,就會(huì)產(chǎn)生嘯叫現(xiàn)象。

常見于VOIP會(huì)議,通話雙方,如果距離太近,就容易產(chǎn)生嘯叫。聽起來,響度非常大,聲音尖銳,感官非常難受。嘯叫的產(chǎn)生會(huì)掩蓋正常語音,給人的聽感也不好,而且嘯叫頻點(diǎn)能量很高,嚴(yán)重時(shí)甚至能破壞會(huì)議中的擴(kuò)聲設(shè)備,因此我們需要對(duì)嘯叫進(jìn)行抑制。受限于尺寸,和算法功耗,手機(jī)側(cè)嘯叫抑制能力有限。

從波形上,可以看到,響度非常大。

圖片

2.6.2 嘯叫的原理

圖片

正反饋系統(tǒng),信號(hào)一次又一次的被循環(huán)放大。

2.7 環(huán)境底噪

主要是指mic錄到的環(huán)境噪聲,經(jīng)過降噪算法后,通常比較輕微,不仔細(xì)幾乎聽不出來。但是如果降噪算法調(diào)音的不均衡,可能導(dǎo)致底噪大,或者音質(zhì)損失。

圖片

2.8 其他無規(guī)律噪聲

白噪聲。

圖片

3. 音頻通路基礎(chǔ)

3.1 播放流程拆解

對(duì)系統(tǒng)來說,APP是黑盒子,不同的應(yīng)用有不同的邏輯實(shí)現(xiàn)方式,特別是游戲,可能有更復(fù)雜的業(yè)務(wù)邏輯,多達(dá)100+個(gè)線程,系統(tǒng)不知道那個(gè)是音頻播放相關(guān)的。音頻能識(shí)別到的只有AudioTrack/AudioRecord調(diào)用入口,涉及到最多兩個(gè)線程。以賴線程之間的喚醒關(guān)系推測(cè),梳理大概的流程如下:

音樂、視頻類三方APP:

圖片

游戲音頻流程:

圖片

3.2 音頻通路

3.2.1 音頻播放鏈路

圖片

3.2.2 音頻通路

圖片

限于篇幅,這里不對(duì)每個(gè)通路進(jìn)行展開介紹,可自行閱讀代碼和google官方文檔。

3.2.3 AAudio通路

Aaudio是google基于mmap思路構(gòu)建的,音頻通路。盡量減少數(shù)據(jù)傳遞環(huán)節(jié),以降低負(fù)載和功耗,同時(shí)降低層層傳遞帶來的時(shí)延。這個(gè)通路是google強(qiáng)烈推薦使用的API,可替代OpenSL ES,支持音頻低延遲。也可以通過設(shè)置參數(shù),走到低功耗通路,例如deep buffer通路。

以下以低延遲播放實(shí)例來分析AAUDIO工作原理,從systrace上看,aaudio并沒有經(jīng)過audiohal write數(shù)據(jù),可以看到audiohal的writer線程并沒有被喚醒產(chǎn)生負(fù)載。可以證明:aaudio是通過共享內(nèi)存方式來傳遞數(shù)據(jù),這種方式非常高效,避免了buffer數(shù)據(jù)層層Copy。

圖片

這里簡單介紹一下AAudio性能相關(guān)的內(nèi)容:

aaudio流程圖如下:

圖片

AAudio設(shè)置性能模式:

每個(gè) AAudioStream 都具有性能模式,而這對(duì)應(yīng)用行為的影響很大。共有三種模式:

  • AAUDIO_PERFORMANCE_MODE_NONE 是默認(rèn)模式。這種模式使用在延遲時(shí)間與節(jié)能之間取得平衡的基本流。

  • AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 使用較小的緩沖區(qū)和經(jīng)優(yōu)化的數(shù)據(jù)路徑,以減少延遲時(shí)間。

  • AAUDIO_PERFORMANCE_MODE_POWER_SAVING 使用較大的內(nèi)部緩沖區(qū),以及以延遲時(shí)間為代價(jià)換取節(jié)能優(yōu)勢(shì)的數(shù)據(jù)路徑。

可以通過調(diào)用 setPerformanceMode() 來選擇性能模式,并通過調(diào)用 getPerformanceMode() 來獲得當(dāng)前模式。

  • 音頻低延遲場(chǎng)景,需要設(shè)置AAUDIO_PERFORMANCE_MODE_LOW_LATENCY。系統(tǒng)路由到AAUDIO低延遲通路,通路buffer 只有1ms,控制周期為2ms。

  • 如果對(duì)低延遲沒有要求,盡可能的節(jié)省功耗,可設(shè)置AAUDIO_PERFORMANCE_MODE_POWER_SAVING。例如播放音樂,會(huì)走到deep buffer通路上。

在當(dāng)前版本的 AAudio 中,為了盡量減少延遲時(shí)間,必須將 AAUDIO_PERFORMANCE_MODE_LOW_LATENCY 性能模式與高優(yōu)先級(jí)回調(diào)配合使用。請(qǐng)參閱以下示例:

圖片

關(guān)于AAUDIO的詳細(xì)介紹,請(qǐng)移步Android SPEC,這里不再詳細(xì)展開:

https://developer.android.google.cn/ndk/guides/audio/aaudio/aaudio?hl=zh-cn

3.2.4 Low Latency通路

也就是常說的Fast通路,有的平臺(tái)和primary通路放在一起,有的是獨(dú)立的Low Latency通路,這樣實(shí)現(xiàn)依賴平臺(tái)的性能,特別是ADSP性能,都有各自的優(yōu)缺點(diǎn)。

圖片

FastMixer的buffer 通常設(shè)置為4-5ms,通路時(shí)延較低,但更容易受到整機(jī)性能的影響,產(chǎn)生性能雜音。

FastMixer的使用,通常是通過OpenSL ES,或者Sound pool API。

3.2.5 ULL 通路

極致低延遲通路ULL實(shí)質(zhì)上,為lowlatency通路的進(jìn)一步壓縮buffer,buffer甚至壓縮到1ms,Android非實(shí)時(shí)系統(tǒng),因此CPU調(diào)度上,無法保障1ms內(nèi)得到調(diào)度,因此,這個(gè)通路,很少有開放使用。如果追求極致低延遲,推薦使用AAudio來實(shí)現(xiàn)。

4. 音頻與CPU調(diào)度

4.1 TASK運(yùn)行狀態(tài)機(jī)

圖片
  • R:(TASK_RUNNING ,running + runnable),運(yùn)行狀態(tài),并不意味著進(jìn)程一定在運(yùn)行中,也可以在運(yùn)行隊(duì)列里;

  • S:( TASK_INTERRUPTIBLE,sleeping),可中斷的睡眠狀態(tài),進(jìn)程在等待事件完成;(淺度睡眠,可以被喚醒)

  • D:( TASK_UNINTERRUPTIBLE ),不可中斷睡眠(深度睡眠,不可以被喚醒,通常在磁盤寫入時(shí)發(fā)生,也有非IO的內(nèi)核原子操作)

  • T:( TASK_STOPPED or TASK_TRACED),停止?fàn)顟B(tài),或者跟蹤狀態(tài),可以通過發(fā)送SIGSTOP信號(hào)給進(jìn)程來停止進(jìn)程,可以發(fā)送SIGCONT信號(hào)讓進(jìn)程繼續(xù)運(yùn)行

  • X:( TASK_DEAD – EXIT_DEAD ),退出狀態(tài),進(jìn)程即將被銷毀;

  • Z:( TASK_DEAD – EXIT_ZOMBIE ),僵尸狀態(tài),子進(jìn)程退出,父進(jìn)程還在運(yùn)行,但是父進(jìn)程沒有讀到子進(jìn)程的退出狀態(tài),子進(jìn)程進(jìn)入僵尸狀態(tài);

圖片

4.2 調(diào)度器

4.2.1 調(diào)度器分類

圖片

Stop調(diào)度器,

stop_sched_class:優(yōu)先級(jí)最高的調(diào)度類,可以搶占其他所有進(jìn)程,不能被其他進(jìn)程搶占;

Deadline調(diào)度器, dl_sched_class:使用紅黑樹,把進(jìn)程按照絕對(duì)截止期限進(jìn)行排序,選擇最小進(jìn)程進(jìn)行調(diào)度運(yùn)行;

RT調(diào)度器, rt_sched_class:實(shí)時(shí)調(diào)度器,為每個(gè)優(yōu)先級(jí)維護(hù)一個(gè)隊(duì)列;

CFS調(diào)度器, cfs_sched_class:完全公平調(diào)度器,采用完全公平調(diào)度算法,引入虛擬運(yùn)行時(shí)間概念;

IDLE-Task調(diào)度器, idle_sched_class:空閑調(diào)度器,每個(gè)CPU都會(huì)有一個(gè)idle線程,當(dāng)沒有其他進(jìn)程可以調(diào)度時(shí),調(diào)度運(yùn)行idle線程;

4.2.2 調(diào)度優(yōu)先級(jí)策略

linux內(nèi)核的三種調(diào)度方法:1,SCHED_OTHER 分時(shí)調(diào)度策略,2,SCHED_FIFO實(shí)時(shí)調(diào)度策略,先到先服務(wù)3,SCHED_RR實(shí)時(shí)調(diào)度策略,時(shí)間片輪轉(zhuǎn) 

實(shí)時(shí)線程將得到優(yōu)先調(diào)用,實(shí)時(shí)進(jìn)程根據(jù)實(shí)時(shí)優(yōu)先級(jí)決定調(diào)度權(quán)值,分時(shí)進(jìn)程則通過nice和counter值決定權(quán)值,nice越小,counter越大,被調(diào)度的概率越大,也就是曾經(jīng)使用了cpu最少的進(jìn)程將會(huì)得到優(yōu)先調(diào)度。

4.2.3 CFS調(diào)度類

cfs是絕對(duì)公平調(diào)度算法,理想情況下,優(yōu)先級(jí)相同的兩個(gè)task,運(yùn)行時(shí)間應(yīng)該各占cpu的50%,同理3個(gè)則cpu利用率為1/3。但是cfs中弱化了優(yōu)先級(jí)的概念而是使用權(quán)重weight來決定任務(wù)的運(yùn)行時(shí)間。

例如:3個(gè)任務(wù)A,B,C權(quán)重(priority)分別1,2,3;則總權(quán)重,一個(gè)調(diào)度周期為6單位時(shí)間,理想狀態(tài)下,A應(yīng)占用1單位,B為2,C為3。

圖片
圖片

cfs中使用虛擬時(shí)間vruntime來決定運(yùn)行的task,nice值-20到20 --> weight -->vruntime; cfs中使用rbtree來管理調(diào)度實(shí)體se。每次選取vruntime最小的task進(jìn)行執(zhí)行。在rb tree中vruntime最小的se在rbtree的最左側(cè)。

cfs是通過限制當(dāng)前task的運(yùn)行時(shí)間來實(shí)現(xiàn)公平的,task的vruntime單調(diào)遞增,它在rbtree中向右移動(dòng),讓出cpu使用權(quán)給vruntime更小的task。

圖片

Task數(shù)據(jù)結(jié)構(gòu)

圖片
圖片

4.2.4 RT 調(diào)度類

實(shí)現(xiàn)調(diào)度分為RR和FIFO兩類

SHCED_RR和SCHED_FIFO的不同:

當(dāng)采用SHCED_RR策略的進(jìn)程的時(shí)間片用完,系統(tǒng)將重新分配時(shí)間片,并置于就緒隊(duì)列尾。放在隊(duì)列尾保證了所有具有相同優(yōu)先級(jí)的RR任務(wù)的調(diào)度公平。    

SCHED_FIFO一旦占用cpu則一直運(yùn)行。一直運(yùn)行直到有更高優(yōu)先級(jí)任務(wù)到達(dá)或自己放棄。

如果有相同優(yōu)先級(jí)的實(shí)時(shí)進(jìn)程(根據(jù)優(yōu)先級(jí)計(jì)算的調(diào)度權(quán)值是一樣的)已經(jīng)準(zhǔn)備好,F(xiàn)IFO時(shí)必須等待該進(jìn)程主動(dòng)放棄后才可以運(yùn)行這個(gè)優(yōu)先級(jí)相同的任務(wù)。而RR可以讓每個(gè)任務(wù)都執(zhí)行一段時(shí)間。

相同點(diǎn):

  RR和FIFO都只用于實(shí)時(shí)任務(wù)。

  創(chuàng)建時(shí)優(yōu)先級(jí)大于0(1-99)。

  按照可搶占優(yōu)先級(jí)調(diào)度算法進(jìn)行。

  就緒態(tài)的實(shí)時(shí)任務(wù)立即搶占非實(shí)時(shí)任務(wù)。

rt 調(diào)度類數(shù)據(jù)結(jié)構(gòu)

圖片

4.3 CGroup與TimerSlack

Android平臺(tái)的進(jìn)程會(huì)根據(jù)運(yùn)行狀態(tài),在不同的cgroup進(jìn)行遷移,不同cgroup設(shè)置的timerslack不同,導(dǎo)致后臺(tái)進(jìn)程的定時(shí)器超時(shí)喚醒變長。Oomadj會(huì)觸發(fā)推組,切后臺(tái),activity會(huì)從top group 推組到background組,相應(yīng)的timerslack會(huì)從0.5ms 提高到40ms。如果播放器,實(shí)現(xiàn)上沒有使用service,那么就可能會(huì)遇到Android應(yīng)用切換到后臺(tái)播放音頻卡頓。

圖片

圖片


圖片

4.3.1 TimerSlack基本原理

以下簡單介紹一下timerslack基本原理:

圖片

4.3.2 音頻播放錄制請(qǐng)使用Service

Service在Android定義中,一直處于foreground,因此不會(huì)有切后臺(tái)timerslack變大,導(dǎo)致的卡音問題。因此,APP實(shí)現(xiàn)中,特別注意,音頻解碼器,播放關(guān)聯(lián)線程,需要放到service中。避開前后臺(tái)切換,cgroup遷移的問題。

查看app的timerslack方法:/proc/pid/timerslack_ns

例如,pid為11871:查看timerslack為:0.5ms

圖片

當(dāng)按home按鍵,這項(xiàng)數(shù)值就會(huì)變成40000000

4.4 Android音頻優(yōu)先級(jí)

AOSP定義上,音頻定義為CFS最高優(yōu)先級(jí)Nice。

圖片

Android默認(rèn)優(yōu)先級(jí)為120, 數(shù)量越小,優(yōu)先級(jí)越高。例如最高的 ANDROID_PRIORITY_URGENT_AUDIO 為 120 -19 =101, 對(duì)應(yīng)cfs線程級(jí)別的101。

總體優(yōu)先級(jí)為:Audio > Video > UI

5. 性能卡音案例

有了以上的音頻性能基礎(chǔ),分析起問題來,就不那么困難了。

分析音頻卡頓,最有效的還是systrace,以下介紹幾個(gè)案例:

5.1 APP播放線程調(diào)度延遲

圖片

通過systrace我們可以看到,APP的Aplayer為音頻關(guān)鍵線程,出現(xiàn)了調(diào)度上的延遲,我們可以看到問題點(diǎn),緩存buffer已經(jīng)消化空了,CPU已經(jīng)滿載,但是有明顯的壓頻行為。因此最終原因?yàn)椋?/p>

  1. APP的線程優(yōu)先級(jí)設(shè)置不合理,默認(rèn)nice的120,沒有按著google的標(biāo)準(zhǔn)來設(shè)置,至少設(shè)置為104,必要時(shí)設(shè)置為101。如果nice設(shè)置的過低,CPU滿載時(shí),就容易搶不到cpu。

  2. 負(fù)載太高,觸發(fā)溫升限頻。

5.2 游戲音頻線程調(diào)度延遲

5.2.1 audio dump發(fā)現(xiàn)蜂鳴雜音

在track階段就dump到了雜音,可以確定問題發(fā)生在app內(nèi)部。

圖片

持續(xù)4ms重復(fù)數(shù)據(jù)

圖片

5.2.2 Systrace發(fā)現(xiàn)游戲音頻線程調(diào)度延遲

圖片
圖片

5.2.3 根因:NativeThread優(yōu)先級(jí)設(shè)置太低

NativeThread使用OpenSL ES發(fā)起了FastTrack播放,實(shí)時(shí)性要求較高,當(dāng)前優(yōu)先是默認(rèn)的120,優(yōu)先級(jí)非常低。推薦APP參考google的建議來將NativeThread線程設(shè)置為nice 101即 ANDROID_PRIORITY_URGENT_AUDIO級(jí)別。

5.3 音頻系統(tǒng)線程調(diào)度

圖片

Android音頻優(yōu)先級(jí)設(shè)置偏保守,IOS,Windows系統(tǒng)調(diào)度上,對(duì)音頻有明顯的傾斜。在加上鏈路太長,多個(gè)進(jìn)程周轉(zhuǎn)數(shù)據(jù),所以Android音頻流暢性體驗(yàn),要低于IOS和windows。

5.4 APP在播放線程調(diào)用音頻耗時(shí)接口

最常見的接口是isBluetoothScoOn(), 這個(gè)接口在系統(tǒng)沒有切換時(shí)候的時(shí)候,會(huì)很快返回,但是如果再插拔耳機(jī),或者接聽,掛斷電話/VOIP電話時(shí),就會(huì)耗時(shí)很長,導(dǎo)致caller線程,長時(shí)間阻塞,不產(chǎn)生數(shù)據(jù)。而fastmixer callback,一遍又一遍的吧,4ms的buffer重復(fù)callback過來,導(dǎo)致了蜂鳴雜音的產(chǎn)生。類似的耗時(shí)API還有,isWiredHeadsetOn(),isSpeakerPhoneOn()。

圖片

從systrace上看,nativeThread被阻塞,根據(jù)喚醒關(guān)系結(jié)合logcat,可以定位到,此時(shí)正在做isBluetoothScoOn接口調(diào)用。由于lock被切換設(shè)備的線程持有,而切換設(shè)備又比較耗時(shí),從而導(dǎo)致,nativethread被卡住,無法生產(chǎn)數(shù)據(jù)。Audio系統(tǒng)callback到了重復(fù)的雜音數(shù)據(jù)。

誤區(qū):AudioManager接口可以立刻返回。事實(shí)上,AudioService內(nèi)部有復(fù)雜的邏輯,例如切換設(shè)備,有很多業(yè)務(wù)邏輯要處理。一個(gè)簡單的場(chǎng)景,voip來電,這時(shí)候,需要和藍(lán)牙耳機(jī)建立鏈路,握手過程,可能就需要150-300ms,整個(gè)過程非常耗時(shí)。如果這時(shí)候在主線程去調(diào)用,getMode,isBluetoothScoOn等接口,都會(huì)被阻塞掉,直到耳機(jī)返回,或者連接超時(shí)。

圖片

5.5 渲染線程調(diào)用AudioTrack導(dǎo)致幀率低

例如這份日志里,在渲染線程中,去執(zhí)行AudioTrack.start(),結(jié)果阻塞580ms,導(dǎo)致畫面卡頓。應(yīng)盡量避免在UI線程,圖形render線程中,調(diào)用AudioTrack接口。

圖片

5.6 FrameCount設(shè)置過大過小

FrameCount的作用是控制APP起播的起播緩存大小,設(shè)置越大,時(shí)延越高,越不容易卡音。設(shè)置越小,時(shí)延越低,越容易卡音。

圖片

5.6.1 短促音FrameCount設(shè)置過大導(dǎo)致無聲

圖片


5.6.2 FrameCount設(shè)置過小卡音

常見的問題是使用OpenSL ES播放,framecount設(shè)置的太小,緩存buffer提效,當(dāng)cpu調(diào)度不及時(shí),就容易觸發(fā)性能卡音。


圖片

5.7 頻繁調(diào)用Audio接口,導(dǎo)致整機(jī)重啟

Android系統(tǒng)只提供了31個(gè)Binder線程池,也就是系統(tǒng)最大并發(fā)數(shù)為31個(gè),如果APP頻繁調(diào)用Audio耗時(shí)接口,binder被占用,甚至被耗盡,導(dǎo)致整機(jī)重啟。以下某APP,持續(xù)不停的去查音量,導(dǎo)致系統(tǒng)Binder資源被耗盡,最終觸發(fā)重啟。

圖片

SystemServer Binder線程池設(shè)置為31個(gè)

圖片

6.音頻聯(lián)合調(diào)優(yōu)建議

6.1規(guī)范設(shè)置音頻優(yōu)先級(jí)

圖片

APP 需要參考AOSP標(biāo)準(zhǔn),將音頻數(shù)據(jù)關(guān)聯(lián)線程,例如解碼器,至少nice設(shè)置為104級(jí)別。

6.2 高實(shí)時(shí)的線程,避免調(diào)用耗時(shí)的音頻API

盡量不要在音頻解碼器,以及主線程中調(diào)用音頻耗時(shí)接口,例如isBluetoothScoOn(),getMode等等接口,這些接口大都會(huì)持鎖,導(dǎo)致切換設(shè)備時(shí),耗時(shí)較長。

6.3播放邏輯進(jìn)Service

盡量將音頻播放邏輯,放在service中,避免切后臺(tái),timerslack被改大,CPU被限頻限核,導(dǎo)致的聲音卡頓。

6.4 非必要避免頻繁調(diào)用耗時(shí)的Audio接口

例如查詢音量,音頻設(shè)備連接狀態(tài)。

7. 參考文獻(xiàn)

[1]https://developer.apple.com/documentation/audiotoolbox/workgroup_management/understanding_audio_workgroups/#3625591

[2] https://learn.microsoft.com/en-us/windows/win32/procthread/multimedia-class-scheduler-service

[3] https://zhuanlan.zhihu.com/p/556295381?utm_id=0

[4]https://zhuanlan.zhihu.com/p/658488322

圖片

高通camx入門學(xué)習(xí) | camx初認(rèn)識(shí)

學(xué)習(xí)完Camera入門課程視頻,可以去找工作了?

Camera Hal|如何學(xué)習(xí)一個(gè)新平臺(tái)

Android Camera 學(xué)習(xí)路線 | 個(gè)人推薦

一篇文章帶你了解Android 最新Camera框架

獨(dú)家 | Android Camera 面試流程、經(jīng)驗(yàn)分享

《Android Camera開發(fā)入門》視頻課程、《Camx入門學(xué)習(xí)》已經(jīng)上架了,可以加我微信咨詢,目前針對(duì)星球成員免費(fèi)開放,也歡迎加入“小馳成長圈”星球圖片

覺得不錯(cuò),點(diǎn)個(gè)贊唄 圖片

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多