关于环形缓冲区的使用

前一段时间在做视频接收的时候,自己写了一个很简单的环形缓冲区例子,参照了ffmpeg  AVFifoBuffer类,但是又简化了这个类,并增加一个内存映射链表,用来动态移动读指针。现在我来介绍一下具体原理。

其实环形缓冲区就是在最开始的时候申请一个大buffer,有一个读指针,一个写指针,随着数据写入和读取改变读写指针,具体分为三总情况:一、是读写速度差不多,这种情况比较简单。二、写的很快读的慢、这种情况写指针很快回头追上了读指针,这时候就会出现写buffer覆盖掉读指针的内存块,如果还继续读取数据那么数据就会错乱,如果存储视频里相当于界面变花了。这时候我们就用到了内存映射链表,我们通过内存映射链表把读指针移动到下一个完整的内存块上,实际上就是丢帧。这样就能够保证数据的完整性,三、就是读速度快,这种情况也比较简单,直接返回即可。下面我们来分析一下我的具体读写代码:

int CRAMFifoBuffer::fifo_write(char* pBuf, size_t size)
{
Mutex::Lock lock(mutex);
if (size > m_iLen)
return -1;

int iRet = 1;
NodeBuffer node;//这个是内存映射的具体结点数据
node.pNode = pwrite;//每次保存的写指针位置和大小
node.iLen = size;
m_NodeList.push_back(node);//内存映射链表

int len = MIN(ptail - pwrite, size);//用于判断尾指针到写指针直接距离和写入距离大小,其实就是看到没到buffer尾部
memcpy(pwrite, pBuf, len);
pwrite += len;  //将数据拷贝到pwrite


if (pwrite >= ptail)//写指针已经超过了尾指针
{
m_iswritefirst = false; //写在读后面
pwrite = phead;     //到结尾后,从起点又开始
memcpy(pwrite, pBuf + len , size - len);
pwrite += (size - len);
}

bool isLast = false;
if (!m_iswritefirst)
{
while (pwrite > pread)//写指针覆盖读指针
{
if (m_NodeList.size() == 0)
{
pwrite = pread = phead;
m_iswritefirst = true;
return -2;
}
//这个逻辑是为了判断读指针是否已经到了尾段
NodeList::iterator itr = m_NodeList.begin();
NodeBuffer Item = *itr;
if (Item.pNode + Item.iLen > ptail)//说明已经读到了buffer最后
{
isLast = true;
}
m_NodeList.erase(itr);

if (m_NodeList.size() == 0)
{
pwrite = pread = phead;
m_iswritefirst = true;
return -2;
}
itr = m_NodeList.begin();
Item = *itr;
pread = Item.pNode;//读指针移动到下一个内存映射位置

if (isLast)
return 1;
}
}
return 1;
}

//读取内存缓冲区数据

int CRAMFifoBuffer::fifo_read(char *dest, int& buf_size)
{
Mutex::Lock lock(mutex);
if (m_NodeList.size() == 0)
return -1;

NodeList::iterator itr = m_NodeList.begin();
NodeBuffer Item = *itr;

buf_size = Item.iLen;
if (buf_size == 0)
{
return -1;
}

int len = MIN(ptail - pread, buf_size);
memcpy(dest, pread, len); //将数据拷贝到dest
pread += len;  //将数据拷贝到pwrie

if (pread >= ptail)
{
m_iswritefirst = true; //写在读前面
pread = phead;     //到结尾后,从起点又开始
memcpy(dest + len, pread, buf_size - len);//dest需要在外部申请buffer,确保足够装下buffer
pread += (buf_size - len);
}
m_NodeList.erase(itr);//读取后删除内存节点


return 1;
}

其实原理很简单,如果感兴趣的同学可以看些源码

http://download.csdn.net/detail/u011569253/9838903

猜你喜欢

转载自blog.csdn.net/u011569253/article/details/71637100