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端与第一种情况一样。
08.音频系统:第004课_Android音频系统详解:第010节_音频数据的传递
应用程序的代码在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对象 :
08.音频系统:第004课_Android音频系统详解:第010节_音频数据的传递
我们可以猜测得到,当调用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会使用到环形缓存区,一个生产数据,一个消费数据,这个时候使用环形缓冲区是最可靠的。