📹 第十章 GB/T 28181协议原理
📄 一句话概括

SDP 是国标里唯一「人能一眼读懂」的东西:一行一个字段,全部是 类型=值 的纯文本。 它只有两百来字节,却决定了「用什么编码、往哪个地址发」——写错一个字段,整路黑屏。

📝 本节和前面几节的关系

上一节讲 PS 封装,是「媒体长什么样」。这一节讲「媒体往哪走」,也就是 SDP。 它是被 SIP 报文捎带的,不单独走线。

一、一行一个字段的纯文本

SDP 的语法简单到不像协议:每行开头一个字母加等号,后面跟值;行与行之间用换行分开。 必填的有五个:v= 写版本(固定 0)、o= 写会话归属者、s= 写会话名、 t= 写起止时间(固定 0 0)、m= 写媒体类型和端口。 可选的有三个最常出现:c= 写连接地址、a= 写属性、y= 写 SSRC。 m= 那一行信息量最大:m=video 6000 RTP/AVP 96 拆开就是「媒体类型是视频、端口 6000、 传输协议 RTP/AVP、负载类型 96」。 96 这个数字要特别注意: 它是「动态负载类型」,意思是「具体是什么编码,去看 a=rtpmap 那行」。 0 到 95 是固定含义,96 以上由会话自己定义。 国标在这里规定死了一件事:a=rtpmap:96 PS/90000, 也就是「96 号负载是 PS 封装、时钟频率 90000」。

二、国标扩展的三个字段

标准 SDP 里没有 y=,这是国标加的: 它装的是 SSRC,一个 32 位整数,写成十位十进制、不足补零。 为什么需要它: 同一台设备可能同时给多个平台发流,接收方要靠 SSRC 把「这一包属于哪一路」分开。 没有 y=,多平台并发时就会串流。 第二个扩展是 f=:它留给厂商塞私有信息,标准里没有定义内容。 多数设备直接写成空值,看到 f= 后面没东西是正常的。 第三个是 s= 的取值:实时点播写 Play,历史回放写 Playback,文件下载写 Download。 这个字段看着无关紧要,其实是「这次请求要干什么」的开关。 审核 SDP 时按这个顺序看: 先看 s= 对不对,再看 m= 的端口,然后看 c= 的地址,最后看 y= 和 rtpmap。 前两个错了整路不通,后两个错了能通但会乱。

三、两份 SDP 怎么对上

点播时 SDP 出现两次,方向相反: 平台在 INVITE 里写「往我这发」,设备在 200 OK 里写「我从这发」。 所以平台那份的 c= 是平台地址、a=recvonly;设备那份的 c= 是设备地址、a=sendonly。 这一对关系是理解国标点播的钥匙:收流端口是平台挑的,发流端口是设备挑的, 两边各说各的,拼起来才是一条完整的路径。 最容易搞错的是 o= 那行:它是「会话归属者」,记录谁发起的会话,不是媒体地址。 改 o= 不影响流往哪走,改 c= 才影响。 还有一种情况会让人懵:有些平台在做级联时会把 c= 改成自己的地址,让流先到平台再转出去。 这时抓包会看到「设备发的流打到了平台」,这是设计如此,不是配错。

四、动手 Demo:一份国标 SDP 逐行拆开看

造两份 SDP:一份是平台在 INVITE 里写的,一份是设备在 200 OK 里回的。 量字节、拆字段、算 SSRC,看两边到底差在哪。

inv = ["v=0", "o=34020000002000000001 0 0 IN IP4 10.1.1.10",
       "s=Play", "c=IN IP4 10.1.1.10", "t=0 0",
       "m=video 30000 RTP/AVP 96", "a=recvonly",
       "a=rtpmap:96 PS/90000", "y=0200000001"]
ok  = [ ... 同样的九行,但 c= 是 192.168.1.64、
        m= 是 6000、a= 是 sendonly、y= 是 0100000001 ]

for name, body in (("INVITE", inv), ("200 OK", ok)):
    print(name, len(body), sum(len(x) + 2 for x in body))

print(int("0100000001") < 2 ** 32)
print(90000 * 40 // 1000)      # 一帧 40 毫秒的时钟计数

实跑输出:

--------------------------------------------------------------------
一份国标 SDP 逐行拆开看
--------------------------------------------------------------------
**点播时两边都要交底**:**平台先说「往我这发」,设备再答「我从这发」。**
**两份 SDP 长得几乎一样,只有地址和端口是反的。**
**这正是不容易看懂的地方。**

  位置                        行数         字节
  ---------------------------------------------------------
  **INVITE 里(平台写)**     **9 行**     **159 字节**
  **200 OK 里(设备写)**     **9 行**     **164 字节**

**两份都不到两百字节,而且差别只在三个字段**:**归属者编号、`c=` 的地址、`m=` 的端口。**

**`c=` 和 `m=` 是最容易搞反的一对**:

  字段     INVITE 里      200 OK 里        含义
  -----------------------------------------------------------------
  `c=`     10.1.1.10      192.168.1.64     **媒体走哪个 IP**
  `m=`     30000          6000             **媒体走哪个端口**
  `a=`     recvonly       sendonly         **这一端是收还是发**
  `y=`     0200000001     0100000001       **这一端的 SSRC**

**看第三行就知道谁收谁发**:**平台写 `recvonly`,设备写 `sendonly`,两边一拼就对上了。**
**唯独 `o=` 那行容易误读**——**它是「会话归属者」,不是「媒体地址」**,
**改 `o=` 不影响流往哪走,改 `c=` 才影响。**

**`y=` 那 10 位数字装的是 32 位 SSRC**:
  `0200000001` → 200000001,占 32 位空间的 4.6566%,补零 1 位
  `0100000001` → 100000001,占 32 位空间的 2.3283%,补零 1 位

**注意左边那个补上的 0**:
**它不是符号位也不是标志位,只是补位**——
**SSRC 是 32 位整数,写成十进制最多十位,不足前面补零凑够十位。**
**所以解析时先 `int()` 再比,别按字符一位位读。**

**`a=rtpmap:96 PS/90000` 里的 90000 是时钟频率**:
**一秒钟走九万个计数,一帧 40 毫秒就是 3600 个。**
**这个数决定 RTP 时间戳怎么涨**,
**写错了不会立刻报错,只会让播放器算错帧间隔**——**表现是画面「快放」或「慢放」,而不是花屏。**

**最后算一下它有多不起眼**:
  一份 SDP 约 210 字节,一分钟的 4 Mbps 流约 3.0 MB
  占比 0.00700000%

**小到可以忽略,但少一个字段就一路黑屏**——**这就是信令的脾气:字节上不重要,语义上要命。**

两份都是九行、一百六十字节上下,差别只有归属者编号、c=、m=、a=、y= 五处。 y= 那两行值得看第二眼:0200000001 换成整数是两亿,只占 32 位空间的 4.7%。 最前面那个 0 不是标志位,纯粹是为了凑够十位补上去的。 所以解析 y= 时直接 int(),别按字符一位位读—— 按位读会把补位的 0 当成 SSRC 的一部分,算出来小十倍。 90000 * 40 // 1000 = 3600 这个数说明一件事:90 千赫兹的时钟走 40 毫秒,时间戳涨 3600。 时钟频率写错,播放器就会按错误的间隔播,表现是快放或慢放。 最后那个占比是 0.007%:两百多字节对三兆的媒体,小到可以忽略。 但它是「有没有」的问题,不是「多不多」的问题。

30 秒实验

  1. 算一下:y=0100000001 转成整数是多少?(100000001)
  2. 想一下:为什么收流端口由平台定、发流端口由设备定?
  3. 查一下:m=video 6000 RTP/AVP 96 里 96 代表什么编码?(看 a=rtpmap)

常见误区

  1. 以为「SDP 是一份独立的协议报文」。 它是被 SIP 的正文捎带的,本身不单独发。
  2. 以为「o= 里那个 IP 就是媒体地址」。 媒体地址看 c=,o= 只记会话归属者。
  3. 以为「y= 前面的 0 是某种标志」。 它就是补位,凑够十位而已。
自测三问(点开对答案) 1. 国标为什么要加 `y=` 这个字段? —— **为了让接收方能区分 SSRC**。**一台设备同时给多个平台发流时,靠它分开不同的路。** 2. `s=` 有哪三种取值? —— **`Play` 实时点播、`Playback` 历史回放、`Download` 文件下载**。 3. 时钟频率写错会怎样? —— **不会报错,只会让播放器算错帧间隔**,**表现是画面快放或慢放。**

下一节看「转换」:非国标的设备要接进来,中间那个网关到底在干什么。

相关笔记