WebRTC 是一个开源项目,可在 Web 和本机应用程序中实现音频、视频和数据的实时通信。
WebRTC 代表网络实时通信。它是一个开源免费项目,用于借助 API(应用程序编程接口)为移动应用程序和 Web 浏览器提供实时通信。JavaScript API 、HTML5 标签、底层通信协议等由 W3C(万维网联盟)和 IETF(互联网工程任务组)共同定义,以便在未来的 Web 浏览器之间建立一个值得信赖的通信通道。基本上,主要思想是定义 WebRTC API,它允许安全访问设备上的输入外围设备(例如麦克风和网络摄像头),以对等方式与远程设备共享或交换媒体数据、实时数据。
使用场景不仅仅是下面的这些:视频会议、实时视频聊天、与所有朋友分享我们最喜欢的时刻都是 WebRTC 内部存在的一些示例。 你日常使用的所有小工具,如手机、笔记本电脑、智能电视和人工智能等,都连接到互联网。借助 WebRTC ,所有这些设备都可以在一个通用平台上平稳、安全地相互共享语音、视频和实时数据,这也就是 WebRTC 的愿景。WebRTC 是实时通信的未来。
为什么选择 WebRTC?
WebRTC 受欢迎的原因有很多。其中一些原因如下:
- WebRTC 是一种无插件的现代实时通信技术。它不需要任何额外的插件或应用程序来进行音频、视频流和数据共享。它使用 Javascript 、应用程序编程接口 (API) 和 HTML5 将通信技术嵌入到浏览器中。Google Hangouts 、Whatsapp 、Facebook Messenger 、ZOOM 、Zendesk 客户支持、Skype for Web 等产品均与 WebRTC 集成。
- 浏览器能够以点对点的方式直接与其他浏览器交换实时媒体。
- 比其他各种流媒体系统提供更高级别的安全性,且无需第三方软件。
- 无需插件即可进行实时通信。
WebRTC 在浏览器中如何工作?
WebRTC 基本上是通过浏览器进行网络实时通信。它允许浏览器之间进行通信。WebRTC Web 应用程序被编程为 HTML 和 JavaScript 的混合体。用户还可以使用 CSS 来自定义通信的外观。它通过标准化的 WebRTC API 与 Web 浏览器工作并通信。因此,WebRTC API 必须提供一系列实用程序。其中一些像连接管理(以点对点方式)、编码/解码功能协商、选择和控制、媒体控制、防火墙等。
WebRTC 的实现范围非常广,因为它是高度可定制的。WebRTC 的功能可以分为三个部分:
- MediaStream :第一步是获得用户想要共享的数据。在这种情况下,捕获用户想要的流(音频/视频)、建立的通信模式。本地媒体流允许浏览器访问流设备,例如摄像头、网络麦克风。它还允许浏览器捕获媒体。用户可以利用
getUserMedia()
浏览器的功能来获取访问权限。 - RTCPeerConnection :一旦用户决定了通信流,下一步就是将其与远程服务的系统连接。它允许我们的浏览器直接与远程服务浏览器(对等点)交换数据以进行语音和视频通话。它允许通过STUN 和TURN服务器在发送方和接收方之间进行关联。
- RTCDataChannel:它允许浏览器双向点对点交换数据。
WebRTC 三角形:
- WebRTC 包含供 Web 开发人员使用的三个不同的 API 层。
- 第一层包含 Web 开发人员所需的所有 API,包括 RTCPeerConnection 、RTCDataChannel 和媒体流对象及其功能。
- 第二层是浏览器制造商的 API,比如 safia , chrome ,firefox ,edge 等等。
- 第三个是 Overridable API。
- 如果我们从客户端-服务器端查看 WebRTC 架构,那么我们会发现最常用的模型之一是受到 SIP(会话启动协议)梯形的启发。
- 想象一下我们和我们的朋友正在运行 WebRTC 应用程序。我想与我们的朋友交流。然后使用信令消息,其工作是建立和结束通信。
- 这些消息通过 HTTP 或 WebSocket 协议传输到 Web 服务器,Web 服务器可以根据需要修改、翻译或管理它们。
- 至于数据路径,PeerConnection 允许媒体直接在浏览器之间流动,无需任何中间服务器。两个网络服务器可以使用标准信令协议进行通信,并且在我们和我们的朋友之间建立通信。
什么是信号传递?
WebRTC 使用 RTCPeerConnection 在浏览器之间传输流数据,但还需要一种机制来协调通信和发送控制消息,该过程称为信令。WebRTC 未指定信令方法和协议。
什么是 STUN 和 TURN?
WebRTC 设计为点对点工作,因此用户可以通过最直接的路由进行连接。然而,WebRTC 的构建是为了应对现实世界的网络:客户端应用程序需要穿越 NAT 网关和防火墙,并且点对点网络需要在直接连接失败时进行回退。在此过程中,WebRTC API 使用 STUN 服务器获取计算机的 IP 地址,并使用 TURN 服务器作为中继服务器,以防点对点通信失败。
WebRTC 安全吗?
所有 WebRTC 组件都必须加密,并且其 JavaScript API
只能从安全来源(HTTPS
或本地主机)使用。信令机制不是由 WebRTC 标准定义的,因此您需要确保使用安全协议。
WebRTC 的局限性:
- 必须能够访问快速的互联网连接才能与 WebRTC 进行通信。
- 它不提供任何离线服务。
- 它并非在所有浏览器中都可用。
- 没有固定的服务为每个浏览器提供基础。它不断更新,一些软件可能比其他软件提供更复杂的服务,并且具有易于访问和其他设施。
快速入门
WebRTC 有多个 JavaScript API。
getUserMedia()
:捕获音频和视频。MediaRecorder
:录制音频和视频。RTCPeerConnection
:在用户之间传输音频和视频。RTCDataChannel
:用户之间的流数据。
第一步:终端创建一个项目
shell
❯ mkdir webrtc-web && cd webrtc-web/
❯ pwd
❯ /Users/oo7/Developer/webrtc-web
❯ ~/Developer/webrtc-web
第二步:从网络摄像头流式传输视频
- 从网络摄像头获取视频流。
- 操作流播放。
- 使用 CSS 和 SVG 来操作视频。
在工作目录中向 index.html
添加一个 video
元素和一个 script
元素:
html
<!DOCTYPE html>
<html>
<head>
<title>与WebRTC的实时通信</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>与WebRTC的实时通信</h1>
<video src="" autoplay playsinline></video>
<script src="./js/main.js"></script>
</body>
</html>
在 js 文件夹中的 main.js编写一下内容:
js
'use strict';
const mediaStreamConstraints = {
video: true,
};
//将放置流的视频元素
const localVideo = document.querySelector("video");
//将在视频中播放本地流。
let localStream;
//通过将MediaStream添加到视频元素来处理
function gotLocalMediaStream(mediaStream) {
localStream = mediaStream;
localVideo.srcObject = mediaStream;
}
//通过将带有错误消息的记录输出到控制台
function handleLocalMediaStreamError(error) {
console.log("navigator.getUserMedia错误: ", error);
}
// 初始化媒体流
navigator.mediaDevices
.getUserMedia(mediaStreamConstraints)
.then(gotLocalMediaStream)
.catch(handleLocalMediaStreamError);
打开浏览器输入服务地址:http://127.0.0.1:5500/work/
虽然是静态文件但是也需要以服务的形式启动
运作方式
调用 getUserMedia()
后,浏览器会请求用户访问其相机的权限(如果这是第一次请求对当前源的相机访问)。如果成功,则返回 mediaStream ,媒体元素可以通过以下 srcObject
属性使用该流:
js
// 初始化媒体流
navigator.mediaDevices
.getUserMedia(mediaStreamConstraints)
.then(gotLocalMediaStream)
.catch(handleLocalMediaStreamError);
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
该 constraints
参数允许您指定要获取的媒体。在此示例中,仅视频,因为默认情况下音频处于禁用状态:
js
const mediaStreamConstraints = {
video: true,
};
如果我们对视频如分辨率有要求,使用以下参数来做微调:
js
const hdConstraints = {
video: {
width: {
min: 1280
},
height: {
min: 720
}
}
}
MediaTrackConstraints 规范列出了所有的约束类型,但并不是所有浏览器都支持这些选项。 如果当前选择的相机不支持所请求的分辨率,getUserMedia()
则会被拒绝,OverconstrainedError
并且不会提示用户授予访问其相机的权限。
如果getUserMedia()
执行成功,来自网络摄像头的视频流将被设置为视频元素的源:
js
function gotLocalMediaStream(mediaStream) {
localVideo.srcObject = mediaStream;
}
ps:
video
元素需要增加autoplay
上的属性,如果没有它,我们将只能看到一个边框,或者什么都看不到!
使用 RTCPeerConnection 流式传输视频
什么是 RTCPeerConnection?
RTCPeerConnection 是一个 API ,用于进行 WebRTC 调用以流式传输视频和音频以及交换数据。
我们在同一页面上的两个 RTCPeerConnection 对象(称为对等体)之间建立连接。
添加视频元素和控制按钮
在 index.html中,将单个视频元素替换为两个视频元素和三个
html
<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<div>
<button id="startButton">开始</button>
<button id="callButton">拨号</button>
<button id="hangupButton">挂断</button>
</div>
一个视频元素将显示来自 RTCPeerconnection 的流,getUserMedia()
另一个视频元素将显示通过 RTCPeerconnection 流式传输的相同视频。(在实际场景的应用程序中,一个视频元素将显示本地流,另一个视频元素将显示远程流。不然自己跟自己视频那叫一个没意思🤪)
添加适配器 adapter.js
我们直接引用网络的就好了
js
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
dapter.js 用于使应用程序免受规范更改和前缀差异的影响。(实际情况中用于WebRTC实现的标准和协议非常稳定,并且只有几个前缀名称。)
改造 index.html, 现在应该如下所示:
html
<!DOCTYPE html>
<html>
<head>
<title>与WebRTC的实时通信</title>
<link rel="stylesheet" href="css/main.css" />
</head>
<body>
<h1>与WebRTC的实时通信</h1>
<!-- <video src="" autoplay playsinline></video> -->
<video id="localVideo" autoplay playsinline></video>
<video id="remoteVideo" autoplay playsinline></video>
<div>
<button id="startButton">开始</button>
<button id="callButton">拨号</button>
<button id="hangupButton">挂断</button>
</div>
<script src="./js/main.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
</body>
</html>
编写 RTCPeerConnection 代码
// 可以github中查看源码
WebRTC 使用 RTCPeerConnection API 建立连接以在 WebRTC 客户端(称为对**等点)**之间传输视频。
在此示例中,两个 RTCPeerConnection 对象位于同一页面上:pc1
和pc2
。没有太多实际用途,但有助于演示 API 的工作原理。
在 WebRTC 对等点之间建立呼叫涉及三个任务:
- 为呼叫的每一端创建一个 RTCPeerConnection,并在每一端添加来自 的本地流
getUserMedia()
。 - 获取和共享网络信息:潜在的连接端点称为 ICE 候选端点。
- 获取并共享本地和远程描述:有关 SDP 格式的本地媒体的元数据。
拍照并通过数据通道分享
- 拍摄照片并使用 canvas 元素从中获取数据。
- 与远程用户交换图像数据。
我们通过 RTCDataChannel API 使得共享整个文件成为可能:在此示例中,通过getUserMedia()
.
该步骤的核心部分如下:
- 建立数据通道。请注意,在此步骤中,您不会向对等连接添加任何媒体流。
- 使用以下命令捕获用户的网络摄像头视频流
getUserMedia()
:
js
var video = document.getElementById('video');
function grabWebCamVideo() {
console.log('正在获取用户媒体 (video) ...');
navigator.mediaDevices.getUserMedia({
video: true
})
.then(gotStream)
.catch(function(e) {
alert('getUserMedia() error: ' + e.name);
});
}
- 当用户单击Snap 按钮时,从视频流中获取快照(视频帧)并将其显示在元素中
canvas
:
js
var photo = document.getElementById('photo');
var photoContext = photo.getContext('2d');
function snapPhoto() {
photoContext.drawImage(video, 0, 0, photo.width, photo.height);
show(photo, sendBtn);
}
- 当用户单击**"发送"**按钮时,将图像转换为字节并通过数据通道发送:
js
function sendPhoto() {
//将数据通道消息拆分为此字节长度的块。
var CHUNK_LEN = 64000;
var img = photoContext.getImageData(0, 0, photoContextW, photoContextH),
len = img.data.byteLength,
n = len / CHUNK_LEN | 0;
console.log('共发送 ' + len + ' byte(s)');
dataChannel.send(len);
//拆分照片并以大约64KB的数据块发送
for (var i = 0; i < n; i++) {
var start = i * CHUNK_LEN,
end = (i + 1) * CHUNK_LEN;
console.log(start + ' - ' + (end - 1));
dataChannel.send(img.data.subarray(start, end));
}
//发送提醒(如果有)
if (len % CHUNK_LEN) {
console.log('last ' + len % CHUNK_LEN + ' byte(s)');
dataChannel.send(img.data.subarray(n * CHUNK_LEN));
}
}
- 接收端将数据通道消息字节转换回图像并将图像显示给用户:
js
function receiveDataChromeFactory() {
var buf, count;
return function onmessage(event) {
if (typeof event.data === 'string') {
buf = window.buf = new Uint8ClampedArray(parseInt(event.data));
count = 0;
console.log('Expecting a total of ' + buf.byteLength + ' bytes');
return;
}
var data = new Uint8ClampedArray(event.data);
buf.set(data, count);
count += data.byteLength;
console.log('count: ' + count);
if (count === buf.byteLength) {
//所有数据块都已接收
console.log('Done. Rendering photo.');
renderPhoto(buf);
}
};
}
function renderPhoto(data) {
var canvas = document.createElement('canvas');
canvas.width = photoContextW;
canvas.height = photoContextH;
canvas.classList.add('incomingPhoto');
// trail是保存传入图像的元素
trail.insertBefore(canvas, trail.firstChild);
var context = canvas.getContext('2d');
var img = context.createImageData(photoContextW, photoContextH);
img.data.set(data);
context.putImageData(img, 0, 0);
}
以下是 index.html 文件的代码
html
<!DOCTYPE html>
<html>
<head>
<title>与WebRTC的实时通信</title>
<link rel="stylesheet" href="/css/main.css" />
</head>
<body>
<h1>与WebRTC的实时通信</h1>
<h2>
<span>Room URL: </span><span id="url">...</span>
</h2>
<div id="videoCanvas">
<video id="camera" autoplay></video>
<canvas id="photo"></canvas>
</div>
<div id="buttons">
<button id="snap">Snap</button><span> then </span><button id="send">Send</button>
<span> or </span>
<button id="snapAndSend">Snap & Send</button>
</div>
<div id="incoming">
<h2>Incoming photos</h2>
<div id="trail"></div>
</div>
<script src="/socket.io/socket.io.js"></script>
<script src="https://webrtc.github.io/adapter/adapter-latest.js"></script>
<script src="js/main.js"></script>
</body>
</html>
总结
WebRTC 是实时通信的未来。日常使用的所有小工具,如手机、笔记本电脑、智能电视和人工智能等,都连接到互联网。借助 WebRTC ,所有这些设备都可以在一个通用平台上平稳、安全地相互共享语音、视频和实时数据,这也就是 WebRTC 的愿景。
WebRTC 相关的 API 很多,如果想要构建好一个 webRTC 的应用程序需要参考 webRTC 相关的资料.