前言
交通部与2016年10月份推出了JT/T 1078-2016标准,全称是<道路运输车辆卫星定位系统视频通信协议>。该标准将改变以往两客一危车辆的视频监控设备通信协议都是设备厂商私有协议的局面。
部标1078协议和808协议的区别: 部标1078协议文档明确说明了,协议是在JT/T 808协议的基础上进行增加了大量的视频指令,以前的终端32位报警,由于增加了视频报警,拓展为64位报警。终端参数中也增加了大量与视频相关的参数设置。协议的通信方式、数据类型、传输规则和消息组成按照 JT/T 808-2011中第 4 章的要求。协议中信令数据报文的通信连接方式按照 JT/T 808-2011中第 5 章的要求。协议中信令数据报文的消息处理机制按照 JT/T 808-2011中第 6 章的要求。协议中信令数据报文的加密机制按照 JT/T 808-2011中第 7 章的要求。 可见1078协议仍然是基于部标808协议继续拓展的协议指令集。
格式描述
十六进制数据格式
30 31 63 64 81 62 00 00 02 02 10 80 10 96 02 01 00 00 01 7F 97 AB E6 21 00 00 00 00 03 B6 00 00 00 01 67
30 31 63 64 81 62 00 01 02 02 10 80 10 96 02 03 00 00 01 7F 97 AB E6 21 00 00 00 00 03 B6 A4 00 05 02 6F
30 31 63 64 81 62 00 02 02 02 10 80 10 96 02 03 00 00 01 7F 97 AB E6 21 00 00 00 00 03 B6 C1 52 7E 45 06
30 31 63 64 81 62 00 03 02 02 10 80 10 96 02 03 00 00 01 7F 97 AB E6 21 00 00 00 00 03 B6 24 42 8D 3B B6
30 31 63 64 81 62 00 04 02 02 10 80 10 96 02 03 00 00 01 7F 97 AB E6 21 00 00 00 00 03 B6 09 51 01 81 D1
30 31 63 64 81 E2 00 14 02 02 10 80 10 96 02 02 00 00 01 7F 97 AB E6 21 00 00 00 00 01 EB D3 88 A2 A0 4D (E2关键这是一个NALU单元的结束标志)
30 31 63 64 81 62 00 15 02 02 10 80 10 96 02 11 00 00 01 7F 97 AB E6 49 00 27 00 27 03 B6 00 00 00 01 61
蓝色是报文的固定标识
红色是报文的序号sequence
代码解读
virtual bool ReadNextH264NALUFromJTRTP()
{
//m_strRTPBuf保存了接收到的报文,每接收一次,在该字符串后面追加,因为TCP
//协议作为流传输,每一次接收到的不一定是完整的包
int nBufferLen = m_strRTPBuf.length();
std::uint8_t* pData = (std::uint8_t*)m_strRTPBuf.c_str();
int i = 0;
bool bFindStartRTPHeader = false;
//步骤一:JT1078-RTP前面四个字节是帧头标识,固定为0X30 0X31 0X63 0X64
//第一步是查找到帧头标识
while (1)
{
//帧头长度4个字节,必须满足,否则数组访问越界
if (i + 4 > nBufferLen)
{
break;
}
//查找第一个帧头标识,由于TCP流式传输可能一次接收到多个帧头标识
if ((0x30 == pData[i]) && (0x31 == pData[i+1]) && (0x63 == pData[i+2]) && (0x64 == pData[i+3]))
{
bFindStartRTPHeader = true;
break;
}
else
{
//继续下一个字符
i++;
}
}
//找不到帧头标识,说明接收长度不够,继续接收
if (!bFindStartRTPHeader)
{
return false;
}
//接收长度不满足30个字节的JT1078RTP报头,继续接收
if (i + 30 > nBufferLen)
{
//说明只是收到不完整报头,继续接收
return false;
}
//去掉帧头前面的残留数据
m_strRTPBuf = m_strRTPBuf.erase(0, i);
pData = (std::uint8_t*)m_strRTPBuf.c_str();
int nRealVideoLen = (pData[28] << 8) + pData[29];
//超过950的,扔掉音频数据
if (nRealVideoLen > 1000)
{
m_strRTPBuf.erase(0, 30);
return false;
}
if (nRealVideoLen + 30 > m_strRTPBuf.length())
{
//说明接收到不完整报头,真实视音频数据不完整,继续接收
return false;
}
//第十五个字节的前四位是数据类型
std::uint8_t cFragmentationUnitType = pData[15] >> 4;
std::uint8_t cCompleteFrame = pData[5] >> 7;
bKeyFrame = CVOS_FRAME_TYPE_NON_KEY_FRAME;
switch (cFragmentationUnitType)
{
case 0:
{
bKeyFrame = CVOS_FRAME_TYPE_KEY_FRAME;
m_strCompleteOneFrame.append(&m_strRTPBuf[30], nRealVideoLen);
m_strRTPBuf.erase(0, 30 + nRealVideoLen);
if (1 == cCompleteFrame)
{
//标志为完整的一帧
m_bCompleteOneFrame = true;
std::cout << "recv I frame" << std::endl;
}
break;
}
case 1://P帧
case 2://B帧
{
m_strCompleteOneFrame.append(&m_strRTPBuf[30], nRealVideoLen);
m_strRTPBuf.erase(0, 30 + nRealVideoLen);
if (1 == cCompleteFrame)
{
//标志为完整的一帧
m_bCompleteOneFrame = true;
std::cout << "recv PB frame" << std::endl;
}
break;
}
case 3:
{
break;
}
default:
{
break;
}
}
bool bRet = m_bCompleteOneFrame;
if (m_bCompleteOneFrame)
{
//完整的一帧保存在m_strCompleteOneFrame;
m_strCompleteOneFrame.clear();
m_bCompleteOneFrame = false;
}
return true;
}