加载中…
个人资料
康狄龙
康狄龙
  • 博客等级:
  • 博客积分:0
  • 博客访问:14,991
  • 关注人气:1
  • 获赠金笔:0支
  • 赠出金笔:0支
  • 荣誉徽章:
相关博文
推荐博文
谁看过这篇博文
加载中…
正文 字体大小:

Android 4.4 KitKat的环形缓冲机制

(2014-07-17 13:45:50)
标签:

android4.4

kitkat

ringbuffer

mfront

mrear

分类: Android
Ring Buffer Mechanism for Android 4.4 (KitKat)
1. The definition and initialization of Ring Buffer
class MonoPipe : public NBAIO_Sink {
//.......
private:
           const size_t    mMaxFrames;
           void * const     mBuffer;
           volatile int32_t mFront;
           volatile int32_t mRear;
//.......
}

MonoPipe::MonoPipe(size_t reqFrames, NBAIO_Format format, bool writeCanBlock) :
//.......
        mMaxFrames(roundup(reqFrames)),
        mBuffer(malloc(mMaxFrames * Format_frameSize(format))),
        mFront(0),
        mRear(0),
//.......
{
//.......
}

Android <wbr>4.4 <wbr>KitKat的环形缓冲机制

Structure Pic. of Ring Buffer 

2. Meaning and Function of Key variables
    2.1 mRear
    1) It means the write offset to buffer head for writer.
    2) It can be written only by writer with android_atomic_release_store(), and read by reader with android_atomic_acquire_load() method.
    3) It indicates how many frames writer has already written into the Ring Buffer.
   
2.2 mFront
    1) It means the read offset to buffer head for reader.
    2) It can be written only by reader with updateFrontAndNRPTS(), and read/used by reader with observeFrontAndNRPTS() method.
    3) It indicates how many frames reader has already read from the Ring Buffer.
    2.3 mMaxFrames
    1) It indicates the largest number of frames which can be contained into the Ring Buffer.
    2) be always a power of 2.
    2.4 mFront (for read operation) and mRear (for write operation) variables are only added by a positive integer continuously(the number of read/written frames). They do not recount from zero until they surpass the end boundary value of 32-bit integer and then overflow.
    2.5 Since the written operation priors to the read operation and the read-out frame count can not surpass the written-into frame count, mRear must be more than or equal to mFront. That is, mRear must lie behind mFront.
    2.6 Just since mFront and mRear are all 32-bit integer var., overflow is possible if they are active for a long time. Even that happens, they are safe, and we can see that they recount from zero, and that is adapted to ring mechanism.
    2.7  How to map the mFront and mRear to be the proper position of the Ring Buffer?
                 size_t  front= mFront & (mMaxFrames -1) or mFront mod mMaxFrames
                 size_t  rear=mRear & (mMaxFrames -1) or mRear mod mMaxFrames

    2.8  How to check if the Ring Buffer is FULL, EMPTY/UNDERFLOW, SHORT or OVERFLOW?

    1) FULL: the available frame count to write is zero(mMaxFrames - (mRear - mFront) = 0).
    2) EMPTY or UNDERFLOW: the available frame count to read is zero (mRear – mFront = 0).
    3) SHORT: when the requested frame count is more than the available frame count to read. Reader only firstly reads out a part of data from Ring Buffer, and then sleeps a certain milliseconds to wait writer to write new data into Ring Buffer.
    4) OVERFLOW: when the requested frame count is more than the available frame count to write. Writer only firstly write a part of data into Ring Buffer, and then sleeps a certain milliseconds to wait reader to read out existing data from Ring Buffer.

    2.9  Relationship among Key Variables, arrangement between Avail Area and Data Area, and Map from Ring Buffer to Average Buffer     

Android <wbr>4.4 <wbr>KitKat的环形缓冲机制


Android <wbr>4.4 <wbr>KitKat的环形缓冲机制


3. Explanation for key functions
    3.1
How to calculate how many frames can be read out from Ring Buffer?
                 available frame count to read= mRear - mFront

ssize_t MonoPipeReader::availableToRead()
{
    if (CC_UNLIKELY(!mNegotiated)) {
        return NEGOTIATE;
    }
    ssize_t ret = android_atomic_acquire_load(&mPipe->mRear) - mPipe->mFront;
    ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames));
    return ret;
}
    3.2 How to calculate how many frames can be written into Ring Buffer?
                
available frame count to write= mMaxFrames - (mRear - mFront)

ssize_t MonoPipe::availableToWrite() const
{
    if (CC_UNLIKELY(!mNegotiated)) {
        return NEGOTIATE;
    }
    // uses mMaxFrames not mReqFrames, so allows "over-filling" the pipe beyond requested limit
    ssize_t ret = mMaxFrames - (mRear - android_atomic_acquire_load(&mFront));
    ALOG_ASSERT((0 <= ret) && (ret <= mMaxFrames));
    return ret;
}
    3.3 How to Write a given number of frames into Ring Buffer?

1) To get the available frame count to write of Ring Buffer(avail = mMaxFrames - (mRear - mFront)). 
                                         avail = availableToWrite();
                                         written = avail;
2) When the available area of Ring Buffer is SHORT: the requested frame count is more than the available frame count to write. Writer firstly write a part of data into Ring Buffer, whose length is just equal to the available frame count (mMaxFrames – (mRear - mFront)), and then sleeps a certain milliseconds to wait reader to read out existing data from Ring Buffer.
                                        while (count > 0) {

                                           //......
                                        }

3) Next, it’s only necessary to explain the write rule when Ring Buffer has enough available area to store the requested frame data: the requested frame count is not larger than the available frame count to write.
4) When the available frame count to write is more than the requested frame count by writer, then it need only a part of available area to store all requested frame data.
                                    if (CC_LIKELY(written > count)) {
                                         written = count;
                                   }
5) The available area in Ring Buffer must begin with the rear position. So the first simple thought is to write frames into Ring Buffer from the rear position to the end position of Ring Buffer, whose length is equal to part1 = mMaxFrames – rear.
6) The area from the rear position to the end position of Ring Buffer MUST contain a part of or full available area, but it possibly contains a part of or full data area. Anyway, this part of available area’s length cannot surpass the length of all available area in Ring Buffer (written = mMaxFrames –(mRear - mFront)), and MUST be equal to min{mMaxFrames – rear, mMaxFrames – (mRear -  mFront)}. The count of frames that the writer can store into the available area  from rear to end position MUST be equal to part1= min{count, mMaxFrames – rear, mMaxFrames – (mRear - mFront)}, where count indicates the frame count writer requests.
        size_t part1 = mMaxFrames - rear;
         if (part1 > written) {
            part1 = written;
        }
7) If part1 is equal to 0, that is, there is no available area lying behind rear and close to the end of Ring Buffer, it only means the Ring Buffer is in FULL state, and writer only sleep to wait for available area.
Otherwise, the first step is to store a part of or full requested frames into the available area from rear to the end of Ring Buffer, where the stored frame count is depending on the length of the rear avail area and the requested frame count by writer.
        if (CC_LIKELY(part1 > 0)) {
            memcpy((char *) mBuffer + (rear << mBitShift), buffer, part1 << mBitShift);
             if (CC_UNLIKELY(rear + part1 == mMaxFrames)) {
                size_t part2 = written - part1;
                if (CC_LIKELY(part2 > 0)) {
                    memcpy(mBuffer, (char *) buffer + (part1 << mBitShift), part2 << mBitShift);
                }
            }
            android_atomic_release_store(written + mRear, &mRear);
            totalFramesWritten += written;
        }
    
        If the new written position reaches to the end position of the current loop (rear + part1 == mMaxFrames)), and if there are  remaining requested frame to write into Ring Buffer((part2=written -part1) > 0), then store the remaining frames into Ring Buffer from Head position.
 

the related source code with write operation is shown as below: 

ssize_t MonoPipe::write(const void *buffer, size_t count)
{
    //-------
    while (count > 0) {
        // can't return a negative value, as we already checked for !mNegotiated
        size_t avail = availableToWrite();
        size_t written = avail;
        if (CC_LIKELY(written > count)) {
            written = count;
        }

        size_t rear = mRear & (mMaxFrames - 1);
        size_t part1 = mMaxFrames - rear;
        if (part1 > written) {
            part1 = written;
        }
        if (CC_LIKELY(part1 > 0)) {
            memcpy((char *) mBuffer + (rear << mBitShift), buffer, part1 << mBitShift);
            if (CC_UNLIKELY(rear + part1 == mMaxFrames)) {
                size_t part2 = written - part1;
                if (CC_LIKELY(part2 > 0)) {
                    memcpy(mBuffer, (char *) buffer + (part1 << mBitShift), part2 << mBitShift);
                }
            }
            android_atomic_release_store(written + mRear, &mRear);
            totalFramesWritten += written;
        }
    //-------       
        count -= written;
        buffer = (char *) buffer + (written << mBitShift);
        // Simulate blocking I/O by sleeping at different rates, depending on a throttle.
        // The throttle tries to keep the mean pipe depth near the setpoint, with a slight jitter.
        uint32_t ns;
        if (written > 0) {
        
   //-------
        }
    
   //-------
        if (ns > 0) {
            const struct timespec req = {0, ns};
            nanosleep(&req, NULL);
        }
        //-------
    }
    //..........
    return totalFramesWritten;
}

Android <wbr>4.4 <wbr>KitKat的环形缓冲机制

 

    3.4  How to Read a given number of frames from Ring Buffer?
    1) To get the available frame count to read of Ring Buffer (red=mRear-mFront).
        When there is no available frame in Ring Buffer, it means Ring Buffer is in UNDERFLOW state, and reader directly returns.
    ssize_t red = availableToRead();
    if (CC_UNLIKELY(red <= 0)) {
        // Uh-oh, looks like we are underflowing.  Update the next read PTS and
        // get out.
        return red;
    }
2) When the available frame data in Ring Buffer is NOT ENOUGH: the requested frame count is more than the available frame count to read, reader only read out this part of available frames, whose length is mRear - mFront.
3)  When the requested frame count is less than the available frame count to read, then it is obvious to read only the requested frames, whose length is the input parameter count.
    if (CC_LIKELY((size_t) red > count)) {
        red = count;
    }
4)  The available frames in Ring Buffer must begin with the front position. So the direct thinking is to read the available frames from the front position to the end of Ring Buffer, whose total frame count is (mMaxFrames - front).
5) The area from front to the end of  Ring Buffer MUST contain a part of/full data area, but it possibly contains a part of/full avail area. Fortunately, this part of available frame count cannot surpass the total available frame count in Ring Buffer(red=mRear - mFront), and its value must be equal to part1=min{count, part1=mMaxFrames - front, mRear - mFront} where count indicates the frame count reader requests.
    part1 = mPipe->mMaxFrames - front;
    if (part1 > red) {
        part1 = red;
    }
6) If part1 is equal to 0, it only means that Ring Buffer is in EMPTY  or called UNDERFLOW state: there is no any available frame in Ring Buffer.
     Otherwise, the first step is to read out the available frames stored from front to the end of Ring Buffer.
     If the new read offset/position reaches to the end position of Ring Buffer (front + part1 == mMaxFrames),  and if the remaining requested frame count is still not equal to 0 (part2=red - part1), then continue to read the remaining frames beginning from the head position of Ring Buffer.    
    if (CC_LIKELY(part1 > 0)) {
        memcpy(buffer, (char *) mPipe->mBuffer + (front << mBitShift), part1 << mBitShift);
        if (CC_UNLIKELY(front + part1 == mPipe->mMaxFrames)) {
            size_t part2 = red - part1;
            if (CC_LIKELY(part2 > 0)) {
                memcpy((char *) buffer + (part1 << mBitShift), mPipe->mBuffer, part2 << mBitShift);
            }
        }
        mPipe->updateFrontAndNRPTS(red + mPipe->mFront, nextReadPTS);
        mFramesRead += red;
    }
       

 the related source code with read operation is shown as below:

 ssize_t MonoPipeReader::read(void *buffer, size_t count, int64_t readPTS)
{
    // Compute the "next read PTS" and cache it.  Callers of read pass a read
    // PTS indicating the local time for which they are requesting data along
    // with a count (which is the number of audio frames they are going to
    // ultimately pass to the next stage of the pipeline).  Offsetting readPTS
    // by the duration of count will give us the readPTS which will be passed to
    // us next time, assuming they system continues to operate in steady state
    // with no discontinuities.  We stash this value so it can be used by the
    // MonoPipe writer to imlement getNextWriteTimestamp.
    int64_t nextReadPTS;
    nextReadPTS = mPipe->offsetTimestampByAudioFrames(readPTS, count);

    // count == 0 is unlikely and not worth checking for explicitly; will be handled automatically
    ssize_t red = availableToRead();
    if (CC_UNLIKELY(red <= 0)) {
        // Uh-oh, looks like we are underflowing.  Update the next read PTS and
        // get out.
        mPipe->updateFrontAndNRPTS(mPipe->mFront, nextReadPTS);
        return red;
    }
    if (CC_LIKELY((size_t) red > count)) {
        red = count;
    }
    size_t front = mPipe->mFront & (mPipe->mMaxFrames - 1);
    size_t part1 = mPipe->mMaxFrames - front;
    if (part1 > (size_t) red) {
        part1 = red;
    }
    if (CC_LIKELY(part1 > 0)) {
        memcpy(buffer, (char *) mPipe->mBuffer + (front << mBitShift), part1 << mBitShift);
        if (CC_UNLIKELY(front + part1 == mPipe->mMaxFrames)) {
            size_t part2 = red - part1;
            if (CC_LIKELY(part2 > 0)) {
                memcpy((char *) buffer + (part1 << mBitShift), mPipe->mBuffer, part2 << mBitShift);
            }
        }
        mPipe->updateFrontAndNRPTS(red + mPipe->mFront, nextReadPTS);
        mFramesRead += red;
    }
    return red;
}

0

阅读 评论 收藏 转载 喜欢 打印举报/Report
  • 评论加载中,请稍候...
发评论

    发评论

    以上网友发言只代表其个人观点,不代表新浪网的观点或立场。

      

    新浪BLOG意见反馈留言板 欢迎批评指正

    新浪简介 | About Sina | 广告服务 | 联系我们 | 招聘信息 | 网站律师 | SINA English | 会员注册 | 产品答疑

    新浪公司 版权所有