SIP 谈「和谁通」,SDP 谈「怎么通」——两者是信封与信纸的关系。
国标的 SDP 写 PS/90000 而不是 H264/90000,
这一个字母的差别决定了 FFmpeg 用什么 demuxer。
09.01.05 的 INVITE 里带了正文,那就是 SDP。 01.04.03 讲 RTSP 的 DESCRIBE 时也见过 SDP,同一个格式,只是承载它的协议不同。
一、信封与信纸
| 层 | 谁 | 管什么 |
|---|---|---|
| SIP | 头部 | 会话:谁、到哪、多久 |
| SDP | 正文 | 媒体:编码、地址、端口 |
所以「SIP 通了但没画面」的一半原因在 SDP:信令成功了,但 SDP 里写的地址端口不对,
媒体就发不到地方。
两者的耦合点只有一个头:Content-Type: application/sdp。
这个头声明了正文是什么格式,设备靠它决定要不要解析正文。
所以「正文是 SDP 但忘了写 Content-Type」会让设备忽略正文,
表现是「INVITE 成功了但对方说没收到媒体参数」。
二、十一行,八个字母
| 行首 | 含义 | 必需 |
|---|---|---|
v= |
版本 | 必需 |
o= |
发起者与会话 ID | 必需 |
s= |
会话名 | 必需 |
c= |
连接地址 | 必需 |
t= |
时间 | 必需 |
m= |
媒体行 | 必需 |
a= |
属性 | 可多条 |
y= |
国标 SSRC | 国标特有 |
这八行里前六行是 SDP 规范的要求,a= 可以任意多行(Demo 里就有四条)。
第八行那个 y= 不在 SDP 标准里,
它是 GB/T 28181 自己加的扩展属性——标准 SDP 解析器看到不认识的 y= 会忽略,所以不冲突。
这就是「文本协议的扩展性」的实际样子:加一行就行,不需要改版本号。
三、m= 行:一行定三件事
SDP 里最重要的就是这一行:
m=video 10000 RTP/AVP 96 98 97
五个部分,各管一件事:
| 部分 | 本例 | 含义 | |---|---|---|---| | 媒体类型 | video | video/audio | | 端口 | 10000 | 媒体发到哪 | | 传输 | RTP/AVP | 固定写法 | | payload | 96 98 97 | 对应 rtpmap |
第二行那个端口是最容易出错的地方:它和 RTP 的偶数端口规则是同一件事(01.04.05 讲过),
下一路就是 10002(10001 留给 RTCP)。
第四行那三个编号必须各有一条 a=rtpmap 对应,少了就变成「有编号没编码」,
设备会当成未知格式。
四、PS/90000 与 H264/90000
这是国标与 ONVIF 在 SDP 上最实质的差别:
| SDP 里写 | 流是什么 | FFmpeg 怎么读 |
|---|---|---|
H264/90000 |
裸 H.264 | h264 demuxer |
PS/90000 |
PS 封装 | mpeg demuxer |
第一行是 ONVIF 设备的常态:RTP 里直接装 H.264 NAL 单元,FFmpeg 拿到就能解。
第二行是国标的常态:RTP 里装的是 PS 流(Program Stream),要先拆 PS 头才能拿到 H.264。
所以「国标流接进 FFmpeg」不是换个 URL 就行,它需要不同的 demuxer,
而在 FFmpeg 里这通常意味着「先落一个 .ps 再读」或者用 mpegts 之外的路径。
为什么国标要用 PS?
因为 PS 里能同时装视频、音频和私有数据(比如智能分析结果),而裸 H.264 只能装视频。
五、y= 行:国标的 SSRC
这一行是可选的,但很多平台认它:
| 位 | 本例 | 含义 |
|---|---|---|
| 第 1 位 | 1 | 实时/历史标志 |
| 第 2~6 位 | 00000 | 取自域编号后五位 |
| 第 7~10 位 | 0001 | 序号 |
第一行那个「实时/历史」很实用:1 表示实时流、0 表示历史回放流,
平台靠它区分「看直播」和「看录像」。
第三行那个序号是同一路流的不同会话的编号,重新点播一次就加一。
所以「同一个摄像机、同一次点播」这个组合,SSRC 是唯一的——
如果两次点播拿到同一个 SSRC,说明平台复用了会话,这时候关掉一个会影响另一个。
六、动手 Demo:GB/T 28181 的 SDP 长什么样
Demo 生成一份国标风格的 SDP,量字节、按行首字母统计,再把 m= 与三条 a=rtpmap 拆出来。
body = S.sdp()
print(body)
n, cnt, mline, maps = S.parse_sdp(body)
print("行数", n, "字节", len(body.encode()))
print("m 行", len(mline), mline)
for x in maps:
print(x)
实跑输出:
--------------------------------------------------------------------
GB/T 28181 的 SDP 长什么样
--------------------------------------------------------------------
**SDP 是纯文本,一行一个字段,行首一个字母定类型。**
v=0
o=34020000001320000002 0 0 IN IP4 192.168.1.100
s=Play
c=IN IP4 192.168.1.100
t=0 0
m=video 10000 RTP/AVP 96 98 97
a=recvonly
a=rtpmap:96 PS/90000
a=rtpmap:98 H264/90000
a=rtpmap:97 MPEG4/90000
y=0100000001
**一共 11 行、222 字节**,按行首字母统计:
行首 行数
--------------
a 4
c 1
m 1
o 1
s 1
t 1
v 1
y 1
**`m=` 那一行是 30 字符**:
m=video 10000 RTP/AVP 96 98 97
**它决定了三件事**:媒体类型(video)、端口(10000)、**后面跟的三个 payload 号(96/98/97)。**
**三个 payload 号对应三条 `a=rtpmap`**:
payload 编码 国标特有
-------------------------------------
96 PS/90000 PS
98 H264/90000 —
97 MPEG4/90000 —
**第三列那个 PS 就是 GB/T 28181 的标志**:
**国标不用裸 H.264 流,用 PS 封装**——**因为 PS 里能同时装视频、音频和私有数据。**
**所以 ONVIF 的摄像机给的是 `H264/90000`,而国标平台要的是 `PS/90000`**
**这两个不是一回事**:**前者是裸流、后者是封装流,FFmpeg 里对应的 demuxer 也不同。**
**最后一行 `y=0100000001` 是 SSRC**,**国标用它给每一路流编个号,一共十位十进制。**
**第 1 位是历史/实时标志(1 表示实时)、第 2~6 位取自域编号的后五位、第 7~10 位是序号。**
**这一行是可选的**,**但有些平台不认没有 `y=` 的 SDP**,**所以做国标对接时别省。**
输出第二部分那个按字母的统计很直观:a 四行、其余各一行,一共 11 行。
a 最多是正常的:属性行本来就是用来堆细节的,
国标设备上 a= 可能有七八条(还有 a=setup、a=connection 等)。
m= 那是 30 字符,
而三条 a=rtpmap 各自 20 字符左右——这四行加起来占了整个 SDP 的一半字节。
注意 m= 里的三个 payload 号顺序是 96、98、97,
不是升序——这个顺序表示优先级,第一个是首选。
所以设备会先试 PS,不行再试 H.264,最后 MPEG4。
a=recvonly 那一行表示「我只收不发」,这是国标点播时的常态:
平台是收的一方,摄像机是发的一方。
如果写成 sendonly 就反了。
30 秒实验
- 把
m=里的端口改成 10001(奇数),看设备会不会拒。 - 把
PS/90000改成H264/90000,用 FFmpeg 读同一个流,看会不会报错。 - 把
y=那一行删掉,看平台还要不要。
常见误区
- 以为 SIP 通了就有画面。 SDP 里的地址端口错了照样没画面,而信令是成功的。
- 把
PS/90000当成 H.264。 它是 PS 封装流,需要不同的 demuxer,直接按 h264 解会失败。 - 以为
m=里的 payload 号是升序排列。 顺序表示优先级,第一个是首选, 写反了设备先从最差的编码试。
自测三问(点开对答案)
1. SIP 与 SDP 的分工怎么一句话说? —— **SIP 谈「和谁通」,SDP 谈「怎么通」**。 前者在头部,后者在正文,靠 `Content-Type: application/sdp` 挂接。 2. 国标为什么用 PS 而不是裸 H.264? —— **PS 里能同时装视频、音频和私有数据**,裸 H.264 只能装视频。代价是 FFmpeg 端要换 demuxer。 3. `y=` 行第一位表示什么? —— **实时/历史标志**:1 是实时流、0 是历史回放流。平台靠它区分直播与录像。下一节把注册到挂断串成时间线——看每一步值多少毫秒。
- ☐ (本节完,下一节 09.01.08 SIP工作流程)