🎬 第二章 RTSP流媒体协议
📄 一句话概括

RTSP 是遥控器,不是送货车——它只发命令,画面走另一条通道。 一条 OPTIONS 请求就能问清服务器会哪八招,客户端不必硬编码。

📝 本节和前面几节的关系

第一章讲了「直播是一条链路上挑一段深挖」,RTSP 挖的就是「控制」那一段。 它是所有协议里唯一带暂停、带回放、带拖动的,因为它是为监控发明的。

一、RTSP 是遥控器,不是送货车

一句话说清 RTSP 的定位:它只负责「让画面开始、暂停、停下」,自己不搬运画面。

一条会话里其实跑着三个协议: ① RTSP:控制。文本协议,跑在 TCP 上,默认端口 554。 ② RTP:搬运。二进制,通常跑在 UDP 上,端口在会话里临时协商(第 3 章)。 ③ RTCP:反馈。和 RTP 成对出现,报丢包和抖动(第 3 章)。

正因为分工这么清楚,RTSP 的报文都很小——几十到几百字节,一条请求往往还没一个 TCP 握手包大。

这和 HLS、HTTP-FLV 是根本区别:那两个协议是「请求一次,换回一大段数据」,RTSP 是「下个命令,数据从别的门进来」。 所以拿浏览器直接打开一个 rtsp:// 地址是播不了的——浏览器只会 HTTP,而 RTSP 根本不是 HTTP。

二、OPTIONS:一条请求问清全部本事

OPTIONS 是所有方法里最「便宜」的一个:它什么都不改,只是问一句「你会什么」。

请求只有两行: ① 第一行:OPTIONS rtsp://127.0.0.1/live/test RTSP/1.0,方法、URL、版本。 ② 第二行:CSeq: 1,序号,响应要原样带回来。 ③ 一个空行:表示头域到此结束。

响应里关键是 Public 头,它把服务器支持的方法列成一串: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER。

从这串里能立刻判断出这台设备能干什么: 有 PLAY 没有 RECORD → 只能看,不能推。 有 RECORD → 这是一台可以推流的设备。 没有 PAUSE → 便宜的网络摄像头,通常只实现最小的四步。

这个思路和 HTTP 的 OPTIONS 完全一样:先探测能力,再决定流程。 工程上的好处是客户端不用为每个厂家写一套适配。

三、八个方法分三组记

八个方法死记很难,按「改不改状态」分三组就清楚了:

① 协商组(只问不改) OPTIONS 问能力;DESCRIBE 要一份 SDP,看清有几路流、什么编码。这两个方法调多少次都不影响会话。 ② 会话组(一建一拆) SETUP 建传输通道并分配 Session 号;TEARDOWN 拆掉它、释放端口。这两条是「端到端」的一对,中间不能凭空跳。 ③ 播放组(改传输状态) PLAY 开始发流,PAUSE 停发但保留会话,RECORD 是反向的推流。这三个可以在会话里反复来回。

剩下的 GET_PARAMETER/SET_PARAMETER 是读写参数的,实际用得最多的是「当心跳」。 因为 RTSP 会话有超时(通常 60 秒),不发东西就会被服务器拆掉,而这两个方法改动最小、最安全。

四、它在协议栈的哪一层

RTSP 是应用层协议,和 HTTP 平级,通常跑在 TCP 上。 这一点常有人问:既然追求实时,为什么不用 UDP?

因为命令不能丢。 一条 PLAY 丢了,画面就永远不开始;一条 TEARDOWN 丢了,端口就一直被占着。控制信令的可靠性比速度重要得多。

而真正的数据(RTP)默认走 UDP——丢一帧画面花一下,总比停下来等重传造成卡顿好。一个要可靠,一个要快,这正是把控制与数据分开设计的理由。

实际部署里也允许 RTP over TCP(Transport 头里写 RTP/AVP/TCP;interleaved=0-1)。穿防火墙时常用这招:反正只有 554 一个端口对外开放。

五、动手 Demo:OPTIONS 一次问清服务器会哪几招

写一个只认 OPTIONS 的服务器,再发一条真实的 OPTIONS 请求过去,把响应原样打出来。 看 Public 头是怎么把八个方法一次交代清楚的。

import socket, threading

PUB = ("OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, "
       "PAUSE, GET_PARAMETER, SET_PARAMETER")

def serve(sock):
    c, _ = sock.accept()
    req = c.recv(8192).decode()
    cseq = "1"
    for l in req.split("\r\n")[1:]:
        if l.lower().startswith("cseq:"):
            cseq = l.split(":", 1)[1].strip()
    resp = ("RTSP/1.0 200 OK\r\nCSeq: %s\r\n"
            "Public: %s\r\n\r\n" % (cseq, PUB))
    c.sendall(resp.encode())
    c.close()

srv = socket.socket()
srv.bind(("127.0.0.1", 0))
srv.listen(4)
port = srv.getsockname()[1]
threading.Thread(target=serve, args=(srv,), daemon=True).start()

req = "OPTIONS rtsp://127.0.0.1/live/test RTSP/1.0\r\nCSeq: 1\r\n\r\n"

s = socket.create_connection(("127.0.0.1", port), 3.0)
s.sendall(req.encode())
print(s.recv(8192).decode())
s.close()

实跑输出:

--------------------------------------------------------------------
RTSP 支持——OPTIONS 一次问清服务器会哪几招
--------------------------------------------------------------------
① 请求长什么样

  | OPTIONS rtsp://127.0.0.1/live/test RTSP/1.0
  | CSeq: 1

② 服务器回什么

  | RTSP/1.0 200 OK
  | CSeq: 1
  | Public: OPTIONS, DESCRIBE, SETUP, TEARDOWN, PLAY, PAUSE, GET_PARAMETER, SET_PARAMETER

  序号     方法              干什么
  -------------------------------------------------------
  1        OPTIONS           问「你支持哪些方法」
  2        DESCRIBE          要媒体描述(SDP)
  3        SETUP             建传输通道,定端口
  4        TEARDOWN          拆掉会话
  5        PLAY              开始发流
  6        PAUSE             暂停
  7        GET_PARAMETER     读参数(也常被当心跳用)
  8        SET_PARAMETER     写参数

  **一共 8 个方法,前 6 个是 RTSP 的核心,后 2 个是参数读写。**
  **注意它是「先问再答」的——客户端不必硬编码,服务器支持什么自己会说。**
  **这一点和 HTTP 的 OPTIONS 是同一个思路。**

八个方法在一行 Public 头里全交代了,客户端不用猜、不用试,读一遍就知道该怎么走。 注意 CSeq: 1 被原样带回来了——这是配对用的,下一条请求就该是 CSeq: 2。 服务器其实什么都没做,只回了一个能力清单,但这就是 RTSP 会话的起点。

30 秒实验

  1. 把请求里的方法换成 DESCRIBE,服务器会回什么?它还是会回 200 加同一串 Public——因为这个最小实现根本没分支。
  2. 把 CSeq 改成 7 再跑,看响应里的 CSeq 是不是跟着变。这就是「响应必须原样带回 CSeq」的含义。
  3. 把 PUB 里的 PAUSE 删掉再跑,想象一下客户端拿到这个清单后,界面上的暂停按钮应该怎么处理。

常见误区

  1. 「RTSP 是一种视频编码」——它是控制协议,一行画面都不编。编码是 H.264/H.265 的事,搬运是 RTP 的事。
  2. 「OPTIONS 是可有可无的」——不调也能跑,但你就不知道对端支不支持 PAUSE,只能靠试错。工业设备差异很大,先探测一下最省事。
  3. 「RTSP 只能看不能推」——支持 RECORD 的服务器就能收推流,监控摄像头的「录像上传」、部分 NVR 的回传都走这条路。
自测三问(点开对答案) 1. RTSP 会话里一共有几个协议在跑?各管什么? —— **三个:RTSP 管控制、RTP 搬数据、RTCP 报统计。**只有 RTSP 是文本协议。 2. 为什么 RTSP 的命令走 TCP,数据却走 UDP? —— **命令丢一条会话就废了,必须可靠;数据丢一帧只是花一下,重传反而造成卡顿。**一个要可靠,一个要快。 3. 服务器 `Public` 头里没有 PAUSE,意味着什么? —— **这台设备不支持暂停。**客户端不该把暂停按钮做成可用的,否则点了会收到 405。

相关笔记