流媒体服务器之视频缓冲区设计
一、为什么要设计这样的缓冲区
a) 为什么要设计缓冲区
流媒体服务器从前端获取视频流,为了达到分发的效果,则必须对每个要分发的视频通道建立一个缓冲区。这样每个分发的线程都可以从缓冲区里读取数据,分发到各个客户端。
客户端在播放时,为了避免数据流上来时的快慢或者过大等情况而引起跳帧等现象,则也需要对每个播放通道建立一个缓冲区。
可以看出,设计一个合理的缓冲区是有必要的。
b) 一般缓冲区的不足
传统的缓冲区是对每个通道建立一个有N个BUF的数组,每个BUF的大小都是固定的,一个BUF用于存储一帧的数据。假设N为100,BUF大小为20K。这样,每个通道的都需要占用2000K的内存。然而,不可能每帧数据都是20K,所以,在缓冲区中,视频数据不是连续存放的,这样存在着很大的内存浪费。而且,如果前端是高清设备,并且是变码流的情况下,则有时候20K根本不足以保存一帧的数据。所以,这时候,只能扩大BUF的大小。这样做了之后,对于正常的视频流,则是更大的浪费了内存占用。
c) 新版缓冲区的特点
新版缓冲区修改了传统缓冲区的不足之处。新版缓冲区为每个通道事先申请一块500K的内存(大小可变动,即每个通道只占用了500K的内存),每帧数据上来,则连续存放在申请来的内存中。这样,即可以充分的利用内存,也可以适应变码流的情况。
二、设计思想
a) 缓冲区结构
对于每个缓冲区,预先申请一个500K的内存,然后为其建立一个动态数组。数组是由结构体(见下面内容)组成。每个结构体记录:填充BUF的时间,BUF在内存中的地址,BUF的大小。
b) 流程图
(详见图片)
三、核心代码
a) BUF结构体
typedef struct tagSENDBUF
{ long buffertime; //记录数据的时间
LPBYTE sendbuf; //记录数据的起始地址
unsigned long buflength; //记录数据的长度
}SENDBUF,*pSendBuf;
b) 通道单元的成员变量
LPBYTE m_DataBufCache; //缓冲区指针
CArray<SENDBUF,SENDBUF&> m_data_buffer; //动态数组
LPBYTE m_NowBufCache; //内存地址,当前写到哪了
int m_Valid_pos; //有效位置。超过这个位置为无效数据;
int m_BufCount; //数组的个数
int m_read_pos; //记录数组中,该从第几个开始写
c) 填充缓冲区代码
MULTIBUFSIZE是个常量,指申请内存的大小。
if ((m_NowBufCache-m_DataBufCache)+dwBufSize>MULTIBUFSIZE)
{
//////////////////////////////////////
//把m_read_pos置为0
//////////////////////////////////////
}
//如果m_BufCount<m_read_pos+1,则需要动态建一个SENDBUF
//,记录要记录的信息,并加入到数组中
if (m_BufCount<m_read_pos+1)
{
SENDBUF _sendbuf;
//////////////////////////////////////
//记录要记录的信息/
//////////////////////////////////////
m_data_buffer.Add(_sendbuf);
m_BufCount=m_read_pos+1;
}
else
{
/////////////////////////////////////////////
//取出m_data_buffer[m_read_pos],
//然后修改其中的内容
/////////////////////////////////////////////
}
m_read_pos++; //把数组写位置指向下一个位置
四、具体应用
使用缓冲区:
m_Binding_Channel是指绑定的一个数据流通道。
m_write_pos是指读通道数组里的第几个SENDBUF。
//如果超过有效位置,则从第0个开始读
if (m_write_pos>m_Binding_Channel->m_Valid_pos)
{
m_write_pos=0;
}
//检验m_Binding_Channel->m_data_buffer[m_write_pos]里数据的合法性
//比如时间,BUG大小等是否符合要求
然后就可以使用m_Binding_Channel->m_data_buffer[m_write_pos]里面记录的信息了。