音视频同步系列文章之----一种基于RTP协议的客户端媒体流Buffer管理思想(2)

三、缓存区的动态分配与实现。

    基于这样的一个事实,音频数据远远小于视频数据。所以在这里把音频缓冲区与视频缓冲区分开管理。并且根据统计,同一媒体流的RTP数据包大小相近,比如,音频大概几百个Byte,视频1.3kByte。对视频缓冲区,我们分为两部分。一部分为正在使用数据区,一部分为空闲区。当RTP接收到数据后,就从空闲区取得一个Memory块。这个块的大小大于或等于接收到的数据与Packet头部之和。取得Memory块后,将数据拷贝进来,然后根据帧序列号和包序列号,插入到正在使用数据区,供播放器调用。播放器在正在使用数据区取得一帧数据,解码,播放。使用完后该数据块并不立即释放,而是有序的插入到空闲数据区。

    这样的Memory重复利用思想要用到上节中介绍的指针数组和指针队列。指针数据用来管理空闲数据块链。实现按大小有序插入和查找。指针队列用来管理排序之后的数据帧,按帧序列号和包序列号排放。我们这里将一个视频帧分成若干个RTP包。

    首先,定义CMyPtrArray  m_FreeList,用来管理空闲数据有序链。

    在空闲区申请Memory块的方法如下:

ppFrameBuf 为返回数据块指针,FRAME_BUFFER是一个虚拟结构,包括Buffer实际长度和指向Buffer的指针和其他自定义成员,nLen为申请大小。

 

void GetOneFreeBuf(FRAME_BUFFER **ppFrameBuf,DWORD nLen)
{
 if(NULL == ppFrameBuf)
  return ;
 FRAME_BUFFER *pFrameBuf = NULL;
 //此处添加互斥代码
 if(m_FreeList.GetSize() > 0)
 {
  int nStart = 0,nEnd = m_FreeList.GetSize();
  int nCur = (nStart + nEnd) / 2;
  FRAME_BUFFER *pTmpBuf = NULL;
  while(1)
  {
   pTmpBuf = (FRAME_BUFFER *)m_FreeList.GetAt(nCur);
   if((pTmpBuf->nBufSize >= nLen) && ((pTmpBuf->nBufSize - nLen) <= 100))
    break;
   else if(pTmpBuf->nBufSize > nLen)
    nEnd = nCur;
   else
    nStart = nCur;
   nCur = (nStart + nEnd) / 2;
   if(nCur == nStart || nCur == nEnd)
   {
    pTmpBuf = (FRAME_BUFFER *)m_FreeList.GetAt(nCur);
    break;
   }
  }
  if(nCur >= m_FreeList.GetSize())
   nCur = m_FreeList.GetSize() -1;
  if(pTmpBuf->nBufSize < nLen && nCur < (m_FreeList.GetSize() - 1))
  {
   nCur ++;
   pTmpBuf = (FRAME_BUFFER *)m_FreeList.GetAt(nCur);
  }
  if(pTmpBuf->nBufSize >= nLen)
  {
   pFrameBuf = pTmpBuf;
  // 
   m_FreeList.RemoveAt(nCur);
  }
 }
 *ppFrameBuf = NULL;
 if(NULL == pFrameBuf)
 {
  pFrameBuf = new FRAME_BUFFER;
  if(NULL == pFrameBuf)
  {
   return;
  }
  pFrameBuf->nBufSize = nLen;
  pFrameBuf->pFrameData = new BYTE[nLen + 100];
  if(NULL == pFrameBuf->pFrameData)
  {
   delete pFrameBuf;
   return ;
  }
 }
//  TRACE("CStreamBuffer::GetOneFreeBuf,NeedSize:%d,ActualSize:%d/n",nLen,pFrameBuf->nBufSize);
 *ppFrameBuf = pFrameBuf;
}

 

释放Memory块到空闲去的方法如下:

void ReleaseBuf(FRAME_BUFFER *pFrameBuf)//从小到大排序
{
 if(NULL == pFrameBuf || NULL == pFrameBuf->pFrameData || 0 == pFrameBuf->nBufSize)
  return ;
 FRAME_BUFFER *pTmpBuf = NULL;
 //添加互斥代码
 DWORD nCur = 0;
 if(m_FreeList.GetSize() > 0)
 {
  DWORD nStart = 0,nEnd = m_FreeList.GetSize();
  nCur = (nStart + nEnd) / 2;
  while(1)
  {
   pTmpBuf = (FRAME_BUFFER *)m_FreeList.GetAt(nCur);
   if(pTmpBuf->nBufSize == pFrameBuf->nBufSize)
    break;
   else if(pTmpBuf->nBufSize > pFrameBuf->nBufSize)
    nEnd = nCur;
   else
    nStart = nCur;
   nCur = (nStart + nEnd) / 2;
   if(nCur == nStart || nCur == nEnd)
    break;
  }
  if(nCur > 0 && pTmpBuf->nBufSize > pFrameBuf->nBufSize)
   nCur --;
 }

 m_FreeList.InsertAt(nCur,pFrameBuf);
}

 

 

当然程序退出时,要记得释放所有申请的空间。


四、缓冲区设计

    有了前面的技术基础。我们就可以从容的设计自己的缓冲区了。因为音频数据比视频数据小一个数量级。所以,可以将一个音频帧打包为一个RTP包。而将一个视频帧打包为多个RTP包。对应的,在客户端。我们收到一个RTP音频包可以直接送入音频帧队列,供播放器使用。收到RTP视频包,首先要组成完整帧,然后供播放器使用。缓冲区示意图如下:




                                                                            

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值