Audiotrack被用于音频流的回放,用来传输数据。
AudioTrack支持两种数据模式:
一种是Static,静态就是指数据一次性交付给对方,简单高效,一次完成所有数据的传递。适用于铃声、系统提醒等对内存要求小的播放操作。
一种是streaming,流模式和基于网络的音频流回放类似,音频数据严格按照要求不断地传递给接收方,直到结束。通常适用于音频文件较大时;音频属性要求高,如采样率高、深度大的数据;音频数据是实时产生的。
源码中有Audiotrack的应用范例:用于测试立体声左右声道最大音量。
Framework/base/media/tests/../MediaAudioTrackTest.java
public void testSetStereoVolumeMax() throws Exception {
final int TEST_SR = 22050;
final int TEST_CONF = AudioFormat.CHANNEL_OUT_STEREO;
final int TEST_FORMAT = AudioFormat.ENCODING_PCM_16BIT;
final int TEST_MODE = AudioTrack.MODE_STREAM;
final int TEST_STREAM_TYPE = AudioManager.STREAM_MUSIC;
// step1,计算最小缓冲区大小
int minBuffSize = AudioTrack.getMinBufferSize(TEST_SR, TEST_CONF, TEST_FORMAT);
//step2,生成audiotrack对象。
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, minBuffSize, TEST_MODE);
byte data[] = new byte[minBuffSize/2];
// step3,写入音频数据
track.write(data, 0, data.length);
track.write(data, 0, data.length);
// step4,开始播放音频
track.play();
//获取最大音量值。
float maxVol = AudioTrack.getMaxVolume();
track.release();
}
上面的例子,包含了AudioTrack的常规操作
Step1,getMinBufferSize,获取最小的Buffer大小。函数实现如下:
AudioTrack.java
static public int getMinBufferSize(int sampleRateInHz, int channelConfig, int audioFormat) {
//获取音频的声道数属性。
switch(channelConfig) {
case AudioFormat.CHANNEL_OUT_MONO:
channelCount = 1;
break;
case AudioFormat.CHANNEL_OUT_STEREO:
channelCount = 2;
break;
}
//检查音频采样深度。
if (!AudioFormat.isPublicEncoding(audioFormat)) { return ERROR_BAD_VALUE; }
//检查采样频率。
if ( (sampleRateInHz < AudioFormat.SAMPLE_RATE_HZ_MIN) ||
(sampleRateInHz > AudioFormat.SAMPLE_RATE_HZ_MAX) ) {
return ERROR_BAD_VALUE;
}
//最小buffer的计算,取决于采样频率,声道数,采样深度这三个属性。具体计算在native层,android_media_audiotrack.cpp中。
int size = native_get_min_buff_size(sampleRateInHz, channelCount, audioFormat);
}
Step2,有了minbuffersize,就可以创建一个audiotrack对象了。
转到audiotrack.java的构造函数
AudioTrack.java
public AudioTrack(int streamType, int sampleRateInHz, int channelConfig, int audioFormat,
int bufferSizeInBytes, int mode)throws IllegalArgumentException {
// native initialization
int initResult = native_setup(new WeakReference<AudioTrack>(this), mAttributes,
sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,
mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
}
AudioTrack的一个重要任务是和Audioflinger建立联系,这是由native代码实现的。
android_media_AudioTrack.cpp
static jint android_media_AudioTrack_setup(…){
//创建一个native层的audiotrack。
lpTrack = new AudioTrack();
//存储音频数据的地方
lpJniStorage = new AudioTrackJniStorage();
//调用Audiotrack的set函数,设置各种属性
status = lpTrack->set(
AUDIO_STREAM_DEFAULT,
sampleRateInHertz,
format,// word length, PCM
nativeChannelMask,
frameCount,
AUDIO_OUTPUT_FLAG_NONE,
audioCallback, &(lpJniStorage->mCallbackData),//callback, callback data (user)
// notificationFrames == 0 since not using EVENT_MORE_DATA to feed the AudioTrack
0,
//不同的内存模式,这个参数不一样,MODE_STREAM,这个值是0;MODE_STATIC,这个是:lpJniStorage->mMemBase
0,// shared mem
true,// thread can call Java
sessionId,// audio session ID
AudioTrack::TRANSFER_SYNC,
NULL, // default offloadInfo
-1, -1, // default uid, pid values
paa);
}
接着看AudioTrack.cpp中的set函数的实现:
status_t AudioTrack::set(…){
//默认流类型是AUDIO_STREAM_MUSIC
if (streamType == AUDIO_STREAM_DEFAULT) {
streamType = AUDIO_STREAM_MUSIC;
}
//默认采样深度是16bit。
if (format == AUDIO_FORMAT_DEFAULT) {
format = AUDIO_FORMAT_PCM_16_BIT;
}
在经过一些有效性检查后,Audiotrack就要使用底层的音频服务了,这里的底层服务是指audioflinger,audiopolicyservice等,android系统在Audiotrack和底层服务之间又加了audiosystem和Audioservice,这就降低了Audiotrack与底层服务间的耦合。就是说,即使不同版本Android的音频系统改动较大,但只要audioSystem,audioservice向上的接口不变,那么audiotrack就不需要做任何修改。
//step1,创建一个线程AudioTrackThread
mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
//step2,创建一个IAudioTrack,createTrack_l函数中会经过Auidosystem,进一步调用到audiopolicyservice的服务接口。
status_t status = createTrack_l();
}
Step1,AudioTrackThread线程,一方面在Audiotrack和audioflinger之间做数据传输,作为客户端audiotrack用这个线程不断的传送数据,作为接收端的audioflinger也有一个线程(playbackthread)用于接收客户端发来的音频数据;另一方面,用于报告数据传输状态,Audiotrack中保存了mCbf变量,是callback_t类型的回调函数,用于回传音频传输过程中的状态给调用者。
Step2,createTrack_l中,会由audiosystem作为中转,调用audiopolicyservice,audioflinger实现的功能,如:getOutputForAttr,getFrameCount等;还有一个重要操作,是建立audiotrack与audioflinger之间跨进程沟通的桥梁IAudioTrack。
status_t AudioTrack::createTrack_l(){
status = AudioSystem::getOutputForAttr(attr, &output,…);
status = AudioSystem::getFrameCount(output, &mAfFrameCount);
sp<IAudioTrack> track = audioFlinger->createTrack(streamType,…);
}
AudioSystem::getOutputForAttr(…);这个功能实现,最终还是有AudioPolicyservice来完成的。Getoutput会在当前系统中寻找最适合audiotrack的audio interface,及output输出通道(由audioflinger通过openoutput打开的通道),然后audiotrack会向这个output申请一个track(PlaybackThread::Track),audiotrack在Audioflinger内部就是以这个track来管理的,因为audiotrack和Audioflinger之间是跨进程的,所以还创建了他们之间的桥梁是IaudioTrack。
createTrack中的关键步骤:
AudioFlinger.cpp
sp<IAudioTrack> AudioFlinger::createTrack(…){
sp<PlaybackThread::Track> track;
sp<TrackHandle> trackHandle;
//这里的output (audio_io_handle_t),是在audioflinger::openoutput时产生的,这个值跟playbackthread是对应的。依据audio_io_handle_t全局标记值,找到匹配的playbackthread。然后在其内部创建playbacktrack::track对象,这个track的父类是trackBase,所有track对象都被添加到playbackthread:: mTracks中进行管理。TrackHandle就是Iaudiotrack.。
PlaybackThread *thread = checkPlaybackThread_l(output);
track = thread->createTrack_l(client, streamType, sampleRate, format,…);
trackHandle = new TrackHandle(track);
}
Tracks.cpp
在创建Track对象时,同时调用了父类Trackbase的构造函数:
AudioFlinger::PlaybackThread::Track::Track(…) : TrackBase(…){…}
在trackbase的构造函数中,申请了一块缓冲区,这块空间是可以跨进程共享的。AudioTrack通过track->getCblk();获取的就是这块内存空间,这块内存空间是针对mode_stream流模式下的音频数据的存放的。静态模式下音频数据的存放空间是由AudioTrackJniStorage来申请的。
AudioFlinger::ThreadBase::TrackBase::TrackBase(…){
mCblkMemory = client->heap()->allocate(size);
}
AudioTrack.cpp
status_t AudioTrack::createTrack_l(){
sp<IAudioTrack> track = audioFlinger->createTrack(…);
sp<IMemory> iMem = track->getCblk();
}
下面是getCblk()的调用堆栈:
sp<IMemory> AudioFlinger::TrackHandle::getCblk() @Tracks.cppconst {
return mTrack->getCblk();
}
sp<IMemory> getCblk()@TrackBase.h const { return mCblkMemory; }
到这里,Audiotrack可以通过IaudioTrack调用audioflinger的服务,应用实例(前面的testSetStereoVolumeMax ()@MediaAudioTrackTest.java)可以通过不断写入音频数据(track.write(data, 0, data.length);)回放声音。