Android Audio 框架简读 <6>



下面大致介绍Android Audio部分的AudioTrack和AudioFlinger的数据交换.

首先要了解数据交换过程的存储方式:这个存储方式一般由在new一个AudioTrack时构造函数里面传递的mode参数决定,有两种选择:MODE_STATIC与MODE_STREAM

<1> : MODE_STATIC:static方式适用于数据较小,实时性比较高的情形,比如ring,系统铃声等。这种模式下,是在AT端创建共享内存,一次性将数据copy到buffer中,然后传递到AF端。
<2> : MODE_STREAM:stream方式适用于数据较大,media播放等更多其他的情况,也比较复杂。在这种模式下,共享内存是由AF创建的,然后通过生产者-消费者的模式,进行数据的传输。即AT是数据的生产者,AF是数据的消费者。这个数据读写的控制,是由struct audio_track_cblk_t来实现的,这篇文章主要来分析这个控制块的实现

[AT:AudioTrack;AF:AudioFlinger]

MODE_STREAM方式需要AF通过IBinder申请内存交换空间,类似一个数据池(容器),而这个数据容器相当于一个供销商,AudioTrack这边不断生成商品送到供销商,然后AudioFlinger不断从供销商这里拿货,拿货要遵循一定规则,如果拿货速度很快,显然供销商无法提供,拿货速度太慢就会在供销商出积压商品,所以要相互协调,对于音频数据,AF拿数据速度太快,会出现播放声音太快,并且因为容器数据得不到及时填补,又会出现声音断带,如果AF拿数据的速度太慢,显然声音会出现变慢变形.而为了协调这个交互过程,android提供了一个结构体来协调两者的交互工作,类似协调人:audio_track_cblk_t

这个结构体在前面提到,实在AF中创建的,当然从上面的可以知道,stream这种模式是由AudioFlinger负责数据容器创建的,所以可想而知,要想了解这个容器,audio_track_cblk_t必须要在AF中创建(反过来说,STATIC的交互数据体是一个共享内存sharebuffer的家伙,在AT中创建,所以也可以看出两种模式存在本质的区别,当然很多情况下一般是感觉不到的).

下面的内容参照了其他的blog:

size_t size = sizeof(audio_track_cblk_t); //计算audio_track_cblk_t的大小
    uint8_t channelCount = popcount(channelMask);
    size_t bufferSize = frameCount*channelCount*sizeof(int16_t); //计算实际数据buffer大小
    if (sharedBuffer == 0) {
        size += bufferSize; //共享内存实际分配大小为:cblk + data buffer
    }

    if (client != NULL) {
        mCblkMemory = client->heap()->allocate(size); //分配共享内存,实际的实现会在其他文章中分析,这里之分析对共享内存的使用
        if (mCblkMemory != 0) {
            mCblk = static_cast<audio_track_cblk_t *>(mCblkMemory->pointer()); // audio_track_cblk_t* mCblk,指向共享内存的地址
            if (mCblk != NULL) { // construct the shared structure in-place.
                new(mCblk) audio_track_cblk_t(); //这里是C++的语法:
                             //(1)struct可以像class一样,使用new来创建。
                                    //(2)new创建的对象并没有在heap上,而是特定在mCblk指向的内存上创建,这样这块共享
                             //    内存的结构就变成了: |<- audio_track_cblk_t ->| ...data buffer... |
                // clear all buffers
                mCblk->frameCount = frameCount; //初始化cblk中的一些属性
                mCblk->sampleRate = sampleRate;
                mChannelCount = channelCount;
                mChannelMask = channelMask;
                if (sharedBuffer == 0) { //sharedBuffer==0表示MODE_STREAM
                    mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t); //data buffer的地址,需要将audio_track_cblk_t的大小跳过
                    memset(mBuffer, 0, frameCount*channelCount*sizeof(int16_t)); //clear buffer
                    // Force underrun condition to avoid false underrun callback until first data is
                    // written to buffer (other flags are cleared)
                    mCblk->flags = CBLK_UNDERRUN_ON;
                } else { //否则就是MODE_STATIC,数据buffer直接使用由AT传递过来的已经申请好的共享内存
                    mBuffer = sharedBuffer->pointer();
                }
                mBufferEnd = (uint8_t *)mBuffer + bufferSize;
            }
        } else {
            ALOGE("not enough memory for AudioTrack size=%u", size);
            client->heap()->dump("AudioTrack");
            return;
        }
    } else {
        ………………
    }

audio_track_cblk_t的声明:

struct audio_track_cblk_t
{
                ...省略...
                // next 4 are offsets within "buffers"
    volatile    uint32_t    user; //user代表AT,生产者已经写了多少个frame
    volatile    uint32_t    server; //server代表AF,消费者已经读取了多少个frame
                uint32_t    userBase; //与user结合使用,使之成为一个环形FIFO
                uint32_t    serverBase; //同userBase

                // if there is a shared buffer, "buffers" is the value of pointer() for the shared
                // buffer, otherwise "buffers" points immediately after the control block
                void*       buffers; //实际数据buffer的起始地址
                      //如果是MODE_STREAM,buffers紧跟在cblk后面
                      //如果是MODE_STATIC,buffers指向sharedBuffer

                uint32_t    frameCount; //数据buffer的大小,以Frame为单位
          //以下3个loopXXX是与循环播放有关
                uint32_t    loopStart;
                uint32_t    loopEnd;        // read-only for server, read/write for client
                int         loopCount;      // read/write for client

private:
                uint32_t    mVolumeLR; //音量相关
public:
                uint32_t    sampleRate; //采样率

                // NOTE: audio_track_cblk_t::frameSize is not equal to AudioTrack::frameSize() for
                // 8 bit PCM data: in this case,  mCblk->frameSize is based on a sample size of
                // 16 bit because data is converted to 16 bit before being stored in buffer
                uint8_t     frameSize; //一单位frame的大小
                ...省略...
public:
    volatile    int32_t     flags;// Since the control block is always located in shared memory, this constructor
                // is only used for placement new().  It is never used for regular new() or stack.
                            audio_track_cblk_t(); //cblk总是new在指定的共享内存上,而不是堆栈上
                uint32_t    stepUser(uint32_t frameCount); //AT更新写位置
                bool        stepServer(uint32_t frameCount); //AF更新读位置
                void*       buffer(uint32_t offset) const; //返回可写的地址
                uint32_t    framesAvailable(); //还有多少空间可写
                uint32_t    framesAvailable_l();
                uint32_t    framesReady(); //是否有可读数据
                bool        tryLock();
         ...省略...
};

AT写数据流程:

framesAvailable()判断是否有可用空间:

uint32_t audio_track_cblk_t::framesAvailable_l()
{
    uint32_t u = user;
    uint32_t s = server;

    if (flags & CBLK_DIRECTION_MSK) { //以前的out标志,放在了flag中?表示AT?
        //计算读的起始位置,取读位置与循环起始位置两者中较小的
        uint32_t limit = (s < loopStart) ? s : loopStart;
        //可读 = user - limit
        //可写 = frameCount - 可读
        //可写 = frameCount - (u - limit)
        //可写 = frameCount + limit - u
        return limit + frameCount - u;
    } else {
        .......
    }
}


buffer()得到写空间起始地址:

void* audio_track_cblk_t::buffer(uint32_t offset) const
{
   //这里传入的offset就是cblk->user,为了在有限长度的buffer内,模拟环形FIFO队列,就有了个userBase。
    //当数据写满整个buffer的大小以后,会继续增加user的值,那么user*frameSize的大小就会超过cblk->buffers+(frameCount*frameSize)
    //这时就需要userBase来进行调整,user每增加frameCount整个buffer的帧数,userBase会增加同样的大小,
    //来保证user-userBase始终小于frameCount,这样cblk->buffers + (user - userBase)*frameSize就可以得到当前的写位置,
    //这个写位置永远不会超过实际cblk->buffer + frameCount*frameSize。
    //环形FIFO的精髓就在这
    return (int8_t *)buffers + (offset - userBase) * frameSize;
}

stepUser()更新写位置:

uint32_t audio_track_cblk_t::framesReady()
{
    uint32_t u = user;
    uint32_t s = server;

    if (flags & CBLK_DIRECTION_MSK) {
        if (u < loopEnd) { //如果还未写到循环结束
            return u - s;
        } else {
            // do not block on mutex shared with client on AudioFlinger side
            if (!tryLock()) {
                ALOGW("framesReady() could not lock cblk");
                return 0;
            }
            uint32_t frames = UINT_MAX;
            if (loopCount >= 0) {
                frames = (loopEnd - loopStart)*loopCount + u - s;
            }
            lock.unlock();
            return frames;
        }
    } else {
        return s - u;
    }
}

stepServe()更新serve:

bool audio_track_cblk_t::stepServer(uint32_t frameCount)
{
    uint32_t s = server;
    bool flushed = (s == user);

    s += frameCount;
    if (flags & CBLK_DIRECTION_MSK) {
        // Mark that we have read the first buffer so that next time stepUser() is called
        // we switch to normal obtainBuffer() timeout period
        if (bufferTimeoutMs == MAX_STARTUP_TIMEOUT_MS) {
            bufferTimeoutMs = MAX_STARTUP_TIMEOUT_MS - 1;
        }
        // It is possible that we receive a flush()
        // while the mixer is processing a block: in this case,
        // stepServer() is called After the flush() has reset u & s and
        // we have s > u
        if (flushed) {
            ALOGW("stepServer occurred after track reset");
            s = user;
        }
    }

    if (s >= loopEnd) {
        ALOGW_IF(s > loopEnd, "stepServer: s %u > loopEnd %u", s, loopEnd);
        s = loopStart;
        if (--loopCount == 0) {
            loopEnd = UINT_MAX;
            loopStart = UINT_MAX;
        }
    }
    //与user类似,调整serveBase实现环形FIFO
    uint32_t fc = this->frameCount;
    if (s >= fc) {
        // common case, server didn't just wrap
        if (s - fc >= serverBase ) {
            serverBase += fc;
        }
    } else if (s >= serverBase + fc) {
        // server just wrapped
        serverBase += fc;
    }

    server = s;

    if (!(flags & CBLK_INVALID_MSK)) {
        cv.signal();
    }
    lock.unlock();
    return true;
}


看如果AudioTrack具体如何操作,直接从AudioTrack.java的write(...)函数开始,一直看到下面:

jint writeToTrack(const sp<AudioTrack>& track, jint audioFormat, jbyte* data,
                  jint offsetInBytes, jint sizeInBytes) {
    // give the data to the native AudioTrack object (the data starts at the offset)
    ssize_t written = 0;
    // regular write() or copy the data to the AudioTrack's shared memory?
    if (track->sharedBuffer() == 0) {
        written = track->write(data + offsetInBytes, sizeInBytes);
    } else {
        if (audioFormat == javaAudioTrackFields.PCM16) {
            // writing to shared memory, check for capacity
            if ((size_t)sizeInBytes > track->sharedBuffer()->size()) {
                sizeInBytes = track->sharedBuffer()->size();
            }
            memcpy(track->sharedBuffer()->pointer(), data + offsetInBytes, sizeInBytes);
            written = sizeInBytes;
        } else if (audioFormat == javaAudioTrackFields.PCM8) {
            // data contains 8bit data we need to expand to 16bit before copying
            // to the shared memory
            // writing to shared memory, check for capacity,
            // note that input data will occupy 2X the input space due to 8 to 16bit conversion
            if (((size_t)sizeInBytes)*2 > track->sharedBuffer()->size()) {
                sizeInBytes = track->sharedBuffer()->size() / 2;
            }
            int count = sizeInBytes;
            int16_t *dst = (int16_t *)track->sharedBuffer()->pointer();
            const int8_t *src = (const int8_t *)(data + offsetInBytes);
            while (count--) {
                *dst++ = (int16_t)(*src++^0x80) << 8;
            }
            // even though we wrote 2*sizeInBytes, we only report sizeInBytes as written to hide
            // the 8bit mixer restriction from the user of this function
            written = sizeInBytes;
        }
    }
    return written;

}

我看了基本上在这个地方分道扬镳了,一个直接通过track写数据,一个通过sharebuffer的方式.

不断的写入到设备是在PlayThread线程中,不断通过上面的audio_track_cblk_t结构信息,获取数据并且写入设备播放.






























  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值