在P2P学习(三)网络传输基本知识---ICE中提及过SDP
一:SDP了解
(一)SDP了解及基本格式
SDP案例: 下面这个例子中,就是一个标准的SDP案例
首先是版本信息v,一般都是0;
那第二个是o,表示为owner,这个SDP归谁所有,比如案例中主机名字jdoe,有多个系列号,最后包含一个IP地址。但是这IP地址并不一定是最终要进行传输的IP的地址,在我们WEBRTC里并不是用这个IP,而是使用candidate中的ip。
c表示connection表示连接这个网络的IPV4,OK,这些都不太重要。
那么这里呢,有一个媒体信息m,就是说在我这次交换媒体信息里,媒体就是一个audio也就是音频,它使用的是RTP的协议,
对于这个音频它有一个参数a=rtpmap,就是我使用的这个音频的编码方式是PCMU采样率是8000,这里大家了解一下就好。
最重要的是最后两行,它检测到有两种Candidate就是候选的路:
第一条是UDP的,IP 是 10.0.1.1端口是 8998,类型是host;
第二种也是UDP的,IP是192.0.2.3端口是45664,类型是穿越NAT的映射地址,
这里没有中继地址,就是最终不可能通过中继传输数据,那要么就是说我们在同一局域网内可以互通,要么就是穿越NAT走P2P,这就是SDP。
(二)显示通讯双方的SDP内容
从WebRTC学习(六)端对端传输中获取!!!
/* * Copyright (c) 2015 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. */ button { margin: 0 20px 25px 0; vertical-align: top; 134px; } textarea { color: #444; font-size: 0.9em; font-weight: 300; height: 20.0em; padding: 5px; calc(100% - 10px); } div#getUserMedia { padding: 0 0 8px 0; } div.input { display: inline-block; margin: 0 4px 0 0; vertical-align: top; 310px; } div.input > div { margin: 0 0 20px 0; vertical-align: top; } div.output { background-color: #eee; display: inline-block; font-family: 'Inconsolata', 'Courier New', monospace; font-size: 0.9em; padding: 10px 10px 10px 25px; position: relative; top: 10px; white-space: pre; 270px; } div#preview { border-bottom: 1px solid #eee; margin: 0 0 1em 0; padding: 0 0 0.5em 0; } div#preview > div { display: inline-block; vertical-align: top; calc(50% - 12px); } section#statistics div { display: inline-block; font-family: 'Inconsolata', 'Courier New', monospace; vertical-align: top; 308px; } section#statistics div#senderStats { margin: 0 20px 0 0; } section#constraints > div { margin: 0 0 20px 0; } h2 { margin: 0 0 1em 0; } section#constraints label { display: inline-block; 156px; } section { margin: 0 0 20px 0; padding: 0 0 15px 0; } video { background: #222; margin: 0 0 0 0; -- 100%; var(--width); height: 225px; } @media screen and (max- 720px) { button { font-weight: 500; height: 56px; line-height: 1.3em; 90px; } div#getUserMedia { padding: 0 0 40px 0; } section#statistics div { calc(50% - 14px); } }
<html> <head> <title> WebRTC PeerConnection </title> <link href="./css/main.css" rel="stylesheet" /> </head> <body> <h1>Index.html</h1> <div id="preview"> <div> <h2>Local:</h2> <video autoplay playsinline id="localvideo"></video> <h2>Offer SDP:</h2> <textarea id="offer"></textarea> </div> <div> <h2>Remote:</h2> <video autoplay playsinline id="remotevideo"></video> <h2>Answer SDP:</h2> <textarea id="answer"></textarea> </div> </div> <div> <button id="start">Start</button> <!--采集音视频数据--> <button id="call">Call</button> <!--创建双方的peerconnection,开始通信--> <button id="hangup">HangUp</button> <!--挂断--> </div> </body> <script type="text/javascript" src="https://webrtc.github.io/adapter/adapter-latest.js"></script> <script type="text/javascript" src="./js/main2.js"></script> </html>
'use strict' var localVideo = document.querySelector("video#localvideo"); var remoteVideo = document.querySelector("video#remotevideo"); var btnStart = document.querySelector("button#start"); var btnCall = document.querySelector("button#call"); var btnHangup = document.querySelector("button#hangup"); var offer = document.querySelector("textarea#offer"); var answer = document.querySelector("textarea#answer"); var localStream; //设置全局流,用来addStream发送给对端时使用 var pc1; //处理pc1与pc2时候,只需要站在一个角度就可以了,因为对端也是一样的 var pc2; function handleError(err){ console.err(err.name+":"+err.message); } function getMediaStream(stream){ localVideo.srcObject = stream; //显示到网页视频控件 localStream = stream; //保存到全局流中 } //采集本机音视频数据 function start(){ if(!navigator.mediaDevices || !navigator.mediaDevices.getUserMedia){ console.error("the getUserMedia is not support!"); return; }else{ var constraints = { audio:false, video:true }; navigator.mediaDevices.getUserMedia(constraints) .then(getMediaStream) //获取数据流 .catch(handleError); } } function getRemoteStream(e){ //会有多个流 remoteVideo.srcObject = e.streams[0]; //只取其中一个就可以了,就将远端的音视频流传给了remoteVideo } function getAnswer(desc){ answer.value = desc.sdp; pc2.setLocalDescription(desc); //7.远端设置本地描述信息 //发送描述信息SDP到signal信令服务端,与pc1进行交换 //8.pc1设置远端描述信息 pc1.setRemoteDescription(desc); //-----这里开始获取了所有对端的SDP信息,双端信息协商完成!!!!---- } function getOffer(desc){ //获取了描述信息,开始设置到peerConnection中去 //4.设置本地的描述信息,添加到peerconnection pc1.setLocalDescription(desc); offer.value = desc.sdp; //发送描述信息SDP到signal信令服务端,与pc2进行交换 //5.对端接收设置SDP信息 pc2.setRemoteDescription(desc); //6.创建Answer信息 pc2.createAnswer() .then(getAnswer) //7.远端设置本地描述信息 .catch(handleError); } function call(){ //1.创建peerConnect,pc1与pc2同时连接到signal服务器(这里是一起到本机) /* 在这个Connection里面实际上是有一个可选参数的, 这个可选参数就涉及到网络传输的一些配置 我们整个ICE的一个配置,但是由于是我们在本机内进行传输,所以在这里我们就不设置参数了,因为它也是可选的 所以它这里就会使用本机host类型的candidate */ pc1 = new RTCPeerConnection(); //调用方 pc2 = new RTCPeerConnection(); //被调用方 //当收到candidate后,会触发事件,获取候选者列表,之后调用send candidate发送给signal服务器,从而发送给对端。双方获取之后进行连通性检测 pc1.onicecandidate = (e) => { pc2.addIceCandidate(e.candidate); //开始添加给对端 }; pc2.onicecandidate = (e) => { pc1.addIceCandidate(e.candidate); //开始添加给对端 }; //pc2是相对特殊的,因为是被调用者,用于接受数据 pc2.ontrack = getRemoteStream; //被调用方,接收数据,有数据经过的时候调用ontrack事件 //下面要先添加媒体流,然后才进行媒体协商 //2.添加媒体流 localStream.getTracks().forEach((track)=>{ //获取所有的轨 pc1.addTrack(track,localStream); //将本地产生的音视频流添加到pc1的peerConnection }); //3.创建offer var offerOptions = { offerToRecieveAudio:0, //不处理音频 offerToRecieveVideo:1 }; pc1.createOffer(offerOptions) .then(getOffer) //4.设置本地的描述信息,添加到peerconnection .catch(handleError); } function hangup(){ pc1.close(); pc2.close(); pc1 = null; pc2 = null; } btnStart.onclick = start; btnCall.onclick = call; btnHangup.onclick = hangup;
Offer SDP:
v=0 o=- 1985257453350254958 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE 0 a=msid-semantic: WMS k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 121 127 120 125 107 108 109 124 119 123 c=IN IP4 0.0.0.0 a=rtcp:9 IN IP4 0.0.0.0 a=ice-ufrag:MDsa a=ice-pwd:gCubNMbGasNreqMgWPCAP8Tu a=ice-options:trickle a=fingerprint:sha-256 13:89:EC:41:89:FA:81:B1:29:19:B5:AA:83:80:EA:63:85:3A:3C:55:87:6F:B1:75:62:7E:62:63:95:27:2D:E2 a=setup:actpass a=mid:0 a=extmap:1 urn:ietf:params:rtp-hdrext:toffset a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time a=extmap:3 urn:3gpp:video-orientation a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay a=extmap:6 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:9 urn:ietf:params:rtp-hdrext:sdes:mid a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id a=sendrecv a=msid:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L 09d23028-9f4d-42ce-9d3d-1d924480e6d4 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: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:124 red/90000 a=rtpmap:119 rtx/90000 a=fmtp:119 apt=124 a=rtpmap:123 ulpfec/90000 a=ssrc-group:FID 2101549168 3058171047 a=ssrc:2101549168 cname:BcnQBDk0kQlMwSCJ a=ssrc:2101549168 msid:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L 09d23028-9f4d-42ce-9d3d-1d924480e6d4 a=ssrc:2101549168 mslabel:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L a=ssrc:2101549168 label:09d23028-9f4d-42ce-9d3d-1d924480e6d4 a=ssrc:3058171047 cname:BcnQBDk0kQlMwSCJ a=ssrc:3058171047 msid:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L 09d23028-9f4d-42ce-9d3d-1d924480e6d4 a=ssrc:3058171047 mslabel:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L a=ssrc:3058171047 label:09d23028-9f4d-42ce-9d3d-1d924480e6d4
v=0 o=- 1985257453350254958 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE 0 a=msid-semantic: WMS k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 121 127 120 125 107 108 109 124 119 123 c=IN IP4 0.0.0.0 a=rtcp:9 IN IP4 0.0.0.0 //-----------a代表属性,是对上面参数m的解释 a=ice-ufrag:MDsa a=ice-pwd:gCubNMbGasNreqMgWPCAP8Tu a=ice-options:trickle a=fingerprint:sha-256 13:89:EC:41:89:FA:81:B1:29:19:B5:AA:83:80:EA:63:85:3A:3C:55:87:6F:B1:75:62:7E:62:63:95:27:2D:E2 a=setup:actpass a=mid:0 a=extmap:1 urn:ietf:params:rtp-hdrext:toffset a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time a=extmap:3 urn:3gpp:video-orientation ...... a=ssrc:3058171047 label:09d23028-9f4d-42ce-9d3d-1d924480e6d4
Answer SDP:
v=0 o=- 1779057502215151538 2 IN IP4 127.0.0.1 s=- t=0 0 a=group:BUNDLE 0 a=msid-semantic: WMS m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 121 127 120 125 107 108 109 124 119 123 c=IN IP4 0.0.0.0 a=rtcp:9 IN IP4 0.0.0.0 a=ice-ufrag:o81l a=ice-pwd:DGamwD2wqNQEX7GeQAw0Q4dq a=ice-options:trickle a=fingerprint:sha-256 99:0E:B4:5E:DE:3D:B2:10:7D:5B:A5:B5:72:12:8B:4E:49:37:94:47:13:77:E2:10:8C:6A:FC:EF:9F:F4:EA:00 a=setup:active a=mid:0 a=extmap:1 urn:ietf:params:rtp-hdrext:toffset a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time a=extmap:3 urn:3gpp:video-orientation a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay a=extmap:6 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:9 urn:ietf:params:rtp-hdrext:sdes:mid a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=extmap:11 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: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:124 red/90000 a=rtpmap:119 rtx/90000 a=fmtp:119 apt=124 a=rtpmap:123 ulpfec/90000
二:SDP规范
SDP的规范将SDP分成两层:会话层和媒体层
(一)会话层
会话层包含了基本的名称和目的。
另外一个还有个存活时间,这个会话时间作用稍微大些,如果设置了十分钟 ,那么会话就是十分钟,对于音视频传输,一般我们都会设置成0,表示无限期,表示会话一直存在,不会主动将它结束掉。
在一个会话层中,包含了多个媒体信息,媒体层包括了重要的信息。
SDP都是由多个类型和值组成的。
每个SDP描述的都是一个会话,但是这个会话中是包含了多个媒体的描述信息。
(二)媒体层
媒体格式:媒体信息首先有媒体格式,是音频还是视频还是应用数据。
传输协议:TCP还是UDP
传输IP和端口:具体使用的Ip地址和端口,对于webrtc来说,端口和ip并不使用SDP这里描述的,它会使用专门的ICE收集到的candidate里面的IP地址和端口,所以在媒体信息中的这个IP地址和端口价值并不是很大,但是对于其他的非webrtc使用SDP就比较有意义。
媒体负载类型 :你是VP8还是VP9还是H264还是H265,这些都是媒体的负载类型。
那基本上媒体信息就包括这些内容,也非常的简单。
那复杂是对于每一种媒体类型我们可以定义更多的属性,在这些属性里面我们可以给他更细的规范,所以现在你可以看到,其实整个SDP的这个规范并不复杂,非常简单。
就是分成了两层,一层是会话层,一层是媒体层,这就是一个总纲性的SDP的规范。那其实还是比较好理解的。
(三)SDP格式
三:SDP结构
会话描述:只有一个
时间描述:只有一个
-----上为会话层,下为媒体层-----
媒体描述:多个媒体描述
(一)会话描述
会话描述的具体内容如下:
v = (protocol version) // 版本,一般是0,这里是说我们SDP使用的版本 o = (owner/create and session identifier) // 这个是创建的session的一个id标识 s = (session name) // session name可以不写,当不想写的时候写一个-,它就代表一个session name c = *(conn info - optional if included at session-leve) // 连接相关的一些信息,我们整个会话的IP地址和端口号,以及这个地址类型是IPv4还是IPv6,还有这个网络类型,是互联网还是其他网络,这些都通过c可以设置,但这个其实在每个媒体中都会自己设置,所以会话层这个c意义也不大 a=*(zero or name session attribute lines) // 这个a代表设置一些全局的属性,这个其实 用处也不大,这个就是整个会话层的描述,所以大家对这个有一个基本的了解就可以了。
一提到v、o、s、c、a能够知道他们在会话层具体的含义了。
(二)时间描述
t = (time the session is active) // 表示存活的时间 r = *(zero or more repeat times) // 表示重复的次数
(三)媒体描述
m = (media name and transport address) // 媒体层就表示媒体的名字和传输地址 c = *(conn info - optional if included at session-leve) // 媒体层也包括了传输相关的信息 b = *(bandwidth information) // 表示传输里的带宽信息是什么,多大 带宽可以设置 a = *(zero or name session attribute lines) // 还有一堆的属性,就是对m具体的解释,SDP复杂就在于a的类型特别多
四:字段含义
o=-中,“-”表示省略用户名,IN表示是因特网
媒体相关的比较重要:具体媒体类型、端口、传输地址(底层UDP,之后使用TLS-->DTLS,基于DTLS使用RTP--->SRTP具体传输的数据,内容SAVPF包括S安全、A:audio,V:Video,P:可以配置,F:反馈,通过SRTCP)、负载类型(一组、多个)
rtpmap修饰的是payload type,对于不同的payload type,比如说vp8,它的这个编码名称、采样率是多少,包括一些编码的参数,都可以在这里设置 例子: a=rtpmap:103 ISAC/16000 // 就说明它的编解码器103对应的ISAC,使用频率是每秒钟采样16000次
fmtp也比较重要,这个是对rtpmap的参数作进一步的说明(可选) a=fmtp:<format/payload type> parameters 例子:a = fmtp:103 apt=106 // 比如它这里是103的ISAC,是需要与payload type为106进行关联的
那么以上就是SDP的一个大体的规范,从我们刚刚描述的规范来看,就是它将整个描述分成两层:会话层和媒体层
会话层是全局唯一的,描述的一些基本信息,意义不大,最重要的是媒体层,媒体层中描述了媒体的类型是音频还是视频,传输相关的地址类型是IPV4还是IPV6还有端口以及payload类型也就是媒体都支持哪些编解码器,对于每一种编解码器可以作进一步指定,通过Attribute属性来指定,那么在属性里有两个重要的类型,一个是rtpmap,在这里可以定义payload和编解码器的对应关系以及它的采样率等基本信息。那如果还需要进一步说明的话,我们就可以利用Attribute的fmtp作进一步说明。
那么以上就是整个SDP的规范。
五:WebRTC中的SDP
在WebRTC中使用SDP相比正常的SDP规范有一些出入,那主要是为了让SDP更能适应WebRTC里面的一些设置以及会话的描述,在WebRTC中的SDP组成是包括了5部分
第一部分是会话元也就是会话层,它是最不重要的。
第二个,有专门的网络描述信息,而不是利用以前SDP规范中媒体层的Ip地址和端口了,所以它要单独的进行网络端信息描述。
第三个以前在SDP中是没有的,这个是流的相关的描述,那么在 WebRTC中它将这个视频音频,每路视频每路音频都形成一个音频轨和视频轨,然后最终形成一个流,将音频轨、视频轨放入一个流中作处理。以前的SDP正常的规范就没有流相关的信息。
第四个是一些安全相关的描述,因为webRTC最初的愿景是在浏览器与浏览器之间快速的创建音频与视频的应用,那么在浏览器中安全性就非常的重要,包括你是否有权利打开这个音频设备,那么在整个浏览器的数据传输之间你的数据的安全性这些都是非常重要的,所以它又加了这个安全相关的描述。
第五个再有的就是服务质量,在整个传输的过程中,网络的质量如何,会不会影响我这个视频音频 整体的感受,那么音频与视频的质量实际与这个传输的质量是密切相关的。
所以WebRTC的SDP就有五大部分组成了。网络描述、流描述、安全描述、服务质量这四大类实际是非常关键的。那下面我们就来看看每一项
(一)会话元
会话元包括v=(版本0)、t=(会话时长)、o=(owner,属于谁)
(二)网络描述
网络描述包括c=(连接数据)、除了包括原始的这个c之外它还包括一个candidate,candidate通过属性a去描述,所以这个就特别重要,a能用到各种各样的地方。我们描述一个网络的属性candidate,那么网络主要是这两部分,一个是c一个是candidate
(三)流描述
流描述里面实际就是m媒体,在webRTC中把整个流当作一个媒体来看待,那么多个媒体可以绑定到一起,首先的是m(media),然后紧接着是它的属性a=rtpmap,对m这个的进一步的说明。还有一个是对每一个payload type格式的详细的设置a=fmtp,这也是一个属性。这就是流相关的一些描述。
(四)安全描述
安全描述一般都通过属性来设置的,第一个是使用的算法,加密算法是通过什么,通过这个属性a=crypto。
另外整个WebRTC是通过整个ICE收集的整个网络地址和连通性检测,最终选出一个最有效的路径来,那么选出这个最有效的路径怎么知道这两台机子的会话是合法的呢?
那就是通过这个a=ice-ufrag,它代表的是一个用户名的片段,还有一个是a=ice-pwd,那就是通过这个ice-ufrag和ice-pwd,然后对这个用户进行连通性检测的时候,对这个用户进行判断,也就是如果我通过SDP将这个信息将这个对方的ufrag和pwd传给你,那当真正建立连接性检测的时候,它就通过这个密码去验证,跟你连通的时候我将这个ice-ufrag和这个ice-pwd传给你,传给你之后如果你验证通过了,那说明咱们确实是应该建立连接的。如果一个第三方,进行偷偷的连接,由于第三方进行连接的用户名和密码与真正放出去的用户名和密码是不一致的,那说明这个第三方是一个非法用户。通过这种方式来保证整个链路的连接是安全的。这就是安全描述。
还有一个就是a=fingerprint也就是指纹,那指纹是用来干什么的呢?指纹就是用来我们进行数据加密的时候,来验证这个证书的。那它首先通过信令层将SDP中的证书的指纹下发给对方,那么下次对数据加密前的它进行一下数据证书的交换,交换证书是通过DPLS进行,那么通过DPS和LS进行证书交换的时候,通过这个指纹去验证你这个证书的有效性,那如果这个证书验证是有效性的,然后后面你才能进行数据加密然后进行传输。如果通过指纹这个证书不匹配,那说明你这个连接也是有问题的。那这个时候就不能进行传输。通过以上这个种种方式呢,在打通的时候进行一次验证,在传数据的时候在交换证书的时候也要进行验证,那么通过这个层层的安全的验证,才能保证整个webRTC传输的安全性。以上就是安全性相关的一些描述。当然最后进行算法加密的时候你可以使用这个a=crypto指定的加密算法,也可以通过DPLS交换的证书里的指定的加密算法进行加密。
(五)服务质量
那么对于WebRTC它的整个服务质量包括网络质量的反馈,包括丢包重传的反馈,那么这些是通过a=rtcp-fb进行信息的反馈,通过这个信息反馈无论是发送方还是接收方,它的这个网络质量的是什么样子的,然后我使用的评估方法是什么?这些它都可以获取到的,所以这个就是用户服务质量的rtcp-fb,那它里面有很多的参数 ,后面我们讲具体的例子的时候,大家会看到。在有一个是a=group,就是这个group是可以将多个音频媒体绑定到一起形成一个媒体流,比如说音频和视频绑定到一起,那它们可以用同样的数据通道,底层的网络通道是可以复用的,他们绑定一起就可以复用,就是通过这种方式。
像这些网络安全和服务质量的都属于attribute属性的描述,作进一步说明的,所以说SDP复杂在哪呢?
就是它的整个规范并不复杂,但是你真正用的时候就可以通过这个属性attribute去作各种各样的限制,那么这个时候每一套限制都有一套这个规范,这样就增加了这个SDP的复杂性,但是实际如果你了解了这个整个的过程的话,了解它这个整个规范的话其实就很容易理解它了。
还有一个是a=rtcpmux复用,也就是这个rtcp与这个rtp复用同样的网络地址和端口,一般情况下这个rtcp与rtp的端口是不一致的,比如说我的rtp端口是1024,那么一般rtcp就是在rtp的端口号加1是1025,那么你如果设置了这个属性之后那么rtp和rtcp就复用了同一个端口,这个就是服务质量相关的。
那么就可以看到WebRTC的SDP是由5大部分组成。
六:实例分析SDP
v=0 版本号,一般为0 o=- 1985257453350254958 2 IN IP4 127.0.0.1 owner, 名字-(省略) 会话sessionID(唯一标识) 版本(每次生成一个SDP,版本号都会+1) IN(互联网) IP4(使用IPV4地址类型) 具体的IP(owner) s=- session名称(-省略) t=0 0 起始时间和结束时间都是0,一直不结束。对于webrtc实时通讯,不涉及时间问题,所以设置为0即可 a=group:BUNDLE 0 代表有一个流进行绑定(我们只使用了视频流),如果再加上音频的话,就有两个数字(0 1),如果还有其它流,则依次增加+1
媒体流可能会有多个,但是我们传输层只有一个链路,通过BUNDLE表示将这一组媒体流绑定到一起(复用),使用这一个链路传输即可
a=msid-semantic: WMS k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L msid就是媒体流id,是一个媒体流的标识(后面的一长串标识)。wms的w表示webrtc,m表示media,s表示stream m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 121 127 120 125 107 108 109 124 119 123 m表示媒体信息,video(媒体类型) 9(端口号9,不接受数据,类似于null文件描述符,告诉对方,不需要使用SDP传输回复,去使用另外的网络传输)
底层UDP/上层RTP,RTP是经过TLS交换证书的/SAVPF表示加密后的数据(传输协议) 后面的数字表示payload type,后面属性进行解释 c=IN IP4 0.0.0.0 connection info IN(因特网) IP4(IPV4地址) 与本机所有网卡连接(0.0.0.0) a=rtcp:9 IN IP4 0.0.0.0 RTCP使用无效端口9 链路检测的时候对链路的有效性进行一个验证,本机需要将用户名片段和密码传给对端,对方与我建立连接后,需要对连通性进行验证。
对端将收到的用户名片段和密码发送到本机,如果和之前发送的数据是一致的,就说明链路建立成功,否则验证不通过,需要把链路切断
a=ice-ufrag:MDsa a=ice-pwd:gCubNMbGasNreqMgWPCAP8Tu 实际使用webrtc开发音视频应用时遇到的一个问题是呼叫“建立很慢”,原因是:ice过程耗费过多时间。原本SDP信息交换需要等待所有通路都收集完成之后,才进行交互SDP信息,后面才能对通路进行连通性检测!!!而连通性检测时间较长
a=ice-options:trickle 通知对端支持trickle,即sdp里面描述媒体信息和ice候选项的信息可以分开传输。
使用Trickle ICE必须定义一个方式表明支持Trickle ICE,双方都要支持trickle ice
这中间时间浪费在所有candidate都获取后才发送,所以为了加速通话建立时间,把连通性检测的时间提前,方案叫trickle ice,就是每手机一通路就检测一个通路,如果后面有优先级更高的,就进行替换即可。
其思想是客户端一边收集candidate一边发送给对方,比如local candidate 不需要通过stun获取直接就可以发起, 这降低了了连通性检测完成的时间
a=ice-options:trickle
256位Hash值,它的作用是在通过DTLS时,在交换证书前先把证书的指纹(报文摘要)传送给对方。如果对方接收到证书后,对证书进行hash处理证书指纹,进行对比,如果不一致则双方不建立信任,不建立连接 a=fingerprint:sha-256 13:89:EC:41:89:FA:81:B1:29:19:B5:AA:83:80:EA:63:85:3A:3C:55:87:6F:B1:75:62:7E:62:63:95:27:2D:E2 a=setup:actpass a=setup 主要是表示dtls的协商过程中角色的问题,谁是客户端,谁是服务器! a=setup:actpass 既可以是客户端,也可以是服务器(由answer选择) a=setup:active 客户端 a=setup:passive 服务器 a=mid:0 代表媒体流的ID 0 ,同a=group:BUNDLE 0一致,表示视频流----------后面的属性,是对这个流的说明!!! 属性扩展,id号递增不重复。
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset 对于RTP头的扩展,增加了toffset字段 a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time a=extmap:3 urn:3gpp:video-orientation a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01 a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay a=extmap:6 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:9 urn:ietf:params:rtp-hdrext:sdes:mid a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id a=sendrecv 表示本机端既可以发送又可以接收流 a=msid:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L 09d23028-9f4d-42ce-9d3d-1d924480e6d4 a=rtcp-mux RTCP控制网络反馈相关,mux表示与RTP复用同一个端口 a=rtcp-rsize rsize:为了减少rtcp包,带宽足够时,反馈包括很多数据。由于有时候没有足够带宽,所以就用rsize来表示发送丢多少包。窄带宽中,适当减少RTCP包,减少其带宽压力!!! 下面开始对payload type进行说明
a=rtpmap:96 VP8/90000 96---编码器VP8,采样率90000 a=rtcp-fb:96 goog-remb 接受端带宽评估 google a=rtcp-fb:96 transport-cc VP8传输端带宽评估 a=rtcp-fb:96 ccm fir ccm:codec cotrol message,编码控制的回馈消息 fir:完整的内部帧 即请求发送一个完整的I帧 a=rtcp-fb:96 nack nack:告诉它有多少包是没有收到应答的,可以将没应答的包重新发一遍 a=rtcp-fb:96 nack pli pli和fir类似 请求完整帧类型 picture lose a=rtpmap:97 rtx/90000 rtx:丢包重传的通道 a=fmtp:97 apt=96 apt:表示97是96关联的通道,当96有数据包丢包时候,需要重传,重传的payload type是97 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: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
red允许发包冗余,带宽足够,防止丢包
a=rtpmap:124 red/90000 a=rtpmap:119 rtx/90000 a=fmtp:119 apt=124 a=rtpmap:123 ulpfec/90000 ssrc相关 ssrc:标识一路流
a=ssrc-group:FID 2101549168 3058171047 a=ssrc:2101549168 cname:BcnQBDk0kQlMwSCJ cname:channel name 和ssrc对应的名字,每个cname可以对应多个ssrc a=ssrc:2101549168 msid:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L 09d23028-9f4d-42ce-9d3d-1d924480e6d4 a=ssrc:2101549168 mslabel:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L a=ssrc:2101549168 label:09d23028-9f4d-42ce-9d3d-1d924480e6d4 a=ssrc:3058171047 cname:BcnQBDk0kQlMwSCJ a=ssrc:3058171047 msid:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L 09d23028-9f4d-42ce-9d3d-1d924480e6d4 a=ssrc:3058171047 mslabel:k6WUHJqHv6ENTK9BfsElLGKIzFtb17wqGR3L a=ssrc:3058171047 label:09d23028-9f4d-42ce-9d3d-1d924480e6d4