【Android 高性能音频】AAudio 音频流 数据回调细节 ( 数据回调函数优先级 | 数据回调函数 | 采样率 | 采样数 | 缓冲区调整 | 线程不安全 )
文章目錄
- I . 數(shù)據(jù)回調(diào)函數(shù)優(yōu)先級(jí)
- II . 數(shù)據(jù)回調(diào)函數(shù) 相關(guān)內(nèi)容
- III . 采樣率 處理細(xì)節(jié)
- IV . 數(shù)據(jù)回調(diào)函數(shù) 每次 采樣個(gè)數(shù) numFrames
- V . 數(shù)據(jù)回調(diào)函數(shù) 緩沖區(qū) ( AAudio 內(nèi)部緩沖區(qū) ) 調(diào)整
- VI . AAudio 音頻系統(tǒng)的線程安全性分析
I . 數(shù)據(jù)回調(diào)函數(shù)優(yōu)先級(jí)
1 . 普通線程操作 : 從普通線程中讀寫(xiě) AAudio 音頻流的 音頻數(shù)據(jù) , 普通線程的優(yōu)先級(jí)比較低 , 容易被搶占 , 或者遇到資源抖動(dòng) , 對(duì)需要連續(xù)性能的音頻流操作造成干擾 , 出現(xiàn)卡頓 電流 等情況 ;
2 . 增加 AAudio 內(nèi)部緩沖區(qū) : 解決上述音頻干擾的方案就是 增加 AAudio 音頻流的內(nèi)部緩沖區(qū) , 這個(gè)緩沖區(qū)在上一篇博客中有詳細(xì)介紹 , 該緩沖區(qū)是維護(hù)在音頻設(shè)備 , 增加該緩沖區(qū)大小會(huì)提高整體 AAudio 系統(tǒng)采樣播放的容錯(cuò)率 , 采樣足夠多 , 即使某一時(shí)刻出現(xiàn)了采樣不足的情況 , 也能掩蓋過(guò)去 , 不會(huì)出現(xiàn)卡頓電流等情況 , 讓用戶無(wú)法發(fā)現(xiàn) , 但是這樣音頻的延遲會(huì)增大 ;
緩沖區(qū)相關(guān)細(xì)節(jié) : 【Android 高性能音頻】AAudio 音頻流 緩沖區(qū) 簡(jiǎn)介 ( AAudio 音頻流內(nèi)部緩沖區(qū) | 緩沖區(qū)幀容量 | 緩沖區(qū)幀大小 | 音頻數(shù)據(jù)讀寫(xiě)緩沖區(qū) )
3 . 低延遲推薦方案 : AAudio 音頻流 提供了一個(gè) 異步的 數(shù)據(jù)回調(diào)函數(shù) AAudioStream_dataCallback , 該函數(shù)運(yùn)行在優(yōu)先級(jí)很高的線程中 , 該線程的資源不容易被搶占 , 可以提供一個(gè)較穩(wěn)定的性能支持 ;
AAudio 音頻流開(kāi)啟播放后 , 會(huì)自動(dòng)回調(diào)該異步數(shù)據(jù)回調(diào)函數(shù) , 在該函數(shù)中執(zhí)行采樣播放的過(guò)程 , 將采樣數(shù)據(jù)寫(xiě)入緩沖區(qū) , 這組數(shù)據(jù)消費(fèi)完畢后 , 又會(huì)調(diào)用回調(diào)函數(shù) , 申請(qǐng)新的數(shù)據(jù) ;
數(shù)據(jù)回調(diào)函數(shù)基本工作流程 : 【Android 高性能音頻】AAudio 音頻流 PCM 采樣 的 采樣 緩沖 播放 的 連續(xù)機(jī)制 ( 數(shù)據(jù)回調(diào)機(jī)制 | 數(shù)據(jù)回調(diào)函數(shù)指針 | 實(shí)現(xiàn)數(shù)據(jù)回調(diào)函數(shù) | 設(shè)置數(shù)據(jù)回調(diào)函數(shù) )
II . 數(shù)據(jù)回調(diào)函數(shù) 相關(guān)內(nèi)容
1 . 數(shù)據(jù)回調(diào)函數(shù)原型 : 由開(kāi)發(fā)者實(shí)現(xiàn) , 返回值 aaudio_data_callback_result_t 類(lèi)型 , 參數(shù) 按照如下參數(shù)順序?qū)崿F(xiàn) ;
typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(AAudioStream *stream,void *userData,void *audioData,int32_t numFrames);2 . 數(shù)據(jù)回調(diào)函數(shù)注冊(cè) : 開(kāi)發(fā)者實(shí)現(xiàn)了 AAudioStream_dataCallback 類(lèi)型的數(shù)據(jù)回調(diào)函數(shù)后 , 需要 調(diào)用 AAudioStreamBuilder_setDataCallback 函數(shù) 設(shè)置給 AAudio 音頻流 , 然后 AAudio 處于 Started 狀態(tài)后 , 就會(huì)立刻第一次回調(diào)該數(shù)據(jù)回調(diào)函數(shù) ;
AAudioStreamBuilder_setDataCallback(builder, myCallback, myUserData);數(shù)據(jù)回調(diào)函數(shù)基本工作流程 : 【Android 高性能音頻】AAudio 音頻流 PCM 采樣 的 采樣 緩沖 播放 的 連續(xù)機(jī)制 ( 數(shù)據(jù)回調(diào)機(jī)制 | 數(shù)據(jù)回調(diào)函數(shù)指針 | 實(shí)現(xiàn)數(shù)據(jù)回調(diào)函數(shù) | 設(shè)置數(shù)據(jù)回調(diào)函數(shù) )
III . 采樣率 處理細(xì)節(jié)
1 . 采樣率 : 每秒鐘的采樣個(gè)數(shù) , 單位是 赫茲 ( Hz ) , 一般是 44100 Hz , 或 48000 Hz ;
在 Android 手機(jī)中 , 一般是 48000 Hz , 即每秒需要處理 48000 個(gè)采樣 ;
2 . AAudio 中采樣率處理 : 在 AAudio 音頻流中 不建議設(shè)置采樣率 , 一般使用默認(rèn)采樣率即可 , 每個(gè)音頻設(shè)備都有一個(gè)最佳采樣率 , 如果不設(shè)置 , 默認(rèn)就按照該最佳采樣率進(jìn)行工作 , 如果設(shè)置錯(cuò)了 , 那么音頻流在打開(kāi)時(shí)就會(huì)失敗 ;
3 . 采樣率獲取 : 如果不設(shè)置采樣率 , 那么使用默認(rèn)的采樣率 , 該默認(rèn)采樣率通過(guò)調(diào)用 AAudioStream_getSampleRate () 方法獲得 ;
4 . 采樣率使用 : 獲取采樣率后 , 需要準(zhǔn)備樣本 , 這些樣本的采樣率需要轉(zhuǎn)換成指定的采樣率 , 才能向 AAudio 音頻流中讀寫(xiě) , 如果采樣率不對(duì) , 播出來(lái)的聲音就會(huì)出問(wèn)題 ;
Android 的音頻設(shè)備采樣率一般是 48000 Hz , 需要將準(zhǔn)備的讀寫(xiě)緩沖區(qū)的音頻樣本數(shù)據(jù)采樣率轉(zhuǎn)為 48000Hz 后才能向 AAudio 音頻流中讀寫(xiě) ;
IV . 數(shù)據(jù)回調(diào)函數(shù) 每次 采樣個(gè)數(shù) numFrames
1 . 采樣個(gè)數(shù) : 數(shù)據(jù)回調(diào)函數(shù)中有如下細(xì)節(jié) , stream , userData , audioData 是指針類(lèi)型 , 需要從外部傳入到 函數(shù)中使用這些數(shù)據(jù) , 但是唯獨(dú) numFrames 參數(shù)不是由用戶指定的 , 每次的采樣個(gè)數(shù)是由 AAudio 系統(tǒng)指定的 ;
typedef aaudio_data_callback_result_t (*AAudioStream_dataCallback)(AAudioStream *stream,void *userData,void *audioData,int32_t numFrames);2 . 采樣個(gè)數(shù)實(shí)際測(cè)試值 : 這個(gè)值在不同系統(tǒng) , 版本 , 硬件手機(jī)上可能不同 , 但是我測(cè)試的 三星 小米 華為等手機(jī) , 該值是 192 , 意味著 每次采集 192 幀的數(shù)據(jù) , 每幀的樣本數(shù)是 通道數(shù) ;
3 . 采集的樣本緩沖區(qū)大小 :
- ① 幀 : numFrames 單位是幀 ;
- ② 樣本數(shù) : 每幀的樣本數(shù) 等于 通道數(shù) , 如果是單聲道 每幀有 1 個(gè)樣本 , 如果是立體聲 , 每幀有 2 個(gè)樣本 ;
- ③ 每個(gè)樣本字節(jié)數(shù) : AAUDIO_FORMAT_PCM_I16 每個(gè)樣本 2 字節(jié) , AAUDIO_FORMAT_PCM_FLOAT 每個(gè)樣本 4 字節(jié) ;
- ④ 立體聲 16 位整形采樣 : AAudio 中每個(gè)樣本都有指定的個(gè)數(shù) , 16 位整形樣本 AAUDIO_FORMAT_PCM_I16 代表 16 位采樣 , 每個(gè)樣本有 兩個(gè)字節(jié) , 那么需要采集的樣本緩沖區(qū)大小為 numFrames×2×2numFrames \times 2 \times 2numFrames×2×2 個(gè)樣本 ;
V . 數(shù)據(jù)回調(diào)函數(shù) 緩沖區(qū) ( AAudio 內(nèi)部緩沖區(qū) ) 調(diào)整
1 . 降低延遲 : 如果要求 AAudio 延遲盡可能低 , 需要將其內(nèi)部緩沖區(qū)大小降到最低 ;
2 . 增加容錯(cuò) : 緩沖區(qū)太小 , 容錯(cuò)空間也跟著變小 , 稍有風(fēng)吹草動(dòng) , 就會(huì)出現(xiàn)卡頓 電流等播放異常的情況 , 這就需要增加緩沖區(qū) ;
3 . 動(dòng)態(tài)修改 : 上述兩個(gè)需求相互沖突 , 就必須在二者之間找到平衡 , 在不出現(xiàn)播放異常的情況下 , 找到能夠在當(dāng)前性能下容錯(cuò)的最小緩沖區(qū) , 該值要隨著系統(tǒng)環(huán)境變化而動(dòng)態(tài)修改 ;
4 . 調(diào)整緩沖區(qū)方法 : 在下面兩篇博客中有調(diào)整緩沖區(qū)的細(xì)節(jié) ;
- ① 【Android 高性能音頻】AAudio 音頻流 緩沖區(qū) 簡(jiǎn)介 ( AAudio 音頻流內(nèi)部緩沖區(qū) | 緩沖區(qū)幀容量 | 緩沖區(qū)幀大小 | 音頻數(shù)據(jù)讀寫(xiě)緩沖區(qū) )
- ② 【Android 高性能音頻】AAudio 緩沖區(qū)控制 ( XRun | 欠載 UnderRun | 超限 OverRun | 獲取緩沖區(qū)大小 | 設(shè)置緩沖區(qū)大小 )
VI . AAudio 音頻系統(tǒng)的線程安全性分析
1 . 線程不安全 : AAudio 的 API 大部分都是線程不安全的 ;
2 . 線程不安全原理 : 線程安全就意味著存在線程同步機(jī)制 , 線程同步就涉及到了線程的阻塞等待機(jī)制 , 在 AAudio 系統(tǒng)中顯然不能出現(xiàn)線程的阻塞 , 每秒鐘回調(diào)幾千次 , 一旦阻塞1毫秒 , 整個(gè)系統(tǒng)都無(wú)法正常運(yùn)行 ; 此外線程阻塞后 , 其會(huì)被搶占甚至干擾 , 導(dǎo)致后續(xù)無(wú)法以高效率運(yùn)行 ;
3 . 避免多線程操作 : 在調(diào)用 AAudio 時(shí) , 盡量避免多線程操作 AAudio ;
- ① 等待狀態(tài)改變操作 : AAudioStream_waitForStateChange() 操作會(huì)造成線程阻塞 , 禁止在不同線程中調(diào)用該方法 ;
- ② 讀寫(xiě)操作 : 禁止在 不同線程中 讀寫(xiě)同一個(gè) AAudio 音頻流 ;
4 . 線程安全的操作 :
- ① 獲取 AAudio 配置的操作 : 除 AAudioStream_getTimestamp 方法是線程不安全的之外 , 其它的 AAudioStream_get*() 類(lèi)的方法 都是線程安全的 ;
- ② 創(chuàng)建 AAudio 音頻流構(gòu)建器 : AAudio_createStreamBuilder() 方法是線程安全的 ;
- ③ 輸出 AAudio 文本 : AAudio_convert*ToText() 類(lèi)的方法也是線程安全的 ;
總結(jié)
以上是生活随笔為你收集整理的【Android 高性能音频】AAudio 音频流 数据回调细节 ( 数据回调函数优先级 | 数据回调函数 | 采样率 | 采样数 | 缓冲区调整 | 线程不安全 )的全部?jī)?nèi)容,希望文章能夠幫你解決所遇到的問(wèn)題。
- 上一篇: 【Android 高性能音频】AAudi
- 下一篇: 【Android 应用开发】Androi