详细参考:WebRTC SDP 详解和剖析
这里已一个webrtc的offer和answer为例研究一下:
offer
v=0 o=- 2661928673431850918 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE 0 1 a=extmap-allow-mixed a=msid-semantic: WMS m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126 c=IN IP4 0.0.0.0 a=rtcp:9 IN IP4 0.0.0.0 a=ice-ufrag:PA7e a=ice-pwd:F1o3tHlhk6OPBtXo8IdhZCRH a=ice-options:trickle a=fingerprint:sha-256 D4:50:20:EA:EE:A6:86:59:77:3B:88:84:95:69:8A:AE:79:1A:C0:35:D9:25:EE:3F:0E:02:CB:2B:AF:99:F5:9E a=setup:actpass a=mid:0 a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id a=recvonly a=rtcp-mux a=rtpmap:111 opus/48000/2 a=rtcp-fb:111 transport-cc a=fmtp:111 minptime=10;useinbandfec=1 a=rtpmap:103 ISAC/16000 a=rtpmap:104 ISAC/32000 a=rtpmap:9 G722/8000 a=rtpmap:0 PCMU/8000 a=rtpmap:8 PCMA/8000 a=rtpmap:106 CN/32000 a=rtpmap:105 CN/16000 a=rtpmap:13 CN/8000 a=rtpmap:110 telephone-event/48000 a=rtpmap:112 telephone-event/32000 a=rtpmap:113 telephone-event/16000 a=rtpmap:126 telephone-event/8000 m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 122 102 121 127 120 125 107 108 109 35 36 124 119 123 118 114 115 116 37 c=IN IP4 0.0.0.0 a=rtcp:9 IN IP4 0.0.0.0 a=ice-ufrag:PA7e a=ice-pwd:F1o3tHlhk6OPBtXo8IdhZCRH a=ice-options:trickle a=fingerprint:sha-256 D4:50:20:EA:EE:A6:86:59:77:3B:88:84:95:69:8A:AE:79:1A:C0:35:D9:25:EE:3F:0E:02:CB:2B:AF:99:F5:9E a=setup:actpass a=mid:1 a=extmap:14 urn:ietf:params:rtp-hdrext:toffset a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time a=extmap:13 urn:3gpp:video-orientation a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=extmap:12 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay a=extmap:11 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space a=extmap:4 urn:ietf:params:rtp-hdrext:sdes:mid a=extmap:5 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=extmap:6 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id a=recvonly 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=rtpmap:98 VP9/90000 a=rtcp-fb:98 goog-remb a=rtcp-fb:98 transport-cc a=rtcp-fb:98 ccm fir a=rtcp-fb:98 nack a=rtcp-fb:98 nack pli a=fmtp:98 profile-id=0 a=rtpmap:99 rtx/90000 a=fmtp:99 apt=98 a=rtpmap:100 VP9/90000 a=rtcp-fb:100 goog-remb a=rtcp-fb:100 transport-cc a=rtcp-fb:100 ccm fir a=rtcp-fb:100 nack a=rtcp-fb:100 nack pli a=fmtp:100 profile-id=2 a=rtpmap:101 rtx/90000 a=fmtp:101 apt=100 a=rtpmap:122 VP9/90000 a=rtcp-fb:122 goog-remb a=rtcp-fb:122 transport-cc a=rtcp-fb:122 ccm fir a=rtcp-fb:122 nack a=rtcp-fb:122 nack pli a=fmtp:122 profile-id=1 a=rtpmap:102 H264/90000 a=rtcp-fb:102 goog-remb a=rtcp-fb:102 transport-cc a=rtcp-fb:102 ccm fir a=rtcp-fb:102 nack a=rtcp-fb:102 nack pli a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f a=rtpmap:121 rtx/90000 a=fmtp:121 apt=102 a=rtpmap:127 H264/90000 a=rtcp-fb:127 goog-remb a=rtcp-fb:127 transport-cc a=rtcp-fb:127 ccm fir a=rtcp-fb:127 nack a=rtcp-fb:127 nack pli a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42001f a=rtpmap:120 rtx/90000 a=fmtp:120 apt=127 a=rtpmap:125 H264/90000 a=rtcp-fb:125 goog-remb a=rtcp-fb:125 transport-cc a=rtcp-fb:125 ccm fir a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=rtpmap:107 rtx/90000 a=fmtp:107 apt=125 a=rtpmap:108 H264/90000 a=rtcp-fb:108 goog-remb a=rtcp-fb:108 transport-cc a=rtcp-fb:108 ccm fir a=rtcp-fb:108 nack a=rtcp-fb:108 nack pli a=fmtp:108 level-asymmetry-allowed=1;packetization-mode=0;profile-level-id=42e01f a=rtpmap:109 rtx/90000 a=fmtp:109 apt=108 a=rtpmap:35 AV1X/90000 a=rtcp-fb:35 goog-remb a=rtcp-fb:35 transport-cc a=rtcp-fb:35 ccm fir a=rtcp-fb:35 nack a=rtcp-fb:35 nack pli a=rtpmap:36 rtx/90000 a=fmtp:36 apt=35 a=rtpmap:124 H264/90000 a=rtcp-fb:124 goog-remb a=rtcp-fb:124 transport-cc a=rtcp-fb:124 ccm fir a=rtcp-fb:124 nack a=rtcp-fb:124 nack pli a=fmtp:124 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=4d001f a=rtpmap:119 rtx/90000 a=fmtp:119 apt=124 a=rtpmap:123 H264/90000 a=rtcp-fb:123 goog-remb a=rtcp-fb:123 transport-cc a=rtcp-fb:123 ccm fir a=rtcp-fb:123 nack a=rtcp-fb:123 nack pli a=fmtp:123 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=64001f a=rtpmap:118 rtx/90000 a=fmtp:118 apt=123 a=rtpmap:114 red/90000 a=rtpmap:115 rtx/90000 a=fmtp:115 apt=114 a=rtpmap:116 ulpfec/90000 a=rtpmap:37 flexfec-03/90000 a=rtcp-fb:37 goog-remb a=rtcp-fb:37 transport-cc a=fmtp:37 repair-window=10000000
answer:
v=0 o=SRS/4.0.161(Leo) 41744832 2 IN IP4 0.0.0.0 s=SRSPlaySession t=0 0 a=ice-lite a=group:BUNDLE 0 1 a=msid-semantic: WMS live/livestream m=audio 9 UDP/TLS/RTP/SAVPF 111 c=IN IP4 0.0.0.0 a=ice-ufrag:8p42d118 a=ice-pwd:ok61un195fg8q8083yy06247w0xg483s a=fingerprint:sha-256 06:5D:44:D5:6A:62:A9:2E:5F:C5:5E:1E:99:3A:9F:11:20:7B:71:B1:D3:DF:CA:70:2D:82:0F:7D:AC:DC:0C:CC a=setup:passive a=mid:0 a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=sendonly a=rtcp-mux a=rtcp-rsize a=rtpmap:111 opus/48000/2 a=rtcp-fb:111 transport-cc a=ssrc:216302819 cname:85n6wsoy1t00k452 a=ssrc:216302819 label:audio-68w76144 a=candidate:0 1 udp 2130706431 10.151.3.77 8000 typ host generation 0 m=video 9 UDP/TLS/RTP/SAVPF 125 c=IN IP4 0.0.0.0 a=ice-ufrag:8p42d118 a=ice-pwd:ok61un195fg8q8083yy06247w0xg483s a=fingerprint:sha-256 06:5D:44:D5:6A:62:A9:2E:5F:C5:5E:1E:99:3A:9F:11:20:7B:71:B1:D3:DF:CA:70:2D:82:0F:7D:AC:DC:0C:CC a=setup:passive a=mid:1 a=extmap:3 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=sendonly a=rtcp-mux a=rtcp-rsize a=rtpmap:125 H264/90000 a=rtcp-fb:125 transport-cc a=rtcp-fb:125 nack a=rtcp-fb:125 nack pli a=fmtp:125 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f a=ssrc:216302820 cname:85n6wsoy1t00k452 a=ssrc:216302820 label:video-35j24601 a=candidate:0 1 udp 2130706431 10.151.3.77 8000 typ host generation 0
WebRTC 使用 Offer-Answer 模型交换 SDP,Offer 中有 SDP,Answer 中也有。SDP主要分为如下几个部分:
- 会话级别描述
- 媒体级别描述
- 网络描述
- DTLS描述
- ICE策略
具体可以通过在谷歌浏览器中输入chrome://webrtc-internals/调试
会话级别描述
会话级别的 SDP 描述字段包括:v、o、s、c、b、t。
v(version):SDP 协议版本,值固定为 0。
o(origin):代表会话的发起者。
o=<username><sess-id><sess-version><nettype><addrtype><unicast-address>
各字段含义如下:
username:发起者的⽤户名,不允许存在空格,如果应⽤不⽀持⽤户名,则为-。
sess-id:会话id,由应⽤⾃⾏定义,规范的建议是NTP(Network Time Protocol)时间戳。
sess-version:会话版本,⽤途由应⽤⾃⾏定义,只要会话数据发⽣变化时(⽐如编码),sessversion
随着递增就⾏。同样的,规范的建议是NTP时间戳。
nettype:⽹络类型,⽐如IN表示Internet。
addrtype:地址类型,⽐如IP4、IV6
unicast-address:域名,或者IP地址。
o=- 7298280855354507719 2 IN IP4 127.0.0.1
s(session name):会话的名称,每个 SDP 中有且仅能有一个 s 描述,其值不能为空。
s=-。
s=<session name>
c(connection data):携带了会话的连接信息,其实就是 IP 地址。
格式如下:
c=<nettype><addrtype><connection-address>
每个SDP⾄少需要包含⼀个会话级别的c=字段,或者在每个媒体描述后⾯各包含⼀个c=字段。
(媒体描述后的c=会覆盖会话级别的c=)
nettype:⽹络类型,⽐如IN,表示 Internet。
addrtype:地址类型,⽐如IP4、IP6。
connection-address:如果是⼴播,则为⼴播地址组;如果是单播,则为单播地址;
举例01:
c=IN IP4224.2.36.42/127
举例02:
c=IN IP4 host.anywhere.com
b(bandwidth):表示会话或媒体使用的建议带宽。
t(timing):指定了会话的开始和结束时间,如果开始和结束时间都为 0,那么意味着这次会话是永久的。
媒体级别描述
SDP可能同时包含多个媒体描述。格式如下:
m=<media><port><proto><fmt> ...
其中:
media:媒体类型。包括 video、audio、text、application、message等。
port:传输媒体流的端⼝,具体含义取决于使⽤的⽹络类型(在c=中声明)和使⽤的协议(proto,在m=中声明)。
proto:传输协议,具体含义取决于c=中定义的地址类型,⽐如c=是IP4,那么这⾥的传输协议运⾏在IP4之上。⽐如:
UDP:传输层协议是UDP。
RTP/AVP:针对视频、⾳频的RTP协议,跑在UDP之上。
RTP/SAVP:针对视频、⾳频的SRTP协议,跑在UDP之上。
RTP/AVPF: 应⽤场景为视频/⾳频的 RTP 协议,⽀持 RTCP-based Feedback。参考 RFC4585
RTP/SAVPF: 应⽤场景为视频/⾳频的 SRTP 协议,⽀持 RTCP-based Feedback。参考 RFC5124
fmt:媒体格式的描述,可能有多个。根据 proto 的不同,fmt 的含义也不同。⽐如 proto 为
RTP/SAVP 时,fmt 表示 RTP payload 的类型。如果有多个,表示在这次会话中,多种payload类
型可能会⽤到,且第⼀个为默认的payload类型。
附加属性:a=
作⽤:⽤于扩展SDP。
有两种作⽤范围:会话级别(session-level)、媒体级别(media-level)。
1. 媒体级别:媒体描述(m=)后⾯可以跟任意数量的 a= 字段,对媒体描述进⾏扩展。
2. 会话级别:在第⼀个媒体字段(media field)前,添加的 a= 字段是会话级别的。
有如下两种格式:
a=<attribute>
a=<attribute>:<value>
格式1举例:
a=recvonly
格式2举例:
a=rtpmap:0 PCMU/8000
- 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 为 audio 和 video 的 M 描述进行复用传输。
a=group:BUNDLE 0 1
a=mid:0
a=mid:1
- M line 的数字 9 代表该媒体类型的传输端口,在 RTC 场景中都是使用 ICE candidate 的地址信息进行数据传输,所以 M line 的 port 并没有用到。不过,在 SIP 的场景下,M line 的 port 就十分重要了,此时,port 代表 RTP 端口,而且必须是偶数。结合 SDP 会话级别描述中的 C line 中的 IP 地址,我们就可以知道 SIP 的这路媒体流的传输地址。
m=audio 9 UDP/TLS/RTP/SAVPF 111
- RTX 表示是重传,比如 video 的 97,就是 apt=96 的重传。也就是说如果用的是 97 这个编码格式,它是在 96(VP8) 基础上加了重传功能。
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
- 协商方式:PlanB 只有一个 M(audio) 和 M(video),他们的编码要相同,多有多路媒体流时,则根据 SSRC 去区分,UnifiedPlan 则可以有多个 M(audio) 和 M(video),每路流都有自己的 M 描述。当只有一路音频流和一路视频流时,Plan B 和 UnifiedPlan 的格式是相互兼容的。
- SSRC 就包含了需要发送的媒体流,另外 Offer 和 Answer 中都可以包含 SSRC。⽐如客户端和 MediaSoup 通信时,MediaSoup 总是给客户端发 Offer,MediaSoup 的 Offer 包含了MediaSoup 要发送(转发其他客户端的流给客户端)的媒体流 SSRC,同时客户端的 Answer 中也包含了⾃⼰要推送的 SSRC 流,他们的类型都是 sendrecv。
- Bundle:⾳频和视频的复⽤
a=group:BUNDLE 0 1
a=mid:0
a=mid:1
- rtcp-mux:RTCP 和 RTP 的复⽤
a=rtcp-mux
- Stream Direction:媒体流方向
sendonly 表示只发送数据,⽐如客户端推流到 SFU,那么会在⾃⼰的 Offer(orAnswer) 中携带 sendonly属性
recvonly表示只接收数据,⽐如客户端向 SFU 订阅流,那么会在⾃⼰的 Offer(orAnswer) 中携带 recvonly 属性
sendrecv 表示可以双向传输,⽐如客户端加⼊到视频会议中,既要发布⾃⼰的流⼜要订阅别⼈的流,那么就需要在⾃⼰的 Offer(or Answer) 中携带 sendrecv 属性
inactive 表示禁⽌发送数据,⽐如在基于 RTP 的视频会议中,主持⼈暂时禁掉⽤户 A 的语⾳,那么⽤户 A 的关于⾳频的媒体级别描述应该携带 inactive 属性,表示不能再发送⾳频数据。
网络描述
offer:
a=ice-ufrag:PA7e // 客户端用户名 a=ice-pwd:F1o3tHlhk6OPBtXo8IdhZCRH // 客户端密码 a=ice-options:trickle // trickle方式表示媒体信息和ice后选项的信息可以分开传输
answer:
a=ice-lite // SRS是Lite ICE,只需要响应客户端的Binding请求 a=ice-ufrag:8p42d118 // SRS端用户名 a=ice-pwd:ok61un195fg8q8083yy06247w0xg483s // SRS端密码 a=candidate:0 1 udp 2130706431 10.151.3.77 8000 typ host generation 0 // {foundation} {component} {protocol} {priority} {ip} {port} typ {type} {generation} // 0 [foundation] : 标识符,用来识别两个candidate是否相等 // 1 [component] : 传输媒体类型 1表示RTP // ubp [protocol] : 协议类型 // 2130706431 [priority] : 优先级 // 10.151.3.77 [ip] : ip地址 // 8000 [port] : 端口 // host [type] : host类型,表示这是真实的主机地址 // generation : 代数。初始值是0,然后会不断+1,大的代数会覆盖掉低代数的候选地址。更新candidate的时候会+1,替换老的candidate
DTLS⻆⾊
offer: a=fingerprint:sha-256 D4:50:20:EA:EE:A6:86:59:77:3B:88:84:95:69:8A:AE:79:1A:C0:35:D9:25:EE:3F:0E:02:CB:2B:AF:99:F5:9E a=setup:actpass answer: a=fingerprint:sha-256 06:5D:44:D5:6A:62:A9:2E:5F:C5:5E:1E:99:3A:9F:11:20:7B:71:B1:D3:DF:CA:70:2D:82:0F:7D:AC:DC:0C:CC a=setup:passive
setup:
active,作为client,主动发起协商
passive,作为server,等待发起协商
actpass,作为client,主动发起协商;作为server,等待发起协商
fingerprint:证书的摘要签名,用来验证证书使用,DTLS收到证书后,基于相同的hash计算哈希值,同fingerprint比较,相等,则说明证书有效。