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

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 秒实验

  1. 对你手上的设备发一次 OPTIONS,把 Public 头列出的方法抄下来。
  2. 在 Require 里故意写一个不存在的扩展名,看设备回 551 还是 501。
  3. 把 Require 去掉重发同一条请求,看是不是就通了。

常见误区

  1. 以为 Require 和 Supported 可以混用。 Require 是硬要求,对方不支持必须失败; Supported 只是告知,不影响执行。
  2. 以为 501 和 551 是一回事。 501 是方法不认识,551 是选项/参数不认识。 虽然厂商常混用,但排障方向不同。
  3. 以为不认识的 X- 头可以忽略。 有的设备靠它传关键参数,忽略不报错但行为不对,这类问题极难查。
自测三问(点开对答案) 1. Require 和 Supported 的本质区别是什么? —— Require 是硬要求,对方不支持就必须失败;Supported 只是告知能力,不影响这条请求能不能执行。 2. 551 和 501 分别针对什么? —— 551 针对选项/参数(动词认识但参数不认),501 针对方法本身(动词不认识)。 3. Require 里写了多个扩展,只有一个不支持,会怎样? —— 整条请求作废,回 551。所以只把真正必需的东西写进 Require。

下一节讲操作模式:为什么 RTSP 明确不做流水线。

相关笔记