08.音频系统:第004课_Android音频系统详解:第010节_音频数据的传递
通过前面的学习,在应用程序中,每创建一个AudioTrack,在AudioFlinger边,某个playbackThread中就会创建一个Track与其对应,Track与AudioTrack之间,通过共享内存传递音频数据,那么怎么传递这个数据呢?其实非常的简单,分为两种情况
1.MODE_STATIC:一次性,提前提供数据。
2.MODE_STREAM:多次传输,是一边播放,一边提供数据
在第一种MODE_STATIC情况下创建共享内存,并且一次性的构造行数据,playbackThread等构造好,取出数据就可以了,不存在同步,或者不同步的问题。其playbackThread工作:获得含有数据的obtiainBuffer(APP一次性提交共享内存的数据有可能很多,playbackThread分分开多次进行播放)。播放完之后,再释放buffer。
在第二种MODE_STREAM情况下,他们也是通过共享内存传递数据,不同的是APP在写数据的时候,playbackThread在播放数据。APP怎么去写数据呢?他也是调用obtiainBuffer,获取空白buffer,然后填充数据,在释放buffer。playbackThread端与第一种情况一样。
应用程序的代码在AudioTrack.cpp之中,playbackThread的代码在
Tracks.cpp之中。我们打开MediaAudioTrackTest.java文件,我们先来那看复杂的MODE_STREAM模式,找到
public void testSetLoopPointsStream() throws Exception {
/*其会导致playbackThread线程中对应的Track与共享内存的创建*/
AudioTrack track = new AudioTrack(TEST_STREAM_TYPE, TEST_SR, TEST_CONF, TEST_FORMAT, 2*minBuffSize, TEST_MODE);
/*写数据,该函数在AudioTrack.java中实现*/
track.write(data, 0, data.length);
/*可以知道,其最终都是调用到C++中实现的native_write_byte函数*/
int ret = native_write_byte(audioData, offsetInBytes, sizeInBytes, mAudioFormat,writeMode == WRITE_BLOCKING);
搜索native_write_byte进入android_media_AudioTrack.cpp文件:
static jint android_media_AudioTrack_writeArray(JNIEnv *env, jobject thiz,T javaAudioData,jint offsetInSamples, jint sizeInSamples,jint javaAudioFormat,jboolean isWriteBlocking) {
/*把java的AudioTrack转化为c++实现的AudioTrack*/
sp<AudioTrack> lpTrack = getAudioTrack(env, thiz);
jint samplesWritten = writeToTrack(lpTrack, javaAudioFormat, cAudioData,offsetInSamples, sizeInSamples, isWriteBlocking == JNI_TRUE /* blocking */);
/*如果应用程序没有提供共享内存*/
if (track->sharedBuffer() == 0) {
/*调用AudioTrack中write函数*/
written = track->write(data + offsetInSamples, sizeInBytes, blocking);
// for compatibility with earlier behavior of write(), return 0 in this case
if (written == (ssize_t) WOULD_BLOCK) {
written = 0;
}
} else {//如果应用程序提供了共享内存
// writing to shared memory, check for capacity
if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
sizeInBytes = track->sharedBuffer()->size();
}
/*直接使用memcpy写入数据*/
memcpy(track->sharedBuffer()->pointer(), data + offsetInSamples, sizeInBytes);
written = sizeInBytes;
}
从上面我们可以看到,如果应用程序提供了共享内存,直接使用memcpy写入数据,如果没有程序没有提供共享内存,则调用AudioTrack中的write函数如下:
ssize_t AudioTrack::write(const void* buffer, size_t userSize, bool blocking)
/*获得空白buffer*/
status_t err = obtainBuffer(&audioBuffer,blocking ? &ClientProxy::kForever : &ClientProxy::kNonBlocking);
/*把数据写入buffer*/
memcpy(audioBuffer.i8, buffer, toWrite);
/*释放buffer*/
releaseBuffer(&audioBuffer);
现在我们来看看Tracks.cpp中的是怎么使用obtainBuffer读取数据的:
status_t AudioFlinger::PlaybackThread::Track::getNextBuffer(
status_t status = mServerProxy->obtainBuffer(&buf);
可以看到其会调用mServerProxy中的obtainBuffer,其释放buffer的函数是在其父类TrackBase中实现的:
void AudioFlinger::ThreadBase::TrackBase::releaseBuffer(AudioBufferProvider::Buffer* buffer)
mServerProxy->releaseBuffer(&buf);
可以看到其调用mServerProxy的releaseBuffer函数。其中ServerProxy* mServerProxy。mServerProxy是什么呢?我们看看上小节我们分析的结果:
AudioFlinger::PlaybackThread::Track::Track(
if (sharedBuffer == 0) {//如果共享内存为应用程序提供
mAudioTrackServerProxy = new AudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize, !isExternalTrack(), sampleRate);
} else {//如果共享内存为PlaybackThread提供
mAudioTrackServerProxy = new StaticAudioTrackServerProxy(mCblk, mBuffer, frameCount,
mFrameSize);
}
mServerProxy = mAudioTrackServerProxy;
Track的构造函数,可以找到如上,知道其根据共享内存的创建者不同,得到不同的mServerProxy对象 :
我们可以猜测得到,当调用mServerProxy中obtainBuffer与releaseBuffer方法,会导致AudioTrack.cpp:
status_t AudioTrack::set(
if (mSharedBuffer == 0) {
mStaticProxy.clear();
mProxy = new AudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize);
} else {
mStaticProxy = new StaticAudioTrackClientProxy(cblk, buffers, frameCount, mFrameSize);
mProxy = mStaticProxy;
对应的mProxy 中的obtainBuffer与releaseBuffer被调用,mProxy 是给应用程序管理共享内存的,mServerProxy是给playbackThread管理共享内存的。
下面我们做一下总结:
1.APP创建AudioTrack,然后playbackThread创建对应的Track。他们之间通过共享内存传递数据。
2.APP有两种共享内存的方式
MODE_STATIC:APP创建共享内存,APO一次性填充数据
MODE_STREAM:APP使用obtiainBuffer获得空白内存,填充数据。然后使用releaseBuffer释放。
3.playbackThread使用obtiainBuffer获得含有数据的内存,消耗数据之后使用releaseBuffer释放。
d.AudioTrack中含有mProxy,用来管理内存,其中包含了obtainBuffer, releaseBuffer函数。
Track中含有mServerProxy, 它被用来管理共享内存, 里面含有obtainBuffer, releaseBuffer函数。
对于不同的MODE(MODE_STATIC或者MODE_STREAM:APP), 这些Proxy指向不同的对象
MODE_STATIC模式
我们继续往下分析,MODE_STATIC模式下,应用程序直接把数据放入共享内存,他跟playbackThread的Track不需要同步,playbackThread通过obtainBuffer获得buffer,那么我们现在分析一下他是怎么获得buffer的。怎么releaseBuffer的。
我们先看看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);
memset(&mState, 0, sizeof(mState));
}
mServerProxy = mAudioTrackServerProxy;
对于我们这种情况,其就会使用StaticAudioTrackServerProxy对buffer进行管理:
StaticAudioTrackServerProxy::StaticAudioTrackServerProxy(audio_track_cblk_t* cblk, void *buffers,
size_t frameCount, size_t frameSize)
: AudioTrackServerProxy(cblk, buffers, frameCount, frameSize),
mObserver(&cblk->u.mStatic.mSingleStateQueue),
mPosLoopMutator(&cblk->u.mStatic.mPosLoopQueue),
mFramesReadySafe(frameCount), mFramesReady(frameCount),
mFramesReadyIsCalledByMultipleThreads(false)
{
memset(&mState, 0, sizeof(mState));
}
可以知道其设置了共享内存的大小,他会通过obtiainBuffer函数获取buffer:
/*该结构体在StaticAudioTrackServerProxy的构造函数中指定*/
struct Buffer {
/*想去几帧数据*/
size_t mFrameCount; // number of frames available in this buffer
/*取出的数据指向谁*/
void* mRaw; // pointer to first frame
size_t mNonContig; // number of additional non-contiguous frames available
};
status_t StaticAudioTrackServerProxy::obtainBuffer(Buffer* buffer, bool ackFlush)
/*获得位置*/
ssize_t positionOrStatus = pollPosition();
/*设置mRaw 指向当前数据的位置(前面提到,去数据的时候,可能因为共享内存数据太多,分多次提取)*/
buffer->mRaw = &((char *) mBuffers)[position * mFrameSize];
下面我们看看MODE_STREAM模式
MODE_STREAM模式
MODE_STREAM会使用到环形缓存区,一个生产数据,一个消费数据,这个时候使用环形缓冲区是最可靠的。