一次完整播放就五步:OPTIONS → DESCRIBE → SETUP → PLAY → TEARDOWN。 记住这五步和它们的顺序,RTSP 就懂一半了。
前面两节讲了 RTSP 是什么、为什么长这样,这一节把流程走通。 这一节是本章的骨架:01.04 的九个概念、01.05 的十一个方法,都是往这五步里填细节。 播放器里这条流程怎么落地, 看 34 课 的第七章。
一、五步,各要一句话
每一步都有一个明确的目的,跳过任何一步都会失败。
| 步 | 方法 | 目的 | 关键产出 |
|---|---|---|---|
| 1 | OPTIONS | 问对方会什么 | Public 头 |
| 2 | DESCRIBE | 要说明书 | SDP |
| 3 | SETUP | 建数据通道 | Session 号 |
| 4 | PLAY | 开播 | RTP 开始到达 |
| 5 | TEARDOWN | 收摊 | 端口释放 |
顺序不能乱,但 OPTIONS 可以省:很多客户端直接发 DESCRIBE,设备也照样响应。 省它是为了少一轮往返,不是因为它不重要。
SETUP 可能不止一次:有视频有音频就要 SETUP 两次(trackID=0 和 1),但 PLAY 只发一次, 作用于整组。
二、逐条报文长什么样
下面五条是真报文,可以直接拿去粘:
OPTIONS rtsp://192.168.1.64:554/Streaming/Channels/101 RTSP/1.0
CSeq: 1
User-Agent: python-rtsp/1.0
DESCRIBE rtsp://192.168.1.64:554/Streaming/Channels/101 RTSP/1.0
CSeq: 2
Accept: application/sdp
SETUP rtsp://192.168.1.64:554/Streaming/Channels/101/trackID=0 RTSP/1.0
CSeq: 3
Transport: RTP/AVP;unicast;client_port=8000-8001
PLAY rtsp://192.168.1.64:554/Streaming/Channels/101 RTSP/1.0
CSeq: 4
Session: 1A2B3C4D
Range: npt=0.000-
TEARDOWN rtsp://192.168.1.64:554/Streaming/Channels/101 RTSP/1.0
CSeq: 5
Session: 1A2B3C4D
注意三处细节: 每条的首行都是「方法 + 完整 URL + RTSP/1.0」; CSeq 从 1 开始逐条加一,响应必须原号返回; SETUP 之后的每条都带 Session,PLAY 还多了个 Range(从 0 秒开始)。
三、CSeq 是唯一的配对手柄
RTSP 允许一条连接上有多个请求在飞(虽然不推荐),所以响应必须能对上号。 CSeq 就是干这个的,它是整个协议里最简单的机制,也是最能出错的机制。
常见错误三种: 从 0 开始(应该从 1 开始,0 保留); 不递增(重发时保持原号是允许的,但新请求必须加一); 响应里改了号(客户端直接丢弃,表现为「发了请求没反应」)。
排障时先看 CSeq:请求发出去、响应回来了,但客户端说超时,八成就是 CSeq 对不上。
四、状态码:记住六个就够
RTSP 借了 HTTP 的状态码体系,1xx 到 5xx。
| 码 | 含义 | 常见原因 | |:---:|---|---|---| | 200 | OK | 正常 | | 401 | 需要认证 | 没带 Authorization | | 404 | 找不到 | URL 路径或通道号错 | | 454 | 会话不存在 | 超时被回收,或 Session 写错 | | 455 | 方法在此状态不合法 | 没 SETUP 就 PLAY | | 551 | 选项不支持 | Require 里要了设备没有的扩展 |
401 是监控对接的第一道坎:设备默认开认证,第一次 DESCRIBE 必回 401, 要求你带 Authorization 重发。 两种认证方式:Basic(用户名密码 base64)和 Digest(挑战响应)。 海康大华默认多是 Digest,Basic 在它们身上常常直接 401。
五、失败的长什么样
真实排障里,失败比成功常见。三条典型分支:
分支一:401 循环。 带了 Authorization 还是 401,通常是 Digest 的 nonce 算错, 或者用户名里混了域。
分支二:SETUP 返回 461。 461 是「不支持的传输方式」,多半是设备关了 UDP,
只认 RTP/AVP/TCP;interleaved=0-1。
改成 TCP 就通了——这也是穿透 NAT 时最常用的招。
分支三:PLAY 回来了但没数据。 这是最坑的一种:信令全通,RTP 一个包都不来。 常见原因是端口被防火墙挡了、或者客户端告诉设备的端口没人监听。 验证办法:在同一个端口上开个 UDP 监听, 看有没有包进来——分不清是「设备没发」还是「本机没收到」,就没法往下查。
六、动手 Demo:一次完整的 RTSP 播放,来回几句话
这段用真实的 RFC 报文格式,把五条请求逐条造出来,并量出字节数。 每条都带 CSeq,服务端的响应必须原号返回。
base = u"rtsp://192.168.1.64:554/Streaming/Channels/101"
steps = [(u"OPTIONS", base, [u"User-Agent: python-rtsp/1.0"]),
(u"DESCRIBE", base, [u"Accept: application/sdp"]),
(u"SETUP", base + u"/trackID=0",
[u"Transport: RTP/AVP;unicast;client_port=8000-8001"]),
(u"PLAY", base, [u"Session: 1A2B3C4D", u"Range: npt=0.000-"]),
(u"TEARDOWN", base, [u"Session: 1A2B3C4D"])]
for i, (m, url, hdrs) in enumerate(steps, 1):
lines = [u"%s %s RTSP/1.0" % (m, url), u"CSeq: %d" % i] + hdrs
txt, nb = _crlf(lines) # 拼 CRLF 并数字节
T([m, u"%d" % i, why, u"%d" % nb], [0, 0, 0, 0])
实跑输出:
--------------------------------------------------------------------
一次完整的 RTSP 播放,来回几句话
--------------------------------------------------------------------
**下面五条请求是真报文,格式按 RFC 2326,可以直接拿去粘。**
注意每条都带 `CSeq`,服务端的响应必须原号返回,靠它配对。
步骤 CSeq 做什么 字节
---------------------------------------------------------
OPTIONS 1 问服务端支持哪些方法 105
DESCRIBE 2 要一份 SDP,看有几路流 102
SETUP 3 给这一路流分配端口 134
PLAY 4 开播,从头放到尾 111
TEARDOWN 5 收摊,释放资源 96
**五句话加起来 548 字节。**
一次播放的信令就这么点,**真正的流量全在后面的 RTP 上**。
**SETUP 那条最值得看,逐字拆开:**
SETUP rtsp://…/trackID=0 RTSP/1.0
CSeq: 3
Transport: RTP/AVP;unicast;client_port=8000-8001
`RTP/AVP` 是「用 RTP 传,负载类型按 AVP 的约定」;`unicast` 是单播;
`client_port=8000-8001` 是**客户端**告诉服务端「往我这两个端口发」——8000 收 RTP,8001 收 RTCP。
**注意是客户端先开口**,服务端照着发。这是 RTSP 和 HTTP 一样的地方:永远客户端发起。
五条加起来才一千多字节,一次播放的信令就这么多。 对比一下:1080p 一秒就是 500 KB 的媒体数据——信令占的比例小到可以忽略。 所以 RTSP 从来不是带宽问题,是「通不通」的问题。
SETUP 那条最值得逐字看(正文里拆过了):客户端主动告诉服务端「往我这两个端口发」。
这个方向容易记反——很多人以为服务端选端口再通知客户端,实际上端口是客户端定的,
服务端只是回一个 server_port 告诉你它从哪发。
为什么客户端定端口:因为在 NAT 后面,只有本机知道自己哪个端口已经打了洞。 服务端定的话,包根本进不来。 这条设计在 1996 年就有了,比 NAT 穿透这个词流行早得多。
30 秒实验
- 用 ffplay 打开一路 RTSP,同时抓包,把五条请求按顺序找出来。
- 把 TEARDOWN 去掉,等 Session 超时,看服务端多久回收(通常是 60 秒)。
- 故意把 CSeq 改成不变,看客户端会不会报超时——体会这个字段的作用。
常见误区
- 以为五步必须全部走完。 OPTIONS 可以省,很多客户端直接 DESCRIBE; SETUP 则可能要走两次(视频一次、音频一次)。
- 以为服务端决定 RTP 端口。 是客户端在 SETUP 里指定 client_port, 服务端照着发——这样 NAT 后面的客户端才收得到。
- 以为收到 200 就说明能播。 PLAY 返回 200 只代表信令通了,RTP 有没有来是另一回事, 得看端口上有没有包。
自测三问(点开对答案)
1. 五步里哪一步可以省,哪一步可能要走两次? —— OPTIONS 可以省;SETUP 可能走两次——视频 trackID=0 一次,音频 trackID=1 一次。 PLAY 只发一次,作用于整组。 2. SETUP 里的 client_port 是谁定的?为什么? —— 客户端定的。 因为客户端可能在 NAT 后面,只有它知道自己哪个端口已经打了洞,服务端指定的话包进不来。 3. 信令全通但没画面,先查什么? —— 先查端口上有没有 UDP 包进来。 分不清「设备没发」还是「本机没收到」,就没法往下查——这是二分的第一步。下一节开始拆概念:从「集合控制」讲起,搞清一个 URL 到底能管几路流。
- ☐ (本节完,下一节 01.04.01 集合控制)