国标GB28181协议客户端开发(四)实时视频数据传输
国标GB28181协议客户端开发(四)实时视频数据传输
本文是《国标GB28181协议设备端开发》系列的第四篇,介绍了实时视频数据传输的过程。通过解读INVITE报文中的SDP信息,读取和解析视频文件或图片文件,进行数据编码,以及h264封装为PS格式,最终通过RTP数据发送,实现了GB28181协议设备端的视频传输功能。本文将逐步详细介绍每个模块的实现步骤和相关技术要点,帮助读者理解和应用GB28181协议进行实时视频传输。
一、INVITE报文的SDP信息解读
在GB28181协议中,在实时音视频传输过程中,使用INVITE报文携带SDP(Session Description Protocol)信息。SDP信息描述了会话的属性和参数,包括媒体类型、传输协议、编解码器、网络地址等。下面是一个示例INVITE报文的SDP内容,并对其中的每一项进行详细解释:
v=0
o=34020000002000000001 0 0 IN IP4 192.168.1.10
s=Play
c=IN IP4 192.168.1.10
t=0 0
m=video 40052 RTP/AVP 96
a=recvonly
a=rtpmap:96 PS/90000
y=0358902090
f=
v=0
表示SDP协议版本号,此处为0。
o=34020000002000000001 0 0 IN 192.168.1.10
o字段标识了会话的发起者和会话的唯一标识。
"34020000002000000001" 表示该会话会话发起者的SIP ID。
0 0 表示会话的起始和结束时间戳。
IN IP4 192.168.1.10 表示会话的网络地址,这里为IPv4地址。s字段为会话的名称或描述,此处为"Play"表面是实时音视频
c=IN IP4 192.168.1.10
c字段指定了会话的连接信息。
IN 表示网络类型为Internet。
IP4 192.168.1.10 表示会话的IPv4地址。t=0 0
t字段指定了会话的时间信息。
0 0 表示会话的起始和结束时间都为0,即持续时间未定义。m=video 40052 RTP/AVP 96
m字段定义了会话中的媒体类型和相关参数。
video 表示媒体类型为视频。
40052 表示媒体流的传输端口号。
RTP/AVP 表示传输协议为RTP,使用AVP(Audio-Visual Profile)配置。
96 表示媒体流使用编号96表示。a=rtpmap:96 PS/90000
a字段包含了媒体流的属性。
rtpmap:96 表示将编号为96的负载类型。
PS 表示使用MPEG-PS格式进行数据封装。
90000 表示时钟速率,即每秒的时钟滴答数。y=0358902090
y字段为十进制整数字符串,表示SSRC值
f=
f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率
这里并没有设置f字段,由数据发送端来填充
二、视频文件或图片文件的读取、解析和编码
为了进行视频数据传输,我们首先需要读取和解析视频文件或图片文件。我们需要使用相应的库或工具,从文件中读取视频或图片数据,并进行解析,以获取关键的视频帧或图像数据,为后续的编码和封装做准备。

三、h264封装PS
在GB28181协议中,视频数据通常以MPEG-PS(MPEG Program Stream)格式进行封装。需要将经过编码的视频数据进行PS格式的封装,包括添加包装头和起始码,然后再进一步封装RTP。
以下是使用C++将H.264的NALU封装为MPEG-PS格式的主要过程(仅展示部分代码):
// 将H.264的NALU列表封装为MPEG-PS格式
void MakeMPEGPS(unsigned char* h264Data, int h264Length,
unsigned char* psData)
{
int totalPES = (h264Length + MAX_PES_LENGTH - 1) / MAX_PES_LENGTH; // 计算总的PES包数
int remainingBytes = h264Length; // 剩余待处理的字节数
// MPEG-PS包头
unsigned char mpegPSHeader[] = {0x00, 0x00, 0x01, 0xBA};
// 分割并封装H.264数据
for (int i = 0; i < totalPES; i++)
{
unsigned char* pbuf = psData;
int pesLength = (remainingBytes > MAX_PES_LENGTH) ? MAX_PES_LENGTH : remainingBytes; // 当前PES包的长度
remainingBytes -= pesLength; // 更新剩余待处理的字节数
// PES包头
unsigned char pesHeader[] = {0x00, 0x00, 0x01, 0xE0, 0x00, 0x00, 0x80, 0x00};
// 设置PES包长度
pesHeader[4] = (pesLength + 8) >> 8; // 高8位
pesHeader[5] = (pesLength + 8) & 0xFF; // 低8位
// 输出MPEG-PS包头和当前PES包头
memcpy(pbuf, mpegPSHeader, sizeof(mpegPSHeader));
pbuf += sizeof(mpegPSHeader);
memcpy(pbuf, pesHeader, sizeof(pesHeader));
pbuf += sizeof(pesHeader);
// 输出当前PES包的H.264数据
memcpy(pbuf, h264Data + (i * MAX_PES_LENGTH), pesLength);
pbuf += pesLength;
int payload_len = (pbuf - psData);
// 封装RTP包并发送
MakeAndSendRTP(psData, payload_len);
}
}
需要注意到,当h264帧比较大的时候,会超出PES可表述的长度大小,这个时候必须对h264帧进行切分,封装成多个PES,再合成到PS包中。
四、RTP数据发送
RTP数据发送的逻辑比较简单,以下为程序中的代码示意图

以下为RTP封装的演示代码(仅展示部分代码):
struct RTPHeader
{
uint8_t version; // RTP协议版本号,固定为2
uint8_t padding: 1; // 填充位
uint8_t extension: 1; // 扩展位
uint8_t csrcCount: 4; // CSRC计数器,指示CSRC标识符的个数
uint8_t marker: 1; // 标记位
uint8_t payloadType: 7; // 负载类型
uint16_t sequenceNumber; // 序列号
uint32_t timestamp; // 时间戳
uint32_t ssrc; // 同步信源标识符
};
void MakeRTPHeader(struct RTPHeader* header, uint16_t sequenceNumber, uint32_t timestamp, uint32_t ssrc, bool isMark)
{
// 设置RTP协议版本号为2
header->version = 2;
// 填充位、扩展位、CSRC计数器等字段根据具体需求进行设置
header->padding = 0;
header->extension = 0;
header->csrcCount = 0;
// 设置标记位为0(如果需要设置为1,则在需要设置的地方进行修改)
header->marker = isMark ? 1 : 0;
// 设置负载类型(payload type),根据具体需求进行设置
header->payloadType = 96;
// 设置序列号和时间戳
header->sequenceNumber = htons(sequenceNumber); // 需要进行字节序转换(网络字节序)
header->timestamp = htonl(timestamp); // 需要进行字节序转换(网络字节序)
// 设置同步信源标识符
header->ssrc = htonl(ssrc); // 需要进行字节序转换(网络字节序)
}
void sendRTPPacket(const uint8_t* mpegPSData, int mpegPSLength, uint16_t sequenceNumber, uint32_t timestamp, uint32_t ssrc)
{
int offset = 0; // 偏移量,用于遍历MPEG-PS包数据
int remainingLength = mpegPSLength; // 剩余长度,用于判断是否需要分割RTP报文
uint8_t rtpbuf[RTP_PAYLOAD_MAX_SIZE]; // RTP负载数据缓冲区
struct RTPHeader rtpHeader; // RTP报文头部
while (remainingLength > 0)
{
// 计算当前RTP负载数据长度(不超过RTP负载最大大小)
bool is_mark = false;
int data_len = RTP_PAYLOAD_MAX_SIZE;
if (remainingLength <= RTP_PAYLOAD_MAX_SIZE)
{
data_len = remainingLength;
is_mark = true;
}
// 填写RTP报文头部
MakeRTPHeader(&rtpHeader, sequenceNumber, timestamp, ssrc);
// 复制RTP头部到RTP负载缓冲区
memcpy(rtpbuf, &rtpHeader, sizeof(RTPHeader));
// 复制MPEG-PS数据到RTP负载缓冲区
memcpy(rtpbuf + RTP_HEADER_LEN, mpegPSData + offset, data_len);
// 将完整RTP包发送出去
if (udp_channel_)
{
udp_channel_->PostSendBuf(rtpbuf, RTP_HEADER_LEN + data_len);
}
// 更新偏移量、剩余长度、序列号等信息
offset += data_len;
remainingLength -= data_len;
sequenceNumber++;
}
}

合作请加WX:hbstream
合作请加作者hbstream(http://haibindev.cnblogs.com),转载请注明作者和出处
国标GB28181协议客户端开发(四)实时视频数据传输的更多相关文章
- 接口自动化测试:Thrift框架RPC协议客户端开发
import java.lang.Thread.State;import java.util.Iterator;import java.util.List; import org.apache.thr ...
- EasyNVR和EasyDSS云平台联手都不能解决的事情,只有国标GB28181能解决了
需求痛点 我们经常收到这样一种需求,就是将客户手里的各种类型的网络摄像机IPC和网络硬盘录像机NVR进行统一的整合接入和管理,并进行常规的直播.存储.录像检索和回放等操作,而这个时候我们通常会选择用E ...
- 基于GBT28181:SIP协议组件开发-----------第四篇SIP注册流程eXosip2实现(一)
原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3945294.html. 上章节讲解了利用自主开发的组件SIP组件l ...
- 基于GBT28181:SIP协议组件开发-----------第一篇环境搭建
原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3930018.html,qq:1269122125. SIP协议在安 ...
- node.js实现国标GB28181设备接入的sip服务器解决方案
方案背景 在介绍GB28181接入服务器的方案前,咱们先大概给大家介绍一下为什么我们选择了用nodejs开发国标GB28181的服务,我大概给很多人介绍过这个方案,大部分都为之虎躯一震,nodejs在 ...
- 开源的C#实现WebSocket协议客户端和服务器websocket-sharp组件解析
很久没有写博客了(至少自己感觉很长时间没有写了),没办法啊,楼主也是需要生活的人啊,这段一直都在找工作什么的.(整天催我代码的人,还望多多谅解啊,我会坚持写我们的项目的,还是需要相信我的,毕竟这是一个 ...
- C#实现WebSocket协议客户端和服务器websocket sharp组件实例解析
看到这篇文章的题目,估计很多人都会问,这个组件是不是有些显的无聊了,说到web通信,很多人都会想到ASP.NET SignalR,或者Nodejs等等,实现web的网络实时通讯.有关于web实时通信的 ...
- 用Jersey为Android客户端开发Restful Web Service
平时在做Android客户端的时候经常要与服务器之间通信,客户端通过服务端提供的接口获取数据,然后再展示在客户端的界面上,作为Android开发者,我们平时更多的是关注客户端的开发,而对服务端开发的关 ...
- [原创]上海好买基金招高级Java技术经理/运维主管/高级无线客户端开发等职位(内推)
[原创]上海好买基金招高级Java技术经理/运维主管/高级无线客户端开发等职位(内推) 内部推荐职位 高级JAVA技术经理: 岗位职责: 负责项目管理(技术方向),按照产品开发流 ,带领研发团队,制定 ...
- 【JAVA EE企业级开发四步走完全攻略】
本文是J2EE企业级开发四步走完全攻略索引,因内容比较广泛,涉及整个JAVA EE开发相关知识,这是一个长期的计划,单个发blog比较零散,所以整理此索引,决定以后每发一季JAVA EE blog后会 ...
随机推荐
- Burp Suite最新版本专业版激活2022.12.1附原文件
Burp Suite 攻击web 应用程序的集成平台 Burp Suite 是用于攻击web 应用程序的集成平台,包含了许多工具.Burp Suite为这些工具设计了许多接口,以加快攻击应用程序的过程 ...
- 2023高效的mysql 随机语句 200万数据为例 用了 0.0030秒
是的,如果数据表中有200万条记录,使用 ORDER BY RAND() 这种方式来随机选择记录会非常慢,因为 MySQL 需要对整个表进行排序,然后再返回指定数量的记录.这个过程需要消耗大量的时间和 ...
- C盘爆满的解决方法,不用删除文件,使用分区助手无损增加内存
一.分区助手傲梅科技 对于我们C盘内存不足的来说,老师推荐的yyds. 我的内存C盘历史最低是900多M,1.5G还是多的,经过我不断的删除文件,发现没什么用,电脑用久了C盘文件占内存自然就多了!!改 ...
- TiDB在X86和ARM混合平台下的离线部署和升级
[是否原创]是 [首发渠道]TiDB 社区 背景 在之前我们团队发布了TiDB基于X86和ARM混合部署架构的文章:TiDB 5.0 异步事务特性体验--基于X86和ARM混合部署架构,最近有朋友问到 ...
- 【Python毕业设计】基于Python+Flask+MySQL的学生信息管理系统(附完整源码)
1.项目说明基于python+Flask+mysql的学生信息管理系统项目实战 项目需要安装pycharm专业版,mysql数据库以及项目所需的所有模块创建数据库名称db_online_notes,然 ...
- ARL:资产侦察灯塔系统
资产灯塔,不仅仅是域名收集 功能简介 "挖洞神器"资产安全灯塔(ARL),旨在快速侦察与目标关联的互联网资产,构建基础资产信息库. 协助甲方安全团队或者渗透测试人员有效侦察和检索资 ...
- Django简介 安装下载 app概念 主要目录介绍
目录 Django简介 前戏 Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式,即模型M,视图V和模版T.这套框架是以比利时的吉普赛爵士吉他手Django Re ...
- jQuery 在图片和文字中插入内容(多种情况考虑)
昨天接到一个新的需要,在后台文章编辑器中,每一个文章的正文前面,可以单独添加一个电头字段,但是如果在富文本编辑器中最上面就添加图片的话,图片就会把电头和正文中的文字给隔开.需要做的是获取到电头字段,然 ...
- Mybatis 坑(1)
org.apache.ibatis.executor.ExecutorException: No constructor found in xxxx [Integer,String] 这种情况一般是类 ...
- Costura.Fody 使用问题
1. Costura.Fody 引用后,未能正常合并资源文件.用着用着就不行了 解决方案:在csproj所在的文件目录,找到FodyWeavers.xml,添加<Costura/> 1 & ...