一个 HTML 页面中的一对一视频通话示例
前言
WebRTC 是一项实时通讯技术!它允许我们在浏览器之间点对点的建立连接,实现音频,视频的的传输!
本文主要是通过完成在一个HTML页面中一对一视频通话的小例子,来简单的介绍如何在浏览器中,使用WebRTC!
基本流程
在正式的编码之前,我们需要先了解WebRTC 建立连接的一个流程!
假设我们有用户 A 和用户 B。我们需要一个可以建立连接的方法-RTCPeerConnection`
RTCPeerConnection
接口代表一个由本地计算机到远端的WebRTC连接。该接口提供了创建,保持,监控,关闭连接的方法的实现。
const pc1 = new RTCPeerConnection(null);
然后用户还需要去从媒体设备中捕获音频和视频。这个我们可以通过navigator完成。
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
现在我们本地的音频和视频。我们是不是就可以两端之间交换了呢?答案是不可以!这是因为在音视频通讯场景下,两端之间所支持的音视频接码,传输,都需要先通知对方!这就是媒体协商!媒体协商就是两台计算机交换媒体参数并达成公示的过程!例如:两个用户交换各自支持的编码器列表,并找到两者共同支持的部分,最后选择其中一个作为使用的编码器!
在WebRTC中我们使用offer-answser模型来完成这一过程!
const offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
// now offer will be sent to pc2 using a signaling server
await pc2.setRemoteDescription(offer);
const answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
// now answer will be sent to pc1 using a signaling server
await pc1.setRemoteDescription(answer);
在双方达成公式之后,我们就可以在它们之前建议P2P连接!
这一过程由候选交换模型完成!候选对象实际上是一个包含 ip/port 和其他地址信息的对象。每个用户通常有不止一个候选人。这些候选者将由信令服务器相互发送。然后每个用户将尝试使用他们收到的远程候选信息建立 p2p 连接。这个过程可以通过下面的代码来完成。
pc1.onicecandidate = async e => {
if (!e.candidate) {
return;
}
try {
await pc2.addIceCandidate(e.candidate);
} catch (error) {
console.log("pc2 add pc1 candidate error", error);
}
}
在RTCPeerConnection 对象中提供一个回调函数来监控 p2p 连接。
pc1.onconnectionstatechange = () => {
console.log(pc1.connectionState)
}
最后,在p2p连接建立之后,我们可以从回调中获取媒体数据!
pc2.ontrack = (e) => {
remoteVideo.srcObject = new MediaStream([e.track]);
}
完整代码
<!DOCTYPE html>
<html lang="en">
<head>
<style>
video {
width: 400px;
}
</style>
</head>
<body>
<button id="start">start</button>
<p>local video</p>
<video autoplay id="local-video"></video>
<p>remote video</p>
<video autoplay id="remote-video"></video>
<script>
const localVideo = document.getElementById("local-video")
const remoteVideo = document.getElementById("remote-video")
// pc1 act as local pc
const pc1 = new RTCPeerConnection(null);
// pc2 act as remote pc
const pc2 = new RTCPeerConnection(null);
start.onclick = async () => {
try {
const stream = await navigator.mediaDevices.getUserMedia({ video: true });
localVideo.srcObject = stream;
const videoTrack = stream.getVideoTracks()[0];
pc1.addTrack(videoTrack);
} catch (error) {
console.log("get local stream error", error);
}
}
pc1.onnegotiationneeded = async () => {
try {
const offer = await pc1.createOffer();
await pc1.setLocalDescription(offer);
// now offer will be sent to pc2 using a signaling server
await pc2.setRemoteDescription(offer);
const answer = await pc2.createAnswer();
await pc2.setLocalDescription(answer);
// now answer will be sent to pc1 using a signaling server
await pc1.setRemoteDescription(answer);
} catch (error) {
console.log("offer answer error", error);
}
}
pc1.onconnectionstatechange = () => {
console.log(pc1.connectionState)
}
pc1.onicecandidate = async e => {
if (!e.candidate) {
return;
}
try {
await pc2.addIceCandidate(e.candidate);
} catch (error) {
console.log("pc2 add pc1 candidate error", error);
}
}
pc2.onicecandidate = async e => {
if (!e.candidate) {
return;
}
try {
await pc1.addIceCandidate(e.candidate);
} catch (error) {
console.log("pc1 add pc2 candidate error", error);
}
}
pc2.ontrack = (e) => {
remoteVideo.srcObject = new MediaStream([e.track]);
}
</script>
</body>
</html>
- 点赞
- 收藏
- 关注作者
评论(0)