1. 为什么看了这么多RTP包,问题还是定位不到
做音视频开发和网络排查的兄弟,大概率都干过这种事:抱着Wireshark抓了一大堆包,然后死盯着RTP的sequence number和timestamp看,看到眼都花了,最后得出结论“嗯,RTP包都在发,数据在走”,然后就卡壳了。如果是更早之前,我也会这么干,但后来被现实狠狠教育了几次,才彻底明白一件事——RTP只是信封,真正告诉你数据“送得怎么样”的,是信封上写的那句话,也就是RTCP里的SR和RR报告。
什么叫送得怎么样?举个例子,你正在会议室开一场视频会议,画面模糊、声音停顿,上下游同事互相甩锅,一个说发出去的码率正常,一个说收到就是卡。这个时候,两边抓包一对比,如果只盯着RTP,你只会看到发送端一直在发,接收端也确实收到了很多包,但卡顿依旧。问题在哪?在网里。网络丢了多少包、抖动有多大、往返时延有多高——这些RTP自己不会告诉你,全靠RTCP在帮你记账。
我之所以建议你把注意力从RTP稍微挪一点到RTCP,是因为SR(Sender Report,发送端报告)和RR(Receiver Report,接收端报告)这两个包,本质上是通信双方对“链路质量”做的定期体检报告。一个负责说“我发了多少、发了哪些、时间基点是什么”,另一个负责说“我收到了多少、丢了多少、等了多久”。把这两份报告合起来看,你就能算出一个非常关键的数字:端到端的实际通信质量,包括丢包率、抖动、RTT(往返时延)。
这篇东西没有高深理论,就是一次完整的抓包实战。我会从抓到包、看懂包、算明白数据、最后落到实际排障,一步一步拆开讲清楚SR和RR报告到底在“说啥”,以及怎么用它们回答那些“RTP答不了的问题”。适合刚接触RTP/RTCP的研发同学,也适合已经在排查音视频问题但总觉得差点工具思路的运维或测试工程师。
2. RTCP的基本盘:SR和RR报告各自扮演什么角色
2.1 别把RTCP当成一个包,把它当成一套“会话体检流程”
RTCP全称是RTP Control Protocol,和RTP是搭档关系。RTP负责搬运音视频数据,RTCP负责搬运统计数据和控制信息。RTCP协议栈里有多种包类型,其中最常见的两类:
- SR(Sender Report,类型200):只有发送过RTP数据的端点才会发SR。
- RR(Receiver Report,类型201):收到对端RTP数据但是自己不发送RTP(或者也发送但不关心同步)的端点,会发RR给对端。
有人只听说过SR,把RTCP当成一个带反馈的扩展来看,实际上SR和RR是两个独立角色,而且方向经常是反的。比如一个单向推流的场景,推流端往服务器推视频,推流端发SR,服务器会回RR。如果服务器不回任何RTCP包,推流端通常会在处理完收到的SR后默默按自己的节奏继续推,但很多推流库(比如基于标准RTSP/RTP实现的库)会根据服务器的反馈来调整发送节奏。所以看抓包时,收到SR的包,你的第一反应应该是:是谁发的?它作为发送端,给我报账了。收到RR包,第一反应则是:谁收到我的数据了,它在给我回执,告诉我收到了多少、丢了多少。
2.2 SR和RR的内部结构:长得像,但各自重点不同
SR和RR的报文头部结构基本都是:
V=2, P, RC(报告块数量), PT(SR=200/RR=201), 长度, 发送方SSRC 发送方的NTP时间戳和RTP时间戳(只有SR有) 若干个报告块(Report Block)RR没有前面的NTP/RTP时间戳部分,直接就是发送方SSRC,然后紧跟报告块。
报告块(Report Block)是核心,内容包含:
- 接收方SSRC(即收到哪个源的包)
- 丢包率(自上次报告以来的比例)
- 累计丢包数
- 收到的最大扩展序列号
- 到达间隔抖动(Interarrival Jitter)
- 上次SR时间戳(LSR)
- 上次SR的接收延迟(DLSR)
看懂报告块,整个RTCP就懂了一大半。我在实际排障中见过不少同事第一次看到报告块,直接被那一堆十六进制和字段名绕晕,以为要完全掌握整个RFC 3550才行。其实完全不用,我只需要关注几类信息:丢包率、抖动、RTT,以及和RTP序列号对应的收发数量。
提示:SR是“我这边发出的数据状态”的汇报,RR是“我这边收到数据的状态”的汇报。后面算RTT时必须同时拿到SR或RR里的时间字段和报告块字段,抓包时最好两边一起抓,只抓一端会漏掉关键信息。
2.3 发送周期的套路:为什么5分钟能“看懂”报告
RTCP的发送周期不是每秒钟狂发,它要控制自己的流量占比。标准RFC 3550建议,RTCP包占整体会话带宽的比例建议控制在5%(固定传输带宽场景),动态场景可以放开一些,但一般不会太夸张。根据session带宽和成员数量,每个端点会算出一个平均发送间隔。通常我的实际观察是音视频通话场景里,RTCP间隔在几百毫秒到几秒量级,并不是每帧都带一个SR/RR。
所以你抓包5分钟,其实能见到的SR/RR数量可能并没有你想象的那么多,可能就几十上百个。如果一帧一个RTCP包,那就是参数配置有问题,典型的实现冲突。实务上我建议抓包时间落在关键问题发生的前后各两三分钟,比如对方反馈卡顿的时间点,这样SR/RR的时间线和RTP丢包时间线能对应上。
3. 实战准备:抓包环境和Wireshark过滤设置
3.1 在哪儿抓、怎么抓,才能保证SR和RR都能抓到
先说结论:要抓就抓两端,至少抓一端网卡的流量,再配合对端的信息来推算。
最理想是能在一台机器上同时抓发送端和接收端的流量,但如果条件不允许(比如跨公网),那就分别在两边各抓一份。我自己常用的场景是:一台机器做媒体服务器或编码推流端,上面用Wireshark抓发出的包;另一台机器做播放接收端,抓接收的包。然后两边的时间做下对齐(同一台NTP服务器或统一时钟源),再去比对SR里带的NTP时间戳和本机抓包时间之间的关系。
抓包要注意一个坑:默认Wireshark只抓RTP/UDP数据,而RTCP经常跑在相邻的奇数UDP端口上。如果你的过滤器只写了rtp,那RTCP虽然能被识别,但显示颜色和协议识别优先级可能会被当作“RTP事件包”或普通UDP,肉眼一不注意就忽略了。特别是当RTP和RTCP往同一端口发、仅用SSRC区分时,这个坑更明显。
建议的抓包过滤器:
udp.port == 5004 || udp.port == 5005如果RTCP和RTP端口未知,直接抓udp全量,再用Wireshark的“分析-解码为”功能把对应UDP流解析为RTCP。而显示过滤器则建议:
rtcp || rtp这样RTP和RTCP都会同时显示,并且Wireshark会自动用不同颜色标出RTCP(默认浅紫色),一眼就能从茫茫UDP包里挑出来。
3.2 过滤出SR和RR,并且用时间线对齐
Wireshark里过滤SR和RR非常简单,直接按包类型过滤:
rtcp.type == 200 # SR rtcp.type == 201 # RR如果你想同时看SR、RR和RTP关键信息,可以写成:
(rtcp.type == 200) || (rtcp.type == 201) || (rtp)这样在Packet List里能看到所有RTCP报告和RTP包,同时点开任意RTCP包都能展开详细的字段树。
我在排障时习惯再加一列“Info”,里面会显示类似“Source SSRC: 0xc0a80001, Sender SSRC: 0xc0a80002, new source”之类信息,方便快速识别是哪个SSRC在说话。SSRC就是RTP流的源标识符,同一通会话里每个参与者的SSRC是唯一值。多路视频通话时,SSRC是区分各路流的钥匙,比看IP端口更直接。
经验之谈:抓RTCP如果只用显示过滤器“rtcp”,Wireshark偶尔会把某些RTCP包识别成UDP,特别是一开始没抓到RTCP握手包、中途加入抓包的情况。遇到这种情况,不要急着以为没抓到包,先选中可疑UDP包,右键“解码为”,把速率改为RTCP,通常就能正确解析出来了。
4. 核心解读一:SR报告怎么读
4.1 发送方的NTP时间戳和RTP时间戳:两台设备的时钟“对表”关键
4.1 发送方的NTP时间戳和RTP时间戳:两台设备的时钟“对表”关键
双击一个SR包,展开字段树,你首先会看到这个结构:
Real-time Transport Control Protocol (Sender Report) 10.. .... = Version: RFC 1889 Version (2) ..0. .... = Padding: 0 ...0 0001 = Reception report count: 1 Packet type: Sender Report (200) Sender SSRC: 0x10101010 (269488144) Sender information... NTP timestamp: 0xe05c5604.0f81f000 (3874141124.0605930) RTP timestamp: 3651474060 Sender's packet count: 82652 Sender's octet count: 31234264 Source 1 (0x20202020) ...0 0000 = Fraction lost: 0.0% Cumulative number of packets lost: 1 Extended highest sequence number received: 77108 Interarrival jitter (ms): 2.427 Last SR timestamp (LSR): 0xe05c56bd (3874141117) Delay since last SR (DLSR): 0x00000000 (0.000000000 seconds)我先讲发送方信息里最核心、也最容易被忽略的两个时间戳:NTP时间戳和RTP时间戳。
NTP时间戳精确到小数秒,单位是秒,它是发送端“当前真实时间”的一个绝对基准。RTP时间戳则是媒体采样频率下的相对时间刻度,它和实际挂钟时间没有直接对应关系,只是用来表示媒体采样的时序,比如视频90000Hz采样频率下,每3000个RTP tick就代表1/30秒的播放时长。
这两个时间戳同时出现,最大的价值在于:把发送端的媒体时间轴和真实时间轴对齐。什么意思?比如你看到一路视频的RTP包携带的time stamp一直在线性增长,但NTP时间戳却和实际抓包时刻对不上,这就说明可能有缓存、重传或者跨时钟域的变换,导致媒体延迟在累积。
简单点说:NTP时间戳告诉你“我真正是在哪个墙钟时刻送出或准备好这包数据的”,RTP时间戳告诉你“这个包在媒体流里处于哪个采样位置”。两者之间的比例关系(RTP timestamp差值 / NTP时间差值)正好就是发送端的采样时钟速率。如果这个比例和预期的采样率相差很多,例如90000Hz的视频实际算出来只有85000Hz,说明发送端时钟有问题,接收端就会出现音频视频不同步。
我在实际项目中还真遇到过这种问题:终端播放器总是越播越慢,看NTP和RTP换算后,才发现编码器在低功耗模式下PLL跑偏了,折算下来的实际帧率只有标称值的95%左右。所以SR里的这段信息不只是给你看的,是真的可以用来做演算的。
另外一个常用场景:你抓包抓了两端,想在接收端的Wireshark里确认某个RTP包对应的真实发送时刻,不好算。这时直接从SR的NTP timestamp里拿绝对时间,然后找接收端对应时刻收到的RTP包,就能算出单向链路延迟。这个方法比我之前只用delta time估算要准得多。
4.2 发送方的包计数和字节计数:确定发送端“有没有藏包”
再往下是Sender's packet count和Sender's octet count。这两个计数表示自发送端开始发送RTP包以来,总共发了多少个RTP包、多少字节。它们是一个“累计值”,只增不减。
读这两个值的意义在于:如果在某个时间窗口内,SR显示发送了10万个包,但接收端收到的最大扩展序列号只增加8万个,那说明中间丢了约2万个包,这是一个非常直接的“链路丢包判断依据”。很多人以为丢包率只能从RR里看,实际上SR的计数和接收端报告块的累计序列号合在一起算,能估算出“端到端发送-接收丢包率”。
这里有个小坑:这个累计值可能是从会话开始算到现在的,如果编码器中途重新初始化(比如码率调整导致编码器重启),这个计数可能会重置或跳变。所以做统计时尽量挑连续的SR序列,不要跨过明显的重置点。
4.3 报告块里的丢包率、累计丢包和扩展最大序列号
到报告块部分,才真正开始教你“收到一大堆包之后如何报数”。当这个SR包的端点同时也作为接收方(双向通话场景非常常见),那么它的报告块里携带的是它对对端某个SSRC的接收统计。
Fraction lost(丢包率比例):自上一个SR/RR发送以来,本周期内丢掉的包数占期望收到包数的比例。这是个百分比,范围从0到255,对应0%到100%。
Cumulative number of packets lost(累计丢包数):从接收开始到当前时刻,总共丢掉的RTP包数量。之所以强调“cumulative”,是因为它不区分时间窗口,只告诉总量。
Extended highest sequence number received(扩展最高序列号):这个字段看起来复杂,其实就是收到的RTP序列号的最大值,但把循环回绕的序列号空间做了扩展,形成48位(Wireshark显示成“Extended sequence number: xxx,实际是32位高位+16位低位组合)。协议里是32位,高位16位是回绕计数,低位16位是当前最大序列号。读这个字段时,不需要自己拼,Wireshark已经帮你算好了实际扩展值。
我在解读报告块时,习惯直接看Wireshark的Info列,如果显示“Fraction lost: 0.0%”,那恭喜,这段链路几乎无丢包;如果是“Fraction lost: 5.0%”,那视频的卡顿概率已经很高了,尤其对交互式通话场景,超过5%的丢包基本就严重影响体验了。测试过几次后我发现一个经验值:视频会议场景丢包2%以内体感还可以,5%以上就会看到花屏、马赛克和持续卡顿;纯语音通话倒是可以稍微扛一扛,超过10%基本没法听。
注意:Fraction lost是“最后一次报告到本次报告期间”的丢包率,如果你只抓到一个SR,它只能反映这个小窗口的质量,不代表全程。所以排查时一定要多抓几个SR(或RR)看趋势,不能看了某一个5%就下结论。
5. 核心解读二:RR报告怎么读,以及怎么算出RTT
5.1 RR报告块字段全解:从源SSRC到接收质量
RR(Receiver Report)的包类型是201,结构相对SR更简单,因为接收方没有发送统计信息需要上报。它的核心还是那一串报告块,请求的也是相同的统计指标。
我们抓一个典型的RR包展开来看(从Wireshark中展开):
Real-time Transport Control Protocol (Receiver Report) 10.. .... = Version: RFC 1889 Version (2) ..0 .... = Padding: 0 ...0 0001 = Reception report count: 1 Packet type: Receiver Report (201) Sender SSRC: 0x30303030 Source 1 (0x10101010) ...0 0000 = Fraction lost: 0.0% Cumulative number of packets lost: 2 Extended highest sequence number received: 77110 Interarrival jitter (ms): 1.325 Last SR timestamp (LSR): 0xe05c56bd (3874141117) Delay since last SR (DLSR): 0x00000001 (0.015625000 seconds)Source 1里的SSRC就是它正在报告的对象——也就是另一端的发送端标识。
Fraction lost和Cumulative number of packets lost的含义和SR报告块里的完全一致。Extended highest sequence number received里面那个数值注意:它是对应SSRC的扩展最大序列号,不是报告方自己的。
还有两个容易混淆的字段:**Interarrival jitter(到达间隔抖动)**计算的是相邻两个RTP包到达时间差的变化量,单位是RTP timestamp的tick数。Wireshark会把它转换成毫秒显示,这样读起来方便一些。抖动越大,说明网络的缓冲变化越剧烈,音画就会忽快忽慢,具体表现就是卡一下、快进一下、再卡一下。Jitter的计算公式约等于J = J + (|D(i-1,i)| - J)/16,这采用了带遗忘因子的指数加权平均,核心目的是让瞬时抖动不至于让指标大幅震荡,但如果持续高抖动,这个值会快速上升并维持在较高水平。
某次在一场线上教学直播的排障中,RTP所见全是连续包,但播放端声音断断续续。看RTCP的jitter从1ms猛涨到15ms以上,我就知道不是丢包,而是网络波动导致包的抵达节奏非常不稳。后来发现是对端网络出口拥塞,一旦高峰时段队列缓存抖动就被放大到ms级。这个案例里RTP根本看不出问题,RR报告直接给出了结论。
5.2 用LSR和DLSR手把手算一次RTT(往返时延)
这可能是全网讲得最多的一个RTCP实操,但确实值得亲自动手算一遍。要计算RTT,需要同时满足三个条件:
- 发送端A发出SR给接收端B,SR里带NTP时间戳,记为T1。
- 接收端B收到SR后,在某个时刻发出RR(或SR)给A,里面携带LSR(Last SR timestamp)和DLSR(Delay since last SR)。
- 发送端A收到B的这条RR后,记录当前时间T2。
RTT的计算公式是:
RTT = T2 - LSR - DLSR其中:
- LSR是B接收到的那个SR的NTP时间戳(的中间32位),即T1;
- DLSR是B从收到A的SR到发出自己的RR之间隔了多少秒;
- T2是A自己的当前真实时间(NTP格式)。
所以这条公式本质上是:A发出SR,B反馈RR,A收到RR的总耗时(T2 - LSR),减去B内部处理这个SR到发出RR的等待时间(DLSR),剩下的就是双向链路的传输时间。
在抓包里实操时,Wireshark会在RTCP字段树里直接显示“Last SR timestamp (LSR): 0xe05c56bd”和“Delay since last SR (DLSR): 0x00000001 (0.015625000 seconds)”,这些都是B在生成RR时填好的值。
接下来是核心操作——找到A收到RR时的当前NTP时间。
实践里有两种取法:
- 方法一:抓包时间是Wireshark的pcap时间(epoch秒),如果两端机器时钟同步都对齐了,那直接用A端抓包时间当作T2,计算出来近似RTT。
- 方法二:更精确的是从A端发出的下一个SR里读它的NTP timestamp,因为A发出下一个SR时自带精确的NTP时间,可以认为这个NTP是A的“当前时间”。然后用这个值减去LSR(这个LSR来自B的RR),再减去DLSR。
我常用的快速计算步骤:
- 在Wireshark里找到发往A的RR(SR)包,记下它的LSR和DLSR显示值。
- 找到A紧接着发出的一个SR包,读出它的NTP timestamp。
- 心算或拿计算器。不放心就直接写个小脚本处理,但我习惯先手算一次确认逻辑。
举个例子(纯粹演示):
- A发的SR中NTP timestamp = 3874141124.060593;
- B发出的RR中LSR = 0xe05c56bd (3874141117),DLSR = 0.015625s;
- A收到该RR后发出的下一条SR中NTP timestamp = 3874141130.100000。
则RTT = 3874141130.1 - 3874141117 - 0.015625 = 13.084375秒?等等,这个例子显然太大了,每秒之间差太多不是正常的RTT。这说明LSR取的是“中间32位”,记得用秒精度还不够,要换算好。
重新来一个贴合实际的小数值示例:
- SR的NTP秒部分为3874141117,RTT计算时核心差在毫秒级。比如A发SR的时间=T1=3874141117.500;
- B发出RR的LSR显示的NTP戳是3874141117.500,DLSR=0.020s;
- A收到RR后,A端抓包时间显示(NTP)T2=3874141117.545。
RTT = (3874141117.545 - 3874141117.500) - 0.020 = 0.025秒,也就是25ms RTT。通常在局域网内RTT应该是个位数毫秒或20ms以内,跨地域可能到几十、上百毫秒。
Wireshark本身其实也提供了计算好的RTT,有些版本会在RTCP字段树里显示“Calculated RTT”。但我个人建议还是手算并理解这个公式,原因有两个:一是很多集成环境中Wireshark拿不到精确的T2(因为抓包是接收端视角,没看到A发出下一个SR的时刻),手算可以自己补数据;二是理解了公式后,你再去查类似WebRTC的RTT统计也不会慌,底层思路都一样。
5.3 抖动(Jitter)到底怎么折算成能看懂的值
报告块里的Interarrival jitter看起来是个整数,但它是基于RTP timestamp tick的,不是毫秒。要读正确,必须配合RTP采样率来折算。
常见场景折算方式:
- 音频:8kHz采样率,每个tick = 1/8000秒 = 0.125ms。
- 音频:48kHz采样率,每个tick = 1/48000秒 ≈ 0.0208ms。
- 视频:90kHz采样率,每个tick = 1/90000秒 ≈ 0.0111ms。
假设某个RR报告里的jitter值是300,且音频采样率是48kHz,那么抖动约等于300 * 0.0208 ≈ 6.24ms。看起来还好,但如果jitter值达到3000,那抖动就约等于62.5ms,此时语音包到达速率的波动已经非常明显,接收端的Jitter Buffer要是设计得不够大,丢包或卡顿就在所难免。
Wireshark在版本更新后通常会在显示树里直接附带一栏“Interarrival jitter (ms)”,直接看这个值会更快。不过我自己偶尔仍会手算核对一下,尤其当Wireshark解析遇到非标准采样率时,避免被误导。
建议:实际诊断中,把jitter阈值粗略定在视频50ms、音频30ms,超过基本就会影响体验。如果看到抖动翻倍持续增长,优先排查网络设备缓存、无线链路质量,而不是怀疑应用代码。
6. 实操现场:5分钟从抓包到定位“是网络还是终端”的完整流程
6.1 案例环境与抓包规划
我模拟一个最常见的场景——线上会议出现音画不同步,用户反馈大概在3分钟前开始模糊卡顿。为了定位,我在发送端电脑上启动了Wireshark,过滤器写成udp.port == 5004 || udp.port == 5005(假设RTP在5004,RTCP在5005),同时又在接收端电脑上也启动了抓包,保证SR和RR都能双向观察到。
抓包时间定了5分钟,覆盖问题发生的前后时段。抓完侧重点和操作顺序如下:
- 先用显示过滤器
rtcp扫一遍RTCP全貌,统计SR/RR数量。 - 按RTCP包类型分别过滤,观察每个端点产生报告的频率。
- 挑出问题时段前后的SR和RR,逐个读报告块。
6.2 从报告块迅速下结论:首个5分钟排障步骤
我建议每次都按照这个顺序排查:
- 先看Fraction lost:如果持续接近0,说明链路基础丢包不严重。
- 再看Jitter:如果持续高于阈值(视频50ms/音频30ms),卡顿大概率是到达节奏波动引起,而不是纯粹的带宽不足。
- 接着看Cumulative number of packets lost增加速度:如果累计丢包数一直上涨,即使Fraction lost是0%也要警惕,因为说明偶发性丢包还是存在,只是占比小。
- 最后核算RTT:如果RTT稳定且低,基本能排除跨网大延迟问题;如果RTT忽高忽低,可能是有重路由或拥塞。
我上面提到的这个线上会议案例,实操结果是这样的:
- 接收端RR报告里Fraction lost基本为0%,但jitter从最初的2ms一路涨到平台期的18ms,RTT基本稳定在8ms左右。
- 查看发送端SR,包计数和字节计数都在正常增长,没有跳变。
- 接收端最大的DLSR从16ms升到40ms左右,说明接收端处理SR到生成RR的内部延迟变高了。
于是结论指向接收端的应用层处理或播放缓冲策略存在问题,而非网络传输丢包。后来接收端升级了解码器缓冲策略、调整了Jitter Buffer深度,症状明显缓解。这个案例里如果只看RTP丢了几个包,那绝对是0丢包,结论就会完全相反,问题也永远找不出来。
6.3 顺手写个脚本统计SR字段,批量处理上百个RTCP包
如果只是手动点击几个包逐个看,5分钟可能够,但如果每个包都手抄一遍字段,就累了。我习惯用tshark把关键字段导出成表格,再用Python快速统计。
导出一条文本模板命令:
tshark -r capture.pcap -Y "rtcp" -T fields \ -e frame.time_epoch \ -e rtcp.ssrc \ -e rtcp.type \ -e rtcp.sender_info.ntp.timestamp \ -e rtcp.sender_info.rtp.timestamp \ -e rtcp.report_block.fraction_lost \ -e rtcp.report_block.cum_lost \ -e rtcp.report_block.jitter \ -e rtcp.report_block.lsr \ -e rtcp.report_block.dlsr这条命令会输出TSV格式,直接拖进Excel或Python处理都行。需要注意tshark里字段名可能随版本略有不同,跑之前先用tshark -G fields | grep rtcp确认字段名。举个例子,旧版本里jitter字段是rtcp.report_block.jitter,新版本也是这个,基本通用。
拿到CSV后我通常会算两个统计值:
- 每5秒一个窗口内的平均Fraction lost;
- RTT的动态变化趋势。
这种方式比肉眼看几十个RTCP包快得多,也方便做趋势图。
小提示:如果tshark版本旧,遇到RTCP解析失败,试试加参数-d udp.port==5005,rtcp强制按RTCP解码。这个参数在migrate到新环境时特别有用。
7. 常见问题与避坑速查
| 问题现象 | 可能原因 | 排查思路与对策 |
|---|---|---|
| 抓包看不到任何RTCP包 | 过滤条件没覆盖RTCP端口;中途加入抓包导致解析器未识别 | 先抓全量UDP,用rtcp显示过滤,必要时右键“解码为”强制RTCP |
| 没有SR包只有RR包 | 发送端如果从未发送过RTP包,理论上不会发SR;也可能是抓包只抓到了接收端方向流量 | 确认RTCP流方向性;SR只有在“发送RTP”的端点才会发 |
| SR里报告块的丢包率为0但画面卡顿 | 链路丢包率低但抖动大、或接收端缓冲策略问题 | 重点看jitter和DLSR,并检查接收端播放缓冲参数 |
| RR的LSR字段为0 | 对端可能还没收到任何SR,或者收到的第一个SR丢失 | 等下一个周期,确认在收到SR后才发RR;不要拿LSR=0去算RTT |
| RTT计算结果异常大 | 忘记减DLSR;或者LSR只取了中间32位导致秒数不对 | 用完整的时间戳计算;找出A端下一个SR的NTP time做T2 |
| RTCP包被当作UDP显示 | 解析器没能识别端口 | 右键解码为,或用-d udp.port==端口,rtcp强制解析 |
| 报告块里的Extended highest sequence number不变 | 接收端可能没收到新的RTP包,或抓包里被过滤掉了 | 核对同一SSRC的RTP序列号是否连续到达 |
| 同一个SSRC出现了多个SR/RR | 端点重启、RTP会话重建或SSRC冲突 | 观察计数是否重置,必要时按SSRC分段统计 |
一个特别容易踩的坑:RTCP端口不一定是RTP端口+1,尤其在使用复合RTCP包且端口复用情况下。不要想当然地去固定匹配端口,靠SSRC去关联才是常态。
8. 最后分享一点个人习惯
做RTCP排障多了之后,我现在的习惯是:一旦链路音视频体验出问题,抓包后先不看RTP,先花5分钟把RTCP的报告块统计一遍。相比RTP的“正在发”、“正在收”,RTCP能给到更高质量的链路画像——丢了多少、抖了多少、链路往返走了多久。这三类数据才是回答“为什么卡”的最好语言。
建议所有要排查音视频问题的朋友,把Wireshark的显示过滤器里面加上rtcp成为肌肉记忆。下次再遇到有人只盯着RTP找原因,你可以顺手打开RTCP报告,然后指着报告块告诉他:别猜了,数字都在这里。