11、SDP协议:SDP是什么、SDP结构解析(媒体描述、编解码信息)、本地SDP与远程SDP、SDP协商过程。

聊到WebRTC,绕不开的一个东西就是SDP。我记得刚接触WebRTC那会儿,看到控制台打印出来的一大串文本,第一反应是——这什么玩意儿?密密麻麻的,像乱码又不像乱码。后来硬着头皮啃了一遍RFC,才明白这东西其实就是个“谈判桌”。

说白了,SDP(Session Description Protocol)不是用来传输媒体数据的,它是用来描述“这场会议该怎么开”的。你用什么编解码器?你的IP和端口是多少?你要发几路流?这些信息都写在SDP里。没有它,两个浏览器之间根本没法建立音视频通话。

核心一句话:SDP 是会话描述协议,不是传输协议。它只负责“描述”,不负责“搬运”。

SDP长什么样?

我们先看一个真实的SDP片段。这是我从一次实际通话中截取出来的,去掉了敏感信息:

v=0
o=- 1234567890 2 IN IP4 192.168.1.100
s=-
t=0 0
m=audio 49170 RTP/AVP 111 103 104
c=IN IP4 192.168.1.100
a=rtpmap:111 opus/48000/2
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=sendrecv
m=video 49172 RTP/AVP 96 97 98
c=IN IP4 192.168.1.100
a=rtpmap:96 H264/90000
a=rtpmap:97 VP8/90000
a=rtpmap:98 VP9/90000
a=sendrecv

嗯,看着还是有点乱对吧?别急,我们拆开来看。

SDP的结构:一行一个属性

SDP的格式非常简洁,每一行都是一个键值对,用等号连接。我习惯把它分成三块来看:

  • 会话层(Session Level):描述整个通话的全局信息,比如版本号、会话ID、时间戳。
  • 媒体层(Media Level):描述每一路媒体流,比如音频、视频各自用什么端口、什么编解码器。
  • 属性行(Attribute Lines):以 a= 开头,补充说明媒体流的行为,比如是发送还是接收。

我们逐行看一下上面那个例子:

含义
v=0 SDP版本号,目前就是0,没变过。
o=- 1234567890 2 IN IP4 192.168.1.100 会话发起者信息。用户名、会话ID、版本号、网络类型、地址。
s=- 会话名称,没填就用短横线占位。
t=0 0 会话有效时间,0 0表示不限制。
m=audio 49170 RTP/AVP 111 103 104 媒体行。音频流,端口49170,使用RTP协议,支持三种编解码器(编号111、103、104)。
c=IN IP4 192.168.1.100 连接信息。IP地址和网络类型。
a=rtpmap:111 opus/48000/2 属性行。把编号111映射为Opus编解码器,采样率48000,双声道。
a=sendrecv 表示这条媒体流既能发送也能接收。
我的小习惯:看SDP时,先找 m= 行,它告诉你总共有几路流。然后看 a=rtpmap,就知道对方支持哪些编解码器。其他行大多是辅助信息。

本地SDP vs 远程SDP

这个其实很好理解。你想想看,在WebRTC里,每个端都要生成一份SDP来描述自己。你生成的叫“本地SDP”,对方生成的叫“远程SDP”。

举个例子:

  • 你的浏览器调用 createOffer(),得到的就是你的本地SDP。
  • 你通过信令服务器把它发给对方,对方收到后,调用 setRemoteDescription(),这份SDP就成了对方的远程SDP。
  • 反过来,对方生成Answer,你收到后设置成你的远程SDP。

我在项目中遇到过一个问题:有个同事把本地SDP和远程SDP搞反了,结果音频一直没声音。排查了半天,发现他把自己的SDP设成了远程的,等于自己跟自己握手——当然没声音了。

注意:本地SDP描述的是“我有什么、我能做什么”,远程SDP描述的是“对方有什么、对方能做什么”。千万别搞混。

SDP协商过程:Offer/Answer模型

WebRTC的SDP协商遵循一个非常经典的模型——Offer/Answer。说白了就是:

  1. 发起方生成一个Offer(包含自己的媒体能力)。
  2. 接收方收到Offer后,生成一个Answer(包含自己的媒体能力,同时回应Offer中的提议)。
  3. 双方各自设置对方的SDP,完成协商。

这个过程看起来简单,但里面有个关键点:编解码器匹配

Offer里会列出所有支持的编解码器,按优先级排序。Answer里会从这些编解码器里选一个它自己也支持的。如果双方没有交集,那这路流就建不起来。

我曾经调试过一个VP9的问题。Offer里写了VP8、VP9、H264,但Answer只回了VP8。我一开始以为是对方不支持VP9,后来发现是Answer的SDP里把VP9的payload type写错了。嗯,这种低级错误排查起来最费时间。

用SVG看SDP协商流程

下面这张图是我自己画的,把Offer/Answer的流程走了一遍。你可以对照着看:

发起方 (Offerer) 接收方 (Answerer) 1. 生成 Offer Offer SDP 2. setRemoteDescription 3. 生成 Answer Answer SDP 4. setRemoteDescription ✅ 协商完成

实际开发中要注意什么?

我做了几年WebRTC开发,踩过不少SDP的坑。这里列几个最常见的:

  • ICE候选信息没带全:SDP里除了媒体描述,还会携带ICE候选信息。如果候选信息不全,P2P连接可能建不起来。我遇到过只发了host候选,没发srflx候选的情况,结果内网能通,外网就不行。
  • 编解码器顺序很重要:Offer里排第一的编解码器,是发起方最想要的。Answer通常会选第一个它支持的。所以如果你想让对方用H264,就把H264排在前面。
  • 不要手动修改SDP:除非你非常清楚自己在做什么。我见过有人为了“优化”SDP,把一些属性行删了,结果通话直接挂掉。SDP的每一行都有它的作用。
调试小技巧:在Chrome的 chrome://webrtc-internals 页面里,可以实时看到SDP的Offer和Answer。我每次排查问题,第一件事就是打开这个页面,把SDP复制出来对比一下。

总结一下

SDP在WebRTC里扮演的角色,就是一份“会议议程”。它告诉对方:我在这里,我用这些编解码器,我能收也能发。双方通过Offer/Answer模型交换SDP,最终达成一致,媒体流才能建立起来。

理解SDP,是理解WebRTC信令流程的关键一步。你不需要背下每一行的含义,但至少要能看懂SDP里哪些是媒体描述、哪些是编解码信息、哪些是网络信息。这样出了问题,你才知道从哪里下手。


公众号:蓝海资料掘金营,微信deep3321