我正在参加「启航计划」
前面几篇文章介绍了MediaCodec
、MediaMuxer
、AudioRecord
等音视频相关常识,这些都是 Android 音视频开发中必须把握的,相关文章链接如下:
- 如何正确编译ijkplayer
- 音视频开发基础常识
- 音频帧、视频帧及其同步
- Camera2、MediaCodec录制mp4
- MediaCodec详解
- 音频基础常识
- AudioRecord采集音频数据及组成
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
播映音频的相关代码能够在回复关键字【audiotrack】关键字获取,事例中用到的本地PCM
文件能够回复关键字【pcm】获取。