RTSP 说的「容器文件」不是 MP4 那种盒子结构,指的是「一次呈现包含多路媒体」。
一份 SDP 里每出现一行 m=,就是一路。两路就要 SETUP 两次。
上一节讲实体,这一节讲实体里装的东西。 「容器文件」这个词容易误会成 MP4/MKV 那种封装格式, 其实 RFC 2326 里它指的是「一组媒体流的集合」,和文件格式无关。 封装格式那件事看 26 课, 那是编码侧的盒子结构。
一、先把这个词摘清楚
同一句话在两种语境里意思完全不同:
| 语境 | 「容器」指什么 | 例子 |
|---|---|---|
| 封装格式 | 把音视频装进一个文件的盒子 | MP4、MKV、AVI |
| RTSP 规范 | 一次呈现里的多路媒体集合 | SDP 里两条 m= 行 |
RFC 2326 的定义是后者:一个 presentation 由一个或多个媒体流组成,这些流共享时间轴, 可以一起控。 所以这一节讲的是「一组」这个概念,不是讲 MP4 的 box 怎么排。
书里放在「RTSP 重要概念」下面,也是这个意思——它和集合控制、实体是同一层的概念,不是编码话题。
二、SDP 里怎么表示多路
SDP 是纯文本,一行一个字段,类型=值。
其中 m= 开头的行最特殊:它标志着「新的一路媒体开始了」。
| 行 | 意思 | 后面跟什么 |
|---|---|---|
| v=0 | 协议版本 | — |
| o= | 发起者与版本号 | — |
| s= | 会话名 | — |
| t= | 起止时间 | — |
| m=video 0 RTP/AVP 96 | 一路视频,负载类型 96 | a= 行 |
| a=rtpmap:96 H264/90000 | 96 是 H.264,时钟 90000 | — |
| m=audio 0 RTP/AVP 97 | 一路音频,负载类型 97 | a= 行 |
| a=rtpmap:97 PCMU/8000 | 97 是 G.711,时钟 8000 | — |
规律很简单:第一个 m= 之前是会话级字段,之后每个 m= 带一组自己的 a=,
一直到下一个 m= 为止。
解析器的写法就是「遇到 m= 就开一个新的媒体对象」。
三、两路就要两次 SETUP
SDP 里有两条 m=,客户端就要发两次 SETUP。
| 步 | 发几次 | 针对谁 |
|---|---|---|
| OPTIONS | 1 | 整组 |
| DESCRIBE | 1 | 整组 |
| SETUP | 2 | trackID=0 和 trackID=1 |
| PLAY | 1 | 整组 |
| TEARDOWN | 1 | 整组 |
为什么 SETUP 要两次而 PLAY 只要一次:SETUP 是「给某一路分配传输参数」,每路都要各自的端口; PLAY 是「让整组开始跑」,一次就够。
这也是最容易写错的地方:只 SETUP 了视频就 PLAY,音频不会来,而代码往往还在等音频包, 表现为「画面有了没声音」。
四、负载类型是关键线索
m= 行里那个数字(96、97)叫负载类型(payload type),它本身不说明编码格式。
真正说明格式的是紧跟其后的 a=rtpmap 行。
| 负载类型 | 含义 | 说明 |
|---|---|---|
| 0 | PCMU(G.711 μ 律) | 静态,固定 |
| 8 | PCMA(G.711 A 律) | 静态,固定 |
| 26 | JPEG | 静态,固定 |
| 96~127 | 动态 | 看 a=rtpmap 才知道 |
96 以上都是动态的——同一份 SDP 里 96 可能是 H.264,换个设备 96 又可能是 H.265。
所以别硬编码「96 就是 H.264」,一定要读 a=rtpmap。
这是国产设备对接里最常见的错:有的设备 H.265 也用 96,不读 rtpmap 就当成 H.264 去解,
解出来当然是花屏。
五、动手 Demo:容器文件就是一个 SDP 里的几个 m= 行
这段造一份带视频和音频的 SDP,逐行标注意思,再数出有几路。 重点看负载类型那两行:96 和 97 都不是固定含义的。
sdp = [(u"v=0", u"协议版本"), (u"s=IP Camera", u"会话名"),
(u"a=control:*", u"整组的控制地址"),
(u"m=video 0 RTP/AVP 96", u"第一路:视频"),
(u"a=rtpmap:96 H264/90000", u"96 就是 H.264"),
(u"m=audio 0 RTP/AVP 97", u"第二路:音频"),
(u"a=rtpmap:97 PCMU/8000", u"97 就是 G.711")]
n = sum(1 for line, _ in sdp if line.startswith(u"m="))
print(n) # 有几路媒体,就要 SETUP 几次
实跑输出:
--------------------------------------------------------------------
容器文件就是一个 SDP 里的几个 m= 行
--------------------------------------------------------------------
**RTSP 说的「容器文件」不是 MP4 那种盒子结构**,指的是「一次呈现里包含多路媒体」。
一份 SDP 里每出现一行 `m=`,就是一路媒体。
行 意思
-----------------------------------------------------------------
v=0 协议版本,永远是 0
o=- 1 1 IN IP4 192.168.1.64 会话发起者与版本号
s=IP Camera 会话名
t=0 0 起止时间,0 0 表示不限
a=control:* 整组的控制地址
m=video 0 RTP/AVP 96 第一路:视频,负载类型 96
a=rtpmap:96 H264/90000 96 就是 H.264,时钟 90000
a=control:trackID=0 这一路的控制后缀
m=audio 0 RTP/AVP 97 第二路:音频,负载类型 97
a=rtpmap:97 PCMU/8000 97 就是 G.711 μ 律,时钟 8000
a=control:trackID=1 这一路的控制后缀
**这份 SDP 有 2 行 m=,所以这一路「呈现」里有 2 路媒体。**
播放器拿到它,就得 SETUP 2 次、PLAY 一次(PLAY 整组)、再各占一对端口。
**只取视频不要音频**也常见:
URL 后面换个通道号(如 101 而不是 102),或者只在 SETUP 时对 trackID=0 发起。
**注意**:只 SETUP 一路,PLAY 时仍然用整组 URL,这时服务端只发你 SETUP 过的那一路。
数出来是 2,所以这份 SDP 对应的会话要 SETUP 两次。 如果只 SETUP 视频那一路,PLAY 之后只会收到视频包——这不是设备坏了,是你没要音频。
负载类型那两行值得再强调一次:96 和 97 都是动态类型,它们的含义完全由 a=rtpmap 决定。
同一台设备换成 H.265,可能还是 96,只是 a=rtpmap:96 H265/90000。
不读这一行就硬编码解码器,遇到 H.265 就是必然花屏。
还有一处容易漏:a=control:trackID=0 这种行每个 m= 下面各有一个,
它们是 SETUP 时的 URL 后缀。
漏了它就只能猜 trackID=0/1——大多数设备确实是这么编号的,但国标设备有时用别的形式,
猜错就是 404。
30 秒实验
- 抓一份真实的 DESCRIBE 响应,数 m= 行有几条。
- 把每条 m= 后面的 a=rtpmap 抄下来,看负载类型分别是多少、对应什么编码。
- 只 SETUP 视频那一路,PLAY 之后看抓包里有没有 RTP 音频包。
常见误区
- 以为 RTSP 的「容器文件」是 MP4 那种封装。 RFC 2326 里它指「一次呈现包含多路媒体」, 和文件格式无关。
- 以为一条 SETUP 能管所有路。 每路都要单独 SETUP,各自分配端口。
- 以为负载类型 96 就是 H.264。 96 以上都是动态的,必须读 a=rtpmap 才知道是 H.264 还是 H.265。
自测三问(点开对答案)
1. SDP 里怎么看出有几路媒体? —— 数 `m=` 开头的行。每一条 m= 就是一路,后面跟着它自己的 a= 行,直到下一个 m= 为止。 2. 两路媒体要 SETUP 几次、PLAY 几次? —— SETUP 两次(各分各的端口),PLAY 一次(作用于整组)。 3. 负载类型 96 一定是 H.264 吗? —— 不一定。96 到 127 是动态类型,含义由同一份 SDP 里的 a=rtpmap 决定。 硬编码 96=H.264 遇到 H.265 就花屏。下一节讲会话:Session 号怎么来的、为什么 60 秒就会被回收、保活该多久一次。
- ☐ (本节完,下一节 01.04.04 RTSP会话)