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毫秒,精度太粗了。

时间戳同步的核心逻辑:

  1. 视频流:同一帧的所有RTP包,时间戳必须相同。不同帧的时间戳按帧率递增。
  2. 音频流:每个音频包的时间戳按采样点数递增。比如8kHz采样,每包160个样本,时间戳就加160。
  3. 音视频同步:接收方根据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负责反馈网络质量并帮助同步。两者配合好了,你的视频传输架构才算真正站稳了脚跟。下一章咱们聊聊实际部署中怎么优化这些参数,让画面更流畅、延迟更低。