Android P的音频架构
音频架构图
谷歌官网:https://source.android.com/devices/audio
在谷歌官网介绍的音频架构中,我们可以看到下面的架构图:
android p音频处理流程
通过用AudioTrack来播放pcm格式音乐的流程,来讲解整个音频架构处理的流程。
android p音频播放从app开始,在framework层创建播放器,在audio library层做音频流和输出流控制,在Hal层将音频数据写入到输出设备进行声音输出。其中audio library层是音频处理的核心。
App --> Frameworks --> Audio Library --> HAL
一、App层
上层应用,包括各种音乐播放器,语音播报等声音输出软件。在app层,应用播放不同的音频文件。播放音频文件主要有两种方式,MediaPlayer和AudioTrack,这两者都提供了Java API供应用开发者使用,虽然都可以播放音频,但他们还是有区别:
1.MediaPlayer可以播放多种格式的声音文件,例如MP3,AAC,WAV,OGG,MIDI等。MediaPlayer会在framework层创建对应的音频解码器。
2.AudioTrack只能播放已经解码的PCM流,如果对比支持的文件格式的话则是AudioTrack只支持wav格式的音频文件,因为wav格式的音频文件大部分都是PCM流。AudioTrack不创建解码器,所以只能播放不需要解码的wav文件。
3.MediaPlayer在framework层还是会创建AudioTrack,把解码后的PCM数流传递给AudioTrack,AudioTrack再传递给AudioFlinger进行混音,然后才传递给硬件播放,所以是MediaPlayer包含了AudioTrack。
总结:MediaPlayer 更加适合在后台长时间播放本地音乐文件或者在线的流式资源;AudioTrack 则更接近底层,提供了非常强大的控制能力,支持低延迟播放,适合流媒体和VoIP语音电话等场景。
本文主要讲解使用AudioTrack播放pcm或者wav格式音频文件:
// 一个使用AudioTrack播放音频的简单例子
public class MainActivity extends Activity {
private Button mPlayBtn; //播放按钮
private Button mPauseBtn; //暂停按钮
private byte[] mAudioData; //音频数据
private AudioTrack mAudioTrack; //AudioTrack对象
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_main);
mPlayBtn = findViewById(R.id.play);
mPauseBtn = findViewById(R.id.pause);
mPlayBtn.setEnabled(false);
//按下播放按钮后做的处理
mPlayBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
mPlayBtn.setEnabled(false);
if(mAudioTrack != null){
mAudioTrack.play();
}else{
// android p之前的版本初始化AudioTrack的方式,在android p中遗弃了该方式,但是兼容这种写法。
// mAudioTrack = new AudioTrack(AudioManager.STREAM_MUSIC, 8000,AudioFormat.CHANNEL_OUT_MONO, AudioFormat.ENCODING_PCM_16BIT,mAudioData.length, AudioTrack.MODE_STATIC);
// android p初始化AudioTrack的方式
/* 音频参数(音频参数如果跟音频文件参数不一致,声音播放会出现异常):
* streamType:音频流类型 AudioManager.STREAM_MUSIC,音乐(不同的声音分类为不同的音频流类型)
* encoding:位宽 AudioFormat.ENCODING_PCM_16BIT,16bit音频文件
* sampleRate:采样率 8000,8k的音频文件
* channelMask:通道数 AudioFormat.CHANNEL_OUT_MONO,单声道
*/
AudioAttributes.Builder attr = (new AudioAttributes.Builder())
.setContentType(AudioAttributes.CONTENT_TYPE_MUSIC)
.setLegacyStreamType(AudioManager.STREAM_MUSIC)
.setFlags(AudioAttributes.FLAG_AUDIBILITY_ENFORCED)
.setUsage(AudioAttributes.USAGE_MEDIA);
AudioFormat.Builder format = (new AudioFormat.Builder())
.setEncoding(AudioFormat.ENCODING_PCM_16BIT)
.setSampleRate(8000)
.setChannelMask( AudioFormat.CHANNEL_OUT_MONO);
/* AudioTrack 创建模式mode
* AudioTrack.MODE_STATIC:播放音频前一次性将全部的音频数据从java传递到native层
* AudioTrack.MODE_STREAM:播放音频时将音频数据用数据流的方式从java传递到native层
*/
mAudioTrack = new AudioTrack(attr.build(), format.build(), mAudioData.length, AudioTrack.MODE_STATIC,AudioManager.AUDIO_SESSION_ID_GENERATE);
mAudioTrack.write(mAudioData, 0, mAudioData.length);
mAudioTrack.play();
}
}
});
mPauseBtn.setOnClickListener(new View.OnClickListener() {
@Override
public void onClick(View v) {
if(mAudioTrack != null){
mAudioTrack.pause();
}
mPlayBtn.setEnabled(true);
}
});
//用一个异步任务将音频数据读出来
new AsyncTask<Void, Void, Void>() {
@Override
protected Void doInBackground(Void... params) {
try {
//音频文件放在res/raw/目录下
InputStream in = getResources().openRawResource(R.raw.bingyu);
try {
//读出音频流数据
ByteArrayOutputStream out = new ByteArrayOutputStream(in.available());
for (int b; (b = in.read()) != -1;) {
out.write(b);
}
mAudioData = out.toByteArray();
} finally {
in.close();
}
} catch (IOException e) {
}
return null;
}
@Override
protected void onPostExecute(Void v) {
mPlayBtn.setEnabled(true);
}
}.execute();
}
//释放AudioTrack资源
private void releaseAudioTrack() {
if (mAudioTrack != null) {
mAudioTrack.stop();
mAudioTrack.release();
mAudioTrack = null;
}
}
@Override
protected void onStop() {
super.onStop();
releaseAudioTrack();
}
@Override
protected void onDestroy() {
super.onDestroy();
releaseAudioTrack();
}
}
具体代码项目地址:https://github.com/sunxiaolin2016/Android-Audio
二、Frameworks层
Frameworks层相关代码:frameworks/base/media/java/android/media/
在app中创建AudioTrack的过程中,比较关键的一段代码为:
mAudioTrack = new AudioTrack(attr.build(), format.build(), mAudioData.length, AudioTrack.MODE_STATIC,AudioManager.AUDIO_SESSION_ID_GENERATE);
这段代码将在/frameworks/base/media/java/android/media/AudioTrack.java中做初始化,接下来我们来看AudioTrack中代码:
private AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,
int mode, int sessionId, boolean offload) throws IllegalArgumentException {
super(attributes, AudioPlaybackConfiguration.PLAYER_TYPE_JAM_AUDIOTRACK);
//省略一部分代码
...
/* native层初始化
* 这里调用了jni代码,/frameworks/base/core/jni/android_media_AudioTrack.cpp中android_media_AudioTrack_setup()函数
* 最终将调用Audio Library层,frameworks/av/media/libaudioclient/AudioTrack.cpp,创建native AudioTrack object
* 在Audio Library层,将执行lpTrack->set(...)函数,后面将详细讲解set()函数
*/
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/,
offload);
if (initResult != SUCCESS) {
loge("Error code "+initResult+" when initializing AudioTrack.");
return; // with mState == STATE_UNINITIALIZED
}
//省略一部分代码
...
}
三、Audio Library层
Library层相关代码:frameworks/av/
Audio Library层是音频处理的核心,主要分为三个部分:AudioTrack,AudioFlinger和AudioPlicy:
- AudioTrack:负责回放数据的输出;
- AudioFlinger:音频策略的执行者,负责输入输出流设备的管理及音频流数据的传输处理;
- AudioPolicy:音频策略的制定者,负责音频设备切换的策略抉择、音量调节策略等;
AudioTrack.cpp所在目录: frameworks/av/media/libaudioclient/
这部分内容被编译成库libaudioclient.so文件。
AudioFlinger.cpp所在目录:frameworks/av/services/audioflinger/
这部分内容被编译成库libaudioflinger.so文件。
AudioPolicyManager.cpp所在目录:frameworks/av/services/audiopolicy/
这部分内容被编译成库libaudioflinger.so文件。
AudioTrack和AudioFlinger的交互原理:
- audiotrackcblk_t实现了一个环形FIFO;
- AudioTrack是FIFO的数据生产者;
- AudioFlinger是FIFO的数据消费者。
下面这张图很好的解释了AudioTrack和AudioFlinger之间的交互关系:
了解了交互原理之后,下面我们来分析一下具体的代码:
AudioTrack.cpp分析:
在frameworks层代码讲到,new AudioTrack()最终会创建生成一个native 的AudioTrack对象,并且执行Set()函数,代码如下:
status_t AudioTrack::set()
{
//省略一部分代码
...
// 这里执行了createTrack_l(),接下来我们看createTrack_l()的处理
// create the IAudioTrack
status = createTrack_l();
//省略一部分代码
...
}
status_t AudioTrack::createTrack_l()
{
//省略一部分代码
...
//音频流类型,音频的采样率等信息放进了input里面
IAudioFlinger::CreateTrackInput input;
if (mStreamType != AUDIO_STREAM_DEFAULT) {
stream_type_to_audio_attributes(mStreamType, &input.attr);
} else {
input.attr = mAttributes;
}
input.config = AUDIO_CONFIG_INITIALIZER;
input.config.sample_rate = mSampleRate;
input.config.channel_mask = mChannelMask;
input.config.format = mFormat;
input.config.offload_info = mOffloadInfoCopy;
input.clientInfo.clientUid = mClientUid;
input.clientInfo.clientPid = mClientPid;
input.clientInfo.clientTid = -1;
//省略一部分代码
...
//跟AudioFlinger进行交互,返回一个Track句柄,可以进行play,pause等控制操作
//通过track->getCblk();得到在AudioFlinger中创建的FIFO(audiotrackcblk_t);
sp<IAudioTrack> track = audioFlinger->createTrack(input,
output,
&status);
if (status != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("AudioFlinger could not create track, status: %d output %d", status, output.outputId);
if (status == NO_ERROR) {
status = NO_INIT;
}
goto exit;
}
//省略一部分代码
...
}
AudioFlinger.cpp文件分析:
AudioTrack.cpp会通过Binder调用AudioFlinger.cpp中的createTrack()函数,代码如下:
// IAudioFlinger interface
sp<IAudioTrack> AudioFlinger::createTrack(const CreateTrackInput& input,
CreateTrackOutput& output,
status_t *status)
{
//省略一部分代码
...
/* AudioSystem::getOutputForAttr() 经过一系列的调用,进入 AudioPolicyManager::getOutputForDevice()
* 最终调用 AudioFlinger::openOutput() 根据音频策略,打开输出标识对应的输出流设备并创建相关的PlaybackThread,
* 并保存该 PlaybackThread 对应的 audio_io_handle_t 给 AudioTrack;
*/
lStatus = AudioSystem::getOutputForAttr(&input.attr, &output.outputId, sessionId, &streamType,
clientPid, clientUid, &input.config, input.flags,
&output.selectedDeviceId, &portId);
if (lStatus != NO_ERROR || output.outputId == AUDIO_IO_HANDLE_NONE) {
ALOGE("createTrack() getOutputForAttr() return error %d or invalid output handle", lStatus);
goto Exit;
}
//省略一部分代码
...
//根据outputId获取相对应的音频回放线程,音频数据的读写主要在这个PlaybackThread中进行
Mutex::Autolock _l(mLock);
PlaybackThread *thread = checkPlaybackThread_l(output.outputId);
if (thread == NULL) {
ALOGE("no playback thread found for output handle %d", output.outputId);
lStatus = BAD_VALUE;
goto Exit;
}
client = registerPid(clientPid);
PlaybackThread *effectThread = NULL;
// check if an effect chain with the same session ID is present on another
// output thread and move it here.
for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
if (mPlaybackThreads.keyAt(i) != output.outputId) {
uint32_t sessions = t->hasAudioSession(sessionId);
if (sessions & ThreadBase::EFFECT_SESSION) {
effectThread = t.get();
break;
}
}
}
ALOGV("createTrack() sessionId: %d", sessionId);
output.sampleRate = input.config.sample_rate;
output.frameCount = input.frameCount;
output.notificationFrameCount = input.notificationFrameCount;
output.flags = input.flags;
//这里创建了一个track并最终返回给AudioTrack.cpp中
track = thread->createTrack_l(client, streamType, input.attr, &output.sampleRate,
input.config.format, input.config.channel_mask,
&output.frameCount, &output.notificationFrameCount,
input.notificationsPerBuffer, input.speed,
input.sharedBuffer, sessionId, &output.flags,
input.clientInfo.clientTid, clientUid, &lStatus, portId);
//省略一部分代码
...
}
//接下来我们来看PlaybackThread::createTrack_l中做了哪些处理
// PlaybackThread::createTrack_l() must be called with AudioFlinger::mLock held
sp<AudioFlinger::PlaybackThread::Track> AudioFlinger::PlaybackThread::createTrack_l(
const sp<AudioFlinger::Client>& client,
audio_stream_type_t streamType,
const audio_attributes_t& attr,
uint32_t *pSampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t *pFrameCount,
size_t *pNotificationFrameCount,
uint32_t notificationsPerBuffer,
float speed,
const sp<IMemory>& sharedBuffer,
audio_session_t sessionId,
audio_output_flags_t *flags,
pid_t tid,
uid_t uid,
status_t *status,
audio_port_handle_t portId)
{
//省略一部分代码
...
//创建了一个Track对象
//核心代码为Track的构造函数,下面我们看看Track的构造函数做了哪些处理
track = new Track(this, client, streamType, attr, sampleRate, format,
channelMask, frameCount,
nullptr /* buffer */, (size_t)0 /* bufferSize */, sharedBuffer,
sessionId, uid, *flags, TrackBase::TYPE_DEFAULT, portId);
//省略一部分代码
...
}
// Track constructor must be called with AudioFlinger::mLock and ThreadBase::mLock held
AudioFlinger::PlaybackThread::Track::Track(...)
{
//省略一部分代码
...
if (sharedBuffer == 0) {
/* MODE_STREAM
* AudioTrack创建时,数据传输模式为 MODE_STREAM 模式,创建一个 AudioTrackServerProxy 对象,
* PlaybackThread 将持续使用它从 FIFO 上取得可读数据的位置.
*/
mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack(), sampleRate);
} else {
/* MODE_STATIC
* AudioTrack创建时,数据传输模式为 MODE_STREAM 模式,创建一个 AudioTrackServerProxy 对象,
* PlaybackThread 将持续使用它从 FIFO 上取得可读数据的位置.
*/
mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize);
}
mServerProxy = mAudioTrackServerProxy;
//省略一部分代码
...
}
到这里,整个从AudioTrack到AudioFlinger之间的流程都走下来了。其中还有一些细节,后面在慢慢分析,关于AudioPolicy的音频策略管理部分,我们放到下一章节在讲。
四、HAL层
HAL是AudioFlinger直接访问的对象,AudioFlingerd打开HAL层的输出流设备后,将从FIFO中读到的音频数据发送HAL层,由HAL层输出到硬件设备中进行声音输出。这一点后面在单独发文章出来分析。
参考资料:
1.https://blog.csdn.net/Ch97CKd/article/details/78641457