framesReady()用于获取FIFO中可读取的空间大小。
- uint32_t audio_track_cblk_t::framesReady()
- {
- uint32_t u = this->user;
- uint32_t s = this->server;
- if (out) {
- if (u < loopEnd) {
- return u - s;
- } else {
- Mutex::Autolock _l(lock);
- if (loopCount >= 0) {
- return (loopEnd - loopStart)*loopCount + u - s;
- } else {
- return UINT_MAX;
- }
- }
- } else {
- return s - u;
- }
- }
我们看看下面的示意图:
_____________________________________________
^ ^ ^ ^
buffer_start server(s) user(u) buffer_end
很明显,frameReady = u - s,frameAvalible = frameCount - frameReady = frameCount - u + s
可能有人会问,应为这是一个环形的buffer,一旦user越过了buffer_end以后,应该会发生下面的情况:
_____________________________________________
^ ^ ^ ^
buffer_start user(u) server(s) buffer_end
这时候u在s的前面,用上面的公式计算就会错误,但是android使用了一些技巧,保证了上述公式一直成立。我们先看完下面三个函数的代码再分析:
- uint32_t audio_track_cblk_t::stepUser(uint32_t frameCount)
- {
- uint32_t u = this->user;
- u += frameCount;
- ......
- if (u >= userBase + this->frameCount) {
- userBase += this->frameCount;
- }
- this->user = u;
- ......
- return u;
- }
- bool audio_track_cblk_t::stepServer(uint32_t frameCount)
- {
-
-
-
- status_t err;
- err = lock.tryLock();
- if (err == -EBUSY) {
- usleep(1000);
- err = lock.tryLock();
- }
- if (err != NO_ERROR) {
-
- return false;
- }
- uint32_t s = this->server;
- s += frameCount;
-
-
- if (s >= serverBase + this->frameCount) {
- serverBase += this->frameCount;
- }
- this->server = s;
- cv.signal();
- lock.unlock();
- return true;
- }
- void* audio_track_cblk_t::buffer(uint32_t offset) const
- {
- return (int8_t *)this->buffers + (offset - userBase) * this->frameSize;
- }
stepUser()和stepServer的作用是调整当前偏移的位置,可以看到,他们仅仅是把成员变量user或server的值加上需要移动的数量,user和server的值并不考虑FIFO的边界问题,随着数据的不停写入和读出,user和server的值不断增加,只要处理得当,user总是出现在server的后面,因此frameAvalible()和frameReady()中的算法才会一直成立。根据这种算法,user和server的值都可能大于FIFO的大小:framCount,那么,如何确定真正的写指针的位置呢?这里需要用到userBase这一成员变量,在stepUser()中,每当user的值越过(userBase+frameCount),userBase就会增加frameCount,这样,映射到FIFO中的偏移总是可以通过(user-userBase)获得。因此,获得当前FIFO的写地址指针可以通过成员函数buffer()返回:
p = mClbk->buffer(mclbk->user);
在AudioTrack中,封装了两个函数:obtainBuffer()和releaseBuffer()操作FIFO,obtainBuffer()获得当前可写的数量和写指针的位置,releaseBuffer()则在写入数据后被调用,它其实就是简单地调用stepUser()来调整偏移的位置。
IMemory接口
在createTrack的过程中,AudioFlinger会根据传入的frameCount参数,申请一块内存,AudioTrack可以通过IAudioTrack接口的getCblk()函数获得指向该内存块的IMemory接口,然后AudioTrack通过该IMemory接口的pointer()函数获得指向该内存块的指针,这块内存的开始部分就是audio_track_cblk_t结构,紧接着是大小为frameSize的FIFO内存。
IMemory->pointer() ---->|_______________________________________________________
|__audio_track_cblk_t__|_______buffer of FIFO(size==frameCount)____|
看看AudioTrack的createTrack()的代码就明白了:
- sp track = audioFlinger->createTrack(getpid(),
- streamType,
- sampleRate,
- format,
- channelCount,
- frameCount,
- ((uint16_t)flags) << 16,
- sharedBuffer,
- output,
- &status);
-
- sp cblk = track->getCblk();
- mAudioTrack.clear();
- mAudioTrack = track;
- mCblkMemory.clear();
- mCblkMemory = cblk;
-
- mCblk = static_cast(cblk->pointer());
-
- mCblk->out = 1;
-
- mFrameCount = mCblk->frameCount;
- if (sharedBuffer == 0) {
-
- mCblk->buffers = (char*)mCblk + sizeof(audio_track_cblk_t);
- } else {
- ..........
- }
阅读(1162) | 评论(0) | 转发(0) |