上小节我们讲解了音频数据的传输,应用程序会创建一个AudioTrack,然后会到时其与playbackThread之间创建一个共享内存,当然这个共享可以是应用程序创建,也可以是playbackThread创建。应用程序只要把数据写入到共享内存,playbackThread就能从共享内存得到数据,然后播放数据。
那么我们来看一个问题,我们有2个应用程序,同时播放声音,每个应用程序都创建了一个AudioTrack,每个应用程序的在playbackThread端都存在一个Track与其一一对应,他们之间通过共享内存传递数据,其结构如下:
每个应用程序发送的音频数据各不相同,假设:
APP1:发送数据2channel,44KZ,16bit。
APP2:发送数据1channel,22KZ,8bit。
他们需要从同一个硬件播放出来,其声卡会支持某一个格式的数据,所以说这些数据会重新采样,变成硬件支持的格式,那么谁来做这件事情呢?由playbackThread完成,其中使用一个.mAudioMixer完成,进行重采样,混音(把硬件不支持的音频格式转化为硬件支持的音频格式)。
mAudioMixer中存在成员mstate,mstate包含了一个hook函数,其会指向不同的处理函数,有哪些处理函数呢?我们来看看,打开AudioMixer.cpp:
/*如果传递过来的数据,是声卡直接支持的格式,则不需要重新采样*/
// generic code with resampling
void AudioMixer::process__genericResampling(state_t* state)
// no-op case如果静音了不做任何处理
void AudioMixer::process__nop(state_t* state)
/*如果需要重新采样,则会调用该函数*/
// generic code without resampling
void AudioMixer::process__genericNoResampling(state_t* state)
.......
从上面,我们可以知道,hook针对不同的情况,会指向不同的函数。如果需要处理process__genericNoResampling,会有一个outputTermp,为临时缓存区,同时还有一个tracks(AudioMixer.h文件中的mState成员包含),每个track跟应用程序的AudioTrack相对应,该track位于AudioMixer.h文件中的mState成员中:
struct state_t {
// FIXME allocate dynamically to save some memory when maxNumTracks < MAX_NUM_TRACKS
track_t tracks[MAX_NUM_TRACKS] __attribute__((aligned(32)));
};
state_t mState __attribute__((aligned(32)));
其中track_t 定义如下:
struct track_t {
AudioResampler* resampler;//重采样器
/*一下为采样的相关,如采样率等等*/
uint32_t sampleRate;//
int32_t* mainBuffer;//保存最终数据
int32_t* auxBuffer;
hook_t hook; //指向不同的处理函数
从上面的分析,我们知道重采样之后的结果保存在mainBuffer中:
可以知道track_t 中的hook指向不同的处理函数如:
/*不做任何处理*/
void AudioMixer::track__genericResample(track_t* t, int32_t* out, size_t outFrameCount,int32_t* temp, int32_t* aux)
/*进行重采样*/
void AudioMixer::track__nop(track_t* t __unused, int32_t* out __unused,size_t outFrameCount __unused, int32_t* temp __unused,int32_t* aux __unused)
我们可以看到有两个类型的hook,即state_t mState中的hook,与track_t track中的hook。
state_t mState中为总的hook,track_t track中的hook为单个的hook。那么他们的调用关系是怎么的呢?
首先是playbackThread对playbackThread中的mTracks进行格式分析,看是否为硬件支持的格式,如果硬件支持的话,那么我们就要设置hook(确定是否重采样),当设置完成之后,再去设置总的hook函数(mState中),总结如下:
确定hook:
逐个分析mState.tracks[x]的数据, 根据它的格式确定tracks[x].hook
再确定总的mState.hook
调用hook:
调用总的mState.hook即可, 它会再去调用每一gemState.tracks[x].hook
这个重新采样之后的数据会放在那里呢?在共享内存之中有原始数据,前面提到过outputTermp,各个track其处理之后的数据进入临时缓存区(比如重采样)。所以outputTermp存放的是各个track处理完成之后叠加的数据。
这些临时的数据,最终保存到mainBuffer中。每个tracks中的mainBuffer指向playbackThread中混合的mMixerBuffer,mMixerBuffer中的数据已经可以进行播放了,能直接发送给硬件(其实并没有,而是发送给msinkbuffer)。
当然在android系统中,其还做了更多,使用mEffectBuffer对声音进行音效处理:
最终他们都会把buffer发送给硬件mSinkbuffer,如果使用音效,其来源可能是mEffectBuffer与mMixerBuffer。
下面是时序图,有兴趣的同学可以根据时序图更加方便的去阅读源代码:
我们现在粗略的查看一下源码,其既然是一个播放线程,在frameworks\av\services\audioflinger\Threads.cpp文件中,其存在threadLoop函数,平时处于休眠状态,当接受到应用程序发送数据的时候,其就会被换醒:
bool AudioFlinger::PlaybackThread::threadLoop()
/*处理配置信息*/
processConfigEvents_l();
/*当前激活的,有数据的Track*/
sp<Track> t = mActiveTracks[i].promote();
/*如果数据为0,则让声卡进入休眠状态*/
if ((!mActiveTracks.size() && systemTime() > mStandbyTimeNs) ||isSuspended()) {
// put audio hardware into standby after short delay
if (shouldStandby_l()) {
/*声卡休眠*/
threadLoop_standby();
mStandby = true;
}
/*线程休眠*/
mWaitWorkCV.wait(mLock);
下面是一些文字的总结:
a. prepareTracks_l :
确定enabled track, disabled track
对于enabled track, 设置mState.tracks[x]中的参数
b. threadLoop_mix : 处理数据(比如重采样)、混音
确定hook:
逐个分析mState.tracks[x]的数据, 根据它的格式确定tracks[x].hook
再确定总的mState.hook
调用hook:
调用总的mState.hook即可, 它会再去调用每一个mState.tracks[x].hook
混音后的数据会放在mState.outputTemp临时BUFFER中
然后转换格式后存入 thread.mMixerBuffer
c. memcpy_by_audio_format :
把数据从thread.mMixerBuffer或thread.mEffectBuffer复制到thread.mSinkBuffer
d. threadLoop_write:
把thread.mSinkBuffer写到声卡上
e. threadLoop_exit