编辑注:自 2013 年这篇文章发布以来,发生了很多变化……WebRTC 现在已在所有主流浏览器中广泛可用,但其 API 却略有不同。作为 Web 标准化流程的一部分,我们看到了诸如更细粒度地控制媒体(通过轨道而不是流)等改进。请查看此 MDN 上的简单 RTCDataChannel 示例 以获取更最新的示例。
在我上一篇文章 WebRTC 和首字母缩略词的海洋 中,我介绍了 WebRTC 背后的网络术语。在这篇续集中,我将详细介绍新的 WebRTC API。在阅读完本文后,您应该能够建立起工作的点对点 DataChannels 和媒体。
垫片
可以想象,对于如此早期的 API,您必须使用浏览器前缀并将其填充到一个公共变量中。
var PeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection;
var IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate;
var SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription;
navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
PeerConnection
这是与对等节点建立连接的起点。它接受有关使用哪些服务器以及连接类型选项的信息。
var pc = new PeerConnection(server, options);
服务器
服务器对象包含有关使用哪些 TURN 和/或 STUN 服务器的信息。这是必需的,以确保大多数用户可以通过避免 NAT 和防火墙中的限制来实际建立连接。
var server = {
iceServers: [
{url: "stun:23.21.150.121"},
{url: "stun:stun.l.google.com:19302"},
{url: "turn:numb.viagenie.ca", credential: "webrtcdemo", username: "louis%40mozilla.com"}
]
}
Google 运行了一个 公共 STUN 服务器,我们可以使用它。我还获得了 http://numb.viagenie.ca/ 上的免费 TURN 服务器帐户。您可能也希望这样做,并替换为您自己的凭据。
选项
根据连接类型,您需要传递一些选项。
var options = {
optional: [
{DtlsSrtpKeyAgreement: true},
{RtpDataChannels: true}
]
}
DtlsSrtpKeyAgreement
是 Chrome 和 Firefox 互操作所需的。
RtpDataChannels
是我们想要在 Firefox 上使用 DataChannels API 时所需的。
ICECandidate
创建 PeerConnection 并传入可用的 STUN 和 TURN 服务器后,一旦 ICE 框架找到一些允许您与对等节点连接的“候选”,就会触发一个事件。这被称为 ICE 候选,它将在 PeerConnection#onicecandidate 上执行回调函数。
pc.onicecandidate = function (e) {
// candidate exists in e.candidate
if (e.candidate == null) { return }
send("icecandidate", JSON.stringify(e.candidate));
pc.onicecandidate = null;
};
执行回调时,我们必须使用信令通道将候选发送到对等节点。在 Chrome 中,通常会找到多个 ICE 候选,我们只需要一个,因此我通常发送第一个,然后删除处理程序。Firefox 将候选包含在 Offer SDP 中。
信令通道
现在我们有了 ICE 候选,我们需要将其发送到我们的对等节点,以便他们知道如何与我们连接。但是,这给我们留下了一个先有鸡还是先有蛋的问题;我们希望 PeerConnection 将数据发送到对等节点,但在那之前,我们需要向他们发送元数据……
这就是信令通道的作用。它是任何允许两个对等节点交换信息的数据传输方法。在本文中,我们将使用 FireBase,因为它非常易于设置,并且不需要任何托管或服务器代码。
现在,假设存在两种方法:send()
将获取一个键并将数据分配给它,而 recv()
在键具有值时将调用处理程序。
数据库的结构如下所示
{
"": {
"candidate:": …
"offer": …
"answer": …
}
}
连接通过 roomId
分割,并将存储 4 个信息片段,即来自提供者的 ICE 候选、来自应答者的 ICE 候选、Offer SDP 和 Answer SDP。
Offer
Offer SDP(会话描述协议)是描述给其他对等节点的格式(视频、格式、编解码器、加密、分辨率、大小等)的元数据。
交换需要来自对等节点的 Offer,然后其他对等节点必须接收 Offer 并提供 Answer。
pc.createOffer(function (offer) {
pc.setLocalDescription(offer);
send("offer", JSON.stringify(offer));
}, errorHandler, constraints);
errorHandler
如果生成 Offer 时出现问题,则此方法将与错误详细信息作为第一个参数一起执行。
var errorHandler = function (err) {
console.error(err);
};
约束
Offer SDP 的选项。
var constraints = {
mandatory: {
OfferToReceiveAudio: true,
OfferToReceiveVideo: true
}
};
OfferToReceiveAudio/Video
告诉其他对等节点您希望从他们那里接收视频或音频。对于 DataChannels,这不需要。
生成 Offer 后,我们必须将本地 SDP 设置为新的 Offer 并将其通过信令通道发送到其他对等节点,并等待他们的 Answer SDP。
Answer
Answer SDP 就像 Offer 一样,但它是响应;有点像接电话。只有在收到 Offer 后,我们才能生成 Answer。
recv("offer", function (offer) {
offer = new SessionDescription(JSON.parse(offer))
pc.setRemoteDescription(offer);
pc.createAnswer(function (answer) {
pc.setLocalDescription(answer);
send("answer", JSON.stringify(answer));
}, errorHandler, constraints);
});
DataChannel
我将首先解释如何使用 PeerConnection 用于 DataChannels API 并在对等节点之间传输任意数据。
注意:在撰写本文时,DataChannels 的 Chrome 和 Firefox 之间的互操作性是不可能的。Chrome 支持类似但私有的协议,并且很快将支持标准协议。
var channel = pc.createDataChannel(channelName, channelOptions);
提供者应该是创建通道的对等节点。应答者将在 PeerConnection 上的 ondatachannel
回调中接收通道。您必须在创建 Offer 之前先调用 createDataChannel()
。
channelName
这是一个用作通道名称标签的字符串。警告:确保您的通道名称中没有空格,否则 Chrome 将在 createAnswer()
上失败。
channelOptions
var channelOptions = {};
目前,Chrome 对这些选项的支持不太好,因此您可以暂时将其留空。请查看 RFC 以获取有关选项的更多信息。
通道事件和方法
onopen
建立连接时执行。
onerror
如果创建连接时出错,则执行。第一个参数是错误对象。
channel.onerror = function (err) {
console.error("Channel Error:", err);
};
onmessage
channel.onmessage = function (e) {
console.log("Got message:", e.data);
}
连接的核心。当您收到消息时,此方法将执行。第一个参数是事件对象,其中包含数据、接收时间和其他信息。
onclose
如果其他对等节点关闭连接,则执行。
绑定事件
如果您是通道的创建者(即提供者),则可以直接将事件绑定到您使用 createChannel
创建的 DataChannel。如果您是应答者,则必须使用 PeerConnection 上的 ondatachannel
回调来访问相同的通道。
pc.ondatachannel = function (e) {
e.channel.onmessage = function () { … };
};
通道在作为 e.channel
传递到处理程序的事件对象中可用。
send()
channel.send("Hi Peer!");
此方法允许您直接将数据发送到对等节点!太棒了。您必须发送字符串、Blob、ArrayBuffer 或 ArrayBufferView,因此请确保将对象字符串化。
close()
连接结束时关闭通道。建议在页面卸载时执行此操作。
媒体
现在我们将介绍传输音频和视频等媒体。要显示视频和音频,您必须在文档中包含一个带有 autoplay
属性的 <video>
标签。
获取用户媒体
var video = document.getElementById("preview");
navigator.getUserMedia(mediaOptions, function (stream) {
video.src = URL.createObjectURL(stream);
}, errorHandler);
mediaOptions
您希望从用户返回的媒体类型的约束。
var mediaOptions = {
video: true,
audio: true
};
如果您只需要音频聊天,请删除 video
键。
errorHandler
如果返回请求的媒体时出错,则执行。
媒体事件和方法
addStream
将来自 getUserMedia
的流添加到 PeerConnection。
pc.addStream(stream);
onaddstream
连接建立且其他对等节点已使用 addStream
将流添加到对等连接时执行。您需要另一个 <video>
标签来显示其他对等节点的媒体。
var otherPeer = document.getElementById("otherPeer");
pc.onaddstream = function (e) {
otherPeer.src = URL.createObjectURL(e.stream);
};
第一个参数是包含其他对等节点媒体流的事件对象。
查看源代码
您可以在 我的 WebRTC 存储库 中看到根据本文中所有代码段构建的源代码。
关于 Louis Stowasser
我是 Mozilla 的合作伙伴工程师,Gamedev Weekly 的维护者以及位于澳大利亚布里斯班的 CraftyJS 游戏引擎的创建者。
14 条评论