「它(WebRTC)容許網絡應用或者站點,在不借助中間媒介的狀況下,創建瀏覽器之間點對點(Peer-to-Peer)的鏈接,實現視頻流和(或)音頻流或者其餘任意數據的傳輸」。
這是 MDN 上對 WebRTC 的描述,初次接觸時沒法理解 WebRTC 爲何要和 WebSocket 搭配,明明說的很清楚 不借助中間媒介 ,那 WebSocket 充當的是什麼角色?整個 WebRTC 通話創建的流程又是怎樣的?git
先看一下最終效果:github
本示例主要使用了 WebRTC
和 WebSocket
:web
WebRTC
(Web Real-Time Communication)即網頁即時通訊,是一個支持網頁瀏覽器進行實時語音對話或視頻對話的API。WebSocket
是一種在單個TCP鏈接上進行全雙工通訊的協議。在 WebSocket 中,瀏覽器和服務器只須要完成一次握手,二者之間就直接能夠建立持久性的鏈接,並進行雙向數據傳輸。簡單說一下流程,如瀏覽器 A 想和瀏覽器 B 進行音視頻通話:瀏覽器
offer sdp
)信息;offer sdp
經過 ws 發送給 B;answer sdp
)信息;answer sdp
經過 ws 發送給 A;引用網上的有關WebRTC
創建的時序圖,可能更加直觀:安全
從上述流程,能夠發現通訊雙方在創建鏈接前須要交換信息,這也就是開頭提到的 WebSocket
充當的角色:信令服務器,用於轉發信息。而 WebRTC 不借助中間媒介 的意思是,在創建對等鏈接後,不須要藉助第三方服務器中轉,而是直接在兩個實體(瀏覽器)間進行傳輸。服務器
獲取視頻標籤,鏈接信令服務器,建立 RTCPeerConnection
對象。其中 RTCPeerConnection 的做用是在兩個對等端之間創建鏈接,其構造函數支持傳一個配置對象,包含ICE「打洞」(因爲本示例在本機進行測試,故不須要)。網絡
const localVideo = document.querySelector('#local-video'); const remoteVideo = document.querySelector('#remote-video'); const socket = new WebSocket('ws://localhost:8080'); const peer = new RTCPeerConnection(); socket.onmessage = () => { // todo } peer.ontrack = () => { // todo } peer.onicecandidate = () => { // todo }
獲取本地攝像頭/麥克風(須要容許使用權限),拿到本地媒體流(MediaStream)後,須要將其中全部媒體軌道(MediaStreamTrack)添加到軌道集,這些軌道將被髮送到另外一對等方。併發
navigator.mediaDevices.getUserMedia({ video: true, audio: true }) .then(stream => { localVideo.srcObject = stream; stream.getTracks().forEach(track => { peer.addTrack(track, stream); }); });
建立發起方會話描述對象(createOffer),設置本地SDP(setLocalDescription),並經過信令服務器發送到對等端,以啓動與遠程對等端的新WebRTC鏈接。socket
peer.createOffer().then(offer => { peer.setLocalDescription(offer); socket.send(JSON.stringify(offer)); });
當調用 setLocalDescription 方法,PeerConnection 開始收集候選人(ice信息),併發送offer_ice到對等方。這邊補充第一步中的peer.onicecandidate
和socket.onmessage
ide
對等方收到ice信息後,經過調用 addIceCandidate 將接收的候選者信息傳遞給瀏覽器的ICE代理。
peer.onicecandidate = e => { if (e.candidate) { socket.send(JSON.stringify({ type: 'offer_ice', iceCandidate: e.candidate })); } }; socket.onmessage = e => { const { type, sdp, iceCandidate } = JSON.parse(e.data); if (type === 'offer_ice') { peer.addIceCandidate(iceCandidate); } }
接收方收到了offer
信令後,開始獲取攝像頭/麥克風,與發起方操做一致。同時將收到offer SDP
指定爲鏈接的遠程對等方屬性(setRemoteDescription),並建立應答SDP(createAnswer),發送到對等端。這邊補充第一步中的socket.onmessage
。
socket.onmessage = e => { const { type, sdp, iceCandidate } = JSON.parse(e.data); if (type === 'offer') { navigator.mediaDevices.getUserMedia(); // 與發起方一致,省略 const offerSdp = new RTCSessionDescription({ type, sdp }); peer.setRemoteDescription(offerSdp).then(() => { peer.createAnswer(answer => { socket.send(JSON.stringify(answer)); peer.setLocalDescription(answer) }); }); } }
注意:當 setLocalDescription 方法調用後,開始收集候選人信息,併發送 answer_ice 到對等方。與發送方同理,不贅述。
經過不斷收集ICE信息(onicecandidate),發起方和應答方最終將創建一條最優的鏈接方式,此時會觸發 ontrack 回調,便可獲取到對等方的媒體流。
peer.ontrack = e => { if (e && e.streams) { remoteVideo.srcObject = e.streams[0]; } };
整合發起方和應答方的代碼,差很少50行,不算標題黨!哈哈哈哈哈哈...
完整示例相關代碼已上傳 github.com/shushushv/webrtc-p2p ,⭐🦆~