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

一个 RTSP 的 URL,可以代表一路流,也可以代表一整组流。 区别只在末尾有没有那个斜杠——这是 RTSP 里最容易看漏的一个字符。

📝 本节和前面几节的关系

从这一节开始拆 RTSP 的九个重要概念,第一个是「集合控制」。 它回答的是「我这条 PLAY 到底控了几路」——搞错这一个字符, 就会出「只想播视频结果音频也来了」或者「什么都播不了」。

一、集合是什么意思

一条 RTSP 会话里可以有多路媒体:一路视频、一路音频,有时还有一路元数据。 这些媒体合在一起叫一个「呈现」(presentation),而那个能把它们当作整体来控制的 URL, 就叫集合控制 URL。

URL 写法 控制范围 典型用途
…/101/ 整组(视频+音频) PLAY 全播
…/101/trackID=0 只有第 0 路 单独控视频
…/101/trackID=1 只有第 1 路 单独控音频
* 当前会话所有流 TEARDOWN 全拆

末尾那个斜杠不是装饰:…/101 和 …/101/ 在不同实现里含义不同,有的设备把它当两个不同资源, 有的当同一个。 写代码时最稳的做法是照抄 DESCRIBE 响应里 Content-Base 给的那个形式,不要自己拼。

二、聚合与分离的两种玩法

同一个集合,可以整体控,也可以拆开控。

玩法 做法 结果
整体 SETUP 两路,PLAY 一次集合 URL 音视频同步播
只要视频 只 SETUP trackID=0,再 PLAY 集合 URL 只有画面
分头控 各自 PLAY 各自的 trackID 可不同步,少用

第二种是监控里的常规操作:多画面轮巡只要画面、不要声音,省下的音频带宽能多带一路视频。 注意它仍然是「PLAY 集合 URL」——因为你只 SETUP 了一路,服务端就只发这一路。

第三种很少用,因为分头 PLAY 就丢掉了「同步」这件事:两路各走各的时钟,画面对不上嘴。

三、Content-Base:相对 URL 的基准

SDP 里的控制地址常常只写一半:a=control:trackID=0。 这一半要拼在谁后面?答案在响应的 Content-Base 头里。

拼法就一句:Content-Base 加 a=control 的值。

Content-Base a=control 拼出来
rtsp://h/101/ trackID=0 rtsp://h/101/trackID=0
rtsp://h/101 trackID=0 rtsp://h/trackID=0

第二行就是那个坑:Content-Base 少了末尾斜杠,按 URL 规则就把最后一段当文件名替换掉, 拼出个不存在的地址。 所以服务端写 Content-Base 一定要带末尾斜杠;客户端则要按标准规则拼,不能简单做字符串相加。

四、为什么要有集合这个概念

因为「一路流」和「一次观看」不是一回事。 看一个镜头,你要的是一段音视频合起来的东西,不是两条独立的流。

集合这个概念解决三件事: 一次 PLAY 就同时启动音视频,天然对齐; 一次 TEARDOWN 就全拆,不会漏掉一路占着端口; 拖进度条时两路一起跳,不会画面跳了声音没跳。

第三条是实战里最要紧的:只对视频发 PLAY 带 Range,音频不跟着跳,就会出现「画面到十秒、 声音还在零秒」。 正确做法是对集合 URL 发带 Range 的 PLAY,让服务端把两路一起挪。

五、动手 Demo:一个 URL 管几路流,就看末尾有没有斜杠

这段把四种写法的控制范围列出来,再用一个 Content-Base 真拼一次。 重点是最后那个拼接:抄错一个斜杠,地址就不存在了。

base = u"rtsp://192.168.1.64/Streaming/Channels/101"
# 四种写法的控制范围
T([base + u"/", u"整组(视频+音频)", u"两路一起播"], [0, 0, 0])
T([base + u"/trackID=0", u"只有第 0 路", u"只播这一路"], [0, 0, 0])
T([u"*", u"当前会话的所有流", u"全停"], [0, 0, 0])

# Content-Base 拼相对控制地址
cb = u"rtsp://192.168.1.64/Streaming/Channels/101/"
print(cb + u"trackID=0")     # 对的
print(cb.rstrip(u"/") + u"trackID=0")   # 错的:少一个斜杠

实跑输出:

--------------------------------------------------------------------
一个 URL 管几路流,就看末尾有没有斜杠
--------------------------------------------------------------------
**RTSP 的「集合控制」说的是:一个 URL 可以代表一整组媒体流。**
  末尾带斜杠,表示「这是一组」;不带斜杠,表示「这是其中某一路」。

  URL                                                      控制的是               PLAY 之后
  ----------------------------------------------------------------------------------------------
  rtsp://192.168.1.64/Streaming/Channels/101/              整组(视频+音频)     两路一起播
  rtsp://192.168.1.64/Streaming/Channels/101/trackID=0     只有第 0 路            只播这一路
  rtsp://192.168.1.64/Streaming/Channels/101/trackID=1     只有第 1 路            只播这一路
  *                                                        当前会话的所有流       全停

  最后那个 `*` 是通配——`TEARDOWN *` 就是「这个会话的所有流全拆了」,比一条条拆省事。

**相对 URL 是怎么回事:**
  服务端在 DESCRIBE 的响应里给一个 `Content-Base`,之后 SDP 里的控制地址就可以只写相对部分。

  Content-Base: rtsp://192.168.1.64/Streaming/Channels/101/
  SDP 里写:a=control:trackID=0
  拼出来:rtsp://192.168.1.64/Streaming/Channels/101/trackID=0

  **这一步是很多对接踩坑的地方**:客户端要是没处理 `Content-Base`,
  就会把 `trackID=0` 当成相对当前 URL 的路径,拼出个不存在的地址。
  **有的服务端干脆直接给绝对 URL**,就是为了躲开这个坑。

最后两行的对比是本节的重点:cb + trackID=0 得到 …/101/trackID=0,是对的; 把末尾斜杠去掉再简单拼接,得到的地址里 101 被吃掉了。 正确做法不是字符串相加,是按 URL 规则做「相对解析」——Python 里就是 urljoin, 它会按标准处理最后的路径段。

* 那一行值得单独说:它不是个真实地址,是个通配符,只在 TEARDOWN *、PLAY * 这类地方用。 TEARDOWN * 是最省事的一次性收摊——一条报文拆掉所有流,不用记住自己 SETUP 了几路。

实战里最容易出问题的是「只 SETUP 一路却 PLAY 了 trackID」:那一路会播,但另外一路没 SETUP 过, 服务端要么忽略要么报 455。 记法:SETUP 决定「有哪几条腿」,PLAY 决定「一起跑还是单跑」。

30 秒实验

  1. 用抓包看一次播放的 DESCRIBE 响应,把 Content-Base 抄下来。
  2. 用 urljoin 把 Content-Base 和 a=control 的值拼一次,再手工字符串相加拼一次,比对结果。
  3. 试着只 SETUP 视频那一路,然后 PLAY 集合 URL,看还能不能出画面。

常见误区

  1. 以为末尾斜杠可有可无。 在 URL 语义里它是「这是目录」的标记, 少了它相对解析会替换掉最后一段。
  2. 以为 PLAY 集合 URL 就必须 SETUP 过所有路。 只 SETUP 一路也能 PLAY, 服务端只发你 SETUP 过的那一路。
  3. 以为拖进度条只要给视频发 Range。 音频不跟着跳就会音画不同步,Range 必须发在集合 URL 上。
自测三问(点开对答案) 1. `…/101` 和 `…/101/` 有什么区别? —— 末尾斜杠表示「这是一个集合」,相对解析时作为目录处理; 不带斜杠会被当成最后一段文件名,拼接时被替换掉。 2. 只想要画面不要声音,该怎么 SETUP 和 PLAY? —— 只 SETUP trackID=0(视频那一路),然后对集合 URL 发 PLAY。服务端只会发你 SETUP 过的那一路。 3. 拖进度条时 Range 该发给谁? —— 发给集合 URL,让服务端把音视频一起挪。只发给视频那一路会造成音画不同步。

下一节讲「实体」:RTSP 报文里那块可选的消息体,以及为什么长度总有人数错。

相关笔记