Android音频播放详解

本文主要探讨了Android平台上的音频播放,特别是AudioTrack的使用。AudioTrack用于播放解码后的PCM数据流,与MediaPlayer相比,适合对声音时延要求高的场景。文章详细介绍了AudioTrack的getMinBufferSize()接口,以及在Native层的四种数据传输模式。此外,还讲解了AudioTrack的输出标识、关键函数getMinFrameCount()的计算方法,以及音频帧、传输延迟等概念。最后,分析了AudioTrack的播放流程和写数据过程,以及AudioFlinger的线程Loop工作原理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一 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的一一对应关系。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值