RTSP 允许厂商加自己的东西,规矩是「要用的那一方先声明」。 声明用 Require,表态用 Supported,谈不拢回 551 而不是 501。
上一节讲连接,这一节讲「连接上跑的协议怎么长出新东西」。 这一节涉及的状态码很容易混:551 和 501 差一个数字,含义完全不同,而设备厂商经常混用。
一、四个头,两问两答
扩展机制就是一句话:我说我要用什么,你说你支不支持。
| 头 | 谁说 | 说什么 | |---|---|---|---| | Require | 请求方 | 我这条请求必须用到 X | | Proxy-Require | 请求方 | 中间代理也必须支持 X | | Supported | 双方 | 我支持这些 | | Unsupported | 响应方 | 这些我都不支持 |
Require 是硬的:对方不支持就不能执行,必须报错。
Supported 是软的:只是告知,不影响执行。
这个区别很重要:把要求写成 Supported,对方不支持也会照做,结果就是行为不可预期; 把软的表态写成 Require,就白白把能跑的请求搞失败了。
二、551 和 501 差在哪
两个状态码都表示「不支持」,但对象不同:
| 码 | 名字 | 对象 | 典型原因 |
|---|---|---|---|
| 501 | Not Implemented | 方法本身 | 不认识 ANNOUNCE 这个动词 |
| 551 | Option not supported | 选项/参数 | Require 里要了设备没有的扩展 |
| 405 | Method Not Allowed | 方法在此资源上不允许 | 对静态资源发 RECORD |
记法:501 是「你这句话里的动词我不认识」,551 是「动词认识,但你带的参数我不认」。
实测里这两个码经常被厂商用混:有的设备遇到不认识的 Require 回 501。 所以写客户端时要两个都当「不支持」处理,别只判断一个。
三、公开声明支持什么:Public 头
OPTIONS 的响应里有个 Public 头,列出设备支持的所有方法。
这是客户端最该先看的一个头——它决定了后面能怎么调。
Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER
看到这个列表就能定三件事: 有没有 PAUSE(没有就只能 TEARDOWN 再重连); 有没有 GET_PARAMETER(有它就能拿来做保活); 有没有 ANNOUNCE/RECORD(有它才可能往里推流)。
这比翻说明书可靠:说明书说的可能是理论支持,固件里未必开着。
四、私有扩展:X- 头
标准没规定的头,约定用 X- 开头。
但这个约定不是强制的,所以现实里五花八门。
| 头 | 常见于 | 能不能猜 | |---|---|---|---| | X-Session-Id | 部分国产平台 | 不能,得查文档 | | X-Vendor | 厂商标识 | 大概能猜 | | X-DVR-Display | 硬盘录像机显示参数 | 不能 | | 厂商名开头(不带 X-) | 海康、大华都有 | 更不能猜 |
所以对接第三方设备时有个铁律:看到不认识的头,去要文档,别猜。 猜错的后果往往是「参数被忽略」——不报错,但行为不对,特别难查。
五、动手 Demo:扩展靠 Require 谈条件,谈不拢就回 551
这段造一次真实的扩展协商:请求带 Require,响应回 551。 再看 Unsupported 头怎么把「我不支持什么」说清楚。
# 请求:我要用这个私有扩展
req = [u"SETUP rtsp://192.168.1.64/Streaming/Channels/101/trackID=0 RTSP/1.0",
u"CSeq: 3",
u"Require: com.real.retain-entity-for-setup",
u"Transport: RTP/AVP;unicast;client_port=8000-8001"]
# 响应:不支持,用 551 + Unsupported 说清楚
resp = [u"RTSP/1.0 551 Option not supported",
u"CSeq: 3",
u"Unsupported: com.real.retain-entity-for-setup"]
print(_crlf(req)[1], _crlf(resp)[1]) # 各自多少字节
实跑输出:
--------------------------------------------------------------------
扩展靠 Require 谈条件,谈不拢就回 551
--------------------------------------------------------------------
**RTSP 允许在标准之外加头和参数,规矩是:要用的那一方先声明。**
声明用 `Require`,表态支持什么用 `Supported`。
头 意思 出现在
-----------------------------------------------------------
Require 我必须用到这个扩展 请求
Proxy-Require 代理必须支持 请求
Supported 我支持这些 请求与响应
Unsupported 这些我都不支持 响应
Public 我支持这些方法 OPTIONS 的响应
一个真会出现的交换:
请求:SETUP … ; Require: com.real.retain-entity-for-setup
响应:RTSP/1.0 551 Option not supported
Unsupported: com.real.retain-entity-for-setup
**551 就是「你要求的选项我不支持」**,和 501(方法不支持)不是一回事:
501 说的是「你这个动词我不认识」,551 说的是「动词认识,但你带的参数我不认」。
**私有扩展最喜欢用 X- 开头:**
例子 常见于
--------------------------------------------
X-Session-Id 某些国产平台的会话标识
X-Vendor 厂商标识
X-DVR-Display 硬盘录像机的显示参数
**X- 不是标准,是约定**。对接第三方设备时,看到 X- 头就得去找那家的文档,猜是猜不出来的。
两条报文加起来两百多字节,一次协商就这么多。
关键在响应的两处:状态码 551 说明「选项不支持」,Unsupported 头说明「具体是哪一个不支持」。
少了 Unsupported 头,客户端只知道失败,不知道是哪个扩展的问题——要是有多个 Require,
就得逐个试。
客户端拿到 551 之后该怎么办,这是实战里的关键决策: 如果这个扩展是必需的(比如某种加密),直接失败; 如果只是优化(比如减少一次往返),去掉 Require 重发。 分不清这两类,就会把「本来能播」变成「直接报错」。
还有一条容易忽略:Require 里可以写多个扩展,用逗号分隔,这时候要全部支持才能执行。
所以别把可有可无的东西塞进去——一个谈不拢,整条请求作废。
30 秒实验
- 对你手上的设备发一次 OPTIONS,把 Public 头列出的方法抄下来。
- 在 Require 里故意写一个不存在的扩展名,看设备回 551 还是 501。
- 把 Require 去掉重发同一条请求,看是不是就通了。
常见误区
- 以为 Require 和 Supported 可以混用。 Require 是硬要求,对方不支持必须失败; Supported 只是告知,不影响执行。
- 以为 501 和 551 是一回事。 501 是方法不认识,551 是选项/参数不认识。 虽然厂商常混用,但排障方向不同。
- 以为不认识的 X- 头可以忽略。 有的设备靠它传关键参数,忽略不报错但行为不对,这类问题极难查。
自测三问(点开对答案)
1. Require 和 Supported 的本质区别是什么? —— Require 是硬要求,对方不支持就必须失败;Supported 只是告知能力,不影响这条请求能不能执行。 2. 551 和 501 分别针对什么? —— 551 针对选项/参数(动词认识但参数不认),501 针对方法本身(动词不认识)。 3. Require 里写了多个扩展,只有一个不支持,会怎样? —— 整条请求作废,回 551。所以只把真正必需的东西写进 Require。下一节讲操作模式:为什么 RTSP 明确不做流水线。
- ☐ (本节完,下一节 01.04.09 RTSP操作模式)