之前公司准备用 webRTC 来实现视频聊天,研究了几天,撸了个 demo 出来,(虽然最后并没有采用这项技术,囧),但是还是写一个出来吧!
WebRTC简单介绍
WebRTC (Web Real-Time Communication) 是一个可以用在视频聊天,音频聊天或P2P文件分享等Web App中的 API。- MDN
目前这项Web技术支持的浏览器有chrome, firefox和safari。
WebRTC 有三个主要的API
- getUserMedia - 采集本地音频和视频流
- RTCPeerConnection - 用来创建对端连接并传输音视频的API
- RTCDataChannel - 用于传输二进制数据。
这三个API里面涉及了很多知识,要想全面细致的了解这项技术,建议去看官网,这篇教程只会介绍重要的知识点并且以此为前提,写一个聊天室和多人视频聊天的demo
直播软件(比如斗鱼直播,熊猫直播等)是在客户端采集和编码主播的音频和视频,传输给流媒体服务器,流媒体服务器将媒体数据转发出去,客户端收到视频流进行解码和播放
架构简化图
WebRTC提供端对端的音视频通讯,不需要媒体服务器转发媒体数据,架构简化图如下,
其中每个相互连接的客户端叫做对等端
WebRTC采集和传输音视频数据的过程可以分为三步进行
- 实时捕获本地的音视频流
- 实时编码音视频并在网络中向对等端传输多媒体数据
- 对等端接受发送者的音视频,实时解码播放
捕获本地媒体流
这一步非常简单,调用navigator.getUserMedia这个api就可以,第一个参数是音视频的限制条件,第二个参数是捕获媒体流成功的回调,回调参数是stream, 第三个参数是捕获失败的回调,在成功的回调函数种将stream赋值给video的srcObject即可显示视频
示例代码如下:
<!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>
本地视频已经以采集和播放了,接下来要解决如何和对方进行视频会议,也就是实时传输音视频,WebRTC是提供端对端的音视频传输,也就是封装好的RTCPeerConnection这个API,调用这个API可以创建对等端连接并传输音视频,但是光有这个api还不够,在传送音视频数据之前,我们需要先传输信令。
什么是信令
信令是协调通信的过程。为了使WebRTC应用程序能够建立一个"通话",其客端户需要交换以下信息:
会话控制消息用于打开或关闭通信
错误消息
媒体元数据,如编解码器和编解码器设置,带宽和媒体类型
密钥数据,用于建立安全的连接
网络数据,如主机IP地址和端口
这个信令过程需要一个方式使客户端之间来回地进行消息传递, WebRTC标准并没有规定信令方法和协议,我们可以采用JavaScript会话建立协议JSEP来实现信令的交换,这里用一个例子来说明这个过程,假设A和B之间要建立RTCPeerConnection连接
1. A创建一个RTCPeerConnection对象。
2. A使用RTCPeerConnection .createOffer()方法产生一个offer(一个SDP会话描述)。
3. A用生成的offer调用setLocalDescription(),设置成自己的本地会话描述。
4. A将offer通过信令机制发送给B。
5. B用A的offer调用setRemoteDescription(),设置成自己的远端会话描述,以便他的RTCPeerConnection知道A的设置。
6. B调用createAnswer()生成answer
7. B通过调用setLocalDescription()将其answer设置为本地会话描述。
8. B然后使用信令机制将他的answer发回给A。
9. A使用setRemoteDescription()将B的应答设置为远端会话描述。
A和B除了交换会话信息,还需要交换网络信息。"查找候选项"是指使用ICE框架查找网络接口和端口的过程。
1. A使用onicecandidate创建一个RTCPeerConnection对象,注册一个处理器函数。
2. 处理器在网络候选变得可用时被调用。
3. 在处理器中,A通过信令通道将候选数据发送给B。
4. 当B从A那里获得候选消息时,调用addIceCandidate(),将候选项添加到远端对等描述中。
稳住,别晕,我们用代码来说明这个过程吧,交换信令需要信令服务器,这个我们晚点讨论,我们现在考虑同一个页面上的两个RTCPeerConnection对象,一个代表本地,一个代表远端, 来说明信令交换和网络信息交换的过程和细节。
这个是官方文档的demo,第五个实验室教程, 能够科学上网的小伙伴建议直接去看教程,由浅入深地教你搭建WebRTC项目,在这里借用这个demo来解释建立RTCPeerConnection连接建立的过程
首先建立index.html
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>
<h1>Realtime communication with WebRTC</h1>
<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<div>
<button id=