🎬 第七章 流媒体开源库简介
📄 一句话概括

一次握手只有六步:offer、setLocal、信令、setRemote、answer、setRemote。 顺序错了必失败,而且报错往往看不出是顺序问题。

📝 本节和前面几节的关系

7.7.5 讲了 API 组成,这一节讲最重要的那个的调用顺序。 下一章开始实战:用 Live555 搭一个能跑的平台。

一、六步的顺序

① createOffer:发起方生成 SDP 提议。 ② setLocalDescription:把它设为本地描述。 ③ 信令转发:把 SDP 送到对端。这一步不走 WebRTC。 ④ setRemoteDescription:对端接收提议。 ⑤ createAnswer:对端生成应答。 ⑥ setRemoteDescription:发起方接收应答,开始 ICE。

注意第 ③ 步是唯一的「非 WebRTC」动作: SDP 得靠你自己的服务器传。 这是初学者最容易忽略的一步—— API 都调对了,但两端永远收不到对方的描述。

二、SDP 有多大

实测一份最小 SDP 只有 200 字节、10 行。 真实场景会把 ICE 候选、编解码参数、SSRC 全塞进去, 一份完整的 SDP 常有 3 到 8 KB。

为什么差这么多?因为 ICE 候选一行一条: 本机地址、局域网地址、公网地址、TURN 中继地址各一条, 再乘以音视频两路,很容易就几十行。

SDP 大不是问题,但要注意它的「时机」: ICE 候选是陆续产生的,不是一次给全。 所以要支持「增量交换」——每产生一个候选就发一条。

三、最容易出错的三处

① 顺序颠倒:没 setLocalDescription 就 createAnswer,必失败。 而且报错信息往往只说「状态不对」,看不出是顺序问题。 ② 忘了信令:WebRTC 自己不会传 SDP,得靠你的服务器。 ③ ICE 候选早到:对端还没 setRemoteDescription 就收到候选, 要缓存起来等,直接加会报错。

第 ③ 条最隐蔽: 因为候选产生得很快,常常比信令服务器转发 SDP 还快。 所以正确做法是「收到的候选先排队,等 setRemoteDescription 之后再逐个加」。

四、动手 Demo:重要 API——六步走完一次握手

先把六步的顺序列清楚,再量一份最小 SDP 有多大。 最后指出三处最容易出错的地方。

steps = [("createOffer", "发起方生成 SDP 提议"),
         ("setLocalDescription", "把提议设为本地描述"),
         ("信令转发", "把 SDP 送到对端(不走 WebRTC)"),
         ("setRemoteDescription", "对端接收提议"),
         ("createAnswer", "对端生成应答"),
         ("setRemoteDescription", "发起方接收应答")]
for i, (a, d) in enumerate(steps, 1):
    print("%d. %-22s %s" % (i, a, d))

print()
sdp = ("v=0\r\n"
       "o=- 1234567890 2 IN IP4 127.0.0.1\r\n"
       "s=-\r\n"
       "t=0 0\r\n"
       "a=group:BUNDLE 0 1\r\n"
       "m=audio 9 UDP/TLS/RTP/SAVPF 111\r\n"
       "a=rtpmap:111 opus/48000/2\r\n"
       "m=video 9 UDP/TLS/RTP/SAVPF 96\r\n"
       "a=rtpmap:96 H264/90000\r\n"
       "a=sendrecv\r\n")
print("最小 SDP %d 字节、%d 行" % (len(sdp.encode()), sdp.count("\r\n")))

实跑输出:

--------------------------------------------------------------------
重要 API——六步走完一次握手
--------------------------------------------------------------------
**① 六步的顺序**

  步     API                      做什么
  ------------------------------------------------------------------
  1      createOffer              发起方生成 SDP 提议
  2      setLocalDescription      把提议设为本地描述
  3      信令转发                 把 SDP 送到对端(不走 WebRTC)
  4      setRemoteDescription     对端接收提议
  5      createAnswer             对端生成应答
  6      setRemoteDescription     发起方接收应答,开始 ICE

**② SDP 有多大**

  **一份最小 SDP 有 200 字节、10 行。**
  **真实场景会把 ICE 候选、编解码参数、SSRC 全塞进去,**
  **一份完整的 SDP 常有 3 到 8 KB。**

**③ 最容易出错的三处**

  **① 顺序颠倒**:**没 setLocalDescription 就 createAnswer,必失败。**
  **② 忘了信令**:**WebRTC 自己不会传 SDP,得靠你的服务器。**
  **③ ICE 候选早到**:**对端还没 setRemoteDescription 就收到候选,**
  **要缓存起来等,直接加会报错。**

六步里只有第 3 步不走 WebRTC,这一步最容易被漏掉。 200 字节的 SDP 只有音视频各一路,加 ICE 候选就会涨到几 KB。 两个 setRemoteDescription 分别在不同端,这是最容易看混的地方。

30 秒实验

  1. 把第 3 步删掉,想想会发生什么——两端都生成了 SDP 但永远不交换,连接一直停在 connecting。
  2. 给 SDP 加 10 条 a=candidate 行,看字节数——每条约 60 字节,这就是它涨到几 KB 的原因。
  3. 把 a=sendrecv 改成 a=recvonly,想想是什么场景——只收不发,观播模式就用它。

常见误区

  1. 「WebRTC 自带信令」——没有,信令协议由你自己定。
  2. 「SDP 一次就发全」——ICE 候选是陆续产生的。
  3. 「顺序错了会报明确的错」——报错很含糊,得自己推。
自测三问(点开对答案) 1. 一次握手的六步是什么? —— **offer、setLocal、信令、setRemote、answer、setRemote。** 2. 哪一步不走 WebRTC? —— **第 3 步信令转发**,SDP 必须靠你自己的服务器传。 3. ICE 候选早到怎么办? —— **先缓存,等 setRemoteDescription 之后再逐个添加。**

相关笔记