在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; width: 134px; } textarea { color: #444; font-size: 0.9em; font-weight: 300; height: 20.0em; padding: 5px; width: calc(100% - 10px); } div#getUserMedia { padding: 0 0 8px 0; } div.input { display: inline-block; margin: 0 4px 0 0; vertical-align: top; width: 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; width: 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; width: calc(50% - 12px); } section#statistics div { display: inline-block; font-family: 'Inconsolata', 'Courier New', monospace; vertical-align: top; width: 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; width: 156px; } section { margin: 0 0 20px 0; padding: 0 0 15px 0; } video { background: #222; margin: 0 0 0 0; --width: 100%; width: var(--width); height: 225px; } @media screen and (max-width: 720px) { button { font-weight: 500; height: 56px; line-height: 1.3em; width: 90px; } div#getUserMedia { padding: 0 0 40px 0; } section#statistics div { width: 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