1. 项目背景与核心需求
最近在折腾一个视频监控项目,需要把摄像头采集的实时画面传输到后端服务器。市面上现成的方案要么延迟太高,要么协议太重,于是决定自己撸一套轻量级的UDP视频传输基础层。这个方案的核心目标就三点:低延迟(控制在200ms以内)、高吞吐(支持1080P@30fps)、抗弱网(丢包率20%还能正常看)。
做过视频传输的兄弟都知道,UDP虽然快但不可靠,直接裸传视频流分分钟花屏给你看。所以得在应用层自己实现一套保障机制,既要保留UDP的速度优势,又要解决丢包、乱序这些糟心问题。下面就把我这段时间趟过的坑和最终方案详细拆解下。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 协议设计关键点
2.1 报文结构设计
先看协议头设计,每个数据包包含12字节的固定头部:
code复制0 4 8 12 16
+-------+-------+-------+-------+
| 魔数 | 包序号 | 时间戳| 分片信息
+-------+-------+-------+-------+
| payload... (最大1400字节)
- 魔数:0x55AA用于快速校验报文有效性
- 包序号:32位循环计数,用于检测丢包和乱序
- 时间戳:帧采集时间,单位毫秒
- 分片信息:高16位是帧总包数,低16位是当前包索引
实测发现单包控制在1500字节以下最稳妥,避免IP分片。视频帧超过MTU时就拆分成多个包,接收端按帧重组。
2.2 抗丢包策略
采用选择性重传+前向纠错混合方案:
- 每收到10个包发一次ACK,携带最近50个包的接收状态(用bitmap表示)
- 关键帧(I帧)附加20%的FEC冗余包
- 动态调整重传次数:根据RTT计算,网络差时最多重传2次
这里有个骚操作:把视频帧分成关键片和非关键片。非关键片丢了就直接跳过,等下一个关键帧,避免雪崩效应。实测在15%丢包率下,PSNR仍能保持在28dB以上。
3. 核心实现细节
3.1 发送端工作流
cpp复制while (running) {
AVFrame* frame = grab_frame(); // 从摄像头抓帧
if (frame->key_frame) {
insert_fe
