WebRTC SDP 詳解和剖析

WebRTC 是 Web Real-Time Communication,即網頁實時通訊的縮寫,是 RTC 協議的一種 Web 實現,項目由 Google 開源,並和 IETF 和 W3C 制定了行業標準。在國內 WebRTC 已經得到了愈來愈多廠商的支持,應用前景變得更加廣闊,因此咱們也開設專欄,分享阿里雲內部的 WebRTC 研究工做。html

本篇是阿里雲視頻雲 WebRTC 技術專欄系列文章的第一篇,做者將從 WebRTC SDP 例子和關鍵屬性的角度爲你們深度剖析解讀,其中也分享了阿里雲技術專家的一些實踐經驗,但願能對你們有所幫助或者啓發。後續 WebRTC 技術專欄系列將繼續推出 WebRTC ICE/DTLS/SRTP/RTCP/TURN 的詳解與剖析,歡迎關注咱們的公衆號。web

做者:
忘籬,阿里雲高級技術專家,負責阿里雲 RTC 服務器研發;
泰一,阿里雲高級開發工程師,從事阿里雲 RTC 服務器研發算法

SDP關鍵屬性

Overview

狹義的說 WebRTC 是指瀏覽器端,瀏覽器端如何直接交換數據呢?確定是無法徹底獨立完成的,必須得依靠服務器。通常依賴幾種服務器:chrome

  1. Signaling 信令服務器,也就是交換房間和會議的媒體信息,以及會議期間的消息,媒體描述使用的是 SDP 協議,也就是本文剖析的重點。
  2. ICE 服務器,能夠分爲幫助兩個客戶端打洞創建 P2P 鏈接的 STUN 服務器,還有若是連不通就直接轉發的 TURN 服務器。ICE 的信息叫 Candidate,能夠經過 SDP 交換,或者經過 Trickle。
  3. SFU 或 MCU 服務器,若是多我的開會,每一個端都向其餘參會的端直接發送數據叫 MESH,可是 MESH 明顯有侷限性,SFU 就是轉發可讓客戶端只上行一路流轉給其餘客戶端,而 MCU 更強大,能夠上下行都只有一路流。

Note: WebRTC 除了傳輸,還有一個重要特性就是安全性,也就是 DTLS,而 DTLS 有些信息就是經過 SDP 傳遞的,後面會有相關的技術文章來介紹 DTLS。瀏覽器

下面,咱們正式介紹 SDP 協議。安全

What's SDP

本文開篇的 SDP 關鍵屬性圖,已經幫助咱們以全局的視角一窺 SDP 的模樣。SDP 描述了媒體會話,網絡信息、安全特性、傳輸策略等,圖中的每個 SDP 屬性都在不一樣的應用場景下發揮着不一樣的做用,不可小覷。服務器

接下來,咱們進一步給出 SDP 的官方定義:SDP(Session Description Protocol) 是一種會話描述協議,基於文本,其自己並不屬於傳輸協議,須要依賴其它的傳輸協議(好比 SIP 和 HTTP)來交換必要的媒體信息,用於兩個會話實體之間的媒體協商。網絡

WebRTC 的 Offer 和 Answer 包含了 SDP。相關的 RFC 包括:session

  1. 1998, RFC2327
  2. 2006, RFC4566

一個不錯的 WebRTC 的 SDP 例子分析app

Offer and Answer

WebRTC 使用 Offer-Answer 模型交換 SDP,Offer 中有 SDP,Answer 中也有。例如 Alice 和 Bob 經過 WebRTC 通訊:

// Alice Offer
v=0
o=- 2397106153131073818 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE video
a=msid-semantic: WMS gLzQPGuagv3xXolwPiiGAULOwOLNItvl8LyS
m=video 9 UDP/TLS/RTP/SAVPF 96 97
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:l5KU
a=ice-pwd:+Sxmm3PoJUERpeHYL0HW4/T9
a=ice-options:trickle
a=fingerprint:sha-256 7C:93:85:40:01:07:91:BE:DA:64:A0:37:7E:61:CB:9D:91:9B:44:F6:C9:AC:3B:37:1C:00:15:4C:5A:B5:67:74
a=setup:actpass
a=mid:video
a=sendrecv
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=***c-group:FID 2527104241
a=***c:2527104241 cname:JPmKBgFHH5YVFyaJ
a=***c:2527104241 msid:gLzQPGuagv3xXolwPiiGAULOwOLNItvl8LyS c7072509-df47-4828-ad03-7d0274585a56
a=***c:2527104241 mslabel:gLzQPGuagv3xXolwPiiGAULOwOLNItvl8LyS
a=***c:2527104241 label:c7072509-df47-4828-ad03-7d0274585a56

// Bob Answer
v=0
o=- 5443219974135798586 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE video
a=msid-semantic: WMS uiZ7cB0hsFDRGgTIMNp6TajUK9dOoHi43HVs
m=video 9 UDP/TLS/RTP/SAVPF 96 97
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:MUZf
a=ice-pwd:4QhikLcmGXnCfAzHDB++ZjM5
a=ice-options:trickle
a=fingerprint:sha-256 2A:5A:B8:43:66:05:B3:6A:E9:46:36:DF:DF:20:11:6A:F6:11:EA:D9:4E:26:E3:CE:5A:3A:C6:8D:03:49:7B:DE
a=setup:active
a=mid:video
a=sendrecv
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=***c-group:FID 3587783331
a=***c:3587783331 cname:INxZnBV2Sty1zlmN
a=***c:3587783331 msid:uiZ7cB0hsFDRGgTIMNp6TajUK9dOoHi43HVs a3b297e7-cdbe-464e-a32c-347465ace055
a=***c:3587783331 mslabel:uiZ7cB0hsFDRGgTIMNp6TajUK9dOoHi43HVs
a=***c:3587783331 label:a3b297e7-cdbe-464e-a32c-347465ace055

Remark: 用 Chrome 瀏覽器,先打開 webrtc-internals,而後打開 Alice 頁面點 Share 按鈕,接着打開 Bob 頁面點 Share,看到上面的 Offer 和 Answer。

交換完 SDP 後,會交換 Candidate:

// Alice Candidate
candidate: candidate:1912876010 1 udp 2122260223 30.2.220.94 52832 typ host generation 0 ufrag l5KU network-id 1 network-cost 10
candidate: candidate:1015535386 1 tcp 1518280447 30.2.220.94 9 typ host tcptype active generation 0 ufrag l5KU network-id 1 network-cost 10

// Bob Candidate
candidate:1912876010 1 udp 2122260223 30.2.220.94 51551 typ host generation 0 ufrag MUZf network-id 1 network-cost 10

最後 Alice 和 Bob 通訊的 Candidate pair,選擇的是 UDP 通道:

file

Alice 發送的 Video 的信息:

file

Alice 收到的 (Bob 的) Video信息:

file

通常來講,推流方先發起 Offer,接收方給 Answer。好比客戶端推流到 SFU,客戶端發起 Offer 推流,SFU 給客戶端 Answer,客戶端將流推到 SFU,SFU 再轉發給其餘客戶端。Licode 和 Janus 都是這種作法,這種方式下,若是客戶端須要拉取其餘的客戶端的流,通常須要使用另外的 PeerConnection,接收 SFU 的 Offer,生成 Answer 後迴應給 SFU。

不過,推流方發起 Offer 不是必須的,接收方也能夠給 Offer,推流方給 Answer。好比 MediaSoup 這種 SFU,客戶端先給一個 Offer 給 SFU,SFU 只是檢查這個 Offer 中的媒體特性,而後 SFU 會生成 Offer(包含會議中的其餘客戶端的流,若是沒有人則沒有 ***C)給客戶端,客戶端發送 Answer 給 SFU。這種方式的好處是其餘客戶端加入,以及流的變動(好比關閉視頻打開視頻時),均可以使用 Reoffer,也就是統一由 SFU 發起新的 Offer,客戶端響應,SFU 和客戶端的交互模式只有一種。

SDP Structure

SDP 描述分爲兩部分,分別是會話級別的描述(session level)和媒體級別的描述(media level),其具體的組成可參考 RFC4566,帶星號 (*) 的是可選的。常見的內容以下:

Session description(會話級別描述)
         v=  (protocol version)
         o=  (originator and session identifier)
         s=  (session name)
         c=* (connection information -- not required if included in all media)
         One or more Time descriptions ("t=" and "r=" lines; see below)
         a=* (zero or more session attribute lines)
         Zero or more Media descriptions

Time description
         t=  (time the session is active)

Media description(媒體級別描述), if present
         m=  (media name and transport address)
         c=* (connection information -- optional if included at session level)
         a=* (zero or more media attribute lines)

對照 Alice 的 Offer(只包含了視頻沒有開啓音頻):

// Session description
v=0
o=- 2397106153131073818 2 IN IP4 127.0.0.1
s=-
c=IN IP4 0.0.0.0

// Time description
t=0 0

// Session Attributes
a=group:BUNDLE video
a=msid-semantic: WMS gLzQPGuagv3xXolwPiiGAULOwOLNItvl8LyS

// Media description
m=video 9 UDP/TLS/RTP/SAVPF 96 97
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:l5KU
a=ice-pwd:+Sxmm3PoJUERpeHYL0HW4/T9
a=ice-options:trickle
a=fingerprint:sha-256 7C:93:85:40:01:07:91:BE:DA:64:A0:37:7E:61:CB:9D:91:9B:44:F6:C9:AC:3B:37:1C:00:15:4C:5A:B5:67:74
a=setup:actpass
a=mid:video
a=sendrecv
a=rtcp-mux
a=rtcp-rsize
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=***c-group:FID 2527104241
a=***c:2527104241 cname:JPmKBgFHH5YVFyaJ
a=***c:2527104241 msid:gLzQPGuagv3xXolwPiiGAULOwOLNItvl8LyS c7072509-df47-4828-ad03-7d0274585a56
a=***c:2527104241 mslabel:gLzQPGuagv3xXolwPiiGAULOwOLNItvl8LyS
a=***c:2527104241 label:c7072509-df47-4828-ad03-7d0274585a56

SDP Line 是順序相關的,好比 a=rtpmap:96 後面的都是它相關的設置,直到下一行是a=rtpmap或者其餘屬性。

SDP Line 沒有統一的 Schema 描述,也就是沒有一個固定的規則能解析全部 Line,SDP Grammer 只是描述了 SDP 相關的屬性,具體每一個屬性的表達須要根據屬性定義,定義在 RFC 4566,例如:

a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]

SDP 解析時,每一個 SDP Line 都是以 key=... 形式,解析出 key 是 a 後,可能有兩種方式,可參考 RFC4566

a=<attribute>
a=<attribute>:<value>

好比 c=IN IP4 0.0.0.0,key 爲 c。
好比 a=rtcp-mux,key 爲 a,attribute 爲 rtcp-mux,沒有 value。
好比 a=rtpmap:96 VP8/90000,key 爲 a,attribute 爲 rtpmap,value=96 VP8/90000。

有時候並不是冒號 (:) 就必定是 &lt;attribute&gt;:&lt;value&gt;,實際上 value 裏面也會有冒號,好比:

a=fingerprint:sha-256 7C:93:85:40:01:07:91:BE
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=***c:2527104241 msid:gLzQPGuagv3xXolwPiiGAULOwOLNItvl8LyS

Session Level Field

會話級別的 SDP 描述字段包括:v、o、s、c、b、t。

  • v(version)
    SDP 協議版本,值固定爲 0。

  • o(origin)
    表明會話的發起者。

  • s(session name)
    會話的名稱,每一個 SDP 中有且僅能有一個 s 描述,其值不能爲空。

  • c(connection data)
    攜帶了會話的鏈接信息,其實就是 IP 地址。
    SDP 的會話級別描述能夠包含該字段,每個媒體級別的描述也能夠包含該字段,若是會話級別和媒體級別都有 c line,那麼以媒體級別的 c line 爲準。
    由於 WebRTC 使用 ICE candidate 交換地址信息,因此不會用到 c line,不過這並不表明 c line 沒有用,在 SIP 視頻會議場景中,c line 就必不可少了,文末會再次介紹該字段。

  • b (bandwidth)
    表示會話或媒體使用的建議帶寬。

  • t(timing)
    指定了會話的開始和結束時間,若是開始和結束時間都爲 0,那麼意味着此次會話是永久的。

關於會話級別字段的更詳細的描述,請參考 RFC 4566

Media Codecs

會話級別描述完成後,後面就是零到多個媒體級別描述,好比:

// Session Description
v=0
......

// Audio Media Description
m=audio 9 UDP/TLS/RTP/SAVPF 111
......

// Video Media Description
m=video 9 UDP/TLS/RTP/SAVPF 96 97
......

這個 SDP 描述了一個音頻和一個視頻,它的格式參考 RFC4566:

m=<media> <port> <proto> <fmt> ...

其中,後面的一串數字 11196 97 就是 fmt,分別表明音頻和視頻的 Media Codec,後面會跟着 rtpmap、rtcp-fb、fmtp 這些屬性來作進一步的詳細的描述。

m=audio 9 UDP/TLS/RTP/SAVPF 111
a=mid:audio
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1

m=video 9 UDP/TLS/RTP/SAVPF 96 97
a=mid:video
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96

Remark: 固然,M line 的類型不是隻有 audio 和 video,還有 application(bfcp)、text 等媒體類型。

Remark: a=mid 屬性能夠認爲是每一個 M 描述的惟一 ID。好比 a=mid:audio,那麼 audio 這個字符串就是這個 M 描述的 ID。有的時候 mid 屬性值也能夠用數字表示,好比 a=mid:0,那麼 0 也是這個 M 描述的 ID。mid 值通常和 grouping 傳輸屬性的 BUNDLE 策略結合來用,好比 a=group:BUNDLE audio video,表明本次會話將對 mid 爲 audiovideo 的 M 描述進行復用傳輸。

Remark: M line 的數字 9 表明該媒體類型的傳輸端口,在 RTC 場景中都是使用 ICE candidate 的地址信息進行數據傳輸,因此 M line 的 port 並無用到。不過,在 SIP 的場景下,M line 的 port 就十分重要了,此時,port 表明 RTP 端口,並且必須是偶數。結合 SDP 會話級別描述中的 C line 中的 IP 地址,咱們就能夠知道 SIP 的這路媒體流的傳輸地址。

Remark: RTX 表示是重傳,好比 video 的 97,就是 apt=96 的重傳。也就是說若是用的是 97 這個編碼格式,它是在 96(VP8) 基礎上加了重傳功能。

而一共有多少媒體流,則是經過 ***C 指定的:

m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
a=***c:2582129002 cname:8Y1pmIKBijmWeALu
a=***c:2582129002 msid:34fD1qguf2v79436S1khLkth8Nb6LbedcF9H bab38910-40cd-4581-9a20-e3f558abb397
a=***c:2582129002 mslabel:34fD1qguf2v79436S1khLkth8Nb6LbedcF9H
a=***c:2582129002 label:bab38910-40cd-4581-9a20-e3f558abb397

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 127 122 125 107 108 109 124
a=***c:565530905 cname:8Y1pmIKBijmWeALu
a=***c:565530905 msid:34fD1qguf2v79436S1khLkth8Nb6LbedcF9H 2c533cfe-b6bf-41a8-93f0-1ca031436702
a=***c:565530905 mslabel:34fD1qguf2v79436S1khLkth8Nb6LbedcF9H
a=***c:565530905 label:2c533cfe-b6bf-41a8-93f0-1ca031436702

Remark: ***C 就包含了須要發送的媒體流,另外 Offer 和 Answer 中均可以包含 ***C。好比客戶端和 MediaSoup 通訊時,MediaSoup 老是給客戶端發 Offer,MediaSoup 的 Offer 包含了 MediaSoup 要發送(轉發其餘客戶端的流給客戶端)的媒體流 ***C,同時客戶端的 Answer 中也包含了本身要推送的 ***C 流,他們的類型都是 sendrecv。

Remark: msid 對應了NetStream.id,也就是表明了不一樣的媒體源,這些 ***C 能夠是不一樣的媒體源。

如何肯定最後的編碼?對方會在 Answer 中給出,好比上面 Offer 給出了多個編碼,在 Answer 中會選擇一個:

m=audio 9 UDP/TLS/RTP/SAVPF 111
m=video 9 UDP/TLS/RTP/SAVPF 100 102 127 125 108 124
a=rtpmap:100 H264/90000
a=rtpmap:102 H264/90000
a=rtpmap:127 H264/90000
a=rtpmap:125 H264/90000
a=rtpmap:108 red/90000
a=rtpmap:124 ulpfec/90000

雖然 Video 編碼有 100 到 125,可是他們都是 H.264,而 108 和 124 則是 FEC,基於 H.264。

PlanB and UnifiedPlan

上面的 MediaCodecs 中,沒有規定如何指定多條流。實際上 Audio 和 Video 都有多個 ***C,每一個 ***C 的編碼可能相同但也可能不一樣。好比互聯網視頻會議,用移動端接入時,編碼可能都是 H.264,可是和其餘終端接入時可能會有其餘編碼。

若是 ***C 的編碼不相同,那麼將這些 ***C 放在同一個 M 描述就會有問題,這就是 PlanB 和 UnifiedPlan 的關鍵所在。對於 PlanB 只有一個 M(audio) 和 M(video),他們的編碼要相同,當有多路媒體流時,則根據 ***C 去區分。UnifiedPlan 則能夠有多個 M(audio) 和 M(video),每路流都有本身的 M 描述,這樣就能夠支持不一樣的編碼。

PlanB 和 UnifiedPlan 其實就是 WebRTC 在多路媒體源(multi media source)場景下的兩種不一樣的 SDP 協商方式。若是引入 Stream 和 Track 的概念,那麼一個 Stream 可能包含 AudioTrack 和 VideoTrack,當有多路 Stream 時,就會有更多的 Track,若是每個 Track 惟一對應一個本身的 M 描述,那麼這就是 UnifiedPlan,若是每個 M line 描述了多個 Track(track id),那麼這就是 Plan B。

Note: 當只有一路音頻流和一路視頻流時,Plan B 和 UnifiedPlan 的格式是相互兼容的。

Remark: Chrome 早期支持的是 PlanB,目前最新版本也支持了 UnifiedPlan,參考 Need to implement WebRTC "Unified Plan" for multistream

PlanB 參考下圖:

file

UnifiedPlan 參考下圖:

file

Candidate

Candidate 就是傳輸的候選人,客戶端會生成多個 Candidate,好比有 host 類型的、有 relay 類型的、有 UDP 和 TCP 的,以下圖所示:

sdpMid: audio, sdpMLineIndex: 0, candidate:2213672593 1 udp 2122260223 30.2.228.19 51068 typ host
sdpMid: video, sdpMLineIndex: 1, candidate:2213672593 1 udp 2122260223 30.2.228.19 55061 typ host

sdpMid: audio, sdpMLineIndex: 0, candidate:3446803041 1 tcp 1518280447 30.2.228.19 9 typ host
sdpMid: video, sdpMLineIndex: 1, candidate:3446803041 1 tcp 1518280447 30.2.228.19 9 typ host

sdpMid: video, sdpMLineIndex: 1, candidate:150963819 1 udp 41885439 182.92.80.26 54400 typ relay raddr 42.120.74.91 rport 37714
sdpMid: audio, sdpMLineIndex: 0, candidate:150963819 1 udp 41885439 182.92.80.26 59241 typ relay raddr 42.120.74.91 rport 49618

Remark: 咱們去掉了後面的屬性,好比 generation 0 ufrag kce9 network-id 1 network-cost 10,這些屬於 Candidate 的描述,和連通性檢查等相關。

客戶端本身生成了 6 個 Candidates,3 個 Audio 和 3 個 Video,2 個 TCP 和 4 個 UDP,4 個 host 和 2 個 relay。固然對方也會有不少 Candidate,接下來就是本身的 Candidates 和對方的 Candidates 匹配連通(ICE Connectivity Checks),造成 CandidatePair 也就是傳輸通道。Candidate 還附帶了網絡屬性,好比network-cost 會在 ICE Connectivity Checks 時用到。

Remark: 關於 Candidate 的類型,還有 srflx 以及 prflx,關於這兩種 Candidate 類型的定義以及區分,後面會在 ICE 相關的技術文章中介紹。

Remark: 關於 ICE Connectivity Checks 咱們會在後面給出詳細的分析,涉及到了STUN協議。下面會總結出 ICE 相關的 SDP 信息。

SDP 和 Candidate 都是經過信令交換的。若是對方只給了 relay 的 Candidate,例如:

sdpMid: audio, sdpMLineIndex: 0, candidate:150963819 1 udp 41885439 182.92.80.26 51542 typ relay raddr 42.120.74.91 rport 56380

這種狀況下,確定最後連通的 CandidatePair 是 Relay 對 Relay,以下圖所示:

file

file

從這個圖中能看出來這個傳輸通道的發送和接收碼率、包的個數、RTT 和丟包率等信息。

實際上,因爲咱們這個客戶端還有 host 類型的 Candidate,因此它會嘗試直接用 host 的這個 Candidate 和對方的 relay 直接鏈接:

sdpMid: audio, sdpMLineIndex: 0, candidate:2213672593 1 udp 2122260223 30.2.228.19 51068 typ host

Statistics Conn-audio-1-1
googActiveConnection    false

file

固然,因爲沒有連通因此這個 CandidatePair 就不可用。

Remark: WebRTC 是具有在多個 Candidate 之間切換的能力的,具體在 ICE Connectivity Checks 中咱們再分析。

上面的 Candidates 本身生成了 2 個 Relay 的 Candidates,一個是 audio 的一個是 video 的,爲什麼只用到了audio 的呢?這就是下面的 BUNDLE 涉及的了。

Bundle and RTCP-MUX

傳輸時,能夠複用媒體通道,一種是音頻和視頻的複用,一種是 RTCP 和 RTP 的複用。

RTCP 和 RTP 複用,表示 Sender 使用一個傳輸通道(單一端口)發送 RTP 和 RTCP:

m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
a=rtcp-mux

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 127 122 125 107 108 109 124
a=rtcp-mux

此時,Receiver 必須準備好在 RTP 端口上接收 RTCP 數據,並須要預留一些資源,好比 RTCP 帶寬。

音頻和視頻複用時,最後只會用一個 Candidate 傳輸,好比客戶端本身的 SDP Offer,和兩個 relay 的Candidates:

a=group:BUNDLE audio video

m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
a=mid:audio

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 127 122 125 107 108 109 124
a=mid:video
sdpMid: video, sdpMLineIndex: 1, candidate:150963819 1 udp 41885439 182.92.80.26 54400 typ relay raddr 42.120.74.91 rport 37714
sdpMid: audio, sdpMLineIndex: 0, candidate:150963819 1 udp 41885439 182.92.80.26 59241 typ relay raddr 42.120.74.91 rport 49618

這表示最終 audio 和 video 儘管可能有獨立的 Candidate,可是若是對方也是 BUNDLE,那麼最終只會用一個 Candidate。例如,若是對方的 Answer 是:

a=group:BUNDLE audio video

m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
a=mid:audio

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 127 122 125 107 108 109 124
a=mid:video
sdpMid: audio, sdpMLineIndex: 0, candidate:150963819 1 udp 41885439 182.92.80.26 51542 typ relay raddr 42.120.74.91 rport 56380

最後它們只會用一個 Candidate 傳輸。以下圖所示:

file

rtcp-mux 將 RTP 和 RTCP 複用到單一的端口進行傳輸,這簡化了 NAT traversal,而 BUNDLE 又將多路媒體流複用到同一端口進行傳輸,這不只使 candidate harvesting 等 ICE 相關的 SDP 屬性變得簡單,並且又進一步簡化了 NAT traversal。

rtcp-mux 是與 RTC 傳輸相關的重要的 SDP 屬性,關於它的 SDP 協商的原則以下:

  1. 若是 Offer 攜帶 rtcp-mux 屬性,而且 Answer 方但願複用 RTP 和 RTCP 到單一端口,那麼 Answer 必須也要攜帶該屬性。
  2. 若是 Offer 沒有攜帶 rtcp-mux 屬性,那麼 Answer 也必定不能攜帶 rtcp-mux 屬性,並且 Answer 方禁止 RTP 和 RTCP 複用單一端口。
  3. rtcp-mux 的協商和使用必須是雙向的。

舉個例子。客戶端去訂閱服務器的流,客戶端的 Offer 沒有攜帶 rtcp-mux 屬性,那麼服務器會認爲客戶端不支持 rtcp-mux,也不會走 rtcp 複用的流程。相反,服務器會分別建立 RTP 和 RTCP 兩個傳輸通道,只有當兩個通道的 ICE 和 DTLS 都成功,纔會認爲本次訂閱的傳輸通道創建成功,繼而向客戶端發流。

試想,若是由於你的疏忽致使 Offer 漏掉了 rtcp-mux 屬性,那麼你將永遠等不到服務器 Ready 的那一天。因此,SDP 看似只是一些文本,很簡單,可是隻有在項目的實戰中,多遇到幾個坑,才能更深切的體會到 SDP 屬性的含義以及這些屬性是如何在 RTC 場景中去發揮做用的。

Remark: 關於 rtcp-mux 更詳細的協商細節請參考 RFC 8035

Remark: 關於 rtcp-mux 場景下如何經過頭部字段區分 rtp 和 rtcp,請參考 RFC 5761

ICE Connectivity

這裏咱們只說明 SDP 中和 ICE Connectivity Checks 相關的信息,具體的過程咱們會在其餘文章中單獨分析。

SDP 中和 ICE 相關的信息包括:

m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
a=ice-ufrag:kce9
a=ice-pwd:M31WxfrwmrFvPws4+tPdbsCE
a=ice-options:trickle

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 127 122 125 107 108 109 124
a=ice-ufrag:kce9
a=ice-pwd:M31WxfrwmrFvPws4+tPdbsCE
a=ice-options:trickle

ufrag 和 pwd 就是 ICE short-term 認證算法用到的用戶名和密碼。而 trickle 說明 SDP 中沒有包含 candidate 信息,Candidate 是經過信令單獨交換的,這樣能夠作到 Connectivity checks 和 Candidate harvesting 並行處理,提升會話創建的速度。

DTLS

這裏咱們只說明 SDP 中關於 DTLS 的信息,具體的 DTLS 握手過程會在 DTLS 相關的技術文章中單獨分析。

SDP 中和 DTLS 相關的信息包括:

m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126B0:A2:B3:AB:0B:A3:44:22:B1:C8:69:52:ED:04:E8:5A:A4:C3:7A:A6:55:F3:BA:76:62:26:4B:F7:9F:DD:F1:BD
a=setup:actpass

m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 127 122 125 107 108 109 124
a=fingerprint:sha-256 B0:A2:B3:AB:0B:A3:44:22:B1:C8:69:52:ED:04:E8:5A:A4:C3:7A:A6:55:F3:BA:76:62:26:4B:F7:9F:DD:F1:BD
a=setup:actpass

其中 fingerprint 是 DTLS 過程當中的 Certificate 證書的簽名,防止客戶端和服務器的證書被篡改。

另外,setup 指的是 DTLS 的角色,也就是誰是 DTLS Client(active),誰是 DTLS Server(passive),若是本身兩個均可以那就是 actpass。這裏咱們是 actpass,那麼就要由對方在 Answer 中肯定最終的 DTLS 角色:

m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
a=fingerprint:sha-256 B1:FD:D6:2D:94:4E:33:A1:8C:9D:EF:ED:EB:AC:CC:2D:E2:37:15:9B:24:8C:BF:F2:7D:6A:B3:81:23:AA:13:54
a=setup:active

對方是 active,也就是 DTLS Client,那麼本身就只能是 DTLS Server,會由對方發起 DTLS ClientHello 開始DTLS 過程。

Stream Direction

媒體流的方向有四種,分別是 sendonly、recvonly、sendrecv、inactive,它們既能夠出如今會話級別描述中也能夠出如今媒體級別的描述中。

  • sendonly 表示只發送數據,好比客戶端推流到 SFU,那麼會在本身的 Offer(or Answer) 中攜帶 senonly 屬性
  • revonly 表示只接收數據,好比客戶端向 SFU 訂閱流,那麼會在本身的 Offer(or Answer) 中攜帶 recvonly 屬性
  • sendrecv 表示能夠雙向傳輸,好比客戶端加入到視頻會議中,既要發佈本身的流又要訂閱別人的流,那麼就須要在本身的 Offer(or Answer) 中攜帶 sendrecv 屬性
  • inactive 表示禁止發送數據,好比在基於 RTP 的視頻會議中,主持人暫時禁掉用戶 A 的語音,那麼用戶 A 的關於音頻的媒體級別描述應該攜帶 inactive 屬性,表示不能再發送音頻數據。

NOTE: RFC 4566: senonly 和 recvonly 屬性僅應用於媒體,不用於媒體控制相關的協議。好比在基於 RTP 的媒體會話中,即便是 recvonly 模式,也仍然要發送 RTCP 包,即便是 senonly 模式,也依然會接收並正常處理 RTCP 包。

媒體流方向的四個屬性很重要,在組裝 SDP 時要仔細校驗,保證流方向的正確性。

舉個例子,客戶端去訂閱服務器的流。若是此時客戶端的 Offer 攜帶的屬性並非 recvonly 而是 sendonly,那麼即便在信令層面的確是訂閱的語義,可是因爲某些服務器對 SDP 各屬性的校驗是十分全面和嚴格的(本該如此),這種場景下,服務器將不會發送媒體流到客戶端,並且服務器回覆的 Answer 可能根本不會攜帶 ***C。

RTCP Feedback

下面,咱們聊一下 rtcp-fb 這個媒體級別的 SDP 屬性,它能告訴咱們媒體會話可以對哪些 RTCP 消息進行反饋,是一個和 QoS 相關的重要的 SDP 屬性。

m=video 9 UDP/TLS/RTP/SAVPF 96
a=mid:video
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack pli

如上 SDP 信息,這是一個視頻的 M 描述,VP8 編碼,payload type 是 96。最後的 3 個 rtcp-fb 屬性則說明了對於 96 這個 media codec 來說,在網絡擁塞控制方面支持 twcc;在 ARQ 方面支持 nack 處理,可以重傳丟失的 RTP 包;在關鍵幀方面支持 fir 和 pli 處理,有能力進行關鍵幀的發送。

在作 SIP 的時候,遇到過一個坑:向某臺型號的 SIP 設備發送 PLI 請求後,並無收到關鍵幀,通過一番折騰,最後發現,這臺設備的 rtcp-fb 描述以下:

m=video 16402 RTP/AVP  34
a=rtpmap:34 H263/90000
a=fmtp:34 CIF4=1;CIF=1;QCIF=1;SQCIF=1
a=sendrecv
a=rtcp-fb:* ccm tmmbr
a=rtcp-fb:* ccm fir

也就是說這臺設備只支持 FIR 請求,沒有處理 PLI 請求的能力(PS: 爲何沒能早一些檢查 SDP 的 rtcp 反饋能力,淚目)。在此也想着重強調一下:對於一些很專業嚴謹的系統或者設備而言,SDP 徹底體現了它們所擁有的能力,也可讓咱們發現其不具有的能力。SDP 的每個屬性都是有其存在乎義的,萬萬不可忽略。

Note: rtcp-fb 不能用於會話級別的描述中,只能用於媒體級別的描述,並且其 M 描述的 proto 字段必定要指定 AVPF。

Note: 存在這種格式,a=rtcp-fb:* ccm fir,星號是一個通配符,表示該 M 描述下的全部類型的 media codec 都支持 fir 的處理和關鍵幀的反饋。

Compare with SIP SDP

RTC 場景與 SIP 場景下的 SDP 描述的不一樣表如今傳輸、媒體、信令三個層面。

Transmission Level

  1. 建連流程。RTC 場景下的音視頻媒體流建連流程通常是 ICE + DTLS,而 SIP 場景下沒有這套建連流程,因此也沒有 ICE/DTLS 相關的 SDP 屬性,好比 ufrag、pwd、setup、fingerprint 等。

  2. 端口複用。RTC 場景下通常都是音視頻流以及 RTP/RTCP 複用單一端口,經過 ***C 區分每一路流,經過數據包的頭部字段值來區分 RTP/RTCP,而 SIP 場景下不會複用端口,所以沒有 rtcp-mux 屬性,也沒有 grouping 相關的屬性,好比 BUNDLE,且音視頻的 RTP 和 RTCP 都是獨立端口進行傳輸,共有四個,因此自然可使用端口來區分流以及 RTP/RTCP,所以也沒有 ***C 屬性。

  3. 鏈路探測。RTC 場景下通常經過 ICE 的 STUN 探測環節來發現對端通過 NAT 映射以後的出口地址,稱爲 srflx,而 SIP 場景下須要本身實現對端地址發現的功能,以獲取到 SIP 設備通過 NAT 映射以後的出口地址。

  4. 地址信息。RTC 場景下經過 SDP 的 candidate 交換對端地址信息,SIP 場景下經過 C line 的 ip 以及 M line 的端口來交換對端地址信息。
// RTC 場景
a=candidate:1 1 udp 2013266431 30.27.136.138 14306 typ host

// SIP 場景
c=IN IP4 30.41.5.131
m=audio 2352 RTP/AVP 107 108 114 104 105 9 18 8 0 101 123
m=video 2374 RTP/AVP 97 126 96 34 123

Media Level

  1. 屏幕共享。SIP 場景下經過 BFCP 協議來進行屏幕共享的協商,經過 a=content 屬性來區分主流(main)和共享流 (slides),而 RTC 場景下經過外部/業務信令來進行屏幕共享的協商,主流和共享流的 SDP 描述一致,不會區分。
  2. Media Codec。目前,RTC 場景下的音視頻編碼廣泛是 Opus + H.264/VP8,SIP 場景下,對於音頻編碼,有不少 SIP 設備並不支持 Opus,而採用比較古老的音頻編碼,好比 G72二、PCMA、PCMU,對於視頻編碼,廣泛支持 H.264,通常不支持 VP8。

Signaling Level

  1. SDP 交換。都是 Offer/Answer 模型,RTC 場景下主要經過 HTTP/TCP 協議交換 SDP,通常是在 HTTP body 中攜帶 SDP 信息。SIP 場景下能夠經過 UDP/TCP/TLS 協議交換 SDP,在 INVITE 和 200 OK 中攜帶 SDP 信息。

Summary

其實,SDP 文本化的協議格式自己很簡單,其難點在於不一樣的應用場景(好比傳統 SIP 視頻會議或者 RTC 場景)下擴展出的紛繁複雜的屬性及其含義,這些 SDP 屬性散落在衆多的 RFC 以及草案之中,不下必定的功夫是很難作到全面理解與掌握的(PS:每當說到此處,內心老是一萬個馬奔騰,WebRTC 的 RFC 太多了並且互相關聯互相引用,看完這些 RFC 要準備好視力降低 0.2 度)。

下一篇,咱們會重點講一下 WebRTC ICE,包括連通性檢測、狀態切換、trickle 以及 nomination。感謝閱讀。

阿里雲視頻雲技術公衆號分享視頻雲行業和技術趨勢,打造「新內容」、「新交互」。

相關文章
相關標籤/搜索