音频数据流
音频正常的回放过程:
比如用MediaPlayer播放音频,先要把音频文件读取到内存中,然后执行对应的解码操作,mediaplayer是在mediaplayerservice的帮助下完成解码相关操作的,mediaplayerservice会使用audiotrack完成播放功能。
一个audiotrack代表一个播放实例,系统中可能同时运行多个audiotrack实例,同时系统中也会有多个音频回放设备,audiotrack和音频回放设备的对应关系由audiopolicyservice来确定,aps根据每个audiotrack所属“流类型”来为他选择与当前系统最匹配的输出设备,然后查找支持这个输出设备的audiofligner中的output输出通道(也就是openoutput所打开的通道)。
Audioflinger执行audiopolicyservice确定好的路由策略,将各路音频数据进行混音,然后传到hal层,进一步输出到最终的音频设备中。在回放时,Audiofligner中起关键作用的有两个:一个是playbackthread,往通道中传输数据;一个是audiomixer,这是混音的核心。
Audiotrack中的音频流
以MediaAudioTrackTest.java中范例来分析:public void testSetStereoVolumeMax() @MediaAudioTrackTest.java throws Exception {
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF,
TEST_FORMAT, minBuffSize, TEST_MODE);
track.write(data, 0, data.length);
}
上面的代码中,write函数把data数据写入audiotrack中,说明audiotrack内部一定申请了内存空间来存储音频数据,这是java层代码,下面直接看native层的实现:
static jint android_media_AudioTrack_setup(…)@ android_media_AudioTrack.cpp{
sp<AudioTrack> lpTrack = new AudioTrack();
//流模式下,音频数据是分多次传递的,这里传入的参数是0,audiotrack需要的内存空间是后面由audioflinger根据需要分配的一块缓冲区。
status = lpTrack->set(…0,// shared mem …);
//静态模式下,把内存空间的地址(lpJniStorage->mMemBase)传给了audiotrack,这块空间是由AudioTrackJniStorage事先分配的。
status = lpTrack->set(…lpJniStorage->mMemBase,// shared mem …);
}
接着看流模式下分配的缓冲区:
status_t AudioTrack::createTrack_l()@AudioTrack.cpp {
// frameCount决定了申请缓冲区的大小。
sp<IAudioTrack> track = audioFlinger->createTrack(…frameCount…);
//向audioflinger申请缓冲区
sp<IMemory> iMem = track->getCblk();
//与audioflinger的ipc通信中介
mAudioTrack = track;
mCblkMemory = iMem;
audio_track_cblk_t* cblk = static_cast<audio_track_cblk_t*>(iMemPointer);
mCblk = cblk;
}
IAudioTrack在audioflinger中的实现是TrackHandler。
sp<IMemory> AudioFlinger::TrackHandle::getCblk() const @ Tracks.cpp {
return mTrack->getCblk();
}
这里的mTrack是一个playbackThread::track,是在audioflinger::createTrack时生成的。mTrack属于某个特定的playbackthread实例,每个audiotrack在audioflinger中都有一个对应的playbackthread。看看playbackthread::track如何生成一块内存区域:
Track继承自trackbase,trackbase的构造函数中将申请所需的内存单元。
AudioFlinger::ThreadBase::TrackBase::TrackBase(…frameCount…)@ Tracks.cpp {
if (client != 0) {
mCblkMemory = client->heap()->allocate(size);
}
}
mCblkMemory指向的内存地址由client指定,这个client是在Audioflinger::createtrack中生成的,每个playbackthread虽然有使用内存空间的权利,但是对内存的申请、回收仍是由audioflinger来掌控的。
sp<IAudioTrack> AudioFlinger::createTrack( )@AudioFlinger.cpp{
sp<Client> client = registerPid(pid);
}
函数registerPid为当前的pid(audiotrack所在进程的pid)生成一个audioflinger::client实例,并将<pid,client>键值对添加到mclients中。每个audiotrack在audioflinger中有且仅有一个client实例。
到这里,audiotrack和audioflinger中与音频数据相关的内存空间就分析完了。大致是这样的:
Audiotrack(java)在构造时,通过本地方法native_setup来传递应用层所需的最小音频数据空间。
Native_setup负责生成audiotrack(native)对象,此时还没涉及内存申请,随后调用Audiotrack::set接口,并将应用层计算出的数据空间大小转化为framecount。
在set实现中,首先找到匹配的audioflinger中的playbackthread,接着调用audioflinger::createtrack在playbackthread中新增一个track,同时建立audiotrack和audioflinger间的通信接口Iaudiotrack。在audioflinger内部的track生成过程中,会有trackbase申请相应的内存空间。
接下来audiotrack可以通过Iaudiotrack::getCblk来获得这个数据空间。
之后audiotrack和audioflinger就能够利用这个数据缓冲区空间来传递音频数据,在audiotrack中,它是有audiotrack::mCblkmemory来表示,在audioflinger中,它是由Trackbase::mCBlkMemory来表示的。
AudioMixer是由playbackthread(MixerThread)生成的,跟audioflinger属于同一个进程。AudioMixer中用于混音操作的缓冲区对象,和audiotrack,audiofligner中的数据区是同一个。