WebRTC(Web Real-Time Communications)是Google公司开源的一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流、音频流或者其他任意数据的传输。
基本条件
WebRTC 终端(两个)
:本地和远端,负责音视频采集、编解码、NAT 穿越以及音视频数据传输等;
信令服务器
:自行实现的信令服务,负责信令处理,如加入房间、离开房间、媒体协商消息的传递等;
STUN/TURN 服务器
:负责获取 WebRTC 终端在公网的 IP 地址,以及 NAT 穿越失败后的数据中转服务。
示例
连接过程
- 客户端A/客户端B 分别连接信令服务器;
- 客户端A(发起方)开始执行,创建RTCPeerConnection实例,开启音视频采集,将音视频流挂载到peerConnection实例,生成offer sdp;
- 通过信令服务器中转将客户端A生成的offer sdp发送到客户端B;
- 客户端B(接收方)开始执行,创建RTCPeerConnection实例,将客户端A的offer设置进去,生成answer;
- 通过信令服务器中转将answer发送回客户端A;
- 客户端A设置本地offer后,通过onicecandidate回调收集的candidate发送给客户端B,客户端B也通过设置本地answer后,将收集的candidate发送回客户端A。
- 客户端A/客户端B拿到对方的candidate后,通过addIceCandidate添加到本端,如此就可以建立p2p连接;
- 最后Client B在连接之上拿到Client A的音视频数据;
注:
offer sdp/answer
:用于建立RTC连接candidate(ICE Candidate)
,双方通过交换ICE,来识别候选者身份ICE
(Interactive Connectivity Establishment
,交互式连接建立技术):用户之间建立连接的方式,用来选取用户之间最佳的连接方式(双方进行通信的必要条件)
步骤
说明
发送方:用于发送音视频流的客户端A
接收方:用于接受音视频流的客户端B
第一步:初始化
// 链接socket
message.log('信令通道(WebSocket)创建中......');
const socket = new WebSocket('ws://localhost:8080');
socket.onopen = () => {
message.log('信令通道创建成功!');
}
socket.onmessage = e => {
const { type, sdp, iceCandidate } = JSON.parse(e.data)
if (type === 'answer') {
peer.setRemoteDescription(new RTCSessionDescription({ type, sdp }));
} else if (type === 'answer_ice') {
peer.addIceCandidate(iceCandidate);
} else if (type === 'offer') {
startLive(new RTCSessionDescription({ type, sdp }));
} else if (type === 'offer_ice') {
peer.addIceCandidate(iceCandidate);
}
};
const PeerConnection = window.RTCPeerConnection || window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
!PeerConnection && message.error('浏览器不支持WebRTC!');
const peer = new PeerConnection();
peer.ontrack = e => {
if (e && e.streams) {
message.log('收到对方音频/视频流数据...');
remoteVideo.srcObject = e.streams[0];
}
};
peer.onicecandidate = e => { // 发送方设置offer/接收方设置answer
if (e.candidate) {
message.log('搜集并发送候选人');
socket.send(JSON.stringify({
type: `${target}_ice`,
iceCandidate: e.candidate
}));
} else {
message.log('候选人收集完成!');
}
};
第一步:音视频采集
1.mediaDevices
let stream;
try {
message.log('尝试调取本地摄像头/麦克风');
stream = await navigator.mediaDevices.getUserMedia({ video: true, audio: true });
message.log('摄像头/麦克风获取成功!');
localVideo.srcObject = stream; // 本地播放
} catch {
message.error('摄像头/麦克风获取失败!');
return;
}
stream.getTracks().forEach(track => {
peer.addTrack(track, stream); // 添加媒体轨道到轨道集
});
第二步:创建并发送sdp/answer
// 发送方
message.log('创建本地SDP');
const offer = await peer.createOffer();
await peer.setLocalDescription(offer); // 设置本地描述(获取candidate) 会调用上面绑定的onicecandidate事件传递candidate
message.log('传输发起方本地SDP');
socket.send(JSON.stringify(offer));
// 接收方
await peer.setRemoteDescription(offerSdp); // 设置远程描述(获取candidate) 会调用上面绑定的onicecandidate事件传递candidate
message.log('创建接收方(应答)SDP');
const answer = await peer.createAnswer();
message.log('传输接收方(应答)SDP');
socket.send(JSON.stringify(answer));
await peer.setLocalDescription(answer);
// 发送方接受到接收方传递的answer,建立远程描述ice,并发送自己的描述ice
peer.setRemoteDescription(new RTCSessionDescription({ type, sdp })); // 执行完这行代码会执行 上面的onicecandidate事件,将自己的 candidate ice 分别传递给对方建立ice链接
发送方生成的offer数据格式:
接收方生成的answer数据格式:
描述设置后会执行的内容:
执行完上面的内容传递给socket推送给对方会执行的内容:
完结:
offer、answer、ice
处理完毕之后,完成的WebRTC连接就建立完成,任意一方推送音频流数据会同步到另一方上。
数据通道
WebRTC
擅长进行数据传输,不仅仅是音频和视频流,还可以进行x相对于复杂的数据,创建一个数据通道这个主要功能可以通过RTCDataConnection
实现:
let dataCannel = peer.createDataChannel("label",dataChannelOptions);
dataChannel.onerror = function (error){
console.log(error)
}
dataChannel.onmessage = function (event){
console.log(event.data)
}
dataChannel.onopen = function (error){
console.log('data channel opened')
// 当创建一个数据通道后,你必须等onopen事件触发后才能发送消息
dataChannel.send('Hello world')
}
dataChannel.onclose = function (error){
console.log('data channel closed')
}
//接收方此时并不需要再次调用createDataChannel方法,只需要监听RTCPeerConnection实例对象上的ondatachannel事件就可以在回调中拿到发送方的请求,数据通道就建立起来了。
peer.ondatachannel = function(event){
console.log(event.data)
}
传输方式
WebRTC也是如此,在信令控制方面采用了可靠的TCP, 但是音视频数据传输上,使用了UDP作为传输层协议。只使用UDP是不够的,还需要RTP(实时传输协议)
音视频中一个视频帧数据量需要多个包来传送,并在接收端组成对应帧,正确还原出视频信号。需要正确的接受顺序,而UDP并没有这个能力,所以音视频传输中,并不直接使用UDP,而是需要RTP作为实时音视频中的应用层协议。
WebRTC使用了一些关键的协议和标准:
1、SDP(Session Description Protocol):SDP用于在对等连接之间交换会话描述信息。它描述了媒体流的类型、编码参数和网络地址。
2、ICE(Interactive Connectivity Establishment):ICE协议用于通过STUN和TURN服务器解决对等连接的网络可达性问题。
3、STUN(Session Traversal Utilities for NAT):STUN协议用于发现本地IP地址和绕过NAT。
4、TURN(Traversal Using Relays around NAT):TURN协议用于在对等连接无法直接建立时,通过中继服务器中转流量。
5、RTP(Real-Time Transport Protocol):RTP协议用于传输音频和视频流。
6、RTCP(Real-Time Control Protocol):RTCP协议用于实时传输控制信息,如丢包率、延迟等。
公网使用
STUN
:Session Traversal Utilities for NAT
,用来帮助我们获取本地计算机的公网 IP 地址,以及端口号。 TURN
:Traversal Using Relays around NAT
,用来帮助我们穿越 NAT 网关,实现公网中的 WebRTC 连接。
const pc = new RTCPeerConnection({
iceServers: [
// STUN 服务器
{
urls: 'stun:stun.voipbuster.com ',
},
// TURN 服务器
{
urls: 'turn:turn.xxxx.org',
username: 'webrtc',
credential: 'turnserver',
},
],
})
场景
视频会议、实时音视频通话、数据共享、直播、远程协作等