4. RTP/RTCP协议:RTP数据包格式、时间戳同步、RTCP的SR/RR报告
好,咱们进入第四讲。RTP和RTCP,这两个家伙在视频传输里是形影不离的搭档。RTP负责扛着视频数据往前冲,RTCP则在旁边做后勤保障。我刚开始接触流媒体时,总觉得RTCP是个累赘,后来被坑过一次才明白——没有RTCP,RTP就是个无头苍蝇。
4.1 RTP数据包格式——视频数据的“集装箱”
RTP的全称是实时传输协议。说白了,它就是把摄像头采集到的每一帧画面,切成合适大小的数据块,再打上标签扔到网络上。这个“标签”就是RTP头部。
一个标准的RTP包长这样:
0 1 2 3
0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
|V=2|P|X| CC |M| PT | sequence number |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| timestamp |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
| synchronization source (SSRC) identifier |
+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+=+
| contributing source (CSRC) identifiers |
| .... |
+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
我来拆开讲讲每个字段的用处:
- V(版本号):固定为2。目前主流就是RTPv2,别问我v1去哪了,我也没见过。
- P(填充位):有时候包末尾需要补几个字节对齐,这个位就置1。我在对接某些老旧的DVR设备时遇到过,解码器如果不处理这个位,画面就会花掉。
- X(扩展位):如果需要自定义头部扩展,就置1。我一般不用,除非客户有特殊需求。
- CC(CSRC计数):表示后面跟着几个CSRC标识符。混流场景下会用到,比如视频会议。
- M(标记位):对于视频,通常表示一帧的结束。我习惯在H.264的IDR帧最后一个包置M位,方便解码器快速定位关键帧。
- PT(负载类型):告诉接收方这个包里面装的是什么编码格式。比如96表示H.264,98表示H.265。嗯,这里要注意,96-127是动态类型,具体映射需要双方协商好。
- sequence number(序列号):每个RTP包唯一的序号,从随机值开始递增。接收方靠它来检测丢包和排序。我在项目中遇到过序列号回绕的问题,32位无符号整数,满了会归零,代码里一定要处理这个边界。
- timestamp(时间戳):这个太关键了,后面单独讲。
- SSRC(同步源标识符):每个视频流的唯一ID。同一个DVR的不同通道,SSRC必须不同,否则接收方会串流。
我的小技巧:调试RTP问题时,我习惯先用Wireshark抓包,过滤条件设成rtp,然后看序列号是否连续。如果跳号严重,基本可以断定网络有问题或者发送端封装有bug。
4.2 时间戳同步——让视频和音频“对齐”
时间戳是RTP里最容易让人迷糊的地方。我刚开始做NVR项目时,视频和音频总是对不上,画面里人嘴都闭上了,声音还在响。后来才发现是时间戳的时钟频率没搞对。
RTP时间戳的单位不是毫秒,而是根据负载类型定的时钟频率。常见的:
| 编码格式 | 时钟频率(Hz) | 说明 |
|---|---|---|
| PCMU / PCMA | 8000 | 音频,每125微秒递增1 |
| H.264 / H.265 | 90000 | 视频,每11.11微秒递增1 |
| MPEG4 | 90000 | 同上 |
为什么会用90000?因为90kHz能被常见的帧率整除。25fps的话,每帧时间戳增量是90000/25=3600。30fps就是3000。你想想看,如果直接用毫秒,25fps每帧40毫秒,精度太粗了。
时间戳同步的核心逻辑:
- 视频流:同一帧的所有RTP包,时间戳必须相同。不同帧的时间戳按帧率递增。
- 音频流:每个音频包的时间戳按采样点数递增。比如8kHz采样,每包160个样本,时间戳就加160。
- 音视频同步:接收方根据RTCP的SR报告里的NTP时间,把RTP时间戳映射到绝对时间,然后对齐播放。
我曾经踩过的坑:有一次对接海康的DVR,视频时间戳突然跳变了几十万。排查了半天,发现是设备端在I帧重置了时间戳基准。后来我在代码里加了容忍逻辑——如果时间戳跳变超过5秒,就重新同步,而不是直接丢弃。
4.3 RTCP的SR/RR报告——网络质量的“体检报告”
RTCP有两个核心报告:发送端报告(SR)和接收端报告(RR)。SR由发送端发出,RR由接收端发出。它们周期性地交换,让双方了解网络状况。
4.3.1 SR(发送端报告)
SR包里最重要的信息是NTP时间戳和RTP时间戳的对应关系。接收方拿到这个对应关系,就能把RTP时间戳换算成真实时间,从而实现音视频同步。
SR包结构:
+---------------+----------------+----------------+----------------+
| V=2 |P|RC=0 | PT=200(SR) | length |
+---------------+----------------+----------------+----------------+
| SSRC of sender |
+---------------------------------------------------------------+
| NTP timestamp, most significant word |
+---------------------------------------------------------------+
| NTP timestamp, least significant word |
+---------------------------------------------------------------+
| RTP timestamp |
+---------------------------------------------------------------+
| sender's packet count |
+---------------------------------------------------------------+
| sender's octet count |
+---------------------------------------------------------------+
NTP时间戳是64位的,高32位表示秒数,低32位表示小数秒。精度极高。我一般用这个公式把NTP转成Unix时间戳:
ntp_seconds = (ntp_msb << 32) | ntp_lsb
unix_time = ntp_seconds - 2208988800 // 1900年到1970年的秒数差
4.3.2 RR(接收端报告)
RR是接收端反馈给发送端的。里面包含丢包率、累计丢包数、抖动、延迟等信息。发送端根据这些数据动态调整码率或帧率。
RR报告块的关键字段:
| 字段 | 含义 | 我的经验 |
|---|---|---|
| fraction lost | 丢包率(8位,单位1/256) | 超过10%基本就花屏了,需要降码率 |
| cumulative lost | 累计丢包数 | 注意24位有符号,超过8388607会变负数 |
| interarrival jitter | 到达时间抖动 | 单位是RTP时间戳步长,视频的话除以90得到毫秒 |
| LSR / DLSR | 上次SR时间 / 上次SR到现在的延迟 | 用来计算RTT(往返时间) |
核心公式:RTT = 收到RR的时间 - LSR - DLSR。这个值如果超过500ms,直播体验就会很差。我在项目中一般设置阈值,超过1秒就切换为TCP传输或者降低分辨率。
4.4 实战中的RTCP策略
RTCP的发送间隔是有讲究的。RFC 3550规定,RTCP带宽一般占RTP会话总带宽的5%。其中,发送端报告占25%,接收端报告占75%。
我个人的习惯是:
- 视频流:RTCP间隔设为1-2秒。太频繁浪费带宽,太稀疏无法及时感知网络变化。
- 音频流:间隔可以设到5秒。音频对延迟更敏感,但对丢包率容忍度更高。
- 关键场景:比如安防报警触发时,我会临时缩短RTCP间隔到500ms,确保第一时间发现网络异常。
避坑指南:我曾经遇到一个奇葩问题——RTCP的SSRC和RTP的SSRC不一致。接收端根据SSRC匹配SR和RTP流,结果匹配不上,时间戳同步完全失效。后来我在代码里加了校验,如果SSRC不匹配,就丢弃该RTCP包并打印警告。
好了,这一章的内容就这些。RTP负责把视频数据打包送出去,RTCP负责反馈网络质量并帮助同步。两者配合好了,你的视频传输架构才算真正站稳了脚跟。下一章咱们聊聊实际部署中怎么优化这些参数,让画面更流畅、延迟更低。