一、WebRTC简介
WebRTC 是由一家名为 Gobal IP Solutions,简称 GIPS 的瑞典公司开发的。Google 在 2011 年收购了 GIPS,并将其源代码开源。然后又与 IETF 和 W3C 的相关标准机构合作,以确保行业达成共识。其中:
- Web Real-Time Communications (WEBRTC) W3C 组织:定义浏览器 API。
- Real-Time Communication in Web-browsers (RTCWEB) IETF 标准组织:定义其所需的协议,数据,安全性等手段。
WebRTC(Web Real-Time Communication)是一项开源的技术,旨在实现实时音视频以及数据通信,而无需安装任何插件或扩展。WebRTC 允许用户之间通过 P2P(Peer-to-Peer)模式进行实时通信,减轻服务器的负担并提高通信效率。
所以,我们可以在不需要任何第三方插件的情况下,实现一个浏览器到浏览器的点对点(P2P)连接,从而进行音视频实时通信。当然,WebRTC 提供了一些 API 供我们使用,在实时音视频通信的过程中,我们主要用到以下三个:
- getUserMedia:获取音频和视频流(MediaStream)
- RTCPeerConnection:点对点通信
- RTCDataChannel:数据通信
不过,虽然浏览器给我们解决了大部分音视频处理问题,但是从浏览器请求音频和视频时,我们还是需要特别注意流的大小和质量。因为即便硬件能够捕获高清质量流,CPU 和带宽也不一定可以跟上,这也是我们在建立多个对等连接时,不得不考虑的问题。
1、WebRTC基本概念
1. 基本组件
WebRTC P2P 由以下几个主要组件组成:
- Camera 和 AudioRecord:通过 Camera(Camera1 或 Camera2)和 AudioRecord 类访问音视频设备。处理 Android 设备的权限请求,以便访问摄像头和麦克风。
- PeerConnectionFactory:创建 RTCPeerConnection 和 RTCDataChannel 实例的核心类。
- RTCPeerConnection:实现 P2P 通信,处理音视频编解码、网络通信等。负责处理信令过程、交换媒体信息以及协调 NAT 穿透。
- RTCDataChannel:用于在 P2P 通信中传输非音视频数据,如文字、图片、文件等。实现低延迟、可靠的数据通信通道。
- VideoTrack 和 AudioTrack:代表视频轨道和音频轨道。操作音视频流,例如添加到 RTCPeerConnection 中,实现音视频的传输。
- VideoRenderer:用于显示远端视频流。为本地和远端视频流分别创建 VideoRenderer 实例,如 SurfaceViewRenderer 或 TextureViewRenderer。将视频流渲染到 Android 界面上。
- EglBase:在渲染视频时,可能需要使用 EglBase 类。EglBase 提供了 OpenGL ES 上下文,用于渲染视频到 Android 视图(如 SurfaceViewRenderer 或 TextureViewRenderer)中。
- 音频编解码器:WebRTC 支持多种音频编解码器,如 Opus、G.711、iSAC 等。根据 SDP 信息自动选择合适的编解码器。
- 视频编解码器:WebRTC 支持多种视频编解码器,如 VP8、VP9、H.264 等。根据 SDP 信息自动选择合适的编解码器。
- 编解码器设置与优化:在特定场景下,可能需要对编解码器的参数进行调整,以获得更好的音视频质量或降低带宽占用。例如,调整视频编码器的分辨率、帧率、码率等参数。
- 硬件编解码器支持:在 Android 平台上,可以使用 MediaCodec 进行硬件编解码。并且使用硬件编解码器可以显著降低 CPU 占用,大大提高编解码性能。
- ICE(Interactive Connectivity Establishment):处理网络协议,用于在复杂的网络环境中建立 P2P 连接。协助 WebRTC 客户端在 NAT 和防火墙等环境中找到可用的通信路径。
- STUN(Session Traversal Utilities for NAT):STUN 服务器帮助 WebRTC 客户端获取公网 IP 地址和端口,以便在 NAT 环境中建立 P2P 连接。STUN 服务器通过向 WebRTC 客户端返回其公网地址,使客户端能够在信令过程中交换这些信息。
- TURN(Traversal Using Relays around NAT):当 STUN 服务器无法建立直接的 P2P 连接时,TURN 服务器充当中继,将客户端的数据中转至另一个客户端。虽然这会增加通信延迟,但能确保通信成功。TURN 服务器通常与 STUN 服务器一起部署,作为 WebRTC NAT 穿透的备选方案。
2. 信令流程
WebRTC 不包含信令协议,因此需要开发者根据需求自定义信令过程。
信令主要用于协调双方建立 P2P 连接、交换音视频编码信息、网络地址等。
在 WebRTC 中,信令主要负责以下几个方面:
- 协商通信参数:通信双方需要协商一些通信参数,例如音视频编码格式、分辨率等。这些参数被封装在 SDP(会话描述协议)中,并通过信令服务器进行交换。
- 发现和交换网络地址:由于 NAT(网络地址转换)的存在,通信双方需要发现并交换其公共网络地址。这一过程通过 ICE(Interactive Connectivity Establishment)协议来实现。
3. SDP(会话描述协议)
SDP 是一种文本格式的协议,用于描述多媒体会话的属性,例如音视频编码格式、分辨率、帧率等。在 WebRTC 中,SDP 主要用于在通信双方之间协商通信参数。
4. NAT 穿透和 STUN/TURN 服务器
在实际网络环境中,大多数用户都位于 NAT(网络地址转换)环境之下。NAT 会导致用户之间无法直接建立 P2P 连接。为了解决这个问题,WebRTC 使用了 ICE 协议,结合 STUN(Session Traversal Utilities for NAT)和 TURN(Traversal Using Relays around NAT)服务器进行 NAT 穿透。
- STUN:STUN 服务器帮助用户获取公网 IP 地址和端口,供其他用户建立连接。
- TURN:当 STUN 服务器无法实现 NAT 穿透时,TURN 服务器作为中继,将用户的数据中转至另一个用户。虽然这会增加通信延迟,但能确保通信成功。
ICE 的工作原理如下:
- 首先,通信双方收集本地网络地址(包括私有地址和公共地址)以及通过 STUN 和 TURN 服务器获取的候选地址。
- 接下来,双方通过信令服务器交换这些候选地址。
- 通信双方使用这些候选地址进行连接测试,确定最佳的可用地址。
- 一旦找到可用的地址,通信双方就可以开始实时音视频通话。
5.信令服务器
信令服务器负责协调客户端之间的通信,主要包括交换 SDP 信息和 ICE 候选(NAT 穿越信息)。信令服务器可以使用 WebSocket、Socket.IO 等技术来实现。
在选择信令服务器时,开发者需要考虑以下几个方面:
- 可靠性:信令服务器需要具有高可靠性,以确保通话过程中的信令消息能够准确无误地传输。
- 延迟:信令服务器的延迟应尽可能低,以减少通话建立过程中的等待时间。
- 扩展性:随着用户数量的增长,信令服务器需要具有良好的扩展性,以满足更高的并发需求。
- 安全性:信令服务器应提供安全机制,例如 SSL/TLS 加密,以防止信令消息被窃听或篡改。
2、WebRTC应用场景
1. 实时音视频通信:
- 点对点视频聊天:如 Google Meet、Zoom 等实时视频通话应用。
- 多人视频会议:企业级多人视频会议系统,如飞书、钉钉、腾讯会议、 Cisco Webex、Microsoft Teams 等。
- 在线教育:远程教育、在线培训和在线课堂等,如腾讯课堂、网易云课堂等。
- 电子健康:远程医疗咨询、在线心理咨询等。
- 客户支持:在线客服系统,提供实时音视频客户支持。
2. 实时数据传输和文件共享:
- P2P 文件共享:直接在浏览器之间传输文件,如 ShareDrop、WebDrop 等。
- 在线协作工具:实时文档编辑、在线白板等,如 飞书文档、Google Docs、Microsoft Office 365 等。
- 多人游戏:基于浏览器的实时多人游戏,如 pixelstreaming。
- 实时数据同步:实时数据同步,如物联网设备状态同步。
3. 实时媒体流处理:
- 实时视频监控:使用 WebRTC 进行低延迟的实时视频监控。
- 实时直播:音频和视频直播,如 Facebook Live、Twitch 等。
- 实时录屏和远程桌面:支持远程桌面访问、协助和实时录屏等功能。
- 实时音视频处理:实时滤镜、美颜、背景替换等。
4. 物联网(IoT)和边缘计算:
- 实时设备控制:在浏览器中直接控制物联网设备。
- 边缘计算:将音视频处理和数据处理任务分布到边缘设备上,减轻服务器负担。
- 远程团队协作:使用 WebRTC 实现远程工程师对设备的实时控制和诊断。
5. 社交网络:
- 社交应用:如 QQ、微信、Facebook、WhatsApp 等应用中的实时音视频聊天功能。
- 直播平台:如 TikTok、抖音、快手 等短视频平台的实时互动直播功能。
由于 WebRTC 可以在不依赖插件或外部应用的情况下实现实时通信,因此在各种需要实时音视频通信和数据传输的场景中都可以发挥重要作用。
3、getUserMedia
getUserMedia 主要就是用来获取设备的媒体流(即 MediaStream)。它可以接受一个约束对象 constraints 作为参数,用来指定需要获取到什么样的媒体流。
navigator.mediaDevices.getUserMedia
是新版的 API,旧版的是 navigator.getUserMedia
。为了避免兼容性问题,我们可以稍微处理一下(其实说到底,现在 WebRTC 的支持率还不算高,有需要的可以选择一些适配器,如 adapter.js
)。
// 判断是否有 navigator.mediaDevices,没有赋成空对象
if (navigator.mediaDevices === undefined) {
navigator.mediaDevices = {};
}
// 继续判断是否有 navigator.mediaDevices.getUserMedia,没有就采用 navigator.getUserMedia
if (navigator.mediaDevices.getUserMedia) {
navigator.mediaDevices.getUserMedia = function(prams) {
let getUserMedia = navigator.webkitGetUserMedia || navigator.mozGetUserMedia;
// 兼容获取
if (!getUserMedia) {
return Promise.reject(new Error('getUserMedia is not implemented in this browser'));
}
return new Promise(function(resolve, reject) {
getUserMedia.call(navigator, prams, resolve, reject);
});
};
}
navigator.mediaDevices.getUserMedia(constraints)
.then(stream => {
let video = document.querySelector('#Rtc');
if ('srcObject' in video) { // 判断是否支持 srcObject 属性
video.srcObject = stream;
} else {
video.src = window.URL.createObjectURL(stream);
}
video.onloadedmetadata = function(e) {
video.play();
};
})
.catch((err) => { // 捕获错误
console.error(err.name + ': ' + err.message);
});
navigator.mediaDevices.getUserMedia({ audio: false, video: true });
// 只需要视频流,不要音频
// 获取指定宽高,这里需要注意:在改变视频流的宽高时,
// 如果宽高比和采集到的不一样,会直接截掉某部分
{
audio: false,
video: { width: 1280, height: 720 }
}
// 设定理想值、最大值、最小值
{
audio: true,
video: {
width: { min: 1024, ideal: 1280, max: 1920 },
height: { min: 776, ideal: 720, max: 1080 }
}
}
// 对于移动设备来说,还可以指定获取前摄像头,或者后置摄像头
{ audio: true, video: { facingMode: "user" } } // 前置
{ audio: true, video: { facingMode: { exact: "environment" } } } // 后置
// 也可以指定设备 id,
// 通过 navigator.mediaDevices.enumerateDevices() 可以获取到支持的设备
{ video: { deviceId: myCameraDeviceId } }
// 设置视频源为屏幕,但是目前只有火狐支持了这个属性
{ audio: true, video: {mediaSource: 'screen'} }
4、RTCPeerConnection
RTCPeerConnection 接口代表一个由本地计算机到远端的 WebRTC 连接。该接口提供了创建,保持,监控,关闭连接的方法的实现。
RTCPeerConnection 作为创建点对点连接的 API,是我们实现音视频实时通信的关键。在点对点通信的过程中,需要交换一系列信息,通常这一过程叫做 — 信令(signaling)。
在信令阶段需要完成的任务:
* 为每个连接端创建一个 RTCPeerConnection,并添加本地媒体流。
* 获取并交换本地和远程描述:SDP 格式的本地媒体元数据。
* 获取并交换网络信息:潜在的连接端点称为 ICE 候选者。
我们虽然把 WebRTC 称之为点对点的连接,但并不代表在实现过程中不需要服务器的参与。相反,在点对点的信道建立起来之前,二者之间是没有办法通信的。这也就意味着,在信令阶段,我们需要一个通信服务来帮助我们建立起这个连接。WebRTC 本身没有指定某一个信令服务,所以,我们可以但不限于使用 XMPP、XHR、Socket 等来做信令交换所需的服务。
1. NAT 穿越技术
为每个连接端创建一个 RTCPeerConnection,并添加本地媒体流。事实上,如果是一般直播模式,则只需要播放端添加本地流进行输出,其他参与者只需要接受流进行观看即可。
// 因为各浏览器差异,RTCPeerConnection 一样需要加上前缀
let PeerConnection = window.RTCPeerConnection ||
window.mozRTCPeerConnection ||
window.webkitRTCPeerConnection;
let peer = new PeerConnection(iceServers);
// 参数 — iceServers
// 参数配置了两个 url,分别是 STUN 和 TURN,这便是 WebRTC 实现点对点通信的关键,也是一般 P2P 连接都需要解决的问题:NAT穿越。
{
iceServers: [
{
url: "stun:stun.l.google.com:19302" // 谷歌的公共服务
},
{
url: "turn:***",
username: ***, // 用户名
credential: *** // 密码
}
]
}
一般情况下会采用 ICE 协议框架进行 NAT 穿越,ICE 的全称为 Interactive Connectivity Establishment,即交互式连接建立。它使用 STUN 协议以及 TURN 协议来进行穿越。
关于 NAT 穿越的更多信息可以参考