SDP协议
概述
SDP(会话描述协议),用于两个会话实体之间的媒体协商,并达成一致,属信令语言族,采用文本(字符)描述形式。
SDP(session description Protocal)会话描述协完全是一种会话描述格式 ― 它不属于传输协议 ― 它只使用不同的适当的传输协议,包括会话通知协议(SAP)、会话初始协议(SIP)、实时流协议(RTSP)、MIME 扩展协议的电子邮件以及超文本传输协议(HTTP)。SDP协议是也是基于文本的协议。SDP 不支持会话内容或媒体编码的协商,所以在流媒体中只用来描述媒体信息。媒体协商这一块要用RTSP来实现.
JSEP将客户端之间传递的信令分为两种:offer
信令和answer
信令。他们主要内容的格式都遵循会话描述协议(Session Description Protocal,简称SDP)。
SDP协议格式:SDP描述由许多文本行组成,文本行的格式为<类型>=<值>,
- <类型>是一个字母
- <值>是结构化的文本串,其格式依<类型>而定。
注意:协议的等号前后不可有空格!!!type: 该字节为单字节(如: v,o, m等)区分大小写。
<type>=< value > [CRLF]
其实可以将其简化一下,它就是一个在点对点连接中描述自己的字符串,我们可以将其封装在JSON中进行传输,在PeerConnection建立后将其通过服务器中转后,将自己的SDP描述符和对方的SDP描述符交给PeerConnection就行了。(暂时没试过)
常见的fields有:
通过offer和answer交换SDP描述符
大致上在两个用户(甲和乙)之间建立点对点连接流程应该是这个样子(这里不考虑错误的情况,RTCPeerConnection简称PC):
- 甲和乙(也就是图中的A和B)各自建立一个PC实例
- 甲通过PC所提供的createOffer()方法建立一个包含甲的SDP描述符的offer信令
- 甲通过PC所提供的setLocalDescription()方法,将甲的SDP描述符交给甲的PC实例
- 甲将offer信令通过服务器发送给乙
- 乙将甲的offer信令中所包含的的SDP描述符提取出来,通过PC所提供的setRemoteDescription()方法交给乙的PC实例
- 乙通过PC所提供的createAnswer()方法建立一个包含乙的SDP描述符answer信令
- 乙通过PC所提供的setLocalDescription()方法,将乙的SDP描述符交给乙的PC实例
- 乙将answer信令通过服务器发送给甲
- 甲接收到乙的answer信令后,将其中乙的SDP描述符提取出来,调用setRemoteDescripttion()方法交给甲自己的PC实例
通过在这一系列的信令交换之后,甲和乙所创建的PC实例都包含甲和乙的SDP描述符了,完成了两件事的第一件。我们还需要完成第二件事——获取连接两端主机的网络地址,这里可以参考P2P的链接过程,此处不赘述。
SDP协议分析 RFC4566
会话描述
v = 0 协议版本
协议版本,目前定义0.版本,这是当前使用的唯一SDP版本。o=<username>,<sess-id>,<sess-version>、<nettype>,<addrtype>,<unicast-address>
示例:o = - 6311806030512608073 2 IN IP4 127.0.0.1
<username>是用户在始发主机上的登录名,或者如果始发主机不支持用户标识的概念,则为“ - ” 。<username>不能包含空格。
<sess-id>是一个数字字符串,使得<username>,<sess-id>,<nettype>,<addrtype>和<unicast-address>的元组形成会话的全局唯一标识符。
<sess-version> 是此会话描述的版本号。
<nettype> 网络类型。是一个给出网络类型的文本字符串。最初“IN”被定义为具有“Internet”的含义,但其他值可以在将来注册。
<addrtype> 地址类型 IP4和IP6。
<unicast-address>是创建会话的机器的地址。
-
s = -(会话名称)
“s =”字段是文本会话名称。
每个会话描述必须有且仅有一个“s =”字段。“s =”字段绝不能为空,并且应该包含ISO 10646字符(但也请参阅“a = charset”属性)。如果会话没有有意义的名称,则应使用值“s =”(即一个空格作为会话名称)。
-
i = (会话描述)
该字段提供有关会话的文本信息。有且最多只能有一个会话级的“i =”每个会话描述字段,并且最多一个“I =”每传媒领域。如果存在“a = charset”属性,则它指定在“i =”字段中使用的字符集。如果“a = charset”属性不存在,则“i =”字段必须包含采用UTF-8编码的ISO 10646字符。
-
u = (描述的URI)
URI是由WWW客户端使用的统一资源标识符。 URI应该是一个指向 会话附加信息的指针。这个字段是可选的,但是如果它存在,它必须 在第一个媒体字段之前被指定。 每个会话描述不允许超过一个URI字段。
e = (电子邮件地址)和 p = (电话号码)
可选。负责会议的人员的联系信息,但不一定是创建会议通知的同一个人。
如果有电子邮件地址或电话号码,则必须在第一个媒体字段之前指定。
RFC 2327要求“e =”或“p =”是必需的。两者都是 现在可选,以反映实际使用情况*c = (连接信息 - 如果包含,则不需要所有媒体)
c = <nettype> <addrtype> <connection-address>。第一个字段是网络类型;第二个字段是地址类型,它使得SDP可用于不基于IP的会话;第三个字段是链接地址。
-
b = <bwtype>:<带宽>
这个可选字段表示会话或媒体使用的建议宽带。
bwtype可以是CT或AS,CT方式是设置整个会议的带宽,AS是设置单个会话的带宽。缺省带宽是千比特每秒。
时间描述
-
t = <start-time> <stop-time>
以秒为单位的网络时间协议(NTP)时间值的表示。要将这些值转换为UNIX时间,需要减去 十进制2208988800.NTP
如果<stop-time>设置为零,则会话不受限制,尽管直到<开始时间>之后它才会变为活动状态。如果<开始时间>也为零,则该会话被认为是永久的。
- r = * <重复间隔> <活动持续时间> <偏离开始时间>
如果一个会话在多个不规则间隔时间激活,可以使用多个“t =”行; 每个额外的“t =”行指定了会话活动的额外时间段。如果会话在正常时间处于活动状态,则应在“t =”行之外和之后使用“r =”行 -- 在这种情况下,“t =”行指定开始和重复序列的停止时间。
一个或更多时间描述(如下所示):
z = * <调整时间> <偏移> <调整时间> <偏移> ....
k = <方法>:<加密密钥>
用于实验参数的“x-”前缀符号的大多数用法是不允许使用,其他用途已弃用。a = *(零个或多个会话属性行)
零个或多个媒体描述
媒体描述,如果有的话
m = <media> <port> <proto> <fmt> ...
<media>是媒体类型。<media>可以是,"audio","video", "text", "application" and "message"。
<port>是媒体流发送到的传输端口。
<proto>是传输协议。传输协议的含义取决于相关“c =”字段中的地址类型字段。因此IP4的“c =”字段表示传输协议在IP4上运行。定义了以下传输协议,但可以通过注册新协议来扩展。i = *(媒体标题)
c = *(连接信息 - 可选,如果包含在会话级别)
b = *(零个或多个带宽信息行)
k = *(加密密钥)
a = *(零个或多个媒体属性行)
详情参考这里
SDP属性
a = cat:<category>
该属性给出会话的点分隔层次类别。这是为了使接收者能够按类别过滤不需要的会话。a = keywds:<关键字>
与cat属性一样,这有助于在接收方识别想要的会话a =tool:<工具的名称和版本>
这给出了用于创建会话描述的工具的名称和版本号。它是一个会话级别的属性,它不依赖于字符集。a = ptime:<数据包时间>
这给出了数据包中介质所代表的时间长度(以毫秒为单位)a = maxptime:<最大数据包时间>
这给出了可以封装在每个数据包中的媒体的最大数量,以毫秒为单位表示。a = rtpmap:<有效载荷类型> <编码名称> / <时钟速率> [/ <编码参数>]
a = recvonly
这指定工具应在适用时以只收模式启动。它可以是会话或媒体 - level属性,它不依赖于字符集。请注意,仅适用于介质,而不适用于任何关联的控制协议a = sendrecv
这指定工具应在发送和接收模式下启动。a = sendonly
这指定工具应以只发送模式启动。a = inactive
这指定工具应在非活动模式下启动。
如果没有任何属性“sendonly”,“recvonly”,“inactive”,和“sendrecv”时,应将“sendrecv”假定为非会议类型“broadcast”或“H332”(见下文)的会话的默认值。
a = orient:<orientation>
通常,这仅用于白板或演示工具。它指定屏幕上工作区的方向。这是媒体级属性。允许的值是“肖像”,“风景”和“海景”(颠倒的风景)。它不依赖于字符集。a = charset:<字符集>
它指定用于显示会话名称和信息数据的字符集。默认情况下,使用UTF-8编码的ISO-10646 字符集。a = sdplang:<language tag>
这可以是会话级属性或媒体级属性。作为会话级属性,它指定了会话描述的语言。作为媒体级属性,它指定与该媒体关联的任何媒体级SDP 信息字段的语言。如果会话描述或媒体使用多种语言,则可以在会话或媒体级别提供多个sdplang 属性多种语言,在这种情况下,属性的顺序表示会话或媒体中从最重要到最不重要的各种语言的重要性的顺序。a = lang:<language tag>
这可以是会话级属性或媒体级属性。作为会话级属性,它指定了正在描述的会话的默认语言。a =framerate:<帧速率>
这给出了以帧/秒为单位的最大视频帧速率。它旨在作为视频数据编码的建议。a =quality:<质量>
这将编码质量作为整数值提出建议。视频质量属性的意图是指定帧率和静止图像质量之间的非默认折衷a = fmtp:<格式> <格式特定参数>
此属性允许特定于特定格式的参数以SDP无需理解的方式传送。格式必须是为媒体指定的格式之一。特定于格式的参数可以是由SDP传送并且由Handley等人给出的任何一组参数。
SDP包括以下一些方面:
- 1) 会话的名称和目的
- 2) 会话存活时间
- 3) 包含在会话中的媒体信息,包括:
- 媒体类型(video, audio, etc)
传输协议(RTP/UDP/IP, H.320, etc)
媒体格式(H.261 video, MPEG video, etc)
多播或远端(单播)地址和端口
- 媒体类型(video, audio, etc)
- 4) 为接收媒体而需的信息(addresses, ports, formats and so on)
- 5) 使用的带宽信息
- 6) 可信赖的接洽信息(Contact information)
协议实例
Offer from localPeerConnection:
// SDP开头4个必填字段
- **v=0** // SDP版本号,设置为0,这是当前使用的唯一SDP版本
- **o=**- 6976870941538595051 2 IN IP4 127.0.0.1
// 来源包含一系列字段,其中包括用户、会话ID、版本ID、网络地址、地址类型和地址;这里的地址是一个环回地址(127.0.0.1)
- **s=-** // 主题,会话名,没有的话使用-代替
- **t=0 0** // 时间,两个值分别是会话的起始时间和结束时间,这里都是0代表没有限制
// 接下来是SDP的属性用于定义此SDP中使用的媒体流ID语义。
- **a=group:**BUNDLE audio video // 需要共用一个传输通道传输的媒体,如果没有这一行,音视频,数据就会分别单独用一个udp端口来发送
- **a=msid-semantic:** WMS qovvqrAafFmIE8VZCxLsWvxdW7zQtcDpzzsb
//WMS是WebRTC Media Stream简称,这一行定义了本客户端支持同时传输多个流,一个流可以包括多个track,
//一般定义了这个,后面a=ssrc这一行就会有msid,mslabel等属性
// 接下来,此SDP提议包含三个用于建立数据通道的媒体行(m =)
- **m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126**
// 第一个音频媒体行列出了端口号(9,不会被使用,因为实际端口位于a = candidate字段中)、扩展的安全音频/视频配置文件( UDP/TLS/RTP/SAVPF),
//随后列出可能的编解码器(有效负载类型分别是111 103 104 9 0 8 106 105 13 110 112 113 126),用于发送DTMF
// PCM编解码器使用静态有效负载(0~95),而OPUS和telephone-event则使用动态有效负载(范围96~127)。
// 每个编码器都有一个a=rtpmap属性,但此属性对于静态有效负载而言为可选属性
- **c=**IN IP4 0.0.0.0 //媒体链接数据字段,设置了一个无效的IP地址(人们常说的“黑洞”地址0.0.0.0)
- **a=**rtcp:9 IN IP4 0.0.0.0 // 包括一个RTCP端口和IP地址属性,但后者却设置为无效的IP地址和端口,暂不清楚该属性的用途
- **a=**ice-ufrag:U8F/ //用户名片段
- **a=ice-pwd:**Hfo+6wYJCMeLWahO6CLPwbLZ // ICE密码。每个m行,将包括不同的用户名和密码。使用时使用第一组
- **a=ice-options:**trickle
- **a=fingerprint:**sha-256 EF:BD:53:BB:A4:E9:28:87:74:A4:15:27:59:B8:2D:05:61:04:CA:76:C8:2D:1C:BF:F0:CC:12:D8:8A:CC:AD:49
// 用于建立DTLS链接的自签名证书的SHA-256散列,因此必须协商确定由哪一方开通链接
- **a=setup:actpass** //以上这行代表本客户端在dtls协商过程中,可以做客户端也可以做服务端,参考rfc4145 rfc4572
- **a=mid:audio** //在前面BUNDLE这一行中用到的媒体标识
- **a=extmap:**1 urn:ietf:params:rtp-hdrext:ssrc-audio-level // 指出我要在rtp头部中加入音量信息,参考 rfc6464
- **a=sendrecv** // 指示视频通话为双向会话。另外几种类型是recvonly,sendonly,inactive(不收不发)
- **a=rtcp-mux** // RTP多路复用属性指示将通过用于RTP的同一端口对RTCP进行多路传输
- **a=rtpmap:**111 opus/48000/2
- **a=rtcp-fb:**111 transport-cc
// 以上这行说明opus编码支持使用rtcp来控制拥塞,
- **a=fmtp:**111 minptime=10;useinbandfec=1
// 对opus编码可选的补充说明,minptime代表最小打包时长是10ms,useinbandfec=1代表使用opus编码内置fec特性
// 13个可能的编解码器的rtpmap属性
- 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
- a=ssrc:1815301143 cname:JsTxr0Ps83IbEYOT
//cname用来标识一个数据源,ssrc当发生冲突时可能会发生变化,但是cname不会发生变化,也会出现在rtcp包中SDEC中,
//用于音视频同步
- a=ssrc:1815301143 msid:qovvqrAafFmIE8VZCxLsWvxdW7zQtcDpzzsb 2e0529d2-d3bd-460f-a325-71d148367b1b
//以上这一行定义了ssrc和WebRTC中的MediaStream,AudioTrack之间的关系,msid后面第一个属性是stream-id,
//第二个是track-ida=ssrc:1815301143 mslabel:qovvqrAafFmIE8VZCxLsWvxdW7zQtcDpzzsb
- a=ssrc:1815301143 label:2e0529d2-d3bd-460f-a325-71d148367b1b
- **m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 123 127 122 125 107 108 109 124**
- c=IN IP4 0.0.0.0
- a=rtcp:9 IN IP4 0.0.0.0
- a=ice-ufrag:U8F/
- a=ice-pwd:Hfo+6wYJCMeLWahO6CLPwbLZ
- a=ice-options:trickle
- a=fingerprint:sha-256 EF:BD:53:BB:A4:E9:28:87:74:A4:15:27:59:B8:2D:05:61:04:CA:76:C8:2D:1C:BF:F0:CC:12:D8:8A:CC:AD:49
- a=setup:actpass
- **a=mid:video**
- a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
- a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
- a=extmap:4 urn:3gpp:video-orientation
- a=extmap:5 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
- a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
- a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
- a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
- **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
//ccm是codec control using RTCP feedback message简称,意思是支持使用rtcp反馈机制来实现编码控制,
//fir是Full Intra Request简称,意思是接收方通知发送方发送幅完全帧过来
- **a=rtcp-fb:**96 nack // 支持关键帧丢包重传,参考rfc4585
- **a=rtcp-fb:**96 nack pli // 支持关键帧丢包重传,参考rfc4585
- **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=rtpmap:**99 rtx/90000
- **a=fmtp:**99 apt=98
- **a=rtpmap:**100 H264/90000
- **a=rtcp-fb:**100 goog-remb // 支持使用rtcp包来控制发送方的码流
- **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 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42001f
//h264编码可选的附加说明
- **a=rtpmap:**101 rtx/90000
- **a=fmtp:**101 apt=100
- **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=42e01f
//h264编码可选的附加说明
- **a=rtpmap:**123 rtx/90000
- **a=fmtp:**123 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=1;profile-level-id=4d0032
//h264编码可选的附加说明
- **a=rtpmap:**122 rtx/90000
- **a=rtcp-fb:**122 apt=127
- **a=rtcp-fb:**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=640032
//h264编码可选的附加说明
- **a=rtpmap:**107 rtx/90000
- **a=fmtp:**107 apt=125
- **a=rtpmap:**108 red/90000
- **a=rtpmap:**109 rtx/90000
- **a=fmtp:**109 apt=108
//以上两行是VP8编码的重传包rtp类型
- **a=rtpmap:**124 ulpfec/90000 //支持ULP FEC,参考rfc5109
- **a=ssrc-group:**FID 2893345836 1252482188
// 在webrtc中,重传包和正常包ssrc是不同的,上一行中前一个是正常rtp包的ssrc,后一个是重传包的ssrc
- **a=ssrc:**2893345836 cname:JsTxr0Ps83IbEYOT
- **a=ssrc:**2893345836 msid:qovvqrAafFmIE8VZCxLsWvxdW7zQtcDpzzsb f10f7f49-dd82-4916-a517-a28547e2c2ff
- **a=ssrc:**2893345836 mslabel:qovvqrAafFmIE8VZCxLsWvxdW7zQtcDpzzsb
- **a=ssrc:**2893345836 label:f10f7f49-dd82-4916-a517-a28547e2c2ff
- **a=ssrc:**1252482188 cname:JsTxr0Ps83IbEYOT
- **a=ssrc:**1252482188 msid:qovvqrAafFmIE8VZCxLsWvxdW7zQtcDpzzsb f10f7f49-dd82-4916-a517-a28547e2c2ff
- **a=ssrc:**1252482188 mslabel:qovvqrAafFmIE8VZCxLsWvxdW7zQtcDpzzsb
- **a=ssrc:**1252482188 label:f10f7f49-dd82-4916-a517-a28547e2c2ff
**media之前的行是Session级别,也就是对会话的描述,而media及它后面的 a =行称作media行,属于media级别。**
注:
- v,o,s,t,m为必须的,其他项为可选。
- 如果SDP语法分析器不能识别某一类型(Type),则整个描述丢失;
- 如果”a=”的某属性值不理解,则予以丢失
- 整个协议区分大小写
- “=”两侧不允许有空格
- 会话级的描述就是媒体级描述的缺省值
- 所有均格式为<type>=<value>
参考资料
1、使用WebRTC搭建前端视频聊天室——信令篇
2、做WebRTC,千万别把媒体和信令混在一起
3、webrtc进阶-信令篇-之三:信令、stun、turn、ice
4、SDP 协议分析
5、WebRTC56版本SDP详细解析
6、SDP协议介绍 有必要多看几遍!!