前言
GB28181规范指定了RTP over TCP采用的协议格式
简单来说,就是TCP传输RTP/RTCP数据的是时候,先传输报文长度(采用两个字节保存发送)
代码实现
源码rtptcptransmitter.cpp
发送代码
int RTPTCPTransmitter::SendRTPRTCPData(const void *data, size_t len)
{
if (!m_init)
return ERR_RTP_TCPTRANS_NOTINIT;
MAINMUTEX_LOCK
if (!m_created)
{
MAINMUTEX_UNLOCK
return ERR_RTP_TCPTRANS_NOTCREATED;
}
if (len > RTPTCPTRANS_MAXPACKSIZE)
{
MAINMUTEX_UNLOCK
return ERR_RTP_TCPTRANS_SPECIFIEDSIZETOOBIG;
}
std::map<SocketType, SocketData>::iterator it = m_destSockets.begin();
std::map<SocketType, SocketData>::iterator end = m_destSockets.end();
vector<SocketType> errSockets;
int flags = 0;
#ifdef RTP_HAVE_MSG_NOSIGNAL
flags = MSG_NOSIGNAL;
#endif // RTP_HAVE_MSG_NOSIGNAL
while (it != end)
{
uint8_t lengthBytes[2] = { (uint8_t)((len >> 8)&0xff), (uint8_t)(len&0xff) };
SocketType sock = it->first;
if (send(sock,(const char *)lengthBytes,2,flags) < 0 ||
send(sock,(const char *)data,len,flags) < 0)
errSockets.push_back(sock);
++it;
}
MAINMUTEX_UNLOCK
if (errSockets.size() != 0)
{
for (size_t i = 0 ; i < errSockets.size() ; i++)
OnSendError(errSockets[i]);
}
// Don't return an error code to avoid the poll thread exiting
// due to one closed connection for example
return 0;
}
接收代码
int RTPTCPTransmitter::SocketData::ProcessAvailableBytes(SocketType sock, int availLen, bool &complete, RTPMemoryManager *pMgr)
{
JRTPLIB_UNUSED(pMgr); // possibly unused
const int numLengthBuffer = 2;
if (m_lengthBufferOffset < numLengthBuffer) // first we need to get the length
{
assert(m_pDataBuffer == 0);
int num = numLengthBuffer-m_lengthBufferOffset;
if (num > availLen)
num = availLen;
int r = 0;
if (num > 0)
{
r = (int)recv(sock, (char *)(m_lengthBuffer+m_lengthBufferOffset), num, 0);
if (r < 0)
return ERR_RTP_TCPTRANS_ERRORINRECV;
}
m_lengthBufferOffset += r;
availLen -= r;
assert(m_lengthBufferOffset <= numLengthBuffer);
if (m_lengthBufferOffset == numLengthBuffer) // we can constuct a length
{
int l = 0;
for (int i = numLengthBuffer-1, shift = 0 ; i >= 0 ; i--, shift += 8)
l |= ((int)m_lengthBuffer[i]) << shift;
m_dataLength = l;
m_dataBufferOffset = 0;
//cout << "Expecting " << m_dataLength << " bytes" << endl;
// avoid allocation of length 0
if (l == 0)
l = 1;
// We don't yet know if it's an RTP or RTCP packet, so we'll stick to RTP
m_pDataBuffer = RTPNew(pMgr, RTPMEM_TYPE_BUFFER_RECEIVEDRTPPACKET) uint8_t[l];
if (m_pDataBuffer == 0)
return ERR_RTP_OUTOFMEM;
}
}
if (m_lengthBufferOffset == numLengthBuffer && m_pDataBuffer) // the last one is to make sure we didn't run out of memory
{
if (m_dataBufferOffset < m_dataLength)
{
int num = m_dataLength-m_dataBufferOffset;
if (num > availLen)
num = availLen;
int r = 0;
if (num > 0)
{
r = (int)recv(sock, (char *)(m_pDataBuffer+m_dataBufferOffset), num, 0);
if (r < 0)
return ERR_RTP_TCPTRANS_ERRORINRECV;
}
m_dataBufferOffset += r;
availLen -= r;
}
if (m_dataBufferOffset == m_dataLength)
complete = true;
}
return 0;
}
相关代码
while (it != end)
{
uint8_t lengthBytes[2] = { (uint8_t)((len >> 8)&0xff), (uint8_t)(len&0xff) };
SocketType sock = it->first;
if (send(sock,(const char *)lengthBytes,2,flags) < 0 ||
send(sock,(const char *)data,len,flags) < 0)
errSockets.push_back(sock);
++it;
}
代码分析
外层循环,遍历每一个目标主机的套接字准备发送数据,先构建准备发送数据包长度的缓存lengthBytes,先发送两个字节长度的缓存,然后才真正发送真实的数据包,符合RFC4571标准
抓包分析
0030 20 00 2b 5b 00 00 80 60 0d 99 04 e6 47 07 88 23 .+[...`....G..#
0040 8e b5 00 00 01 ba 44 00 04 04 e4 01 00 03 ff f8 ......D.........
0050 04 cf 80 60 0d 9a 04 e6 55 17 88 23 8e b5 00 00 ...`....U..#....
0060 01 e0 04 bd 80 c0 0a 31 00 03 6d a1 11 00 03 6d .......1..m....m
参考例子
JRTPLIB/tcptest.cpp at master · j0r1/JRTPLIB · GitHub