原文:Get Started with WebRTC - HTML5 Rocks
想象一下,在这样一个世界中,您的手机、电视和计算机可以在一个通用平台上进行通信。想象一下,将视频聊天和对等数据共享添加到 Web 应用很容易。这就是WebRTC的愿景。
想试试吗?WebRTC可在桌面和移动设备上使用Google Chrome,Safari,Firefox和Opera。一个好的起点是 appr.tc 的简单视频聊天应用程序:
没有时间阅读本文或只想要代码?
要获得WebRTC的概述,请观看以下Google I / O视频或查看这些幻灯片:
getUserMedia
RTCPeerConnection
或者,直接跳转到WebRTC代码实验室,这是一个分步指南,解释了如何构建一个完整的视频聊天应用程序,包括一个简单的信令服务器。
网络面临的最后一个主要挑战之一是通过语音和视频实现人类通信:实时通信或简称RTC。RTC 在 Web 应用中应与在文本输入中输入文本一样自然。没有它,你创新和开发新互动方式的能力就会受到限制。
从历史上看,RTC一直是企业和复杂的,需要昂贵的音频和视频技术在内部许可或开发。将 RTC 技术与现有内容、数据和服务集成既困难又耗时,尤其是在 Web 上。
Gmail视频聊天在2008年开始流行,2011年,谷歌推出了使用Talk的Hangouts(Gmail也是如此)。谷歌收购了GISP,该公司开发了RTC所需的许多组件,例如编解码器和回声消除技术。Google 开源了 GIPS 开发的技术,并与互联网工程任务组 (IETF) 和万维网联盟 (W3C) 的相关标准机构合作,以确保行业共识。2011年5月,爱立信构建了WebRTC的第一个实现。
WebRTC为实时,无插件的视频,音频和数据通信实施了开放标准。需求是真实的:
WebRTC项目的指导原则是,其API应该是开源的,免费的,标准化的,内置于Web浏览器中,并且比现有技术更有效。
WebRTC用于各种应用程序,例如Google Meet。WebRTC还与WebKitGTK+和Qt原生应用程序集成。
WebRTC实现了以下三个API:
getUserMedia
)API 定义在以下两个规范中:
Chrome,Safari,Firefox,Edge和Opera在移动设备和桌面上都支持这三个API。
getUserMedia
:有关演示和代码,请参阅 WebRTC 示例或尝试 Chris Wilson 用作 Web 音频输入的惊人示例。getUserMedia
RTCPeerConnection
:有关简单的演示和功能齐全的视频聊天应用程序,请参阅 WebRTC 示例分别对等连接和 appr.tc。这个应用程序使用适配器.js,一个由谷歌在WebRTC社区的帮助下维护的JavaScript填充程序,以抽象出浏览器的差异和规格变化。
RTCDataChannel
:若要查看实际效果,请参阅 WebRTC 示例以查看其中一个数据通道演示。
WebRTC codelab 展示了如何使用所有三个 API 来构建一个简单的应用程序,用于视频聊天和文件共享。
WebRTC应用程序需要做几件事:
为了获取和通信流数据,WebRTC 实现了以下 API:
(稍后将详细讨论WebRTC的网络和信令方面。
MediaStream
接口(也称为接口)getUserMedia
MediaStream API 表示同步的媒体流。例如,从摄像头和麦克风输入中获取的流具有同步的视频和音频轨道。(不要与
理解API的最简单方法可能是在野外查看它:MediaStream
stream
每个都有一个输入(可能是由 生成的),还有一个输出(可能传递给视频元素或 )。MediaStream
MediaStream
getUserMedia()
RTCPeerConnection
该方法采用 MediaStreamConstraints 对象参数,并返回解析为对象的 参数。getUserMedia()
Promise
MediaStream
每个都有一个 ,例如 。由 and 方法返回 s 数组。MediaStream
label
'Xk7EuLhsuHKbnjLWkW4yYGNJJ8ONsgwHBvLQ'
MediaStreamTrack
getAudioTracks()
getVideoTracks()
对于 getUserMedia 示例,返回一个空数组(因为没有音频),并且假设连接了一个工作正常的网络摄像头,则返回一个数组,该数组表示来自网络摄像头的流。每个都有一个种类 ( 或 ),a(类似于 ),并表示音频或视频的一个或多个通道。在这种情况下,只有一个视频轨道,没有音频,但很容易想象用例中还有更多,例如从前置摄像头,后置摄像头,麦克风获取流的聊天应用程序以及共享其屏幕的应用程序。stream.getAudioTracks()
stream.getVideoTracks()
MediaStreamTrack
MediaStreamTrack
'video'
'audio'
label
'FaceTime HD Camera (Built-in)'
可以通过设置 srcObject 属性将 A 附加到视频元素。以前,这是通过将属性设置为使用 创建的对象 URL 来完成的,但这已被弃用。MediaStream
src
URL.createObjectURL()
正在主动使用相机,这会占用资源,并使相机保持打开状态并打开相机灯亮。当您不再使用轨道时,请确保呼叫以关闭摄像机。MediaStreamTrack
track.stop()
getUserMedia
也可以用作 Web 音频 API 的输入节点:
// Cope with browser differences.
let audioContext;
if (typeof AudioContext === 'function') {
audioContext = new AudioContext();
} else if (typeof webkitAudioContext === 'function') {
audioContext = new webkitAudioContext(); // eslint-disable-line new-cap
} else {
console.log('Sorry! Web Audio not supported.');
}
// Create a filter node.
var filterNode = audioContext.createBiquadFilter();
// See https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#BiquadFilterNode-section
filterNode.type = 'highpass';
// Cutoff frequency. For highpass, audio is attenuated below this frequency.
filterNode.frequency.value = 10000;
// Create a gain node to change audio volume.
var gainNode = audioContext.createGain();
// Default is 1 (no change). Less than 1 means audio is attenuated
// and vice versa.
gainNode.gain.value = 0.5;
navigator.mediaDevices.getUserMedia({audio: true}, (stream) => {
// Create an AudioNode from the stream.
const mediaStreamSource =
audioContext.createMediaStreamSource(stream);
mediaStreamSource.connect(filterNode);
filterNode.connect(gainNode);
// Connect the gain node to the destination. For example, play the sound.
gainNode.connect(audioContext.destination);
});
基于Chromium的应用程序和扩展程序也可以合并。通过向清单添加和/或权限,只能在安装时请求和授予一次权限。此后,不会要求用户获得相机或麦克风访问权限。getUserMedia
audioCapture
videoCapture
对于 , 只需授予一次权限。第一次,浏览器的信息栏中会显示"允许"按钮。Chrome 在 2015 年底弃用了 HTTP 访问,因为它被归类为功能强大的功能。getUserMedia()
getUserMedia()
其意图可能是为任何流数据源启用 a,而不仅仅是摄像头或麦克风。这将允许从存储的数据或任意数据源(如传感器或其他输入)进行流式传输。MediaStream
getUserMedia()
真正与其他 JavaScript API 和库结合使用:
正在上传…重新上传取消
咕噜咕噜的艺术!
约束可用于设置 的视频分辨率值。这也允许支持其他约束,例如宽高比;对置模式(前置或后置摄像头);帧速率,高度和宽度;和 applyConstraints() 方法。getUserMedia()
有关示例,请参阅 WebRTC 示例 getUserMedia:选择分辨率。
一个陷阱:约束可能会影响共享资源的可用配置。例如,如果相机在 640 x 480 模式下通过一个选项卡打开,则另一个选项卡将无法使用约束以更高分辨率模式打开它,因为它只能在一种模式下打开。请注意,这是一个实现细节。可以让第二个选项卡以更高分辨率的模式重新打开相机,并使用视频处理将第一个选项卡的视频轨道缩小到640 x 480,但尚未实现。getUserMedia
设置不允许的约束值会给出 a 或 a,例如,如果请求的分辨率不可用。若要查看此操作的实际效果,请参阅 WebRTC 示例 getUserMedia:为演示选择分辨率。DOMException
OverconstrainedError
屏幕和选项卡捕获
Chrome 应用还允许通过 chrome.tabCapture 和 chrome.desktopCapture API 共享单个浏览器标签页或整个桌面的实时视频。(有关演示和更多信息,请参阅使用 WebRTC 进行屏幕共享。这篇文章已经有几年的历史了,但它仍然很有趣。
在 Chrome 中,也可以使用实验性约束将屏幕捕获用作源。请注意,屏幕捕获需要 HTTPS,并且只能用于开发,因为它是通过命令行标志启用的,如本文所述。MediaStream
chromeMediaSource
WebRTC用于在浏览器(也称为对等体)之间传输流数据,但也需要一种机制来协调通信和发送控制消息,这一过程称为信令。WebRTC未指定信令方法和协议。信令不是 API 的一部分。RTCPeerConnection
RTCPeerConnection
相反,WebRTC应用程序开发人员可以选择他们喜欢的任何消息传递协议,例如SIP或XMPP,以及任何适当的双工(双向)通信通道。appr.tc 示例使用 XHR 和通道 API 作为信令机制。代码实验室使用在 Node 服务器上运行的 Socket.io。
信令用于交换三种类型的信息:
通过信令进行的信息交换必须已成功完成,然后才能开始对等流。
例如,假设爱丽丝想和鲍勃交流。下面是 W3C WebRTC 规范中的代码示例,其中显示了运行中的信令过程。该代码假定存在该方法中创建的某些信令机制。另请注意,在 Chrome 和 Opera 上,当前带有前缀。createSignalingChannel()
RTCPeerConnection
// handles JSON.stringify/parse
const signaling = new SignalingChannel();
const constraints = {audio: true, video: true};
const configuration = {iceServers: [{urls: 'stuns:stun.example.org'}]};
const pc = new RTCPeerConnection(configuration);
// Send any ice candidates to the other peer.
pc.onicecandidate = ({candidate}) => signaling.send({candidate});
// Let the "negotiationneeded" event trigger offer generation.
pc.onnegotiationneeded = async () => {
try {
await pc.setLocalDescription(await pc.createOffer());
// Send the offer to the other peer.
signaling.send({desc: pc.localDescription});
} catch (err) {
console.error(err);
}
};
// Once remote track media arrives, show it in remote video element.
pc.ontrack = (event) => {
// Don't set srcObject again if it is already set.
if (remoteView.srcObject) return;
remoteView.srcObject = event.streams[0];
};
// Call start() to initiate.
async function start() {
try {
// Get local stream, show it in self-view, and add it to be sent.
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
selfView.srcObject = stream;
} catch (err) {
console.error(err);
}
}
signaling.onmessage = async ({desc, candidate}) => {
try {
if (desc) {
// If you get an offer, you need to reply with an answer.
if (desc.type === 'offer') {
await pc.setRemoteDescription(desc);
const stream =
await navigator.mediaDevices.getUserMedia(constraints);
stream.getTracks().forEach((track) =>
pc.addTrack(track, stream));
await pc.setLocalDescription(await pc.createAnswer());
signaling.send({desc: pc.localDescription});
} else if (desc.type === 'answer') {
await pc.setRemoteDescription(desc);
} else {
console.log('Unsupported SDP type.');
}
} else if (candidate) {
await pc.addIceCandidate(candidate);
}
} catch (err) {
console.error(err);
}
};
首先,Alice 和 Bob 交换网络信息。(表达式查找候选项是指使用 ICE 框架查找网络接口和端口的过程。
RTCPeerConnection
onicecandidate
addIceCandidate
WebRTC 客户端(也称为对等体,在本例中称为 Alice 和 Bob)还需要确定和交换本地和远程音频和视频媒体信息,例如解析和编解码器功能。通过会话描述协议 (SDP) 交换产品/服务和答案来发出信号以交换媒体配置信息:
RTCPeerConnection
createOffer()
RTCSessionDescription
setLocalDescription()
RTCPeerConnection
setLocalDescription()
setRemoteDescription()
RTCPeerConnection
createAnswer()
createAnswer()
RTCSessionDescription
setRemoteDescription
确保允许 在不再需要时通过调用来回收 垃圾回收。否则,线程和连接将保持活动状态。在WebRTC中可能会泄漏大量资源!RTCPeerConnection
close()
RTCSessionDescription
对象是符合会话描述协议 SDP 的 Blob。序列化后,SDP 对象如下所示:
v=0
o=- 3883943731 1 IN IP4 127.0.0.1
s=
t=0 0
a=group:BUNDLE audio video
m=audio 1 RTP/SAVPF 103 104 0 8 106 105 13 126
// ...
a=ssrc:2223794119 label:H4fjnMzxy3dPIgQ7HxuCTLb4wLLLeRHnFxh810
网络和媒体信息的获取和交换可以同时完成,但是在对等体之间的音频和视频流开始之前,这两个过程必须已经完成。
前面描述的要约/答案体系结构称为 JavaScript 会话建立协议或 JSEP。(在爱立信的第一个WebRTC实现的演示视频中,有一个很棒的动画解释了信令和流式传输的过程。
JSEP 架构
一旦信令过程成功完成,数据就可以在呼叫者和被叫方之间直接对等流式传输,或者,如果失败,则通过中间中继服务器(稍后将详细介绍)。流式传输是 的工作。RTCPeerConnection
RTCPeerConnection
是 WebRTC 组件,用于处理对等体之间流数据的稳定和高效通信。
下面是一个 WebRTC 体系结构图,显示了 的作用。您会注意到,绿色部分很复杂!RTCPeerConnection
正在上传…重新上传取消
WebRTC架构(来自 webrtc.org)
从JavaScript的角度来看,从这张图中要了解的主要内容是,它保护Web开发人员免受潜伏在背后的无数复杂性的影响。WebRTC使用的编解码器和协议做了大量的工作,使实时通信成为可能,即使在不可靠的网络上也是如此:RTCPeerConnection
前面的 W3C 代码从信令角度展示了 WebRTC 的简化示例。以下是两个工作 WebRTC 应用程序的演练。第一个是要演示的简单示例,第二个是完全可操作的视频聊天客户端。RTCPeerConnection
以下代码取自 WebRTC 示例对等连接,该连接在一个网页上具有本地和远程(以及本地和远程视频)。这并不构成任何非常有用的东西 - 调用方和被调用方在同一页面上 - 但它确实使API的工作原理更加清晰,因为页面上的对象可以直接交换数据和消息,而无需使用中间信令机制。RTCPeerConnection
RTCPeerConnection
RTCPeerConnection
在此示例中,表示本地对等方(调用方)和远程对等方(被调用方)。pc1
pc2
创建一个新流并从中添加流:RTCPeerConnection
getUserMedia()
// Servers is an optional configuration file. (See TURN and STUN discussion later.)
pc1 = new RTCPeerConnection(servers);
// ...
localStream.getTracks().forEach((track) => {
pc1.addTrack(track, localStream);
});
创建产品/服务并将其设置为 的本地描述和 远程描述。这可以直接在代码中完成,而无需使用信令,因为调用方和被调用方都在同一页面上:pc1
pc2
pc1.setLocalDescription(desc).then(() => {
onSetLocalSuccess(pc1);
},
onSetSessionDescriptionError
);
trace('pc2 setRemoteDescription start');
pc2.setRemoteDescription(desc).then(() => {
onSetRemoteSuccess(pc2);
},
onSetSessionDescriptionError
);
创建并在添加来自 的流时,将其显示在视频元素中:pc2
pc1
pc2 = new RTCPeerConnection(servers);
pc2.ontrack = gotRemoteStream;
//...
function gotRemoteStream(e){
vid2.srcObject = e.stream;
}
RTCPeerConnection
API 加服务器在现实世界中,WebRTC需要服务器,无论多么简单,因此可能会发生以下情况:
换句话说,WebRTC需要四种类型的服务器端功能:
NAT 遍历、对等网络以及构建用于用户发现和信令的服务器应用的要求超出了本文的讨论范围。可以说,ICE框架使用STUN协议及其扩展名TURN来应对NAT遍历和其他网络变幻莫测。RTCPeerConnection
ICE 是用于连接对等方(如两个视频聊天客户端)的框架。最初,ICE 尝试通过 UDP 以尽可能低的延迟直接连接对等方。在此过程中,STUN 服务器只有一个任务:使 NAT 后面的对等方能够查找其公共地址和端口。(有关 STUN 和 TURN 的详细信息,请参阅构建 WebRTC 应用所需的后端服务。
查找连接候选项
如果 UDP 失败,ICE 将尝试 TCP。如果直接连接失败(特别是由于企业 NAT 遍历和防火墙),ICE 将使用中间(中继)TURN 服务器。换句话说,ICE 首先将 STUN 与 UDP 结合使用来直接连接对等体,如果失败,则回退到 TURN 中继服务器。表达式查找候选项是指查找网络接口和端口的过程。
WebRTC数据路径
WebRTC工程师Justin Uberti在2013年Google I / O WebRTC演示中提供了有关ICE,STUN和TURN的更多信息。(演示幻灯片给出了 TURN 和 STUN 服务器实现的示例。
一个简单的视频聊天客户端
尝试WebRTC的好地方,包括使用STUN服务器的信令和NAT /防火墙穿越,是 appr.tc 的视频聊天演示。此应用使用适配器.js、填充程序将应用与规范更改和前缀差异隔离开来。
代码在其日志记录中故意冗长。检查控制台以了解事件的顺序。下面是代码的详细演练。
如果你觉得这有点令人困惑,你可能更喜欢 WebRTC codelab。本分步指南介绍了如何构建完整的视频聊天应用程序,包括在 Node 服务器上运行的简单信令 服务器。
目前实现的WebRTC仅支持一对一通信,但可用于更复杂的网络场景,例如多个对等体直接或通过多点控制单元(MCU)相互通信,多点控制单元(MCU)是可以处理大量参与者并进行选择性流转发的服务器,以及混合或录制音频和视频。
多点控制单元拓扑示例
许多现有的WebRTC应用程序仅演示Web浏览器之间的通信,但网关服务器可以使在浏览器上运行的WebRTC应用程序与设备(如电话(也称为PSTN))和VOIP系统进行交互。2012年5月,Doubango Telecom开源了使用WebRTC和WebSocket构建的sipml5 SIP客户端,该客户端(以及其他潜在用途)支持在iOS和Android上运行的浏览器和应用程序之间的视频通话。在Google I / O上,Tethr和Tropo展示了一个使用OpenBTS单元在公文包中进行灾难通信的框架,以通过WebRTC实现功能手机和计算机之间的通信。电话通信没有运营商!
Tethr/Tropo:公文包中的灾难通信
RTCDataChannel
应用程序接口除了音频和视频,WebRTC还支持其他类型的数据的实时通信。
该 API 支持以低延迟和高吞吐量对等方式交换任意数据。有关单页演示以及如何构建简单的文件传输应用程序,请分别参阅 WebRTC 示例和 WebRTC 代码实验室。RTCDataChannel
该 API 有许多潜在的用例,包括:
该 API 具有多项功能,可充分利用并实现强大而灵活的对等通信:RTCPeerConnection
RTCPeerConnection
语法有意类似于 WebSocket,具有方法和事件:send()
message
const localConnection = new RTCPeerConnection(servers);
const remoteConnection = new RTCPeerConnection(servers);
const sendChannel =
localConnection.createDataChannel('sendDataChannel');
// ...
remoteConnection.ondatachannel = (event) => {
receiveChannel = event.channel;
receiveChannel.onmessage = onReceiveMessage;
receiveChannel.onopen = onReceiveChannelStateChange;
receiveChannel.onclose = onReceiveChannelStateChange;
};
function onReceiveMessage(event) {
document.querySelector("textarea#send").value = event.data;
}
document.querySelector("button#send").onclick = () => {
var data = document.querySelector("textarea#send").value;
sendChannel.send(data);
};
通信直接发生在浏览器之间,因此即使当打孔以应对防火墙和 NAT 失败时需要中继 (TURN) 服务器,也可以比 WebSocket 快得多。RTCDataChannel
RTCDataChannel
可在 Chrome、Safari、Firefox、Opera 和 Samsung Internet 中使用。Cube Slam 游戏使用 API 来传达游戏状态。玩朋友或玩熊!创新平台Sharefest通过peerCDN实现了文件共享,并提供了WebRTC如何实现点对点内容分发的一瞥。RTCDataChannel
有关 的更多信息,请查看 IETF 的协议规范草案。RTCDataChannel
实时通信应用程序或插件可能会通过多种方式危及安全性。例如:
WebRTC有几个功能可以避免这些问题:
关于流媒体安全性的完整讨论超出了本文的范围。有关更多信息,请参阅 IETF 提议的 WebRTC 安全架构。
WebRTC的API和标准可以使内容创建和通信工具民主化和分散化,包括电话,游戏,视频制作,音乐制作和新闻采集。
没有比这更具颠覆性的技术了。
正如博主菲尔·埃德霍尔姆(Phil Edholm)所说,"WebRTC和HTML5可能实现与原始浏览器对信息相同的实时通信转换。
chrome://webrtc-internals 屏幕截图
getUserMedia
)MediaStream
和 APIgetUserMedia
RTCPeerConnection
应用程序接口RTCDataChannel
应用程序接口有关 API 的跨平台支持(例如 和)的更多详细信息,请参阅 caniuse.com 和 Chrome 平台状态。getUserMedia
RTCPeerConnection
本机 API 也可在有关 webrtc.org 的文档中找到。RTCPeerConnection