一 AudioTrack 播放
播放声音可以使用 MediaPlayer 和 AudioTrack,两者都提供 Java API 给应用开发者使用。两者的差别在于:MediaPlayer 可以播放多种格式的音源,如 mp3、flac、wma、ogg、wav 等,而 AudioTrack 只能播放解码后的 PCM 数据流。从上面 Android 音频系统架构图来看:MediaPlayer 在 Native 层会创建对应的音频解码器和一个 AudioTrack,解码后的数据交由 AudioTrack 输出。所以 MediaPlayer 的应用场景更广,一般情况下使用它也更方便;只有一些对声音时延要求非常苛刻的应用场景才需要用到 AudioTrack。这里主要聚焦AudioTrack的方式:
一个 AudioTrack Java API 的测试例子(MODE_STREAM 模式):
[-->AudioTrackAPI使用例子(Java层)]
//1. 根据音频数据的特性来确定所要分配的缓冲区的最小size
int bufsize =
AudioTrack.getMinBufferSize(48000, //采样率:每秒48000个点
AudioFormat.CHANNEL_CONFIGURATION_STEREO, //声道数:双声道
AudioFormat.ENCODING_PCM_16BIT //采样精度:一个采样点16比特,相当于2个字节
);
//2. 创建AudioTrack
AudioTrack trackplayer = new AudioTrack(
AudioManager.STREAM_MUSIC, //音频流类型
48000,AudioFormat.CHANNEL_CONFIGURATION_STEREO,
AudioFormat.ENCODING_PCM_16BIT, bufsize*2,
AudioTrack.MODE_STREAM); //数据加载模式
//3. 开始播放
trackplayer.play() ;
......
//4. 调用write写数据
trackplayer.write(bytes_pkg, 0,bytes_pkg.length) ; //往track中写数据
......
//5. 停止播放和释放资源
trackplayer.stop(); //停止播放
trackplayer.release(); //释放底层资源
详细说明下 getMinBufferSize() 接口,字面意思是返回最小数据缓冲区的大小,它是声音能正常播放的最低保障,从函数参数来看,返回值取决于采样率、采样深度、声道数这三个属性。MODE_STREAM 模式下,应用程序重点参考其返回值然后确定分配多大的数据缓冲区。如果数据缓冲区分配得过小,那么播放声音会频繁遭遇 underrun,underrun 是指生产者(AudioTrack)提供数据的速度跟不上消费者(AudioFlinger::PlaybackThread)消耗数据的速度,反映到现实的后果就是声音断续卡顿,严重影响听觉体验。
可见最小缓冲区的大小 = 最低帧数 * 声道数 * 采样深度,(采样深度以字节为单位),在视频中,如果帧数过低,那么画面会有卡顿感,对于音频,道理也是一样的。最低帧数如何求得,我们到 native 层再解释。
二 AudioTrack Native API 四种数据传输模式:Transfer Mode
两个维度:
1.1 push或者pull
1.2 static 和 stream方式
从另外一个维度来看,还有两种方式:
(1)static方式,一次性把数据全传送给AF (soundpool 播放的方式)
延时小,简单高效,适用于提示音等操作
(2)stream方式,数据一点点的传送给AF,一块一块的传送 (QQ音乐播放音乐等)
延时小,不受文件大小限制。流媒体播放只能用这种。
Transfer Mode |
描述 |
场景 |
TRANSFER_CALLBACK |
在 AudioTrackThread 线程中通过 audioCallback 回调函数主动从应用进程那里索取数据 |
ToneGenerator 播放采用这种模式 另外一个维度:对应着pull (拉流)数据 这种模式下 audiotrack会专门起一个AudioTrackThread线程去管理callback的回调 |
TRANSFER_OBTAIN |
应用进程需要主动调用 obtainBuffer()/releaseBuffer() 填充数据 |
目前还没有见到实际的使用场景 |
TRANSFER_SYNC |
应用进程需要持续调用 write() 写数据到 FIFO,写数据时有可能遭遇阻塞(等待 AudioFlinger::PlaybackThread 消费之前的数据) |
基本适用所有的音频场景;对应于 AudioTrack Java API 的 MODE_STREAM 模式 (1)exoplayer() (2)网易云/QQ音乐播放音乐: 03-19 14:19:43.715 D/AudioTrack(27447): set() streamType -1, sampleRate 44100, format 0x1, channelMask 0x3, frameCount 14144, flags #0, notificationFrames 0, sessionId 0, transferType 3, uid -1, pid -1 cbf 1 (3)oppo 原生播放器: 03-19 14:23:03.175 D/AudioTrack(28663): set() streamType -1, sampleRate 44100, format 0x1, channelMask 0x3, frameCount 14144, flags #0, notificationFrames 0, sessionId 0, transferType 3, uid -1, pid -1 cbf 1 (4)mediaplayer 播放场景 mediaplayerservice.cpp 03-22 15:13:16.390 1472 8338 D AudioTrack: set() streamType 2, sampleRate 44100, format 0x1, channelMask 0x3, frameCount 22050, flags #1, notificationFrames 0, sessionId 1585, transferType 0, uid 1000, pid 24666 cbf 0 对应的是:TRANSFER_SYNC 另外一个维度:对应着push(推流)数据 |
TRANSFER_SHARED |
应用进程将回放数据一次性付给 AudioTrack,适用于数据量小、时延要求高的场景; |
对应于 AudioTrack Java API 的 MODE_STATIC 模式 |
三 AudioTrack Native API 输出标识:AUDIO_OUTPUT_FLAG
AUDIO_OUTPUT_FLAG |
Description |
场景 |
AUDIO_OUTPUT_FLAG_DIRECT |
表示音频流直接输出到音频设备,不需要软件混音 |
一般用于 HDMI 设备声音输出、voip rx 、96K采样率绕过重采样等播放场景 |
AUDIO_OUTPUT_FLAG_PRIMARY |
表示音频流需要输出到主输出设备 |
一般用于铃声类声音 |
AUDIO_OUTPUT_FLAG_FAST |
表示音频流需要快速输出到音频设备 |
一般用于按键音、游戏背景音等对时延要求高的场景 |
AUDIO_OUTPUT_FLAG_DEEP_BUFFER |
表示音频流输出可以接受较大的时延, |
一般用于音乐、视频播放等对时延要求不高的场景,qq音乐、视频播放等场景 |
AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD |
表示音频流没有经过软件解码,需要输出到硬件解码器,由硬件解码器进行解码 |
Mediaplayer 播放MP3 或者 芯片adsp 支持解码的格式的音视频时候 |
我们根据不同的播放场景,使用不同的输出标识,如按键音、游戏背景音对输出时延要求很高,那么就需要置 AUDIO_OUTPUT_FLAG_FAST,具体可以参考 ToneGenerator、SoundPool 和 OpenSL ES。下图是其和框架的mixerthread以及hal的output的一一对应关系。