国标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=
  1. v=0

    表示SDP协议版本号,此处为0。

  2. o=34020000002000000001 0 0 IN 192.168.1.10

    o字段标识了会话的发起者和会话的唯一标识。

    "34020000002000000001" 表示该会话会话发起者的SIP ID。

    0 0 表示会话的起始和结束时间戳。

    IN IP4 192.168.1.10 表示会话的网络地址,这里为IPv4地址。

  3. s字段为会话的名称或描述,此处为"Play"表面是实时音视频

  4. c=IN IP4 192.168.1.10

    c字段指定了会话的连接信息。

    IN 表示网络类型为Internet。

    IP4 192.168.1.10 表示会话的IPv4地址。

  5. t=0 0

    t字段指定了会话的时间信息。

    0 0 表示会话的起始和结束时间都为0,即持续时间未定义。

  6. m=video 40052 RTP/AVP 96

    m字段定义了会话中的媒体类型和相关参数。

    video 表示媒体类型为视频。

    40052 表示媒体流的传输端口号。

    RTP/AVP 表示传输协议为RTP,使用AVP(Audio-Visual Profile)配置。

    96 表示媒体流使用编号96表示。

  7. a=rtpmap:96 PS/90000

    a字段包含了媒体流的属性。

    rtpmap:96 表示将编号为96的负载类型。

    PS 表示使用MPEG-PS格式进行数据封装。

    90000 表示时钟速率,即每秒的时钟滴答数。

  8. y=0358902090

    y字段为十进制整数字符串,表示SSRC值

  9. 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协议客户端开发(四)实时视频数据传输的更多相关文章

  1. 接口自动化测试:Thrift框架RPC协议客户端开发

    import java.lang.Thread.State;import java.util.Iterator;import java.util.List; import org.apache.thr ...

  2. EasyNVR和EasyDSS云平台联手都不能解决的事情,只有国标GB28181能解决了

    需求痛点 我们经常收到这样一种需求,就是将客户手里的各种类型的网络摄像机IPC和网络硬盘录像机NVR进行统一的整合接入和管理,并进行常规的直播.存储.录像检索和回放等操作,而这个时候我们通常会选择用E ...

  3. 基于GBT28181:SIP协议组件开发-----------第四篇SIP注册流程eXosip2实现(一)

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3945294.html. 上章节讲解了利用自主开发的组件SIP组件l ...

  4. 基于GBT28181:SIP协议组件开发-----------第一篇环境搭建

    原创文章,引用请保证原文完整性,尊重作者劳动,原文地址http://www.cnblogs.com/qq1269122125/p/3930018.html,qq:1269122125. SIP协议在安 ...

  5. node.js实现国标GB28181设备接入的sip服务器解决方案

    方案背景 在介绍GB28181接入服务器的方案前,咱们先大概给大家介绍一下为什么我们选择了用nodejs开发国标GB28181的服务,我大概给很多人介绍过这个方案,大部分都为之虎躯一震,nodejs在 ...

  6. 开源的C#实现WebSocket协议客户端和服务器websocket-sharp组件解析

    很久没有写博客了(至少自己感觉很长时间没有写了),没办法啊,楼主也是需要生活的人啊,这段一直都在找工作什么的.(整天催我代码的人,还望多多谅解啊,我会坚持写我们的项目的,还是需要相信我的,毕竟这是一个 ...

  7. C#实现WebSocket协议客户端和服务器websocket sharp组件实例解析

    看到这篇文章的题目,估计很多人都会问,这个组件是不是有些显的无聊了,说到web通信,很多人都会想到ASP.NET SignalR,或者Nodejs等等,实现web的网络实时通讯.有关于web实时通信的 ...

  8. 用Jersey为Android客户端开发Restful Web Service

    平时在做Android客户端的时候经常要与服务器之间通信,客户端通过服务端提供的接口获取数据,然后再展示在客户端的界面上,作为Android开发者,我们平时更多的是关注客户端的开发,而对服务端开发的关 ...

  9. [原创]上海好买基金招高级Java技术经理/运维主管/高级无线客户端开发等职位(内推)

    [原创]上海好买基金招高级Java技术经理/运维主管/高级无线客户端开发等职位(内推) 内部推荐职位 高级JAVA技术经理: 岗位职责: 负责项目管理(技术方向),按照产品开发流 ,带领研发团队,制定 ...

  10. 【JAVA EE企业级开发四步走完全攻略】

    本文是J2EE企业级开发四步走完全攻略索引,因内容比较广泛,涉及整个JAVA EE开发相关知识,这是一个长期的计划,单个发blog比较零散,所以整理此索引,决定以后每发一季JAVA EE blog后会 ...

随机推荐

  1. Burp Suite最新版本专业版激活2022.12.1附原文件

    Burp Suite 攻击web 应用程序的集成平台 Burp Suite 是用于攻击web 应用程序的集成平台,包含了许多工具.Burp Suite为这些工具设计了许多接口,以加快攻击应用程序的过程 ...

  2. 2023高效的mysql 随机语句 200万数据为例 用了 0.0030秒

    是的,如果数据表中有200万条记录,使用 ORDER BY RAND() 这种方式来随机选择记录会非常慢,因为 MySQL 需要对整个表进行排序,然后再返回指定数量的记录.这个过程需要消耗大量的时间和 ...

  3. C盘爆满的解决方法,不用删除文件,使用分区助手无损增加内存

    一.分区助手傲梅科技 对于我们C盘内存不足的来说,老师推荐的yyds. 我的内存C盘历史最低是900多M,1.5G还是多的,经过我不断的删除文件,发现没什么用,电脑用久了C盘文件占内存自然就多了!!改 ...

  4. TiDB在X86和ARM混合平台下的离线部署和升级

    [是否原创]是 [首发渠道]TiDB 社区 背景 在之前我们团队发布了TiDB基于X86和ARM混合部署架构的文章:TiDB 5.0 异步事务特性体验--基于X86和ARM混合部署架构,最近有朋友问到 ...

  5. 【Python毕业设计】基于Python+Flask+MySQL的学生信息管理系统(附完整源码)

    1.项目说明基于python+Flask+mysql的学生信息管理系统项目实战 项目需要安装pycharm专业版,mysql数据库以及项目所需的所有模块创建数据库名称db_online_notes,然 ...

  6. ARL:资产侦察灯塔系统

    资产灯塔,不仅仅是域名收集 功能简介 "挖洞神器"资产安全灯塔(ARL),旨在快速侦察与目标关联的互联网资产,构建基础资产信息库. 协助甲方安全团队或者渗透测试人员有效侦察和检索资 ...

  7. Django简介 安装下载 app概念 主要目录介绍

    目录 Django简介 前戏 Django是一个开放源代码的Web应用框架,由Python写成.采用了MTV的框架模式,即模型M,视图V和模版T.这套框架是以比利时的吉普赛爵士吉他手Django Re ...

  8. jQuery 在图片和文字中插入内容(多种情况考虑)

    昨天接到一个新的需要,在后台文章编辑器中,每一个文章的正文前面,可以单独添加一个电头字段,但是如果在富文本编辑器中最上面就添加图片的话,图片就会把电头和正文中的文字给隔开.需要做的是获取到电头字段,然 ...

  9. Mybatis 坑(1)

    org.apache.ibatis.executor.ExecutorException: No constructor found in xxxx [Integer,String] 这种情况一般是类 ...

  10. Costura.Fody 使用问题

    1. Costura.Fody 引用后,未能正常合并资源文件.用着用着就不行了 解决方案:在csproj所在的文件目录,找到FodyWeavers.xml,添加<Costura/> 1 & ...