# GB28181Console **Repository Path**: AndroidCoderPeng/GB28181Console ## Basic Information - **Project Name**: GB28181Console - **Description**: 基于 Qt 5.15.2 / C++14 实现的 GB/T 28181-2016 协议推流示例(控制台程序,可作为自启动服务运行,已实现 SIP 自动注册)。功能涵盖:SIP 信令交互、OpenCV 相机采集与预览、FFmpeg 编码、PS 流封装、RTP 发送 PS 流至国标平台(ZLMediaKit)。 - **Primary Language**: Unknown - **License**: Not specified - **Default Branch**: master - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 0 - **Forks**: 0 - **Created**: 2026-06-14 - **Last Updated**: 2026-06-14 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # GB28181Console 基于 Qt 5.15.2 / C++14 实现的 GB/T 28181-2016 协议推流示例(控制台程序,可作为自启动服务运行,已实现 SIP 自动注册)。功能涵盖:SIP 信令交互、OpenCV 相机采集与预览、FFmpeg 编码、PS 流封装、RTP 发送 PS 流至国标平台(ZLMediaKit)。 ## 1. SIP 参数初始化与平台注册 - 初始化 SIP 相关参数(设备 ID、服务器地址、端口等),通过 eXosip2 库向国标平台(GB/T 28181-2016)发起注册请求。 ## 2. 视频采集 - 使用 OpenCV 采集视频画面,获取 cv::Mat (BGR) 格式的原始数据; ## 3. 音频采集 - ⚠️ 方案待确定。 ## 4. 拉流信令与编码 - 客户端默认仅进行本地预览,不执行编码与推流。当国标平台下发拉流信令(Invite 请求)后,客户端才开始编码: - 通过 FFmpeg 软编码生成完整的 H.264 帧数据(包含起始码)并记录编码帧数; - 软编码生成的 H.264 帧数据必须包含起始码(Start Code:`00 00 00 01` 或 `00 00 01`); > ⚠️ 关键提示:若 H.264 帧缺少起始码,PS 封装将失败或平台无法解析,导致推流无画面! ## 5. 视频帧封装为 MPEG-2 PS 流(这一步坑超多!) - 将编码帧数转换为 90kHz 时间基准的时间戳: ```objectivec const auto pts_90k = pkt->pts * 90000 / VIDEO_FPS; ``` > ⚠️ 关键提示:GB/T 28181-2016 要求必须使用 90kHz 时间基准,否则推流将无画面! - 将整帧 H.264 数据按起始码拆分为 NALU(Network Abstraction Layer Unit,H.264 数据的基本单元),同时提取并缓存 SPS/PPS 帧数据,并筛选出 IDR 帧。 - 将 SPS/PPS 与筛选出的 IDR 帧按顺序 `[起始码]+[SPS]+[PPS]+[IDR 帧]` 组帧,封装为 PES 包。 - 将非 IDR 帧按顺序 `[起始码]+[P 帧]` 组帧,封装为 PES 包。 > ⚠️ 关键提示:IDR 帧和 P 帧本质上是一系列 NALU。IDR 帧必须先于任何帧发送到平台,否则平台无法解析画面帧数据——因为 SPS/PPS 携带了视频帧的基本信息。 - 将上述步骤得到的 PES 包作为载荷,按照 IDR 帧(需添加系统头和 PSM)与非 IDR 帧分别封装为 MPEG-2 PS 流。 > ⚠️ 关键提示:添加系统头和 PSM 时坑超多!每个字节、每个 Bit 位的含义都必须搞清楚,否则无法正确封装! ## 6. 音频帧编码与封装为 MPEG-2 PS 流 - ⚠️ 方案待确定。 > ⚠️ 关键提示:GB/T 28181-2016 要求必须使用 90kHz 时间基准,否则推流会出现音画不同步! - 将 PCM 编码为 G.711μ(G.711a); - 将整帧 G.711μ(G.711a)封装为 PES 包。音频封装相对简单,直接填入 PES 包即可。 - 将上述步骤得到的 PES 包作为载荷,封装为 MPEG-2 PS 流。 > ⚠️ 关键提示:音频帧全部是非 IDR 帧,即不需要添加系统头、PSM 和起始码。此处务必注意!!! ## 7. PES 包分片处理 - 由于 RTP MTU 限制约为 1400 字节,需对较大的 PES 包进行分片; - 判断每个 PES 包大小: - 若 ≤ 1300 字节:直接作为单个 RTP 包发送; - 若 > 1300 字节:按 RFC 3984 或 GB/T 28181-2016 规范进行 RTP 分片封装。 - 这一步相对简单:将 PES 包分片并封装为 PS 包,再通过 RTP 发送至国标平台。 > ⚠️ 关键提示:SIP 的 SDP 消息中指定了设备应以何种方式发送数据到平台,务必严格按照 SDP 中的方式发送,否则平台会直接丢弃收到的包。 ## 8. 网络传输 - 将封装好的 RTP 包(单包或分片)通过 TCP 或 UDP 发送至国标平台: - TCP 模式:连接可靠、抗丢包,适合网络不稳定的环境; - UDP 模式:延迟更低,适合实时性要求高的场景; - 传输协议类型由平台信令协商确定,客户端动态适配。 # 语音对讲流程 ## 1. 接收平台 Notify 语音对讲信令 - 监听平台下发的 Notify 语音对讲信令,解析 CmdType 为 `Broadcast` 的信令,提取 SourceID、TargetID; - 回复平台 ACK 200 确认信令。 ## 2. 初始化本地 TCP 客户端 - 初始化 TCP 客户端,获取本地 TCP 端口,并发送 INVITE 到平台。 > ⚠️ 关键提示:使用 TCP 是因为设备和平台通常不在同一网段(设备一般位于局域网或内网,平台在公网),只能先通过 TCP 建立连接,平台再经由该连接链路将语音对讲数据发送过来。UDP 模式需要自行实现 NAT 穿透或打洞。 ## 3. 等待平台回复 SDP - 解析平台回复的 SDP 消息,获取平台的 TCP IP 地址和端口、音频解码类型,并回复 ACK 200; - 连接平台指定的 TCP 地址和端口,开始接收平台数据。 > ⚠️ 关键提示:接收平台数据时频率较高,需使用【独立线程 + 环形缓冲区】实现,否则性能将成为瓶颈。 - 接收到的数据为 G.711μ 或 G.711a 音频数据,无法直接播放,需先解码为 PCM 裸流再进行播放。 > ⚠️ 关键提示:G.711μ 或 G.711a 音频数据的回调类型为 ByteArray,PCM 裸流的回调类型为 ShortArray。