在上小节中我们讲解了,在AudioTrack创建过程中,他会选择一个output,一个output对应一个播放设备,他也对应着一个播放线程,该小节我们讲解在这个线程之中,怎么去创建一个Track,应用程序的AudioTrack与播放线程之中的Track是一一对应的,并且我们还会讲解应用程序的AudioTrack与播放线程之中的Track他们之间是怎么传递数据的,也就是说,他们是通过怎样的内存传递数据的。
之前提到过,作为声音的应用程序,他需要给playbackThread线程提供数据,他是怎么提供的呢?APP给AudioTrack提供音频数据有2种方式: 一次性提供(MODE_STATIC)、边播放边提供(MODE_STREAM)
对于简单的提示音,其数据是十分的少的,我们可以把所有的数据,一次性提供给播放线程,那么对于播放音乐,播放网络传递过来的声音,那么我们需要边播放,边提供。
那么有以下几个问题: 1.音频数据保存在buffer中,这个buffer由谁提供呢?APP还是PlaybackTHread。 2.APP提供数据,PlaybackThread消耗数据,如何同步。
我们先来画一个图: 我们知道在android系统之中,其可能有多个声卡,如上的声卡1,声卡2。每个声卡对应一个output,同时每个output对应一个播放线程PlaybackThread,PlaybackThread中存在一个数组mTracks,其中包含一个或者多个Track,每一个Track都对应应用程序中创建的AudioTrack,正是因为应用程序APP创建了AudioTrack才会导致PlaybackThread中Track的创建(注意:APP与PlaybackThread处于不同的进程)。
那么他们是怎么传递数据的呢?虽然可以使用binder通信,但是其通信的效率并不受很高,其实在声音数据,APP与PlaybackThread声音数据的传递是使用共享内存(即APP与PlaybackThread可以访问同一块内存)。
现在我们先来解决第一个问题,这块共享内存是由谁来创建的,是由APP还是由PlaybackThread呢?我们分为两种情况 1.如果APP提供AudioTrack的数据是一次性提供(MODE_STATIC:一次性,提前提供数据),那么显然这个buffer当然是由应用程序提供。因为应用程序才知道这个buffer有多大。 2.如果应用程序,是一边播放,一边提供数据(MODE_STREAM),那么就由PlaybackThread创建共享内存,因为这样省事(为了简单应用程序的编程)
那么应用程序APP与PlaybackThread如何同步数据呢? 1.如果APP提供AudioTrack的数据是一次性提供(MODE_STATIC:一次性,提前提供数据),APP先构造,PlaybackThread再消费,则无需进行同步。 2.如果应用程序,是一边播放,一边提供数据(MODE_STREAM),使用环形缓冲区进行同步。
我们回答了以上问题,现在我们需要从源码中,验证以上结果。
先打开测试程序shared_mem_test.cpp文件:
int AudioTrackTest::Test01() {
/*首先自己分配了buffer*/
iMem = heap->allocate(BUF_SZ*sizeof(short));
/*设置好数据*/
p = static_cast(iMem->pointer());
memcpy(p, smpBuf, BUF_SZ*sizeof(short));
/*创建AudioTrack,其会传入共享内存iMem,iMem如果为NULL则MODE_STREAM模式,共享内存由PlaybackThread创建*/
sp track = new AudioTrack(AUDIO_STREAM_MUSIC,// stream type,
rate,
AUDIO_FORMAT_PCM_16_BIT,// word length, PCM
AUDIO_CHANNEL_OUT_MONO,
iMem);
我们打开AudioTrack.cpp查看AudioTrack的构造函数:
AudioTrack::AudioTrack(
mStatus = set(streamType, sampleRate, format, channelMask.......
以上的是c++的测试程序,之前我们还提到过java的测试程序,打开MediaAudioTrackTest.java文件,随便查看一个函数如:
public void testSetStereoVolumeMax() throws Exception {
final int TEST_MODE = AudioTrack.MODE_STREAM;
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, minBuffSize, TEST_MODE);
可以看到,其指定了TEST_MODE = AudioTrack.MODE_STREAM,即为一边播放,一边提供数据(MODE_STREAM)模式,对于C++中,我们是不需要指定的。 我们查看java中AudioTrack的构造函数:
public AudioTrack(AudioAttributes attributes, AudioFormat format, int bufferSizeInBytes,int mode, int sessionId)
int initResult = native_setup(new WeakReference(this), mAttributes,sampleRate, mChannelMask, mChannelIndexMask, mAudioFormat,mNativeBufferSizeInBytes, mDataLoadMode, session, 0 /*nativeTrackInJavaObj*/);
看到native_setup,我们就知道,其会调用C++中的函数,我们在源码中进行搜索native_setup,可以找到android_media_AudioTrack.cpp文件,可以找到其对应函数:
android_media_AudioTrack_setup(JNIEnv *env, jobject thiz, jobject weak_this, jobject jaa,jintArray jSampleRate, jint channelPositionMask, jint channelIndexMask,jint audioFormat, jint buffSizeInBytes, jint memoryMode, jintArray jSession,jlong nativeAudioTrack) {
lpTrack = new AudioTrack();
case MODE_STREAM:
status = lpTrack->set()
case MODE_STATIC:
/*分配共享内存*/
if (!lpJniStorage->allocSharedMem(buffSizeInBytes)) {
status = lpTrack->set()
可以看出,如果为MODE_STREAM没事,则APP不会创建共享内存,如果MODE_STATIC(一次性),则会创建共享内存。
其上C++与java的调用过程总结如下图:
可以知道无论C++的AudioTrack还是javaAudioTrack最终都是创建了一个c++的AudioTrack对象,即c++的构造函数被调用,进而其中的set函数也被调用。
之前我们强调,创建AudioTrack对象,就会导致某个PlaybackThread中创建一个Track对象,这里截图一下之前的时序图: 其调用关系就不进行重复了。最后得出结论,APP创建一个AudioTrack对象,导致PlaybackThread中new Trac被创建。打开Tracks.cpp:
AudioFlinger::PlaybackThread::Track::Track(
PlaybackThread *thread,
const sp& client,
audio_stream_type_t streamType,
uint32_t sampleRate,
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
void *buffer,
const sp& sharedBuffer,//该为应用程序创建的共享内存
audio_session_t sessionId,
int uid,
audio_output_flags_t flags,
track_type type)
: TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
(sharedBuffer != 0) ? sharedBuffer->pointer() : buffer,
sessionId, uid, true /*isOut*/,
(type == TYPE_PATCH) ? ( buffer == NULL ? ALLOC_LOCAL : ALLOC_NONE) : ALLOC_CBLK,
type),
我们可以看到sharedBuffer,其为应用程序创建的共享内存,其也有可能为空,我们来想象一下Track实例化对象中,他会做什么事情,如果应用程序已经构建了sharedBuffer,那么他自己则不会再去构建,如果应用程序没有构建,即传入的sharedBuffer为空,则其会分配sharedBuffer。
从上可以看出,Track由TrackBase派生出来,我们查看一下TrackBase的构造函数,还是在Tracks.cpp文件中:
AudioFlinger::ThreadBase::TrackBase::TrackBase(
/*如果buffer为空,并且alloc == ALLOC_CBLK*/
if (buffer == NULL && alloc == ALLOC_CBLK) {
/*size为一个头部(起到控制作用),如果buffer为NULL,即应用程序没有分配,则大小增加bufferSize*/
size += bufferSize;
/*分配内存*/
mCblkMemory = client->heap()->allocate(size);
//如果buffer为应用程序传入
case ALLOC_NONE:
mBuffer = buffer; //则mBuffer指向应用程序提供的buffer
在TrackBase的构造函数被调用之后,会引起Track的构造函数被调用:
AudioFlinger::PlaybackThread::Track::Track(
if (sharedBuffer == 0) {
mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack(), sampleRate);
} else {
mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize);
}
mServerProxy = mAudioTrackServerProxy;
从上知道,如果应用程序没有创建sharedBuffer,那么气会创建一个AudioTrackServerProxy,用来管理Buffer,如果提供了sharedBuffer,则会创建StaticAudioTrackServerProxy,用来管理者一个buffer,到底如何管理呢?下小节为大家讲解。
同样,在应用程序那边,AudioTrack肯定也需要对buffer进行管理,打开AudioTrack.cpp:
status_t AudioTrack::set(
status_t status = createTrack_l();
/*使用audioFlinger的服务创建Track,会导致播放线程中创建Track*/
sp track = audioFlinger->createTrack()
/*如果没有提供buffer,指向PlaybackThread提供的buffer*/
if (mSharedBuffer == 0) {
buffers = cblk + 1;
} else {
/*否则指向自己提供的buffer*/
buffers = mSharedBuffer->pointer();
if (buffers == NULL) {
ALOGE("Could not get buffer pointer");
return NO_INIT;
}
}
/*管理共享内存*/
// update proxy
if (mSharedBuffer == 0) {
mStaticProxy.clear();
mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize);
} else {
mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize);
mProxy = mStaticProxy;
}
可以看看,其与Track中的是意义对应的,AudioTrackClientProxy对应AudioTrackServerProxy,StaticAudioTrackClientProxy对应StaticAudioTrackServerProxy。都是对buffer,共享内存的管理。
APP创建得到AudioTrack对象之后,引起PlaybackThread中Track的创建,具体流程如下: