WebRTC簡介(二)
Webrtc架構(gòu)
Webrtc協(xié)議棧
WebRTC API
WebRTC 雖然底層實(shí)現(xiàn)極其復(fù)雜,但是面向開發(fā)者的API還是非常簡潔的拴事,主要分為三個方面:
MediaStream
MediaStream — MediaStream用來表示一個媒體數(shù)據(jù)流(通過getUserMedia接口獲任纸铩),允許你訪問輸入設(shè)備刃宵,如麥克風(fēng)和 Web攝像機(jī),該 API 允許從其中任意一個獲取媒體流衡瓶。
以下是示例代碼:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<meta http-equiv="X-UA-Compatible" content="ie=edge">
<title>Document</title>
</head>
<body>
<div>
<button id="start">開始錄制</button>
<button id="stop">停止錄制</button>
</div>
<div>
<video autoplay controls id="stream"></video>
</div>
<script>
// 只獲取視頻
let constraints = {audio: false, video: true};
let startBtn = document.getElementById('start')
let stopBtn = document.getElementById('stop')
let video = document.getElementById('stream')
startBtn.onclick = function() {
navigator.getUserMedia(constraints, function(stream) {
video.srcObject = stream;
window.stream = stream;
}, function(err) {
console.log(err)
})
}
stopBtn.onclick = function() {
video.pause();
}
</script>
</body>
</html>
RTCPeerConnection
RTCPeerConnection:一個RTCPeerConnection對象允許用戶在兩個瀏覽器之間直接通訊。
SDP: 用來描述當(dāng)前連接者想要傳輸?shù)膬?nèi)容牲证,支持的協(xié)議類型哮针,支持的編解碼類型等。
RTCIceCandidate:表示一個ICE協(xié)議的候選者从隆,簡單的說诚撵,就是目標(biāo)節(jié)點(diǎn)的IP以及端口。
RTCIceServer:表示一個ICE Server键闺,其主要用于當(dāng)前主機(jī)的IP發(fā)現(xiàn)寿烟,通過和ICE Server通訊搞隐,我們會得到一組可供連接使用的IP:Port候選值豹休,雙方通過交換ICE候選值來建立起連接。
RTCDataChannel
非音視頻數(shù)據(jù)都是通過RTCDataChannel進(jìn)行傳輸
DataChannel:數(shù)據(jù)通道( DataChannel)接口表示一個在兩個節(jié)點(diǎn)之間的雙向的數(shù)據(jù)通道私植,該通道可以設(shè)置成可靠傳輸或非可靠傳輸。
更多API與示例請參考官方示例徘六,此處不再贅述, 官方示例地址:https://webrtc.github.io/samples/
Stun服務(wù)器和Signal服務(wù)器
WebRTC并不提供Stun服務(wù)器和Signal服務(wù)器内边,服務(wù)器端需要自己實(shí)現(xiàn)。
Stun服務(wù)器可以用google提供的實(shí)現(xiàn)stun協(xié)議的測試服務(wù)器(stun:stun.l.google.com:19302)待锈,Signal服務(wù)器則完全需要自己實(shí)現(xiàn)了漠其,它需要在ClientA和ClientB之間傳送彼此的SDP信息和candidate信息,ClientA和ClientB通過這些信息建立P2P連接來傳送音視頻數(shù)據(jù)竿音。由于網(wǎng)絡(luò)環(huán)境的復(fù)雜性和屎,并不是所有的客戶端之間都能夠建立P2P連接,這種情況下就需要有個trun服務(wù)器做音視頻數(shù)據(jù)的中轉(zhuǎn)春瞬。
Stun/turn柴信、Signal服務(wù)器的實(shí)現(xiàn)在WebRTC源碼中都有示例。
Stun/turn有開源實(shí)現(xiàn):coturn
PeerJS
使用WebRTC原生API還是有一些難度的宽气,但是有開源項(xiàng)目對其進(jìn)行了二次封裝随常,此處就說說peerjs
PeerJS提供了一個完整的、可配置的萄涯、易于使用的點(diǎn)對點(diǎn) API绪氛,它構(gòu)建在 WebRTC 之上,支持?jǐn)?shù)據(jù)通道和媒體流窃判。
Github地址:https://github.com/peers/peerjs
Star:9.4k
PeerServer 一個PeerJS的服務(wù)端钞楼,有助于 PeerJS 客戶端之間建立連接
Github地址:https://github.com/peers/peerjs-server
Star:3.2k
coturn:TURN 和 STUN Server 的免費(fèi)開源實(shí)現(xiàn)
Github地址:https://github.com/coturn/coturn
Star:6.6k
服務(wù)端代碼示例:
const express = require('express');
const { ExpressPeerServer } = require('peer');
const app = express();
app.get('/', (req, res, next) => res.send('Hello world!'));
const server = app.listen(9000);
const peerServer = ExpressPeerServer(server, {
path: '/peer-server'
});
app.use('/', peerServer);
app.use(express.static('public'));
學(xué)生端代碼示例:
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<style>
body {
font-family: sans-serif;
}
video {
max-width: 100%;
width: 320px;
}
</style>
</head>
<body>
<video id="video" autoplay playsinline></video>
<div>
<button id="readyButton">準(zhǔn)備</button>
</div>
<script src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
<script>
const video = document.getElementById('video');
const readyButton = document.getElementById('readyButton');
readyButton.addEventListener('click', e => {
const peer = new Peer("dc8a395f-76c2-4607-a071-416ed4086d87", {
host: 'localhost',
port: 9000,
path: '/peer-server'
});
/**
const conn = peer.connect('e3c6447d-182d-4b80-a975-2dd13dbc1721');
peer.on('connection', (conn) => {
conn.on('data', (data) => {
console.log(data);
});
});*/
peer.on('call', call => {
/**
const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
getUserMedia({video: true, audio: false}, (stream) => {
call.on('stream', (remoteStream) => {
video.srcObject = remoteStream;
});
}, (err) => {
console.log('Failed to get local stream' ,err);
});*/
console.log('---');
call.on('stream', (remoteStream) => {
video.srcObject = remoteStream;
});
});
});
</script>
</body>
</html>
老師端代碼示例:
<!DOCTYPE html>
<html>
<head>
<title>Realtime communication with WebRTC</title>
<style>
body {
font-family: sans-serif;
}
video {
max-width: 100%;
width: 320px;
}
</style>
</head>
<body>
<video id="video" autoplay playsinline></video>
<div>
<button id="startButton">創(chuàng)建對等端</button>
<button id="sendButton">發(fā)送</button>
<button id="stopButton">停止</button>
</div>
<script src="https://unpkg.com/peerjs@1.3.1/dist/peerjs.min.js"></script>
<script>
const video = document.getElementById('video');
const startButton = document.getElementById('startButton');
const sendButton = document.getElementById('sendButton');
startButton.addEventListener('click', e => {
})
sendButton.addEventListener('click', e => {
const peer = new Peer("e3c6447d-182d-4b80-a975-2dd13dbc1721", {
host: 'localhost',
port: 9000,
path: '/peer-server'
});
/**
const conn = peer.connect('dc8a395f-76c2-4607-a071-416ed4086d87');
conn.on('open', () => {
conn.send('hi!');
console.log('teacher has open !');
});*/
/**
const getUserMedia = navigator.getUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
getUserMedia({video: true, audio: false}, (stream) => {
video.srcObject = stream;
peer.call('dc8a395f-76c2-4607-a071-416ed4086d87', stream);
}, (err) => {
console.log('Failed to get local stream' ,err);
});*/
})
</script>
</body>
</html>