# GB28181Client **Repository Path**: AndroidCoderPeng/GB28181Client ## Basic Information - **Project Name**: GB28181Client - **Description**: CLion平台集成Qt框架实现GB28181推流例子,包含Sip信令交互+OpenCV采集相机预览数据+FFmpeg编码+PS封装+RTP发送PS流到国标平台(ZLMediaKit) - **Primary Language**: Unknown - **License**: MIT - **Default Branch**: main - **Homepage**: None - **GVP Project**: No ## Statistics - **Stars**: 1 - **Forks**: 0 - **Created**: 2026-06-14 - **Last Updated**: 2026-06-17 ## Categories & Tags **Categories**: Uncategorized **Tags**: None ## README # GB28181 CLion平台集成Qt框架实现GB/T 28181-2016推流例子,包含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(网络应用层单元,是 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 型。