sdp 结构简析
参考:
- rfc4566
- https://segmentfault.com/a/1190000038272539
- https://www.cnblogs.com/chyingp/p/sdp-in-webrtc.html
1. 概述
SDP(Session Description Protocol,会话描述协议) 的具体标准定义在了 rfc4566 中。本篇文章主要以 rfc4566 为基础,通过 webrtc offer/answer 协商中的 sdp 为例进行记录,然后会对比与 sip 呼叫中 sdp 的一些异同。
2. 结构
sdp 是一行一行的文本,每一行之间使用 \n 或者 \r\n 来分割,所以如果要解析 sdp,那么代码需要兼容这两种分割方式。除开分隔符,其他都是可打印字符。
每一行中,采用 type=value 的结构,即使用 = 号进行分割。其中,type 必须为单个字符,而 value 则较为复杂,可以有多种不同格式,= 号之间不能存在空格。
2.1 总体结构
sdp 多个行之间会一起描述一个结构功能,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)
2.2 会话级别描述(Session description level)
会话描述总是在 sdp 的开头,是必须存在的结构。以下面的会话描述为例,说明常见的每个字段:
v=0
o=- 1044094566052576507 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1
a=msid-semantic: WMS g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
2.2.1 v(protocol version)
会话协议版本,rfc4566 固定了版本号为 0,即:
v=0
2.2.2 o(originator and session identifier)
会话发起者标识:
o=<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
- username:发起者的用户名,不允许存在空格,如果应用不支持用户名,则为 -。
- sess-id:会话id,由应用自行定义,规范的建议是 NTP(Network Time Protocol) 时间戳。
- sess-version:会话版本,用途由应用自行定义,只要会话数据发生变化时(比如编码),sess-version 随着递增就行。同样的,规范的建议是 NTP 时间戳。
- nettype:网络类型,比如 IN 表示 Internet。
- addrtype:地址类型,比如 IP4、IV6。
- unicast-address:域名,或者 IP 地址。
2.2.3 s(session name)
会话名,没有具体设置可为 -:
s=-
2.2.4 t(timing)
声明会话的开始时间和结束时间:
t=<start-time> <stop-time>
如果开始和结束时间都为 0,那么意味着这次会话是永久的。
2.2.5 a(attribute)
a 开头的表示附加属性,用于扩展 sdp,有如下两种格式:
a=<attribute>
a=<attribute>:<value>
a 开头的行不仅会出现在 session level 中,也会出现在 media level 中(后文将展示)。
2.3 媒体级别描述(Media description level)
会话描述结束后,接着是媒体描述。在sdp中,可能有多个媒体描述,每个媒体描述由 m= 行开始。不同于 session level,media level 中的一些不同行,会有明确的先后关系,不能乱序,而 session level 中的大部分行可以任意排列。以下面的媒体描述为例,说明常见的字段:
m=audio 9 UDP/TLS/RTP/SAVPF 111 103
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:Fyzi
a=ice-pwd:/NDZTbh8NX+BTFjRyZy7Heqc
a=ice-options:trickle
a=fingerprint:sha-256 60:DF:D2:8E:AC:1B:7C:DB:DA:36:39:57:AA:DD:1A:C2:43:58:36:09:80:56:CA:18:F4:85:6B:9F:B1:8F:78:13
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=sendonly
a=msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 13eb9491-262e-4982-9aea-1c6af9e8ef22
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=ssrc:724846199 cname:wJobAtYmVV7u5Y3x
a=ssrc:724846199 msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 13eb9491-262e-4982-9aea-1c6af9e8ef22
a=ssrc:724846199 mslabel:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
2.3.1 m=(Media Descriptions)
媒体描述起始行:
m=<media> <port> <proto> <fmt> ...
- media:媒体类型。支持包括 video、audio、text、application(bfcp) 等。
- port:流媒体传输端口。具体含义取决于使用的网络类型(在 c= 中声明)和使用的协议(proto,在 m= 中声明)。
- proto:流媒体传输协议。具体含义取决于 c= 中定义的地址类型,比如 c= 是 IP4,那么这里的传输协议运行在 IP4 之上。以 UDP/TLS/RTP/SAVPF 为例:
- UDP:传输层协议是UDP。
- TLS:加密传输基于 DTLS 协议。
- RTP:使用 rtp 协议传输媒体。
- SAVPF:使用 SRTP 协议对 RTP 媒体负载进行加密。如果是 AVPF 或 AVP,表示不对 RTP 中的媒体负载进行加密。AVPF与AVP的区别,见 rfc4585 文档(webrtc 一般用 AVPF,sip 一般用 AVP)。
- fmt:媒体格式的描述,可能有多个。根据 proto 的不同,fmt 的含义也不同。比如 proto 为 RTP/AVP 时,fmt 表示 RTP payload 的类型。如果有多个,表示在这次会话中,多种 payload 类型都可能会用到。
2.3.2 c=(Connection Data)
会话连接信息。此字段也可以只出现在 session level 中,或者只在 media level 中,也可以都出现:
c=<nettype> <addrtype> <connection-address>
- nettype:网络类型,比如IN,表示 Internet。
- addrtype:地址类型,比如IP4、IP6。
- connection-address:如果是广播,则为广播地址组;如果是单播,则为单播地址。
2.4 group bundle 与 mid
在 webrtc 中,a=group:BUNDLE 行用于绑定不同的 media level,而每个 media level 都会有一个 mid 字段,用于媒体行标识:
// Session Description
a=group:BUNDLE 0 1
...
// Audio Media Description
a=mid 0
...
// Video Media Description
a=mid 1
...
每个媒体行都可以创建一个自己的网络连接,用于传递音视频和 rtcp 数据。会话绑定媒体行的意义是,不同媒体行可以复用同一个网络连接进行数据传输。
2.5 ICE(Interactive Connectivity Establishment)
媒体协商完成后,webrtc 双方要做的第一件事就是建立网络连接,而 ice 协议(参考 rfc5245)定义了 webrtc 建立网络连接的方式,反应在 sdp 中,有如下字段:
...
a=ice-ufrag:Fyzi
a=ice-pwd:/NDZTbh8NX+BTFjRyZy7Heqc
a=ice-options:trickle
a=ice-lite
...
- ice-ufrag 和 ice-pwd 用于 ice 中 stun 消息的认证
- ice-options:trickle 表明本端支持独立发送 candidates 和 sdp,参考 https://tools.ietf.org/id/draft-ietf-ice-trickle-16.html
- ice-lite 表明本端不会做连通性检查(本端只会回应 stun binding response)
这些 ice 字段可以出现在所有的 media level 中,如果 sdp 指定了 group:bundle,则绑定的 media level 中,ice 字段应该都相同。
2.5.1 candidate(地址候选项)
地址候选项可以出现在 sdp 中,也可以单独通过 http、websocket 等类似发送 sdp 的方式发送给对端(来自 rfc5245 15.1,更改了排列格式,SP 表示空格):
candidate-attribute="candidate:" foundation SP component-id SP transport SP priority SP connection-address SP port SP "typ" SP cand-type [SP rel-addr] [SP rel-port] *(SP extension-att-name SP extension-att-value)
- foundation:多个 candidates 之间通过此字段来区分是否是同一种候选地址
- component-id:1 表示用于传输 rtp 的候选地址,2 表示用于传输 rtcp 的候选地址,如果启用了 rtcp-mux,那么都为 1 即可
- transport:网络传输类型,udp 或 tcp
- priority:此 candidate 的优先级,用于生成 check-list pair 的时候,谁最先发起连通性检查
- connection-address:候选地址,可以是 ipv4、ipv6、域名(如果是域名,接收端需要进行 dns 解析)
- port:候选端口
- cand-type:host、srflx、prflx、relay 之一
- extension-att-name,extension-att-value:扩展属性
如下是一个 sdp 中的 candidate 示例(只能出现在 media level 中,不能出现在 session level 中,多个不同 media level 中的 candidate 可以相同):
a=candidate:0 1 udp 2130706431 192.168.0.106 8090 typ host generation 0
- foundation 为 0
- component-id 为 1,表明是用于 rtp 传输的候选地址,如果 sdp 启用了 rtcp-mux,则 rtcp 传输也复用这个候选地址
- 网络传输使用 udp
- 优先级为 2130706431,详细计算公式参考 rfc5245
- 候选 ipv4 地址为 192.168.0.106
- 候选 port 为 8090
- 候选地址类型为 host,这种地址要么本端拥有公网 ip,要么与对端在同一个局域网中
- generation 0 是扩展属性
注意以下是一个 janus 通过信令发送的 trickle candidate 示例:
{"janus":"trickle","candidate":{"candidate":"candidate:3805835391 1 udp 2122260223 192.168.0.107 53792 typ host generation 0 ufrag icRG network-id 1","sdpMid":"0","sdpMLineIndex":0},"transaction":"jIToQHjOgntu","session_id":52}
其中,必须指明此 candidate 属于哪个 media stream(这里通过 mid 和 m-line index 进行指定,显然,即使是 ice trickle 模式,也必须先发送 sdp 然后再发送 local candidate,参考 https://datatracker.ietf.org/doc/html/draft-ietf-mmusic-trickle-ice-02#section-9.2)。
2.6 DTLS
ICE 连接建立完成后,接着就是 DTLS 握手协商出用于 SRTP 媒体加密的密钥,DTLS 握手在 sdp 中的字段如下:
a=fingerprint:sha-256 60:DF:D2:8E:AC:1B:7C:DB:DA:36:39:57:AA:DD:1A:C2:43:58:36:09:80:56:CA:18:F4:85:6B:9F:B1:8F:78:13
a=setup:actpass
其中,fingerprint 即握手中的指纹信息(用于自签证书验证),setup 表明本端在握手中的角色:
- active:主动发起 DTLS 握手。
- passive:被动等待接受 DTLS 握手消息。
- actpass:既可以主动发起,也可以被动接受。当 offer 一方为 actpass 时,answer 一方必须指定为 active 或 passive。
2.7 Stream Direction
此属性在 a= 中,如 a=sendonly,在不同 media level 中,可以有不同,以表明不同的媒体收发支持:
- sendonly:本端只支持发送媒体,不接收媒体,因此对端返回的 sdp 可以不携带 ssrc 信息。
- recvonly:本端只支持接收媒体,不发送媒体,因此本端 sdp 可以不携带 ssrc 信息。
- sendrecv:本端支持收发媒体
- inactive:用在某个 media level 协商失败的回应中
2.8 Codec Parameters(编解码格式参数)
编解码参数用于描述媒体编解码格式信息(使用此格式进行编解码),一个 media level 可以支持多个编解码格式,具体最终使用哪个或哪些取决于媒体协商的结果。
offer 中的编解码格式表明 offer 端支持发送接收带这些媒体格式的 rtp 包,answer 中的编解码格式只能从这些给定的编解码格式中进行协商。
2.8.1 rtpmap
a=rtpmap 是编解码格式参数的起始行:
a=rtpmap:<payload type> <encoding name>/<clock rate> [/<encoding parameters>]
- payload type:rtp payload 值,必须是 m line 中支持的某个值
- encoding name:编解码格式名称,如 H264、opus、vpx 等
- clock rate:时钟频率
- encoding parameters:可选项
2.8.2 fmtp(format specific param)
用于对 rtpmap 指定的编码格式做进一步描述:
a=fmtp:<format> <format specific parameters>
- format:等于其上最近一个 rtpmap 的 payload 值,所以 a=fmtp 行必须在 a=rtpmap 行的后面,有顺序之分
- format specific parameters:对 rtpmap 编解码格式的进一步描述
如对 h264 编解码格式:
a=rtpmap:102 H264/90000
a=fmtp:102 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
- level-asymmetry-allowed:指定
- packetization-mode:
- profile-level-id:16进制
2.8.3 rtcp-fb(rtcp feedback)
rtpmap 编解码格式支持的 rtcp 反馈消息类型,如:
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
- goog-remb:Receiver Estimated Max Bitrate 接收端码率反馈,参考 https://tools.ietf.org/html/draft-alvestrand-rmcat-remb-03
- transport-cc:传输层拥塞控制,参考 https://tools.ietf.org/html/draft-holmer-rmcat-transport-wide-cc-extensions-01
- ccm fir:Full Intra Request,关键帧请求,主要用于发送端刚加入会议时,接收端传递此消息,让发送端尽快发送关键帧。fir 在 rfc5104(新的定义,一般用这种)和 rfc2032(旧的定义,不加 ccm 标识) 中进行了定义
- nack:Negative Acknowledgement,nack 实际上分为 RTPFB 和 PSFB,在 rtcp 的 FMT 字段进行区分。这里指 rtp 包丢失重传,参考 rfc4585
- nack pli:nack Picture Loss Indication,当丢 rtp 包太多时,接收端发送此消息,让发送端尽快发送新的关键帧过来
2.8.4 rtx
当 rtp 丢包重传时,可以直接重传原始包,也可以将待重传包的 rtp payload type 填入新的负载类型值,用于表明这是一个重传包。新的负载类型值即 sdp 中的 rtx rtpmap。rtx 依赖于某个非重传的 rtpmap 编解码格式,表示此 rtx 专门用来重传所属的编解码格式:
a=rtpmap:123 H264/90000
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
如上,重传 rtpmap 的 encoding name 为 rtx,pt 值为 118,如何与其上的 rtpmap:123 H264 相关联呢?答案是借助 a=fmtp:118 apt=123,apt= 的值即此 rtx 所属的非重传编解码格式。
注意,rtx 不需要双向协商,即只要本端的 sdp 中出现 rtx 即表明本端重传 rtp 时,以 rtx 的方式重传。
rtx 重传包也会拥有自己独立的 ssrc,rtx 的 ssrc 参见下文。
2.9 extmap
rtp 头部扩展,参考 rfc5285:
a=extmap:<value>["/"<direction>] <URI> <extensionattributes>
如一个 audio level 支持的头部扩展:
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
value 和 url 可以自定定义。如上,offer 中的 extmap 表明 offer 端支持发送接收带这些扩展头的 rtp 包,answer 中的 extmap 只能从这些给定的 extmap 进行协商。
2.10 ssrc
ssrc 即 media level 的源同步标识,参考 rfc5576:
a=ssrc:<ssrc-id> <attribute>
a=ssrc:<ssrc-id> <attribute>:<value>
ssrc-id 是一个在 [0, 2^32-1] 之间的随机值,如某个 audio level 的 ssrc:
a=ssrc:724846199 cname:wJobAtYmVV7u5Y3x
a=ssrc:724846199 msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 13eb9491-262e-4982-9aea-1c6af9e8ef22
a=ssrc:724846199 mslabel:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
a=ssrc:724846199 label:13eb9491-262e-4982-9aea-1c6af9e8ef22
a=ssrc:724846199 出现了多次,但是 attribute 都不同。
2.10.1 ssrc-group
定义一组相关联的 ssrc:
a=ssrc-group:<semantics> <ssrc-id> ...
- semantics:可以取值为 FID(Flow Identification)、FEC(Forward Error Correction)
- ssrc-id:可以有多个,表明是一组相关联的 ssrc
在 rtx 的语义中,rtx 重传包不仅有不同于被重传包的 pt 值,也有不同的 ssrc 值,rtx ssrc 在 ssrc-group 字段中,如下是 video media level 示例:
a=ssrc-group:FID 3473705511 683075964
a=ssrc:3473705511 cname:wJobAtYmVV7u5Y3x
a=ssrc:3473705511 msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 0befb6f8-46a8-4890-9955-b53689962daf
a=ssrc:3473705511 mslabel:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
a=ssrc:3473705511 label:0befb6f8-46a8-4890-9955-b53689962daf
a=ssrc:683075964 cname:wJobAtYmVV7u5Y3x
a=ssrc:683075964 msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 0befb6f8-46a8-4890-9955-b53689962daf
a=ssrc:683075964 mslabel:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
a=ssrc:683075964 label:0befb6f8-46a8-4890-9955-b53689962daf
其中,semantics 为 FID,ssrc-id 有两个,第一个是正常 media level 的 ssrc,第二个 ssrc 即 rtx 的重传 ssrc。
2.11 示例解释
下面是一个 offer 示例,将逐行解释,前文没有描述的字段,在这里进行描述:
// sdp版本号为0
v=0
// 用户名为空,会话id是 1044094566052576507,会话版本是2(会话重协商 sess-version 应加 1),地址类型为 IP4,地址为 127.0.0.1(可忽略)
o=- 1044094566052576507 2 IN IP4 127.0.0.1
// 会话名为空
s=-
// 实时会话
t=0 0
// mid=0 和 mid=1 的 media track 多路复用同一个网络连接
a=group:BUNDLE 0 1
// WMS 是 WebRTC Media Stram 的缩写,这里给 Media Stream 定义了一个唯一的标识符。一个 Media Stream 可以有多个 track(video track、audio track),这些 track 就是通过这个唯一标识符关联起来的,具体见下面的媒体行(m=)以及它对应的附加属性(a=ssrc)
a=msid-semantic: WMS g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
// 音频媒体描述,端口为9(可忽略),采用 UDP 传输加密的 RTP 包,支持 RTCP 反馈,111、103 是 audio 可能采用的编码格式 payload type 值
m=audio 9 UDP/TLS/RTP/SAVPF 111 103
// 音频发送者的 IP4 地址,WebRTC 采用 ICE,这里的 0.0.0.0 可直接忽略
c=IN IP4 0.0.0.0
// RTCP 采用的端口、IP地址,因为下面的 a=rtcp-mux,这里可忽略
a=rtcp:9 IN IP4 0.0.0.0
// ice 协商认证字段
a=ice-ufrag:Fyzi
a=ice-pwd:/NDZTbh8NX+BTFjRyZy7Heqc
// 本端支持的 ice 类型是 trickle
a=ice-options:trickle
// dtls 握手指纹
a=fingerprint:sha-256 60:DF:D2:8E:AC:1B:7C:DB:DA:36:39:57:AA:DD:1A:C2:43:58:36:09:80:56:CA:18:F4:85:6B:9F:B1:8F:78:13
// dtls 握手角色,支持主动和被动
a=setup:actpass
// 本 audio 的 mid 为 0,对应前面的 a=group:BUNDLE 0 1
a=mid:0
// audio 支持的 rtp 扩展头
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
// audio 只支持发送 rtp,不支持接收 rtp。但是注意,仍然支持接收 rtcp
a=sendonly
// msid后面带两个id,第一个是 Media Stream 的 id,第二个是 audio track 的 id
a=msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 13eb9491-262e-4982-9aea-1c6af9e8ef22
// rtcp 多路复用 rtp 的网络通道
a=rtcp-mux
// opus 的 pt 值为 111,clock rate 为 48000,2 声道
a=rtpmap:111 opus/48000/2
// opus 编解码格式支持 transport-cc rtcp 反馈,注意,transport-cc rtcp 反馈需要 a=extmap:3 扩展头的支持
a=rtcp-fb:111 transport-cc
// 进一步描述 opus 编解码格式,支持带内 fec
a=fmtp:111 minptime=10;useinbandfec=1
// audio 也支持 ISAC 编解码格式
a=rtpmap:103 ISAC/16000
// audio 的 ssrc,cname用来唯一标识媒体的数据源
a=ssrc:724846199 cname:wJobAtYmVV7u5Y3x
// msid 与前面的 a=msid 字段相同
a=ssrc:724846199 msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 13eb9491-262e-4982-9aea-1c6af9e8ef22
// mslabel:audio track id
a=ssrc:724846199 mslabel:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
// label:webrtc media stream id
a=ssrc:724846199 label:13eb9491-262e-4982-9aea-1c6af9e8ef22
// 从下面开始是 video track 的媒体描述
m=video 9 UDP/TLS/RTP/SAVPF 96 97 102 121
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:Fyzi
a=ice-pwd:/NDZTbh8NX+BTFjRyZy7Heqc
a=ice-options:trickle
a=fingerprint:sha-256 60:DF:D2:8E:AC:1B:7C:DB:DA:36:39:57:AA:DD:1A:C2:43:58:36:09:80:56:CA:18:F4:85:6B:9F:B1:8F:78:13
a=setup:actpass
// 本 video 的 mid 为 1,对应前面的 a=group:BUNDLE 0 1
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=sendonly
a=msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 0befb6f8-46a8-4890-9955-b53689962daf
a=rtcp-mux
a=rtcp-rsize
// 支持 vp8 编解码格式
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
// 支持 h264 编解码格式
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
// pt 值为 102 的 h264 编解码格式支持 rtp 丢包重传
a=rtpmap:121 rtx/90000
a=fmtp:121 apt=102
// video 发送媒体正常的 ssrc 和支持 rtx 重传的 ssrc
a=ssrc-group:FID 3473705511 683075964
a=ssrc:3473705511 cname:wJobAtYmVV7u5Y3x
a=ssrc:3473705511 msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 0befb6f8-46a8-4890-9955-b53689962daf
a=ssrc:3473705511 mslabel:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
a=ssrc:3473705511 label:0befb6f8-46a8-4890-9955-b53689962daf
a=ssrc:683075964 cname:wJobAtYmVV7u5Y3x
a=ssrc:683075964 msid:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp 0befb6f8-46a8-4890-9955-b53689962daf
a=ssrc:683075964 mslabel:g4n21yfXiVM9K1CrtoSZMNxoNI7kBbY9rHgp
a=ssrc:683075964 label:0befb6f8-46a8-4890-9955-b53689962daf
3. 协商失败
媒体协商可能失败,answer 端会检查每一个 media level,如果某个 media level 协商失败,answer 中的 sdp 如何表明呢?有两种方式:
- m 行中的 port 字段为 0,如 m=video 0 UDP/TLS/RTP/SAVPF 96 97 102 121
- media direction 字段为 inactive,如 a=inactive
4. PlanB and UnifiedPlan
webrtc 引入了 stream 和 track 的概念,一个 track 即一路音频或视频流,比如一个终端,可以发送屏幕共享一路流、摄像头一路流、麦克风音频一路流。每一路单独流是一个 track,集合在一起形成了一个 stream,每一路流通过 ssrc 来进行区分。
两路音频流 planb 的 sdp 格式:
...
a=group:BUNDLE audio
a=msid-semantic: WMS stream-id-2 stream-id-1
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
...
a=mid:audio...
a=rtpmap:103 ISAC/16000
...
a=ssrc:10 cname:cname
a=ssrc:10 msid:stream-id-1 track-id-1
a=ssrc:10 mslabel:stream-id-1
a=ssrc:10 label:track-id-1
a=ssrc:11 cname:cname
a=ssrc:11 msid:stream-id-2 track-id-2
a=ssrc:11 mslabel:stream-id-2
a=ssrc:11 label:track-id-2
两路音频流的 UnifiedPlan 格式:
...
a=group:BUNDLE 0 1
a=msid-semantic: WMS
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
...
a=mid:0
...
a=sendrecv
a=msid:- track-id-1
...
a=rtpmap:103 ISAC/16000
...
a=ssrc:10 cname:cname
a=ssrc:10 msid: track-id-1
a=ssrc:10 mslabel:
a=ssrc:10 label:track-id-1
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
...
a=mid:1
...
a=sendrecv
a=msid:- track-id-2
...
a=rtpmap:103 ISAC/16000
...
a=ssrc:11 cname:cname
a=ssrc:11 msid: track-id-2
a=ssrc:11 mslabel:
a=ssrc:11 label:track-id-2
可见,planb 格式中,audio 只有一个 media level,media level 中有多个 track 时不同 track 通过 ssrc 进行区分,不同 track 必须使用同一种编解码格式。
unified plan 格式中,audio 有两个 media level,每个 media level 对应一个 track,也对应一个 ssrc 进行区分,不同 track 可以使用不同的编解码格式。
planb 和 unified plan 格式的差异注意意义在于:不同音视频流可以支持不同的编解码格式。
当只有一路音频流和一路视频流时,两则一样。
5. sip 呼叫中的 sdp
sip 呼叫中的 sdp 与 webrtc 会有一些显著的区别,下面分别称述。
5.1 网络传输上的区别
- sip 呼叫是一套完整的运行在 TCP/UDP 之上的协议,sdp 只是 sip 呼叫中的一部分;webrtc 中的 sdp 需要依赖其它协议(如 http、websocket等)由用户自定义传输。
- webrtc 引入了 ICE 协议,致力于打通不同网络之间(如两个处于不同 NAT 下的设备)的连接;sip 呼叫则需要开发者根据网络拓扑类型自定义呼叫方式(如经由一个网关),以实现不同网络类型的互通,sip 协议本身带来的跨网络连接能力有限(如自带 rport 功能)。
- webrtc 一般不同 tracks 之间会复用一个网络链接;sip 中不同 tracks,每个 track 的 rtp、rtcp 都会有自己的网络连接。
- webrtc 通过 candidates 传递网络连接信息;sip 的网络连接信息直接写在 sdp 中
// RTC
a=candidate:1 1 udp 2013266431 192.168.2.106 12345 typ host
// SIP
c=IN IP4 192.168.2.106
m=audio 3262 RTP/AVP 107 108
m=video 3264 RTP/AVP 97 126
5.2 媒体流的区别
- webrtc 支持任意路音视频流;sip 只支持一路音频流和一路视频流(通过 BFCP 协议可以扩展多一路屏幕共享视频流)