📹 第一章 流媒体与RTSP/RTP/RTCP简介
📄 一句话概括

Transport 头决定「数据用什么搬、搬到哪个端口」。 RTP 用偶数端口、RTCP 用下一个奇数端口,差这一是为了配对。

📝 本节和前面几节的关系

上一节讲会话,这一节讲会话里最重要的一样参数:传输。 这一节是实战里改得最多的一个头——穿透 NAT、换 TCP、开组播,全是在改它。

一、Transport 头的完整形态

最长的一种写法,五段用分号隔开:

Transport: RTP/AVP;unicast;client_port=8000-8001;server_port=9000-9001;ssrc=1A2B3C4D
段 意思
RTP/AVP 用 RTP 传,负载类型按 AVP 约定
unicast 单播,一对一
client_port 客户端收 RTP/RTCP 的端口对
server_port 服务端发 RTP/RTCP 的端口对
ssrc 这一路流的同步源标识

头四段几乎总在,ssrc 不一定:多数设备在 SETUP 的响应里才告诉你,请求里不用写。

二、为什么成对、为什么一奇一偶

一路流要两个通道:RTP 搬数据,RTCP 送统计。 所以端口也是两个,而且必须挨着,差 1。

端口 用途 端口号的奇偶
8000 收 RTP 偶数
8001 收 RTCP 奇数

RFC 3550 的建议是 RTP 用偶数端口,这样接收方拿到一个端口号, 按「& 1」就能判断这个包该交给 RTP 还是 RTCP。 差 1 的另一个作用是「一眼能配对」:看到 8000 和 8001,就知道它们是同一路的两个通道。

所以给一路流分配端口时,要连分两个——只分一个的话,RTCP 没地方去,丢包统计就永远收不到。

三、换成 TCP 就全不一样

UDP 穿不过防火墙时,改成 TCP 传。 这时不再有端口对,改成「通道号」。

写法 什么时候用 代价
RTP/AVP;unicast;client_port=8000-8001 局域网,最常用 要开 UDP
RTP/AVP/TCP;unicast;interleaved=0-1 穿透 NAT/防火墙 TCP 重传会加剧延迟
RTP/AVP;multicast;ttl=16 一对多分发 要交换机支持组播

interleaved=0-1 的意思是「RTP 走 0 号通道,RTCP 走 1 号通道」, 两个通道挤在同一条 TCP 连接里,靠通道号区分。

TCP 的代价要说清楚:TCP 保证不丢,代价是丢了要重传,重传期间后面的包要排队。 对实时视频来说,一个迟到五秒的包毫无价值,但 TCP 还是会把它送来,占着带宽还堵着后面的新数据。 这就是「宁可 UDP 丢包,不要 TCP 阻塞」的由来。

四、组播:一份数据多人收

multicast 是「发一份,谁想收谁加入」。 这和单播的差别在网络层,不在协议层。

单播 组播
发几份 每个接收者一份 只发一份
交换机负担 按人数翻倍 一份
需要什么 普通网络 交换机支持 IGMP
典型场景 点开一个画面 大屏轮巡

组播的 ttl 参数是「能过几跳路由器」,16 是个保守值。 组播跨网段几乎一定失败,因为大部分企业网不配组播路由。

所以组播在监控里的实际用法是「同一个交换机下的多客户端」, 跨网段还是老老实实单播或者走平台转发。

五、动手 Demo:Transport 头里那对端口,必须一奇一偶

这段把一条完整的 Transport 头逐段拆开,再算端口对的关系。 顺便验证「偶数给 RTP、奇数给 RTCP」这条约定。

th = u"RTP/AVP;unicast;client_port=8000-8001;" \
     u"server_port=9000-9001;ssrc=1A2B3C4D"
parts = th.split(u";")
for p in parts[2:]:
    k, v = p.split(u"=", 1)
    T([k, v, why], [0, 0, 0])

# 端口对:偶数给 RTP,奇数给 RTCP
for port in (8000, 8001, 9000, 9001):
    print(port, port & 1)      # 0 表示 RTP,1 表示 RTCP

实跑输出:

--------------------------------------------------------------------
Transport 头里那对端口,必须一奇一偶
--------------------------------------------------------------------
**SETUP 的 Transport 头决定「数据用什么搬、搬到哪个端口」。**
  最常用的一种:`RTP/AVP;unicast;client_port=8000-8001`

  逐段拆:

  段             值            意思
  -----------------------------------------------------------------
  传输协议       RTP/AVP       RTP over UDP,负载按 AVP 约定
  单播/组播     unicast       unicast 一对一,multicast 一对多
  客户端端口     8000-8001     偶数收 RTP,奇数收 RTCP
  服务端端口     9000-9001     同理,服务端这一对
  同步源         1A2B3C4D      标记这一路流的身份

**为什么必须成对、必须一奇一偶:**
  RTP 用 8000,RTCP 用 8001。**差 1,是为了让同一个会话的两条流能一眼配对。**
  RFC 3550 明确建议 RTP 用偶数端口:奇数留给 RTCP,偶数留给 RTP,
  这样实现方按「端口 & 1」就能判断这条包该交给谁。

**换成 TCP 传,Transport 头就不一样了:**
  写法                                    意思
  -------------------------------------------------------------------------
  RTP/AVP/TCP;unicast;interleaved=0-1     RTP 走 0 通道,RTCP 走 1 通道
  RTP/AVP;multicast;ttl=16                组播,16 跳的生存期

  `interleaved` 是「交织通道号」——TCP 传的时候,RTP 和 RTCP 挤在同一条连接上,靠通道号区分,
  所以不再需要端口对。
  **穿透 NAT 时改 TCP 就是这个原因**:一条 TCP 出去,防火墙好放行得多。

port & 1 这个判断就是全部秘密:偶数是 0,奇数是 1。 接收方拿到一个 UDP 包,看它落在哪个端口,& 1 一下就知道该交给 RTP 还是 RTCP 处理。 这个设计在 1996 年就定了,简单到不需要任何表。

回到那条完整头,最后一段 ssrc 值得看一眼:1A2B3C4D。 它是这一路流的身份,RTCP 的统计报告靠它对应到具体哪一路。 多路流的时候,没有 ssrc 就分不清哪个报告说的是哪一路。

实战里最常改的就是 unicast 那一段:内网不用动,跨网段或过防火墙就换成 TCP 那种写法。 记住改的是两处:协议部分加 /TCP,端口对换成 interleaved。 只改一处是最常见的错——比如加了 /TCP 却还留着 client_port,设备会直接回 461。

30 秒实验

  1. 抓一次 SETUP,把 Transport 头抄下来,标出哪段是端口、哪段是 ssrc。
  2. 把 unicast 改成 multicast 试一次,看设备回什么(很多直接 461)。
  3. 改成 RTP/AVP/TCP;interleaved=0-1,看原来占的 UDP 端口是不是不再需要了。

常见误区

  1. 以为端口是服务端分配的。 客户端在 SETUP 里指定 client_port, 服务端照着发——这样 NAT 后面的客户端才收得到。
  2. 以为一路流只要一个端口。 要两个:RTP 一个、RTCP 一个,差 1。只分一个就收不到丢包统计。
  3. 以为 TCP 传更好。 TCP 保证不丢,但重传会堵住后面的新数据,对实时视频反而更糟。 只在穿不过防火墙时才用。
自测三问(点开对答案) 1. 为什么 RTP 用偶数端口、RTCP 用奇数? —— RFC 3550 的建议。 这样接收方按「端口 & 1」就能判断包该交给谁,不需要维护映射表,也方便一眼配对。 2. 改成 TCP 传时,Transport 头要改哪两处? —— 协议部分加 /TCP,端口对 client_port 换成 interleaved 通道号。只改一处会被回 461。 3. 组播为什么在监控里很少跨网段用? —— 需要沿途路由器都支持组播路由,多数企业网没配。实际只在同一交换机下多客户端时用。

下一节讲通用头:CSeq 怎么配对、Date 为什么必须写 GMT。

相关笔记