【翻译】第三章 - [Dan_Ristic]webrtc开发互动实践

摘要

介于英语考试在即,当前工做须要,特地找了这本书,原书《Learning_WebRTC_Develop_interactive》,以前很久都没有中文版,执意决定自行翻译。后来在豆瓣阅读找到了翻译版,有须要者能够移步购买Learning WebRTC中文版
说实话,WebRTC没有网络基础是不太容易消化理解,这本书的好处是重实践,在实践成果中慢慢理解深奥的计算机网络。2017年著做,其中一些api相继废弃,本人在原书pdf中作了修改批注,有须要的联系641007257@qq.com
第三章是一个核心篇章,因此决定今后开始翻译,前面两章是基础,比较简单,后续会补上翻译文,大概计划与本年末翻译完整本。javascript

能力不高,技术有限,文章仅供参考,勿喷!!!css

建立一个基础应用

理解udp实时传输

​ webrtc应用首选udp传输的缘由是,它提供了高性能传输,不用像tcp那样控制数据的准确性。目前,大多数web应用都创建在tcp协议之上,缘由在于它为用户提供保证,其中一些特性以下:html

  • 发送的全部数据都会接收到确认
  • 数据一旦发送失败,将中止继续发送并从新发送失败的数据
  • 确保数据惟一性

​ 今天,大多数的web应用选择tcp,也正式基于这些特性。假如,你发送一个html页面,让全部数据以正确的顺序排列并确保它到达另外一方是有意义的。可是,这一技术不否和全部的用户场景。例如,在多人游戏中传输数据。视频游戏中的大多数数据在几秒钟甚至更短期内变得陈旧,这就意味着用户只关心过去几秒内发生的事情,仅此而已。若是须要保证每一条数据都能到达另外一方,那么当数据丢失时,这可能会致使很大的瓶颈。java

clipboard.png

​ 由于tcp约束的缘由,使得webrtc的开发人员首选udp做为他们的传输方式。webrtc的音频和视频并不须要的可靠的链接,而是,须要高性能传输。咱们能够接收丢包,从而,也意味着,对于这类的应用程序来讲,udp是一个更好的选择。web

​ UDP经过作出大量的不保证来实现这种状况,它被构建成一个不太可靠的传输层,对您发送的数据进行较少的假设。upd不可靠传输的缘由以下:算法

  • 不肯定数据发送给对方的顺序
  • 传输过程当中可能丢包,不能确保每一个数据包都能准确无误的发送给对方
  • 不跟踪数据包的状态,即时客户端丢失数据,也不会中止发送数据

如今,webrtc能够以尽快的方式发送音频和视频,这也透漏出webrtc是一个多么复杂的主题。并非每一个网络都容许udp流量经过,具备企业防火墙的大型网络能够直接阻止UDP流量,以防止恶意链接。这些链接必须沿着与今天大多数网页下载不一样的路径传播。必须围绕UDP构建许多变通方法和流程,以使其适用于普遍的受众。在WebRTC技术方面,这只是冰山一角。在接下来的几节中,咱们将介绍在浏览器中启用WebRTC的其余支持技术。json

webrtc接口

接下来的几节将介绍目前在浏览器中实现的WebRTC API。这些函数和对象容许开发人员与WebRTC层进行通讯,并与其余用户创建对等链接。它由几个主要技术组成:api

  • The RTCPeerConnection object
  • Signaling and negotiation
  • Session Description Protocol (SDP)
  • Interactive Connectivity Establishment (ICE)

The RTCPeerConnection object

RTCPeerConnection是webrtc api的主入口点。提供初始化链接、对等链接、赋值媒体信息。它处理与另外一个用户的UDP链接的建立。如今咱们开始熟悉这个api,由于在本书的其他部分将会屡次出现。浏览器

RTCPeerConnection的主要做用是在浏览器中维护对等链接的会话和状态。它还处理对等链接的设置和建立。它封装了全部这些内容,并公开了一组在链接过程当中的关键点被触发的事件。经过这些事件,您能够访问对等链接期间发生的配置和内部信息:安全

clipboard.png

RTCPeerConnection对象是浏览器中的一个简单对象,可使用新的构造函数进行实例化,以下所示:

var myConnection = new RTCPeerConnection(configuration);
myConnection.onaddstream = function (stream) {
 // Use stream here
};

链接接受配置对象,咱们将在本章后面介绍。在示例中,咱们还为onaddstream事件添加了一个处理程序。
当远程用户将视频或音频流添加到其对等链接时,会触发此操做。咱们还将在本章后面介绍这一点。

信令和协商

​ 一般,链接到另外一个浏览器须要知道浏览器在网络上的位置。一般状况下是采用ip地址和端口号的方式寻找目的主机。你的计算机或移动设备的IP地址容许其余启用Internet的设备直接在彼此之间发送数据; 这就是RTCPeerConnection的基础。一旦这些设备知道如何在互联网上找到彼此,他们还须要知道如何相互交谈。这意味着交换有关每一个设备支持的协议以及音视频编解码器等有关数据。

​ 这也意味着,为了链接到另外一个用户,你须要更多的了解他们。一种可能的解决方案是在你的计算机上存储能够链接到的用户的列表。要启用与其余用户的通讯,您只需交换联系信息,让WebRTC处理其他的信息。然而,这有一个弊端,你必须和你须要链接的用户手动共享信息。你必须维护一个您想要链接的任何用户的大列表,并经过其余通讯渠道交换信息。使用WebRTC,咱们可使这一过程自动化。

​ 幸运的是,咱们今天使用的大多数Web通讯应用程序中解决了这个问题。要与Facebook或LinkedIn等热门服务上的任何人联系,您只须要知道他们的名字并搜索他们。而后,你能够将他们添加到已知联系人列表中,并随时访问其信息。

​ 这一过程就是WebRTC中的信令和协商。

信令过程包括几个步骤:

  1. 生成对等链接的潜在候选列表。
  2. 用户或算法将选择用户进行链接。
  3. 信令层将通知该用户有人想要与他/她联系,而且他/她能够接受或拒绝。
  4. 通知第一个用户接收要约链接。
  5. 若是接受,第一个用户将与另外一个用户启动RTCPeerConnection
  6. 用户都将经过信令信道交换有关其计算机的硬件和软件信息。
  7. 两个用户还将经过信令信道交换关于他们的计算机的位置信息。
  8. 用户之间的链接成功或失败。

​ 然而,这只是WebRTC信令可能发生的一个例子。实际上,WebRTC规范没有包含关于两个用户如何交换信息的任何标准。这是因为不断增长的链接用户标准列表。今天存在许多标准,甚至在信号和谈判过程当中创造了更多标准。WebRTC标准做者决定,试图就一个标准达成一致意见将阻止它向前发展。

​ 这本书中,咱们将创建本身的信令和协商履行条约。编写一个能够在两个浏览器之间传输信息的简单服务器。虽然它很简单而且容易出现安全漏洞,但它应该让你很好地理解这个过程在WebRTC中是如何工做的。同时,你也能够探究其余公司提供的信令方案。有数百种信令和谈判解决方案,天天都有更多的信息和协商解决方案。有些集成了当前的电话或基于聊天的实现,例如XMPPSIP,有些还提出了一种全新的信令方式。

会话描述协议

​ 要与其余用户创建联系,您须要先了解一下这些用户。关于另外一个客户端的一些最重要的事情是他们支持的音频和视频编解码器,他们的网络带宽,以及他们的计算机能够处理多少数据,并且还须要在客户之间轻松传输。因为咱们没有指定如何传输这些数据,所以它也应该可以经过多种类型的传输协议进行传输。这意味着咱们须要一个基于字符串的名片,其中包含咱们能够发送给其余用户的全部用户信息。
SDP正好为咱们提供了这样的功能。

SDP的伟大之处在于它已经存在了很长时间,能够追溯到90年代末的第一次初稿。这意味着SDP是一种在客户端之间创建基于媒体的链接的可靠方法。在WebRTC以前,它已被用于许多其余类型的应用程序,例如电话和基于文本的聊天。这就意味着在实现和应用方面有不少好的资源。SDP是浏览器提供的基于字符串的blob数据。这个字符串格式是一组由换行符分割的键值对:

<key>=<value>\n

键是一个单一字符,用于创建这个类型的值。该值是一组结构化文本,包含机器可读配置值。而后经过换行符分割不一样的键值对。SDP将涵盖给定用户的描述,时序配置和媒体约束。在与用户创建链接的过程当中,RTCPeerConnection对象给出了SDP。当咱们在本章后面开始使用RTCPeerConnection对象时,您能够轻松地将其打印到JavaScript控制台。这将容许您准确查看SDP中包含的内容,以下所示:

v=0
o=- 1167826560034916900 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE audio video
a=msid-semantic: WMS K44HTOZVjyAyAlvUVD3pOLu8i0LdytHiWRp1
m=audio 1 RTP/SAVPF 111 103 104 0 8 106 105 13 126
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:Vl5FBUBecw/U3EzQ
a=ice-pwd:OtsNG6FzUH8uhNEhOg9/hprb
a=ice-options:google-ice
a=fingerprint:sha-256 
FB:56:7D:B6:E0:C7:E7:39:FE:47:5A:12:6C:B4:4E:0E:2D:18:CE:AE:33:92: 
A9:60:3F:14:E4:D9:AA:0D:BE:0D
a=setup:actpass
a=mid:audio
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=sendrecv
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 
inline:zE+3pkUbJyFG4UmmvPxG/OFC4+QE24X8Zf3iOSCf
a=rtpmap:111 opus/48000/2
a=fmtp:111 minptime=10
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:126 telephone-event/8000
a=maxptime:60
a=ssrc:4274470304 cname:+j4Ma6UfMsCcQCWK
a=ssrc:4274470304 msid:K44HTOZVjyAyAlvUVD3pOLu8i0LdytHiWRp1 
a1751f6b-98de-469b-b6c0-81f46e19009d
a=ssrc:4274470304 mslabel:K44HTOZVjyAyAlvUVD3pOLu8i0LdytHiWRp1
a=ssrc:4274470304 label:a1751f6b-98de-469b-b6c0-81f46e19009d
m=video 1 RTP/SAVPF 100 116 117
c=IN IP4 0.0.0.0
a=rtcp:1 IN IP4 0.0.0.0
a=ice-ufrag:Vl5FBUBecw/U3EzQ
a=ice-pwd:OtsNG6FzUH8uhNEhOg9/hprb
a=ice-options:google-ice
a=fingerprint:sha-256 FB:56:7D:B6:E0:C7:E7:39:FE:47:5A:12:6C:B4:4E:0E:
2D:18:CE:AE:33:92: 
A9:60:3F:14:E4:D9:AA:0D:BE:0D
a=setup:actpass
a=mid:video
a=extmap:2 urn:ietf:params:rtp-hdrext:toffset
a=extmap:3 http://www.webrtc.org/experiments/rtp-hdrext/abs-sendtime
a=sendrecv
a=rtcp-mux
a=crypto:1 AES_CM_128_HMAC_SHA1_80 
inline:zE+3pkUbJyFG4UmmvPxG/OFC4+QE24X8Zf3iOSCf
a=rtpmap:100 VP8/90000
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack
a=rtcp-fb:100 nack pli
a=rtcp-fb:100 goog-remb
a=rtpmap:116 red/90000
a=rtpmap:117 ulpfec/90000
a=ssrc:3285139021 cname:+j4Ma6UfMsCcQCWK
a=ssrc:3285139021 msid:K44HTOZVjyAyAlvUVD3pOLu8i0LdytHiWRp1 
bd02b355-b8af-4b68-b82d-7b9cd03461cf
a=ssrc:3285139021 mslabel:K44HTOZVjyAyAlvUVD3pOLu8i0LdytHiWRp1
a=ssrc:3285139021 label:bd02b355-b8af-4b68-b82d-7b9cd03461cf

这是在会话启动过程当中从我本身的机器上获取的。如您所见,生成的代码第一眼看上去很复杂。它首先肯定IP地址的链接。而后,设置有关请求的基本信息,例如我是否请求音频、视频仍是二者。接下来,它会设置一些音频信息,包括加密类型和ICE配置等主题。它还以相同的方式设置视频信息。

最后,咱们的目标不是理解每一行代码,而是理解SDP的用途。在本书的学习过程当中,您永远没必要直接使用它,但可能须要在未来的某个时候使用它。总之,SDP扮演一个业务名片,供其余用户与您创建链接的时候使用。SDP与信令和协商相结合,是对等链接的前半部分。在接下来的几节中,咱们将介绍在两个用户知道如何找到对方以后会发生什么。

参考
P2P通讯标准协议(三)之ICE

寻找另外一个用户的明确线路

今天大多数网络的很大一部分是安全性。您正在使用的任何网络都有几层访问控制,告诉您的数据在何处以及如何发送。这意味着链接到另外一个用户须要找到一条清晰的路径,不只仅是您本身的网络,还有其余用户的网络。WebRTC内部涉及多种技术:

  • NAT会话遍历(STUN)
  • NAT中继设备遍历(TURN)
  • 交互式链接创建(ICE)

这些涉及许多服务器和链接,以便WebRTC正确使用。要了解它们的工做原理,咱们首先应该可视化典型WebRTC链接过程的布局:

clipboard.png
首先是找出你的IP地址。全部链接到Internet的设备都有一个IP地址,用于标识它们在Web上的位置。这是将数据包定向到正确目的地的方法。在局域网内查找IP地址时会出现问题。路由器隐藏计算机的IP地址并将其替换为另外一个,以提升安全性并容许多台计算机使用相同的网络地址。一般,您能够在本身,网络路由器和公共Internet之间拥有多个IP地址。

NAT会话遍历

STUN是在两个对等体之间找到良好链接的第一步。它有助于识别Internet上的每一个用户,而且旨在由其余协议用于创建对等链接。它首先向服务器发出请求,使用STUN协议启用。而后,服务器识别发出请求的客户端的IP地址,并将其返回给客户端。而后,客户端可使用给定的IP地址标识本身。

​ 使用STUN协议须要启用STUN的服务器才能链接。目前,在Firefox和Chrome中,默认服务器直接由浏览器供应商提供。这很是适合快速启动并运行测试。

参考
P2P通讯标准协议(一)之STUN

NAT中继设备遍历

在某些状况下,防火墙可能限制性太强,不容许任何基于STUN的流量到其余用户。在企业NAT中可能就是这种状况,它利用端口随机化来容许比一般发现的数千个设备更多的设备。在这种状况下,咱们须要一种与另外一个用户链接的不一样方法。 这就是TURN

​ 这种方式是在客户端之间添加一个中继,表明客户端充当对等链接。而后,客户端从TURN服务器获取其信息,就像经过向服务器发出请求从流行的视频服务流式传输视频同样。这要求TURN服务器下载,处理和重定向每一个客户端发送给它的每一个数据包。这就是为何使用TURN在制做WebRTC链接时一般被认为是最后的手段,由于设置高质量的TURN服务的成本很高。

​ 关于STUN与TURN的使用有不少不一样的统计数据,但它们彷佛都指向了相同的结论 - 大部分时间,没有TURN你的用户就会没事。将WebRTC与STUN一块儿使用将适用于大多数网络配置。在设置您本身的WebRTC服务时,最好跟踪此信息并自行决定使用TURN服务的成本是否值得。

参考
P2P通讯标准协议(二)之TURN

交互式链接创建

​ 如今咱们已经介绍了STUN和TURN,咱们能够经过另外一个名为ICE的标准来了解它是如何组合在一块儿的。利用STUN和TURN为对等链接提供成功路由的过程。它的工做原理是找到每一个用户可用的一系列地址,并按排序顺序测试每一个地址,直到找到适合两个客户端的组合。

​ ICE的过程从不对每一个用户的网络配置作出假设开始。它将逐步经过一系列步骤来发现每一个客户端的网络是如何设置的。此过程将使用不一样的技术集来执行此操做。目标是发现有关每一个网络的足够信息,以创建成功的链接。

​ 经过使用STUN和TURN找到每一个ICE候选者。若是链接失败,它将查询STUN服务器以查找外部IP地址并将TURN服务器的位置附加为备份。每当浏览器找到新候选者时,它通知客户端应用程序它须要经过信令信道发送ICE候选者。在找到并测试了足够的地址并创建链接后,该过程终于结束了。

参考
P2P通讯标准协议(三)之ICE

构建一个基本的WebRTC应用程序

​ 如今咱们已经很好地理解了WebRTC的使用方式,咱们将构建咱们的第一个支持WebRTC的应用程序。到本章结束时,您将拥有一个可用的WebRTC网页,您能够在其中查看实际使用的技术。咱们将把咱们刚刚介绍的全部信息提取到一个易于开发的示例中。咱们将涵盖:

  • 建立RTCPeerConnection
  • 建立SDP服务和响应
  • 寻找ICE对等候选人
  • 建立一个成功的WebRTC链接

建立RTCPeerConnection

​ 不幸的是,咱们如今建立的不是一个彻底的对等应用程序,摄像头只能捕获到本身。咱们在本章中的目的是将浏览器窗口链接到自身,从用户的摄像头流式传输视频数据。最终目标是在页面上得到两个视频流,一个直接来自摄像头,另外一个来自浏览器在本地制做的WebRTC链接。

​ 虽然这并不彻底有用,但它可使代码更具可读性和便于理解,从而帮助咱们。咱们将在稍后学习如何使用服务器进行远程链接。因为咱们链接的环境是咱们本地的浏览器,所以咱们没必要担忧网络不稳定或建立服务器。完成项目后,您的应用程序应以下所示:

clipboard.png

正如你所看到的,除了我英俊的面孔,这个例子是很是基本的。首先,咱们将采用与第2章“获取用户媒体”中建立的第一个示例相似的步骤。您须要建立另外一个HTML页面并使用本地Web服务器托管它。在第2章获取用户媒体中参考“获取媒体设备访问权限”部分中的“设置静态服务器”小节多是一个好主意,并查看如何设置开发环境。

​ 咱们将采起的第一步是建立一些处理多个浏览器支持的函数。这些将可以告诉咱们当前的浏览器是否支持咱们须要使用的功能来使咱们的应用程序工做。它还将规范化API,确保咱们始终可使用相同的功能,不管咱们运行什么浏览器。

​ 要开始使用,请使用JavaScript源文件设置新网页。咱们的HTML页面上将包含两个视频元素,一个用于第一个客户端,另外一个用于第二个客户端:

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8" />
 <title>Learning WebRTC - Chapter 4: Creating a 
 RTCPeerConnection</title>
 </head>
 <body>
 <div id="container">
 <video id="yours" autoplay></video>
 <video id="theirs" autoplay></video>
 </div>
 <script src="main.js"></script>
 </body>
</html>

若是您常常作HTML5网页,那么此页面的html和head标签应该很熟悉。这是任何符合HTML5的页面的标准格式。
有不少不一样的样板模板用于建立页面,而这个模板是我以为最简单的,同时还能完成工做。只要视频元素存在,就没有什么会完全改变咱们的应用程序的工做方式,因此若是你须要对这个文件进行更改,请随意这样作。

您会注意到两个标记为您和他们的视频元素。这些将是咱们的两个视频源,将模拟链接到另外一个同伴。在本章的其他部分中,您将被视为启动链接的本地用户。
其余用户 - 他们的 - 将被视为咱们正在进行WebRTC链接的远程用户,即便他们并不是物理上位于其余地方。

​ 最后,包括咱们的脚本功能。请始终牢记在HTML页面的末尾添加此内容。这能够保证正文中的元素可使用,而且页面已彻底加载,以便JavaScript与之交互。
接下来,咱们将建立咱们的JavaScript源代码。建立一个名为main.js的新文件,并使用如下代码开始填写它:

function hasUserMedia() {
 navigator.getUserMedia = navigator.getUserMedia || 
 navigator.webkitGetUserMedia || navigator.mozGetUserMedia || 
 navigator.msGetUserMedia;
 return !!navigator.getUserMedia;
}
function hasRTCPeerConnection() {
 window.RTCPeerConnection = window.RTCPeerConnection || 
 window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
 return !!window.RTCPeerConnection;
}

​ 如今咱们能够告诉用户支持哪些API,让咱们继续使用它们。接下来的几个步骤也应该很是熟悉。咱们将重复第2章“获取用户媒体”中的“约束媒体流”部分中遇到的一些功能,以获取用户的相机流。在咱们对WebRTC作任何事情以前,咱们应该从用户那里获取本地摄像头流。这能够确保用户准备好建立对等链接,而且在创建对等链接以前,咱们没必要等待用户接受摄像头共享。

咱们在WebRTC中构建的大多数应用程序都将经历一系列状态。让WebRTC工做最困难的部分是按正确的顺序作事。若是一个步骤发生在另外一个步骤以前,它能够快速分解应用程序。这些状态是阻塞的,这意味着咱们不能在没有完成前一个状态的状况下进入下一个状态。如下是咱们的应用程序如何工做的概述:

clipboard.png

首先,咱们须要获取用户媒体。确保用户接受视频和音频的媒体流正常。

​ 接下来,咱们建立对等链接。在断开链接以前启动进程。这是咱们可使用咱们想要使用的ICE服务器配置WebRTC链接的地方。此时,浏览器闲置着等待链接过程开始。

​ 当其中一个用户建立offer时,应用就正式开始了。这使浏览器开始行动,并开始准备与另外一个用户创建对等链接。提议和响应是本章讨论的信令过程的一部分。

​ 同时,浏览器还将寻找其余对等方能够链接的候选端口和IP组合。它将在一段时间内继续执行此操做,直到能够创建链接或链接失败。完成此操做后,WebRTC链接过程结束,两个用户能够开始共享信息。

​ 下一段代码将捕获用户的摄像头并使其在咱们的流变量中可用。您如今能够在JavaScript中的两个函数定义以后添加如下代码:

var yourVideo = document.querySelector("#yours"),
    theirVideo = document.querySelector("#theirs"),
    yourConnection, theirConnection;
if (hasUserMedia()) {
    navigator.getUserMedia({ video: true, audio: false }, function
        (stream) {
        yourVideo.srcObject = stream;
        if (hasRTCPeerConnection()) {
            startPeerConnection(stream);
        } else {
            alert("Sorry, your browser does not support WebRTC.");
        }
    }, function (error) {
        alert("Sorry, we failed to capture your camera, please try again.");
    });
} else {
    alert("Sorry, your browser does not support WebRTC.");
}

​ 第一部分从文档中选择咱们的视频元素,并设置一些咱们将在接下来使用的变量。咱们假设浏览器此时支持querySelector API。而后,咱们检查用户是否能够访问getUserMedia API。若是他们不这样作,咱们的程序将停在此处,并提醒用户他们不支持WebRTC

​ 若是成功,咱们会尝试从用户那里获取相机。这是一种异步操做,由于用户必须赞成共享他们的相机。若是成功,咱们将本地视频的流设置为用户的流,以便他们能够成功地看到这一点。若是失败,咱们会通知用户错误并中止该过程。

​ 最后,咱们检查用户是否能够访问RTCPeerConnection API。若是是这样,咱们调用将启动链接过程的函数(这将在下一节中定义)。若是没有,咱们停在这里并再次通知用户。

​ 下一步是实现上一节中调用的startPeerConnection函数。此函数将建立咱们的RTCPeerConnection对象,设置SDP offer和响应,并找到两个对等体的ICE候选对象。
​ 如今咱们为两个对等体建立RTCPeerConnection对象。将如下内容添加到JavaScript文件中:

function startPeerConnection(stream) {
 var configuration = {
 // Uncomment this code to add custom iceServers
 //"iceServers": [{ "url": "stun:stun.1.google.com:19302" }]" 
}]
 };
 yourConnection = new webkitRTCPeerConnection(configuration);
 theirConnection = new webkitRTCPeerConnection(configuration);
};

​ 在这里,咱们定义函数来建立链接对象。在配置对象中,您能够传递要在应用程序中使用的ICE服务器的参数。要使用自建ICE服务器,只需取消注释代码并更改值便可。浏览器将自动获取配置并在进行对等链接时使用它。此时,这不是必需的,由于浏览器应该具备一组默认的ICE服务器。在此以后,咱们建立两个对等链接对象来表明咱们应用程序中的每一个用户。仍请记住,咱们的两个用户都将在此应用程序的同一浏览器窗口中。

建立SDP offer和响应

​ 在本节中,咱们将执行offer和响应应答程序以创建对等链接。咱们的下一个代码块将在两个对等体之间设置提供和响应应答流:

function startPeerConnection(stream) {
 var configuration = {
// Uncomment this code to add custom iceServers
 //"iceServers": [{ "url": "stun:stun.1.google.com:19302" }]
 };
 yourConnection = new webkitRTCPeerConnection(configuration);
 theirConnection = new webkitRTCPeerConnection(configuration);
 // Begin the offer
 yourConnection.createOffer(function (offer) {
 yourConnection.setLocalDescription(offer);
 theirConnection.setRemoteDescription(offer);
 theirConnection.createAnswer(function (offer) {
 theirConnection.setLocalDescription(offer);
 yourConnection.setRemoteDescription(offer);
 });
 });
};

​ 你可能注意到的一件事是,通过整整一章的解释,这段代码看起来至关简单。这是由于两个对等体都在同一个浏览器窗口中。这样,咱们能够保证其余用户什么时候得到offer,而没必要执行许多异步操做。

​ 以这种方式实现offer/answer机制使其更容易理解。您能够清楚地看到所需的步骤以及成功建立对等链接所需的顺序。若是您使用附加到浏览器的调试工具,则能够执行这些步骤并在每一个步骤检查RTCPeerConnection对象,以确切了解发生的状况。

​ 在下一章中,咱们将更深刻地探讨这个主题。一般,您要链接的另外一个对等体不在同一个浏览器中 - 这意味着须要服务器来链接浏览器窗口之间的对等体。这使得这个过程变得更加复杂,由于这些步骤不只须要按照这里显示的确切顺序进行,并且还要跨多个浏览器窗口进行。这须要在可能有时不稳定的环境中进行大量同步操做。

寻找ICE候选

​ 创建对等链接的最后一部分是在对等体之间传输ICE候选者,以便它们能够相互链接。您如今能够将startPeerConnection函数更改成以下所示:

function startPeerConnection(stream) {
 var configuration = {
 // Uncomment this code to add custom iceServers
 //"iceServers": [{ "url": "stun:127.0.0.1:9876" }]
 };
 yourConnection = new webkitRTCPeerConnection(configuration);
 theirConnection = new webkitRTCPeerConnection(configuration);
 // Setup ice handling
 yourConnection.onicecandidate = function (event) {
 if (event.candidate) {
 theirConnection.addIceCandidate(new 
RTCIceCandidate(event.candidate));
 }
 };
 theirConnection.onicecandidate = function (event) {
 if (event.candidate) {
 yourConnection.addIceCandidate(new 
RTCIceCandidate(event.candidate));
 }
 };
 // Begin the offer
 yourConnection.createOffer(function (offer) {
 yourConnection.setLocalDescription(offer);
theirConnection.setRemoteDescription(offer);
 theirConnection.createAnswer(function (offer) {
 theirConnection.setLocalDescription(offer);
 yourConnection.setRemoteDescription(offer);
 });
 });
};

​ 您可能会注意到这部分代码彻底由事件驱动。这是因为找到ICE候选者的异步性质。浏览器将不断寻找候选者,直到它找到了认为能够建立对等链接或创建稳定对等链接的数量。
​ 在接下来的章节中,咱们将构建实际经过信令通道发送此数据的功能。须要注意的一点是,当咱们从他们的链接中得到ICE候选者时,咱们将其添加到yourConnection,反之亦然。咱们必须经过互联网才能链接到不在同一地方的用户。

添加和修饰数据流

​ 使用WebRTC能够轻松地将流添加到对等链接。API负责设置流并经过网络发送数据的全部工做。当其余用户将流添加到其对等链接时,将经过该链接发送此通知,通知第一个用户该更改。而后,浏览器调用onaddstream通知用户已添加流:

// Setup stream listening
 yourConnection.addStream(stream);
 theirConnection.onaddstream = function (e) {
 theirVideo.srcObject = e.stream;
 };

​ 而后,咱们能够经过为流的位置建立对象URL,将此流添加到本地视频。这样作是建立一个标识浏览器中的流的值,以便视频元素能够与之交互。这充当咱们视频流的惟一ID,告诉视频元素播放来自本地流的视频数据做为源。
​ 最后,咱们将为应用程序添加一些样式。视频通讯应用程序最流行的风格是Skype等应用程序中常见的风格。今天,许多使用WebRTC构建的演示都复制了这一点。一般,您呼叫的人位于应用程序的前面和中间,而您本身的摄像头显示为较大的一个小窗口。因为咱们正在构建一个Web页面,所以能够经过一些简单的CSS实现,以下所示:

<style>
 body {
 background-color: #3D6DF2;
 margin-top: 15px;
 }
video {
 background: black;
 border: 1px solid gray;
 }
 #container {
 position: relative;
 display: block;
 margin: 0 auto;
 width: 500px;
 height: 500px;
 }
 #yours {
 width: 150px;
 height: 150px;
 position: absolute;
 top: 15px;
 right: 15px;
 }
 #theirs {
 width: 500px;
 height: 500px;
 }
 </style>

​ 只需将其添加到您的HTML页面,您就应该有一个良好的WebRTC应用程序。此时,若是您仍然认为咱们的应用程序看起来很单调,请随时继续为应用程序添加样式。咱们将在接下来的章节中对此进行构建,而且使用一些CSS作一些更使人兴奋的演示。

运行您的第一个WebRTC应用程序

​ 如今,运行您的网页进行测试。当您运行该页面时,它应该要求您与浏览器共享您的相机。一旦接受,它将启动WebRTC链接过程。浏览器迅速完成咱们到目前为止讨论的步骤,并建立一个链接。而后,您应该看到本身的两个视频,一个来自您的相机,另外一个是经过WebRTC链接进行流式传输。

做为参考,如下是此示例中代码的完整列表。如下是咱们的index.html文件中的代码:

<!DOCTYPE html>
<html lang="en">
 <head>
 <meta charset="utf-8" />
 <title>Learning WebRTC - Chapter 4: Creating a 
RTCPeerConnection</title>
 <style>
 body {
 background-color: #3D6DF2;
 margin-top: 15px;
 }
 video {
 background: black;
 border: 1px solid gray;
 }
 #container {
 position: relative;
 display: block;
 margin: 0 auto;
 width: 500px;
 height: 500px;
 }
#yours {
 width: 150px;
 height: 150px;
 position: absolute;
 top: 15px;
 right: 15px;
 }
 #theirs {
 width: 500px;
 height: 500px;
 }
 </style>
 </head>
 <body>
 <div id="container">
 <video id="yours" autoplay></video>
 <video id="theirs" autoplay></video>
 </div>
 <script src="main.js"></script>
 </body>
</html>

下面是mian.js文件内容:

function hasUserMedia() {
    navigator.getUserMedia = navigator.getUserMedia ||
        navigator.webkitGetUserMedia || navigator.mozGetUserMedia ||
        navigator.msGetUserMedia;
    return !!navigator.getUserMedia;
}

function hasRTCPeerConnection() {
    window.RTCPeerConnection = window.RTCPeerConnection ||
        window.webkitRTCPeerConnection || window.mozRTCPeerConnection;
    return !!window.RTCPeerConnection;
}

var yourVideo = document.querySelector("#yours"),
    theirVideo = document.querySelector("#theirs"),
    yourConnection, theirConnection;
if (hasUserMedia()) {
    navigator.getUserMedia({video: true, audio: false}, function (stream) {
        yourVideo.srcObject = stream;
        if (hasRTCPeerConnection()) {
            startPeerConnection(stream);
        } else {
            alert("Sorry, your browser does not support WebRTC.");
        }
    }, function (error) {
        console.log(error);
    });
} else {
    alert("Sorry, your browser does not support WebRTC.");
}

function startPeerConnection(stream) {
    var configuration = {
        "iceServers": [{"url": "stun:stun.1.google.com:19302"}]
    };
    yourConnection = new webkitRTCPeerConnection(configuration);
    theirConnection = new webkitRTCPeerConnection(configuration);
    // Setup stream listening
    yourConnection.addStream(stream);
    theirConnection.onaddstream = function (e) {
        theirVideo.srcObject = e.stream;
    };
    // Setup ice handling
    yourConnection.onicecandidate = function (event) {
        if (event.candidate) {
            theirConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
        }
    };
    theirConnection.onicecandidate = function (event) {
        if (event.candidate) {
            yourConnection.addIceCandidate(new RTCIceCandidate(event.candidate));
        }
    };
    // Begin the offer
    yourConnection.createOffer(function (offer) {
        yourConnection.setLocalDescription(offer);
        theirConnection.setRemoteDescription(offer);
        theirConnection.createAnswer(function (offer) {
            theirConnection.setLocalDescription(offer);
            yourConnection.setRemoteDescription(offer);
        });
    });
};

自测题

Q1: UDP适合WebRTC链接是由于它不能确保数据的准确性,对仍是错?

Q2: WebRTC标准的信令和协商部分是由浏览器彻底完成,对仍是错?

Q3: 如下描述SDP正确的是:

  1. 一个WebRTC的配置文件
  2. 弄清楚支持视频编码的一种方式
  3. 你电脑的一个商业名片
  4. 一个迷惑的没人理解的技术文档

Q4: 交互式链接创建(ICE)有助于在典型的网络设置中找到两个客户端之间清晰的路径。对或错?

Q5: 下列关于TURN说法不正确的是?

  1. 它须要比普通链接更多的带宽和处理能力
  2. NAT穿透,TURN应该是最好的链接方式
  3. TURN服务器必须处理客户端之间发送的每一个数据包
  4. TURN方法由浏览器提供

总结

​ 恭喜你作到这一点。若是您已成功完成本章,那么您就能够开始制做更大的WebRTC应用程序了。本章的目标不只是建立WebRTC应用程序,还要了解在流程的每一个步骤中发生的状况。

​ 在本章以后,应该已经清楚WebRTC是一项复杂的技术。咱们介绍了WebRTC内部工做的大量信息。虽然如今不须要了解WebRTC如何在浏览器中实现,但了解主要部分如何协同工做将有助于您理解将要到来的示例。

​ 在本章中,咱们介绍了如何在浏览器中建立对等链接的内部工做方式。咱们介绍了支持UDPSDPICE的几种技术。您如今应该对两个浏览器如何找到彼此以及如何经过Internet进行通讯进行表面层次的理解。

​ 最好回顾一下咱们迄今为止所涵盖的材料,以充分了解WebRTC在咱们的示例中的工做原理。重要的是要注意每一个步骤和序列都很重要。这将有助于调试WebRTC应用程序中的问题,由于咱们将在后面的章节中介绍更多的复杂性。

​ 本书的其他部分将以此示例为基础,使其比目前复杂得多。咱们将添加功能,以便在多个不一样环境中的多个浏览器中链接多个用户。每一章都将参考WebRTC流程的一部分,深刻研究它,涵盖常见的陷阱,并处理网络稳定性和安全性等边缘状况。

​ 在下一章中,咱们将开始构建信令服务器以支持链接远程用户。这是咱们将经过本书其他部分使用的信令服务器的基础。它还容许咱们建立咱们的第一个真正的呼叫应用程序,就像Google 环聊同样。

相关文章
相关标签/搜索