前面幾篇文章介紹了MediaCodec
、MediaMuxer
、AudioRecord
等音視頻相關知識,這些都是 Android 音視頻開發中必須掌握的,相關文章鏈接如下:
Android 中常用的播放音頻的接口有MediaPlayer
、AudioTrack
和SoundPool
,音頻的渲染最常用的是AudioTrack
和OpenSL ES
,下面將介紹下AudioTrack
相關知識,主要內容如下:
- AudioTrack 介紹
- AudioTrack 的創建
- AudioTrack 音頻數據寫入
- AudioTrack 生命週期
- AudioTrack 的使用
AudioTrack 介紹#
AudioTrack
用來播放原始 pcm
格式的音頻數據,AudioTrack
有兩種播放模式:
MODE_STATIC
:這種模式會將音頻數據一次寫入音頻緩衝區,適合處理內存少及盡可能小的延遲播放的短聲音場景,如播放的遊戲音效、鈴聲、系統提示音等,此時這種模式開銷最小。MODE_STREAM
:這種模式會不斷的寫入音頻數據,適用於需要不斷接受音頻數據的場景,這種模式主要是由於某些音頻數據持續時間長、或者音頻特性(高採樣率、更高位深等)導致不能一次性寫入內存而出現的,正常播放PCM
原始音頻數據就選擇這種模式。
與MediaPlayer
相較,MediaPlayer
可以播放不同類型、不同格式的聲音文件,會在底層創建與之對應的音頻解碼器,而AudioTrack
只接收PCM
原始音頻數據,MediaPlayer
在底層還是會創建AudioTrack
,把解碼後的PCM
數流傳遞給AudioTrack
,AudioTrack
再傳遞給AudioFlinger
進行混音,然後才傳遞給硬件播放。
AudioTrack 的創建#
AudioTrack
的創建使用如下方式:
// Android5.0開始
AudioTrack(
attributes: AudioAttributes!,
format: AudioFormat!,
bufferSizeInBytes: Int,
mode: Int,
sessionId: Int)
上面構造方法對應的參數含義如下:
- attributes:表示音頻流信息的屬性集合,自從 Android5.0 開始使用
AudioAttributes
來取代流類型的設置,可以比流類型設置傳達更多信息,常用來設置音頻的用途、音頻的內容等。 - format:表示
AudioTrack
接受的音頻格式,對於線性PCM
來說,反應每個樣本大小(8、16、32 位)及表現形式(整型、浮點型),音頻格式定義在AudioFormat
中,常見的音頻數據格式中只有AudioFormat.ENCODING_PCM_16BIT
可以保證在所有的設備上正常使用,像AudioFormat.ENCODING_PCM_8BIT
不能保證在所有設備上正常使用。 - bufferSizeInBytes:表示音頻數據緩衝區的大小,單位是字節,其大小一般是音頻幀大小的非零倍數,如果播放模式是
MODE_STATIC
,則緩衝區大小是本次播放的音頻的大小,如果播放模式是MODE_STREAM
,則緩衝區大小不能小於最小緩衝區大小,也就是不能小於getMinBufferSize
返回的大小。 - mode:表示播放模式,
AudioTrack
提供了MODE_STATIC
和MODE_STREAM
兩種方式,MODE_STATIC
會將音頻資源一次性寫入音頻緩衝區,適用於鈴聲、系統提示音等延遲小、音頻資源內存佔用少的場景,MODE_STREAM
則適用於需要不斷通過write
方法寫入數據的場景,相較MODE_STATIC
會有一定延遲,但是可以持續不斷的接收音頻數據。 - sessionId:音頻會話 Id,這裡使用
AudioManager.AUDIO_SESSION_ID_GENERATE
由底層音頻框架自己生成sessionId
。
AudioTrack 音頻數據寫入#
無論是流模式 (STREAM_MODE
) 還是靜態緩衝模式 (STATIC_MODE
) 模式,都需通過write
方式寫入音頻數據來進行播放,主要的write
方式如下:
// AudioTrack構造函數中指定的格式應為AudioFormat#ENCODING_PCM_8BIT
open fun write(audioData: ByteArray, offsetInBytes: Int, sizeInBytes: Int): Int
// AudioTrack構造函數中指定的格式應為AudioFormat#ENCODING_PCM_16BIT
open fun write(audioData: ShortArray, offsetInShorts: Int, sizeInShorts: Int): Int
// AudioTrack構造函數中指定的格式應為AudioFormat#ENCODING_PCM_FLOAT
open fun write(audioData: FloatArray, offsetInFloats: Int, sizeInFloats: Int, writeMode: Int): Int
寫入音頻數據的返回值大於等於 0,讀取音頻數據常見異常如下:
- ERROR_INVALID_OPERATION:表示
AudioTrack
未初始化。 - ERROR_BAD_VALUE:表示參數無效。
- ERROR_DEAD_OBJECT:表示已經傳輸了一些音頻數據的情況下不返回錯誤碼,將在下次
write
返回處返回錯誤碼。
這個跟AudioRecord
中的 read
函數有點類似,具體細節查看官方文檔。
AudioTrack 生命週期#
AudioTrack
的生命週期主要是STATE_UNINITIALIZED
、STATE_INITIALIZED
和STATE_NO_STATIC_DATA
,其中STATE_INITIALIZED
對應STREAM_MODE
,STATE_NO_STATIC_DATA
對應STATIC_MODE
,至於播放狀態不怎麼重要,如下圖所示:
AudioTrack 的使用#
AudioTrack
的使用主要就是從PCM
文件中讀取數據,然後將讀取到的音頻寫入AudioTrack
進行播放,其關鍵代碼如下:
// 初始化AudioTrack
private fun initAudioTrack() {
bufferSize = AudioTrack
.getMinBufferSize(SAMPLE_RATE, CHANNEL_CONFIG, AUDIO_FORMAT)
attributes = AudioAttributes.Builder()
.setUsage(AudioAttributes.USAGE_MEDIA) // 設置音頻的用途
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC) // 設置音頻的內容類型
.build()
audioFormat = AudioFormat.Builder()
.setSampleRate(SAMPLE_RATE)
.setChannelMask(AudioFormat.CHANNEL_OUT_MONO)
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.build()
audioTrack = AudioTrack(
attributes, audioFormat, bufferSize,
AudioTrack.MODE_STREAM, AudioManager.AUDIO_SESSION_ID_GENERATE
)
}
// AudioTrack寫入音頻數據
private fun writeAudioData(){
scope.launch(Dispatchers.IO){
val pcmFile = File(pcmFilePath)
val ins = FileInputStream(pcmFile)
val bytes = ByteArray(bufferSize)
var len: Int
while (ins.read(bytes).also { len = it } > 0){
audioTrack.write(bytes, 0, len)
}
audioTrack.stop()
}
}
// 開始播放
private fun start(){
audioTrack.play()
writeAudioData()
}
AudioTrack
的使用基本如上,AudioTrack
播放音頻的相關代碼如感興趣可留言獲取。