在音视频开发中音视频的传输主要用UDP来发送视频当发送的数据大于1500时分包发送保证每包小于1500关键是视频接收时的处理。本文根据UDP库传输数据作为代码演示.
intCUDPSession::SplitData(char* pBuff, uint32_t nLen)
{
int nBlockNum =nLen / UDP_BLOCK_SIZE;
if (nLen %UDP_BLOCK_SIZE != 0)
{
nBlockNum++;
}
int sendlen = 0;
for (int i = 0; i< nBlockNum; i++)
{
int poayload_size =UDP_BLOCK_SIZE;
char* payload =pBuff + UDP_BLOCK_SIZE * (i);
if (nLen -UDP_BLOCK_SIZE * (i) < UDP_BLOCK_SIZE)
poayload_size =nLen - UDP_BLOCK_SIZE * (i);
CPackOut* pack =new CPackOut;
(*pack) <<PACK_TYPE_DATA;
(*pack) <<m_nFrameIndex;
if (i == nBlockNum- 1)
{
(*pack) << 1;
}
else
{
(*pack) << 0;
}
(*pack) <<poayload_size;
(*pack).SetBuffer(payload,poayload_size);
int ret =SendPacket(pack, m_destIP, m_destPort);
printf("SendPacketret = %d\n", ret);
sendlen += ret;
delete pack;
pack = NULL;
}
return sendlen;
}
下面详细说一下接收端,因为UDP是不可靠的不保证数据帧一定正常到达即使收到顺序也可能发生变化比如先发的后到当然丢包的可能最大乱序的情况比较少。
正确的处理方法是
假设一个端口只接收固定一个对方数据源,这样收到一个数据包放到缓冲里然后在缓冲里根据帧的序号排序(每一帧的大序号是相同的自己可以给每一个小片加上小序号,包头里可以加上本次数据帧一共分多少片收到一片就统计一下判断是否收齐)。当收齐后这个帧去掉包头回调给上层。当在一定时间内该帧数据还没有收齐就说明传输过程有丢包了把已收到的都丢掉就可以。
当上层的应该收到回调的数据后可以进行解码播放。在解码之前先判断一下帧序列是否连续。如果中间有缺少的就把这一序列都丢掉直到下一个I帧。每个帧的序号最好收发之间协商好在发送的时候带上。
如果把上面整个过程都实现完全自己写的话需要几天的时间。不过从很多RTP开源库里发现处理的都非常简单很多都没有管乱序情况简单地来一份数据就向缓冲里追加一份直到发现mark为1。我们这里做为简单使用的项目也采用了这种简单方法先把功能完成之后有时间再来优化。
简单的重组代码:
intCUDPSession::Reassemble(CPackIn& pack, uint32_t ip, uint32_t port)
{
int nSeq = 0;
int nMark = 0;
int nLen = 0;
pack >> nSeq;
pack >> nMark;
pack >> nLen;
if(m_nRecvFrameIndex != nSeq)
{
if (m_buffer)
{
evbuffer_free(m_buffer);
m_buffer = NULL;
}
m_nRecvFrameIndex =nSeq;
}
if (m_buffer ==NULL)
{
m_buffer =evbuffer_new();
}
char* pBuf = 0;
int nSize;
pack.GetBuffer(pBuf,nSize);
evbuffer_add(m_buffer,pBuf, nSize);
if (nMark == 1)
{
//回调
if (m_pCB)
{
m_pCB(m_udpIO.m_handle,(char*)(m_buffer->buffer), m_buffer->off, ip, port,
m_pParam);
}
evbuffer_free(m_buffer);
m_buffer = NULL;
}
return 0;
}
程序里使用的evbuffer是从libevent里面拿来的主要用来处理数据缓冲非常好用效率也很好见evbuffer.h和buffer.cpp。
完整代码在git上这次实现的功能是本机UDP bind5500端口-->摄像机采集-->编码-->发送给本机的5500端口-->收到后再解码-->显示。
发送的代码m_Sess.Send((char*)pData, nLen, inet_addr("127.0.0.1"), 5500);
这个程序可以分别运行在两台机器上一台是发送另一台是接收。发送方只要把上面这一句里面的127.0.0.1换上你目标的ip另一台机器就可以接收并解码了。
本文结束后完整的客户端功能基本就差不多了下一步开始完成server端的stun,协商穿透, 实p2p和中转视频。如果对音视频开发还有其他疑问可以登录该论坛进行咨询http://bbs.anychat.cn/forum.php
!fryuage:ZH-CN;mso-bidi-language:AR-SA‘>stun,协商穿透, 实p2p和中转视频。如果对音视频开发还有其他疑问可以登录该论坛进行咨询http://bbs.anychat.cn/forum.php