WebRTC 和早期 API

编辑注:自 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 游戏引擎的创建者。

更多 Louis Stowasser 的文章…


14 条评论

  1. Jonathan

    太棒了。这是我见过的关于 WebRTC 最佳的实用指南之一。我希望我在开始使用它时就有这个指南,尤其是这个:“DataChannels 的 Chrome 和 Firefox 之间的互操作性是不可能的”。

    2013 年 7 月 31 日 下午 5:50

    1. bardu

      我同意 Jonathan 的观点。

      2013 年 8 月 1 日 上午 9:09

  2. Fabian Gort

    文章清晰易懂,谢谢!有谁知道 Chrome 和 Firefox 是否以及何时将使用相同的协议来实现可靠的 datachannels?听起来他们似乎走的是不同的路径。

    2013 年 8 月 2 日 上午 3:54

    1. Randell Jesup

      Chrome Canary 应该会很快支持与我们相同的底层协议(draft-ietf-rtcweb-data-protocol)。FirefoxChrome DataChannels 可能需要一段时间才能无缝运行,但由于他们有我们的实现进行测试,我认为不会太久。

      2013 年 8 月 2 日 上午 10:20

  3. Fabian Gort

    啊,听起来很不错!谢谢你的回答。

    2013 年 8 月 2 日 下午 2:26

  4. Binyamin

    http://louisstow.github.io/WebRTC/datachannels.html 在 Chrome 和 Firefox 上都不起作用。

    2013 年 8 月 7 日 上午 0:10

    1. Randell Jesup

      我试过了,在 26 26 和 23 26 版本上都运行正常。

      能否详细说明您使用的版本以及尝试操作的顺序?

      2013 年 8 月 7 日 上午 5:43

    2. Louis Stowasser

      它应该可以在两个 Chrome Canary 或 Firefox Nightly 实例之间工作。Chrome 和 Firefox 无法协同工作。

      2013 年 8 月 11 日 下午 4:06

  5. Nathan Gordon

    非常棒的文章!布局使内容易于理解。

    使用这种结构是否可以轻松地允许其他对等节点连接?

    2013 年 8 月 7 日 下午 4:42

    1. Louis Stowasser

      我认为您需要创建另一个数据通道才能包含新的对等节点。

      2013 年 8 月 11 日 下午 4:07

  6. Sam Dutton

    精彩的文章,写作清晰易懂。

    继续努力!

    2013 年 8 月 8 日 上午 3:17

  7. Benjamin Slater

    我已经为我的网站开发视频聊天功能几个星期了,我必须说,这是迄今为止描述 WebRTC 工作原理的最佳文章。

    我正在测试源代码 (media.html),它在同一台计算机上的不同选项卡(或窗口)中运行良好,但是当我尝试在 2 台计算机之间进行测试时,它只显示本地视频流。我一直在尝试,远程流的 Blob URL 数据是共享的,但远程视频无法播放。您在构建演示时是否遇到过类似的问题?

    2013 年 8 月 19 日 下午 2:16

  8. nicole rivers

    您好。您可以查看这篇博文。它可能会为您提供一些见解。
    http://www.ideyatech.com/2013/06/how-will-webrtc-transform-the-internet/

    2013 年 8 月 22 日 上午 0:00

  9. Jan Honza Odvarko

    是否有任何 Firefox API(钩子、回调等)允许像 Firebug 这样的开发工具监视通信,就像它对 HTTP 请求所做的那样?

    2013 年 8 月 28 日 上午 7:33

本文的评论已关闭。