Get Started with WebRTC  机翻

原文:Get Started with WebRTC - HTML5 Rocks

无需插件即可进行实时通信

想象一下,在这样一个世界中,您的手机、电视和计算机可以在一个通用平台上进行通信。想象一下,将视频聊天和对等数据共享添加到 Web 应用很容易。这就是WebRTC的愿景。

想试试吗?WebRTC可在桌面和移动设备上使用Google Chrome,Safari,Firefox和Opera。一个好的起点是 appr.tc 的简单视频聊天应用程序:

  1. 在浏览器中打开 appr.tc。
  2. 单击"加入"以加入聊天室,并让应用使用您的网络摄像头。
  3. 在新选项卡中打开页面末尾显示的 URL,或者更好的是,在其他计算机上打开 URL。

快速入门

没有时间阅读本文或只想要代码?

  1. 要获得WebRTC的概述,请观看以下Google I / O视频或查看这些幻灯片:

  2. 如果尚未使用 API,请参阅在 HTML5 中捕获音频和视频,simpl.info getUserMedia。getUserMedia
  3. 若要了解 API,请参阅以下示例和 RTCPeerConnection simpl.info。RTCPeerConnection
  4. 要了解 WebRTC 如何使用服务器进行信令以及防火墙和 NAT 遍历,请参阅 appr.tc 中的代码和控制台日志。
  5. 迫不及待,现在只想尝试WebRTC?尝试 20 多个演示中的一些,这些演示演示了 WebRTC JavaScript API。
  6. 您的机器和 WebRTC 遇到问题?访问 WebRTC 疑难解答。

或者,直接跳转到WebRTC代码实验室,这是一个分步指南,解释了如何构建一个完整的视频聊天应用程序,包括一个简单的信令服务器。

WebRTC的非常短的历史

网络面临的最后一个主要挑战之一是通过语音和视频实现人类通信:实时通信或简称RTC。RTC 在 Web 应用中应与在文本输入中输入文本一样自然。没有它,你创新和开发新互动方式的能力就会受到限制。

从历史上看,RTC一直是企业和复杂的,需要昂贵的音频和视频技术在内部许可或开发。将 RTC 技术与现有内容、数据和服务集成既困难又耗时,尤其是在 Web 上。

Gmail视频聊天在2008年开始流行,2011年,谷歌推出了使用Talk的Hangouts(Gmail也是如此)。谷歌收购了GISP,该公司开发了RTC所需的许多组件,例如编解码器和回声消除技术。Google 开源了 GIPS 开发的技术,并与互联网工程任务组 (IETF) 和万维网联盟 (W3C) 的相关标准机构合作,以确保行业共识。2011年5月,爱立信构建了WebRTC的第一个实现。

WebRTC为实时,无插件的视频,音频和数据通信实施了开放标准。需求是真实的:

  • 许多 Web 服务使用 RTC,但需要下载、本机应用或插件。其中包括Skype,Facebook和Hangouts。
  • 下载,安装和更新插件很复杂,容易出错并且很烦人。
  • 插件难以部署、调试、故障排除、测试和维护,并且可能需要许可和与复杂、昂贵的技术集成。首先,通常很难说服人们安装插件!

WebRTC项目的指导原则是,其API应该是开源的,免费的,标准化的,内置于Web浏览器中,并且比现有技术更有效。

我们现在在哪里?

WebRTC用于各种应用程序,例如Google Meet。WebRTC还与WebKitGTK+和Qt原生应用程序集成。

WebRTC实现了以下三个API:

  • MediaStream(也称为getUserMedia)
  • RTCPeerConnection
  • RTCDataChannel

API 定义在以下两个规范中:

  • WebRTC
  • getUserMedia

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应用程序需要做几件事:

  • 获取流式音频、视频或其他数据。
  • 获取网络信息,例如 IP 地址和端口,并将其与其他 WebRTC 客户端(称为对等方)交换以启用连接,甚至通过 NAT 和防火墙。
  • 协调信令通信以报告错误并启动或关闭会话。
  • 交换有关媒体和客户端功能的信息,如分辨率和编解码器。
  • 传输流式音频、视频或数据。

为了获取和通信流数据,WebRTC 实现了以下 API:

  • MediaStream 可以访问数据流,例如来自用户的摄像头和麦克风。
  • RTCPeerConnection 支持音频或视频通话,并提供加密和带宽管理功能。
  • RTCDataChannel 支持通用数据的对等通信。

(稍后将详细讨论WebRTC的网络和信令方面。

MediaStream接口(也称为接口)getUserMedia

MediaStream API 表示同步的媒体流。例如,从摄像头和麦克风输入中获取的流具有同步的视频和音频轨道。(不要与<track>元素混淆,这是完全不同的东西。MediaStreamTrack

理解API的最简单方法可能是在野外查看它:MediaStream

  1. 在浏览器中,导航到 WebRTC 示例 getUserMedia。
  2. 打开控制台。
  3. 检查全局范围内的变量。stream

每个都有一个输入(可能是由 生成的),还有一个输出(可能传递给视频元素或 )。MediaStreamMediaStreamgetUserMedia()RTCPeerConnection

该方法采用 MediaStreamConstraints 对象参数,并返回解析为对象的 参数。getUserMedia()PromiseMediaStream

每个都有一个 ,例如 。由 and 方法返回 s 数组。MediaStreamlabel'Xk7EuLhsuHKbnjLWkW4yYGNJJ8ONsgwHBvLQ'MediaStreamTrackgetAudioTracks()getVideoTracks()

对于 getUserMedia 示例,返回一个空数组(因为没有音频),并且假设连接了一个工作正常的网络摄像头,则返回一个数组,该数组表示来自网络摄像头的流。每个都有一个种类 ( 或 ),a(类似于 ),并表示音频或视频的一个或多个通道。在这种情况下,只有一个视频轨道,没有音频,但很容易想象用例中还有更多,例如从前置摄像头,后置摄像头,麦克风获取流的聊天应用程序以及共享其屏幕的应用程序。stream.getAudioTracks()stream.getVideoTracks()MediaStreamTrackMediaStreamTrack'video''audio'label'FaceTime HD Camera (Built-in)'

可以通过设置 srcObject 属性将 A 附加到视频元素。以前,这是通过将属性设置为使用 创建的对象 URL 来完成的,但这已被弃用。MediaStreamsrcURL.createObjectURL()

正在主动使用相机,这会占用资源,并使相机保持打开状态并打开相机灯亮。当您不再使用轨道时,请确保呼叫以关闭摄像机。MediaStreamTracktrack.stop()

getUserMedia也可以用作 Web 音频 API 的输入节点:

<span style="color:white"><span style="background-color:#444444"><span style="color:#aeaeae"><em>// Cope with browser differences.</em></span><span style="color:#ffffff">
let audioContext</span><span style="color:#ffffff">;</span>
<span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#e28964">typeof</span> <span style="color:#89bdff">AudioContext</span> <span style="color:#ffffff">===</span> <span style="color:#65b042">'function'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">audioContext </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">AudioContext</span><span style="color:#ffffff">();</span>
<span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#e28964">typeof</span><span style="color:#ffffff"> webkitAudioContext </span><span style="color:#ffffff">===</span> <span style="color:#65b042">'function'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">audioContext </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span><span style="color:#ffffff"> webkitAudioContext</span><span style="color:#ffffff">();</span> <span style="color:#aeaeae"><em>// eslint-disable-line new-cap</em></span>
<span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Sorry! Web Audio not supported.'</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">}</span><span style="color:#aeaeae"><em>// Create a filter node.</em></span>
<span style="color:#e28964">var</span><span style="color:#ffffff"> filterNode </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> audioContext</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createBiquadFilter</span><span style="color:#ffffff">();</span>
<span style="color:#aeaeae"><em>// See https://dvcs.w3.org/hg/audio/raw-file/tip/webaudio/specification.html#BiquadFilterNode-section</em></span><span style="color:#ffffff">
filterNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">type </span><span style="color:#ffffff">=</span> <span style="color:#65b042">'highpass'</span><span style="color:#ffffff">;</span>
<span style="color:#aeaeae"><em>// Cutoff frequency. For highpass, audio is attenuated below this frequency.</em></span><span style="color:#ffffff">
filterNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">frequency</span><span style="color:#ffffff">.</span><span style="color:#ffffff">value </span><span style="color:#ffffff">=</span> <span style="color:#3387cc">10000</span><span style="color:#ffffff">;</span><span style="color:#aeaeae"><em>// Create a gain node to change audio volume.</em></span>
<span style="color:#e28964">var</span><span style="color:#ffffff"> gainNode </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> audioContext</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createGain</span><span style="color:#ffffff">();</span>
<span style="color:#aeaeae"><em>// Default is 1 (no change). Less than 1 means audio is attenuated</em></span>
<span style="color:#aeaeae"><em>// and vice versa.</em></span><span style="color:#ffffff">
gainNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">gain</span><span style="color:#ffffff">.</span><span style="color:#ffffff">value </span><span style="color:#ffffff">=</span> <span style="color:#3387cc">0.5</span><span style="color:#ffffff">;</span><span style="color:#ffffff">navigator</span><span style="color:#ffffff">.</span><span style="color:#ffffff">mediaDevices</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getUserMedia</span><span style="color:#ffffff">({</span><span style="color:#ffffff">audio</span><span style="color:#ffffff">:</span> <span style="color:#e28964">true</span><span style="color:#ffffff">},</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#aeaeae"><em>// Create an AudioNode from the stream.</em></span><span style="color:#e28964">const</span><span style="color:#ffffff"> mediaStreamSource </span><span style="color:#ffffff">=</span><span style="color:#ffffff">audioContext</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createMediaStreamSource</span><span style="color:#ffffff">(</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">);</span><span style="color:#ffffff">mediaStreamSource</span><span style="color:#ffffff">.</span><span style="color:#ffffff">connect</span><span style="color:#ffffff">(</span><span style="color:#ffffff">filterNode</span><span style="color:#ffffff">);</span><span style="color:#ffffff">filterNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">connect</span><span style="color:#ffffff">(</span><span style="color:#ffffff">gainNode</span><span style="color:#ffffff">);</span><span style="color:#aeaeae"><em>// Connect the gain node to the destination. For example, play the sound.</em></span><span style="color:#ffffff">gainNode</span><span style="color:#ffffff">.</span><span style="color:#ffffff">connect</span><span style="color:#ffffff">(</span><span style="color:#ffffff">audioContext</span><span style="color:#ffffff">.</span><span style="color:#ffffff">destination</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">});</span></span></span>

基于Chromium的应用程序和扩展程序也可以合并。通过向清单添加和/或权限,只能在安装时请求和授予一次权限。此后,不会要求用户获得相机或麦克风访问权限。getUserMediaaudioCapturevideoCapture

对于 , 只需授予一次权限。第一次,浏览器的信息栏中会显示"允许"按钮。Chrome 在 2015 年底弃用了 HTTP 访问,因为它被归类为功能强大的功能。getUserMedia()getUserMedia()

其意图可能是为任何流数据源启用 a,而不仅仅是摄像头或麦克风。这将允许从存储的数据或任意数据源(如传感器或其他输入)进行流式传输。MediaStream

getUserMedia()真正与其他 JavaScript API 和库结合使用:

  • 网络摄像头玩具是一个照相亭应用程序,它使用WebGL为可以在本地共享或保存的照片添加奇怪而美妙的效果。
  • FaceKat是一款使用headtrackr.js构建的面部跟踪游戏。
  • ASCII 相机使用 Canvas API 生成 ASCII 图像。

正在上传…重新上传取消

咕噜咕噜的艺术!

约束

约束可用于设置 的视频分辨率值。这也允许支持其他约束,例如宽高比;对置模式(前置或后置摄像头);帧速率,高度和宽度;和 applyConstraints() 方法。getUserMedia()

有关示例,请参阅 WebRTC 示例 getUserMedia:选择分辨率。

一个陷阱:约束可能会影响共享资源的可用配置。例如,如果相机在 640 x 480 模式下通过一个选项卡打开,则另一个选项卡将无法使用约束以更高分辨率模式打开它,因为它只能在一种模式下打开。请注意,这是一个实现细节。可以让第二个选项卡以更高分辨率的模式重新打开相机,并使用视频处理将第一个选项卡的视频轨道缩小到640 x 480,但尚未实现。getUserMedia

设置不允许的约束值会给出 a 或 a,例如,如果请求的分辨率不可用。若要查看此操作的实际效果,请参阅 WebRTC 示例 getUserMedia:为演示选择分辨率。DOMExceptionOverconstrainedError

屏幕和选项卡捕获

Chrome 应用还允许通过 chrome.tabCapture 和 chrome.desktopCapture API 共享单个浏览器标签页或整个桌面的实时视频。(有关演示和更多信息,请参阅使用 WebRTC 进行屏幕共享。这篇文章已经有几年的历史了,但它仍然很有趣。

在 Chrome 中,也可以使用实验性约束将屏幕捕获用作源。请注意,屏幕捕获需要 HTTPS,并且只能用于开发,因为它是通过命令行标志启用的,如本文所述。MediaStreamchromeMediaSource

信令:会话控制、网络和媒体信息

WebRTC用于在浏览器(也称为对等体)之间传输流数据,但也需要一种机制来协调通信和发送控制消息,这一过程称为信令。WebRTC指定信令方法和协议。信令不是 API 的一部分。RTCPeerConnectionRTCPeerConnection

相反,WebRTC应用程序开发人员可以选择他们喜欢的任何消息传递协议,例如SIP或XMPP,以及任何适当的双工(双向)通信通道。appr.tc 示例使用 XHR 和通道 API 作为信令机制。代码实验室使用在 Node 服务器上运行的 Socket.io。

信令用于交换三种类型的信息:

  • 会话控制消息:初始化或关闭通信并报告错误。
  • 网络配置:对外界来说,你电脑的IP地址和端口是什么?
  • 媒体功能:您的浏览器和要与之通信的浏览器可以处理哪些编解码器和分辨率?

通过信令进行的信息交换必须已成功完成,然后才能开始对等流。

例如,假设爱丽丝想和鲍勃交流。下面是 W3C WebRTC 规范中的代码示例,其中显示了运行中的信令过程。该代码假定存在该方法中创建的某些信令机制。另请注意,在 Chrome 和 Opera 上,当前带有前缀。createSignalingChannel()RTCPeerConnection

<span style="color:white"><span style="background-color:#444444"><span style="color:#aeaeae"><em>// handles JSON.stringify/parse</em></span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> signaling </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">SignalingChannel</span><span style="color:#ffffff">();</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> constraints </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">audio</span><span style="color:#ffffff">:</span> <span style="color:#e28964">true</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> video</span><span style="color:#ffffff">:</span> <span style="color:#e28964">true</span><span style="color:#ffffff">};</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> configuration </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">iceServers</span><span style="color:#ffffff">:</span> <span style="color:#ffffff">[{</span><span style="color:#ffffff">urls</span><span style="color:#ffffff">:</span> <span style="color:#65b042">'stuns:stun.example.org'</span><span style="color:#ffffff">}]};</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> pc </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">configuration</span><span style="color:#ffffff">);</span><span style="color:#aeaeae"><em>// Send any ice candidates to the other peer.</em></span><span style="color:#ffffff">
pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onicecandidate </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">({</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">})</span> <span style="color:#ffffff">=></span><span style="color:#ffffff"> signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">});</span><span style="color:#aeaeae"><em>// Let the "negotiationneeded" event trigger offer generation.</em></span><span style="color:#ffffff">
pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onnegotiationneeded </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> async </span><span style="color:#ffffff">()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#e28964">try</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setLocalDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createOffer</span><span style="color:#ffffff">());</span><span style="color:#aeaeae"><em>// Send the offer to the other peer.</em></span><span style="color:#ffffff">signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">:</span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">localDescription</span><span style="color:#ffffff">});</span><span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span>
<span style="color:#ffffff">};</span><span style="color:#aeaeae"><em>// Once remote track media arrives, show it in remote video element.</em></span><span style="color:#ffffff">
pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">ontrack </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">(</span><span style="color:#e28964">event</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#aeaeae"><em>// Don't set srcObject again if it is already set.</em></span><span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">remoteView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject</span><span style="color:#ffffff">)</span> <span style="color:#e28964">return</span><span style="color:#ffffff">;</span><span style="color:#ffffff">remoteView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject </span><span style="color:#ffffff">=</span> <span style="color:#e28964">event</span><span style="color:#ffffff">.</span><span style="color:#ffffff">streams</span><span style="color:#ffffff">[</span><span style="color:#3387cc">0</span><span style="color:#ffffff">];</span>
<span style="color:#ffffff">};</span><span style="color:#aeaeae"><em>// Call start() to initiate.</em></span><span style="color:#ffffff">
async </span><span style="color:#e28964">function</span><span style="color:#ffffff"> start</span><span style="color:#ffffff">()</span> <span style="color:#ffffff">{</span><span style="color:#e28964">try</span> <span style="color:#ffffff">{</span><span style="color:#aeaeae"><em>// Get local stream, show it in self-view, and add it to be sent.</em></span><span style="color:#e28964">const</span><span style="color:#ffffff"> stream </span><span style="color:#ffffff">=</span><span style="color:#ffffff">await navigator</span><span style="color:#ffffff">.</span><span style="color:#ffffff">mediaDevices</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getUserMedia</span><span style="color:#ffffff">(</span><span style="color:#ffffff">constraints</span><span style="color:#ffffff">);</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getTracks</span><span style="color:#ffffff">().</span><span style="color:#ffffff">forEach</span><span style="color:#ffffff">((</span><span style="color:#ffffff">track</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span><span style="color:#ffffff">pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addTrack</span><span style="color:#ffffff">(</span><span style="color:#ffffff">track</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">));</span><span style="color:#ffffff">selfView</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">;</span><span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span>
<span style="color:#ffffff">}</span><span style="color:#ffffff">signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onmessage </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> async </span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> candidate</span><span style="color:#ffffff">})</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#e28964">try</span> <span style="color:#ffffff">{</span><span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#aeaeae"><em>// If you get an offer, you need to reply with an answer.</em></span><span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">type </span><span style="color:#ffffff">===</span> <span style="color:#65b042">'offer'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setRemoteDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">);</span><span style="color:#e28964">const</span><span style="color:#ffffff"> stream </span><span style="color:#ffffff">=</span><span style="color:#ffffff">await navigator</span><span style="color:#ffffff">.</span><span style="color:#ffffff">mediaDevices</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getUserMedia</span><span style="color:#ffffff">(</span><span style="color:#ffffff">constraints</span><span style="color:#ffffff">);</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getTracks</span><span style="color:#ffffff">().</span><span style="color:#ffffff">forEach</span><span style="color:#ffffff">((</span><span style="color:#ffffff">track</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span><span style="color:#ffffff">pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addTrack</span><span style="color:#ffffff">(</span><span style="color:#ffffff">track</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> stream</span><span style="color:#ffffff">));</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setLocalDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createAnswer</span><span style="color:#ffffff">());</span><span style="color:#ffffff">signaling</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">({</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">:</span><span style="color:#ffffff"> pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">localDescription</span><span style="color:#ffffff">});</span><span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">type </span><span style="color:#ffffff">===</span> <span style="color:#65b042">'answer'</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setRemoteDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">log</span><span style="color:#ffffff">(</span><span style="color:#65b042">'Unsupported SDP type.'</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span><span style="color:#ffffff">}</span> <span style="color:#e28964">else</span> <span style="color:#e28964">if</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">await pc</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addIceCandidate</span><span style="color:#ffffff">(</span><span style="color:#ffffff">candidate</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span><span style="color:#ffffff">}</span> <span style="color:#e28964">catch</span> <span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">console</span><span style="color:#ffffff">.</span><span style="color:#ffffff">error</span><span style="color:#ffffff">(</span><span style="color:#ffffff">err</span><span style="color:#ffffff">);</span><span style="color:#ffffff">}</span>
<span style="color:#ffffff">};</span></span></span>

首先,Alice 和 Bob 交换网络信息。(表达式查找候选项是指使用 ICE 框架查找网络接口和端口的过程。

  1. Alice 使用处理程序创建一个对象,该处理程序在网络候选程序可用时运行。RTCPeerConnectiononicecandidate
  2. Alice 通过 Bob 使用的任何信令通道(如 WebSocket 或其他机制)将序列化的候选数据发送给 Bob。
  3. 当 Bob 收到来自 Alice 的应聘者消息时,他会打电话将应聘者添加到远程对等描述中。addIceCandidate

WebRTC 客户端(也称为对等体,在本例中称为 Alice 和 Bob)还需要确定和交换本地和远程音频和视频媒体信息,例如解析和编解码器功能。通过会话描述协议 (SDP) 交换产品/服务和答案来发出信号以交换媒体配置信息:

  1. Alice 运行该方法。此返回的传输路径为 —Alice 的本地会话描述。RTCPeerConnectioncreateOffer()RTCSessionDescription
  2. 在回调中,Alice 使用设置本地描述,然后通过其信令通道将此会话描述发送给 Bob。请注意,在被调用之前不会开始收集候选项。这在JSEP IETF草案中得到了编纂。setLocalDescription()RTCPeerConnectionsetLocalDescription()
  3. Bob 使用 将 Alice 发送给他的描述设置为远程描述。setRemoteDescription()
  4. Bob 运行该方法,将他从 Alice 那里获得的远程描述传递给它,以便可以生成与她的会话兼容的本地会话。回调传递一个 .Bob 将其设置为本地描述,并将其发送给 Alice。RTCPeerConnectioncreateAnswer()createAnswer()RTCSessionDescription
  5. 当 Alice 获取 Bob 的会话描述时,她将其设置为带有 的远程描述。setRemoteDescription
  6. 乒!

确保允许 在不再需要时通过调用来回收 垃圾回收。否则,线程和连接将保持活动状态。在WebRTC中可能会泄漏大量资源!RTCPeerConnectionclose()

RTCSessionDescription对象是符合会话描述协议 SDP 的 Blob。序列化后,SDP 对象如下所示:

<span style="color:white"><span style="background-color:#444444"><span style="color:#ffffff">v</span><span style="color:#ffffff">=</span><span style="color:#3387cc">0</span><span style="color:#ffffff">
o</span><span style="color:#ffffff">=-</span> <span style="color:#3387cc">3883943731</span> <span style="color:#3387cc">1</span><span style="color:#ffffff"> IN IP4 </span><span style="color:#3387cc">127.0</span><span style="color:#ffffff">.</span><span style="color:#3387cc">0.1</span><span style="color:#ffffff">
s</span><span style="color:#ffffff">=</span><span style="color:#ffffff">
t</span><span style="color:#ffffff">=</span><span style="color:#3387cc">0</span> <span style="color:#3387cc">0</span><span style="color:#ffffff">
a</span><span style="color:#ffffff">=</span><span style="color:#e28964">group</span><span style="color:#ffffff">:</span><span style="color:#ffffff">BUNDLE audio video
m</span><span style="color:#ffffff">=</span><span style="color:#ffffff">audio </span><span style="color:#3387cc">1</span><span style="color:#ffffff"> RTP</span><span style="color:#ffffff">/</span><span style="color:#ffffff">SAVPF </span><span style="color:#3387cc">103</span> <span style="color:#3387cc">104</span> <span style="color:#3387cc">0</span> <span style="color:#3387cc">8</span> <span style="color:#3387cc">106</span> <span style="color:#3387cc">105</span> <span style="color:#3387cc">13</span> <span style="color:#3387cc">126</span><span style="color:#aeaeae"><em>// ...</em></span><span style="color:#ffffff">a</span><span style="color:#ffffff">=</span><span style="color:#ffffff">ssrc</span><span style="color:#ffffff">:</span><span style="color:#3387cc">2223794119</span><span style="color:#ffffff"> label</span><span style="color:#ffffff">:</span><span style="color:#ffffff">H4fjnMzxy3dPIgQ7HxuCTLb4wLLLeRHnFxh810</span></span></span>

网络和媒体信息的获取和交换可以同时完成,但是在对等体之间的音频和视频流开始之前,这两个过程必须已经完成。

前面描述的要约/答案体系结构称为 JavaScript 会话建立协议或 JSEP。(在爱立信的第一个WebRTC实现的演示视频中,有一个很棒的动画解释了信令和流式传输的过程。

JSEP 架构

一旦信令过程成功完成,数据就可以在呼叫者和被叫方之间直接对等流式传输,或者,如果失败,则通过中间中继服务器(稍后将详细介绍)。流式传输是 的工作。RTCPeerConnection

RTCPeerConnection

RTCPeerConnection是 WebRTC 组件,用于处理对等体之间流数据的稳定和高效通信。

下面是一个 WebRTC 体系结构图,显示了 的作用。您会注意到,绿色部分很复杂!RTCPeerConnection

正在上传…重新上传取消

WebRTC架构(来自 webrtc.org)

从JavaScript的角度来看,从这张图中要了解的主要内容是,它保护Web开发人员免受潜伏在背后的无数复杂性的影响。WebRTC使用的编解码器和协议做了大量的工作,使实时通信成为可能,即使在不可靠的网络上也是如此:RTCPeerConnection

  • 丢包隐蔽
  • 回声消除
  • 带宽适应性
  • 动态抖动缓冲
  • 自动增益控制
  • 降噪和抑制
  • 图像清理

前面的 W3C 代码从信令角度展示了 WebRTC 的简化示例。以下是两个工作 WebRTC 应用程序的演练。第一个是要演示的简单示例,第二个是完全可操作的视频聊天客户端。RTCPeerConnection

没有服务器的 RTCPeerConnection

以下代码取自 WebRTC 示例对等连接,该连接在一个网页上具有本地和远程(以及本地和远程视频)。这并不构成任何非常有用的东西 - 调用方和被调用方在同一页面上 - 但它确实使API的工作原理更加清晰,因为页面上的对象可以直接交换数据和消息,而无需使用中间信令机制。RTCPeerConnectionRTCPeerConnectionRTCPeerConnection

在此示例中,表示本地对等方(调用方)和远程对等方(被调用方)。pc1pc2

访客

  1. 创建一个新流并从中添加流:RTCPeerConnectiongetUserMedia()

    <span style="color:white"><span style="background-color:#444444"><span style="color:#aeaeae"><em>// Servers is an optional configuration file. (See TURN and STUN discussion later.)</em></span><span style="color:#ffffff">
    pc1 </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">servers</span><span style="color:#ffffff">);</span>
    <span style="color:#aeaeae"><em>// ...</em></span><span style="color:#ffffff">
    localStream</span><span style="color:#ffffff">.</span><span style="color:#ffffff">getTracks</span><span style="color:#ffffff">().</span><span style="color:#ffffff">forEach</span><span style="color:#ffffff">((</span><span style="color:#ffffff">track</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">pc1</span><span style="color:#ffffff">.</span><span style="color:#ffffff">addTrack</span><span style="color:#ffffff">(</span><span style="color:#ffffff">track</span><span style="color:#ffffff">,</span><span style="color:#ffffff"> localStream</span><span style="color:#ffffff">);</span>
    <span style="color:#ffffff">});</span></span></span>
  2. 创建产品/服务并将其设置为 的本地描述和 远程描述。这可以直接在代码中完成,而无需使用信令,因为调用方和被调用方都在同一页面上:pc1pc2

    <span style="color:white"><span style="background-color:#444444"><span style="color:#ffffff">pc1</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setLocalDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">).</span><span style="color:#e28964">then</span><span style="color:#ffffff">(()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">onSetLocalSuccess</span><span style="color:#ffffff">(</span><span style="color:#ffffff">pc1</span><span style="color:#ffffff">);</span><span style="color:#ffffff">},</span><span style="color:#ffffff">onSetSessionDescriptionError</span><span style="color:#ffffff">);</span><span style="color:#ffffff">trace</span><span style="color:#ffffff">(</span><span style="color:#65b042">'pc2 setRemoteDescription start'</span><span style="color:#ffffff">);</span><span style="color:#ffffff">pc2</span><span style="color:#ffffff">.</span><span style="color:#ffffff">setRemoteDescription</span><span style="color:#ffffff">(</span><span style="color:#ffffff">desc</span><span style="color:#ffffff">).</span><span style="color:#e28964">then</span><span style="color:#ffffff">(()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">onSetRemoteSuccess</span><span style="color:#ffffff">(</span><span style="color:#ffffff">pc2</span><span style="color:#ffffff">);</span><span style="color:#ffffff">},</span><span style="color:#ffffff">onSetSessionDescriptionError</span><span style="color:#ffffff">);</span></span></span>

被叫方

创建并在添加来自 的流时,将其显示在视频元素中:pc2pc1

<span style="color:white"><span style="background-color:#444444"><span style="color:#ffffff">pc2 </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">servers</span><span style="color:#ffffff">);</span><span style="color:#ffffff">
pc2</span><span style="color:#ffffff">.</span><span style="color:#ffffff">ontrack </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> gotRemoteStream</span><span style="color:#ffffff">;</span>
<span style="color:#aeaeae"><em>//...</em></span>
<span style="color:#e28964">function</span><span style="color:#ffffff"> gotRemoteStream</span><span style="color:#ffffff">(</span><span style="color:#ffffff">e</span><span style="color:#ffffff">){</span><span style="color:#ffffff">vid2</span><span style="color:#ffffff">.</span><span style="color:#ffffff">srcObject </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> e</span><span style="color:#ffffff">.</span><span style="color:#ffffff">stream</span><span style="color:#ffffff">;</span>
<span style="color:#ffffff">}</span></span></span>

RTCPeerConnectionAPI 加服务器

在现实世界中,WebRTC需要服务器,无论多么简单,因此可能会发生以下情况:

  • 用户发现彼此并交换真实世界的详细信息,例如名称。
  • WebRTC客户端应用程序(对等体)交换网络信息。
  • 对等方交换有关媒体的数据,例如视频格式和分辨率。
  • WebRTC 客户端应用程序遍历 NAT 网关和防火墙。

换句话说,WebRTC需要四种类型的服务器端功能:

  • 用户发现和通信
  • 信号
  • NAT/防火墙遍历
  • 对等通信失败时的中继服务器

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

语法有意类似于 WebSocket,具有方法和事件:send()message

<span style="color:white"><span style="background-color:#444444"><span style="color:#e28964">const</span><span style="color:#ffffff"> localConnection </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">servers</span><span style="color:#ffffff">);</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> remoteConnection </span><span style="color:#ffffff">=</span> <span style="color:#e28964">new</span> <span style="color:#89bdff">RTCPeerConnection</span><span style="color:#ffffff">(</span><span style="color:#ffffff">servers</span><span style="color:#ffffff">);</span>
<span style="color:#e28964">const</span><span style="color:#ffffff"> sendChannel </span><span style="color:#ffffff">=</span><span style="color:#ffffff">localConnection</span><span style="color:#ffffff">.</span><span style="color:#ffffff">createDataChannel</span><span style="color:#ffffff">(</span><span style="color:#65b042">'sendDataChannel'</span><span style="color:#ffffff">);</span><span style="color:#aeaeae"><em>// ...</em></span><span style="color:#ffffff">remoteConnection</span><span style="color:#ffffff">.</span><span style="color:#ffffff">ondatachannel </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">(</span><span style="color:#e28964">event</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#ffffff">receiveChannel </span><span style="color:#ffffff">=</span> <span style="color:#e28964">event</span><span style="color:#ffffff">.</span><span style="color:#ffffff">channel</span><span style="color:#ffffff">;</span><span style="color:#ffffff">receiveChannel</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onmessage </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> onReceiveMessage</span><span style="color:#ffffff">;</span><span style="color:#ffffff">receiveChannel</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onopen </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> onReceiveChannelStateChange</span><span style="color:#ffffff">;</span><span style="color:#ffffff">receiveChannel</span><span style="color:#ffffff">.</span><span style="color:#ffffff">onclose </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> onReceiveChannelStateChange</span><span style="color:#ffffff">;</span>
<span style="color:#ffffff">};</span><span style="color:#e28964">function</span><span style="color:#ffffff"> onReceiveMessage</span><span style="color:#ffffff">(</span><span style="color:#e28964">event</span><span style="color:#ffffff">)</span> <span style="color:#ffffff">{</span><span style="color:#ffffff">document</span><span style="color:#ffffff">.</span><span style="color:#ffffff">querySelector</span><span style="color:#ffffff">(</span><span style="color:#65b042">"textarea#send"</span><span style="color:#ffffff">).</span><span style="color:#ffffff">value </span><span style="color:#ffffff">=</span> <span style="color:#e28964">event</span><span style="color:#ffffff">.</span><span style="color:#ffffff">data</span><span style="color:#ffffff">;</span>
<span style="color:#ffffff">}</span><span style="color:#ffffff">document</span><span style="color:#ffffff">.</span><span style="color:#ffffff">querySelector</span><span style="color:#ffffff">(</span><span style="color:#65b042">"button#send"</span><span style="color:#ffffff">).</span><span style="color:#ffffff">onclick </span><span style="color:#ffffff">=</span> <span style="color:#ffffff">()</span> <span style="color:#ffffff">=></span> <span style="color:#ffffff">{</span><span style="color:#e28964">var</span><span style="color:#ffffff"> data </span><span style="color:#ffffff">=</span><span style="color:#ffffff"> document</span><span style="color:#ffffff">.</span><span style="color:#ffffff">querySelector</span><span style="color:#ffffff">(</span><span style="color:#65b042">"textarea#send"</span><span style="color:#ffffff">).</span><span style="color:#ffffff">value</span><span style="color:#ffffff">;</span><span style="color:#ffffff">sendChannel</span><span style="color:#ffffff">.</span><span style="color:#ffffff">send</span><span style="color:#ffffff">(</span><span style="color:#ffffff">data</span><span style="color:#ffffff">);</span>
<span style="color:#ffffff">};</span></span></span>

通信直接发生在浏览器之间,因此即使当打孔以应对防火墙和 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可能实现与原始浏览器对信息相同的实时通信转换。

开发人员工具

  • 正在进行的会话的WebRTC统计数据可以在以下位置找到:

    chrome://webrtc-internals 屏幕截图

    • 在 Chrome 浏览器中 chrome://webrtc-internals
    • 歌剧中的 opera://webrtc-internals
    • 关于:火狐中的webrtc
  • 跨浏览器互操作说明
  • adapter.js是由Google在WebRTC社区的帮助下维护的WebRTC的JavaScript填充程序,它抽象了供应商前缀,浏览器差异和规范更改。
  • 要了解有关 WebRTC 信令过程的更多信息,请查看 appr.tc 日志输出到控制台。
  • 如果太多了,你可能更喜欢使用WebRTC框架,甚至是完整的WebRTC服务。
  • 错误报告和功能请求始终受到赞赏:
    • WebRTC错误
    • Chrome 错误
    • 歌剧错误
    • 火狐错误
    • WebRTC演示错误
    • 适配器.js错误

了解更多信息

  • Justin Uberti 在 Google I/O 2012 上的 WebRTC 会议
  • 艾伦·B·约翰斯顿(Alan B. Johnston)和丹尼尔·C·伯内特(Daniel C. Burnett)在 webrtcbook.com 上以印刷版和电子书格式出版了一本WebRTC书籍,现已推出第三版。
  • webrtc.org 是WebRTC所有内容的所在地,包括演示,文档和讨论。
  • discuss-webrtc是一个用于WebRTC技术讨论的Google Group。
  • @webrtc
  • Google Developers Talk 文档提供了有关 NAT 遍历、STUN、中继服务器和候选者收集的更多信息。
  • WebRTC on GitHub
  • Stack Overflow是寻找答案并提出有关WebRTC问题的好地方。

标准和协议

  • WebRTC W3C 编辑草稿
  • W3C 编辑器草稿:媒体捕获和流(也称为getUserMedia)
  • IETF工作组章程
  • IETF WebRTC 数据通道协议草案
  • IETF JSEP Draft
  • IETF为ICE提出的标准
  • IETF RTCWEB工作组互联网草案:Web实时通信用例和要求

WebRTC 支持摘要

MediaStream和 APIgetUserMedia

  • Chrome 桌面版 18.0.1008 及更高版本;适用于安卓 29 及更高版本的 Chrome 浏览器
  • 歌剧18及以上;适用于安卓 20 及更高版本的 Opera
  • Opera 12,Opera Mobile 12(基于Presto引擎)
  • 火狐 17 及更高版本
  • 微软边缘 16 及更高版本
  • iOS 上的 Safari 11.2 及更高版本,以及 MacOS 上的 11.1 及更高版本
  • 安卓版上的 UC 11.8 及更高版本
  • 三星互联网4及更高版本

RTCPeerConnection应用程序接口

  • Chrome 桌面 20 及更高版本;适用于 Android 29 及更高版本的 Chrome 浏览器(无标志)
  • Opera 18 及更高版本(默认启用);适用于 Android 20 及更高版本的 Opera(默认情况下处于启用状态)
  • Firefox 22 及更高版本(默认启用)
  • 微软边缘 16 及更高版本
  • iOS 上的 Safari 11.2 及更高版本,以及 MacOS 上的 11.1 及更高版本
  • 三星互联网4及更高版本

RTCDataChannel应用程序接口

  • Chrome 25中的实验版本,但在Chrome 26及更高版本中更稳定(并且具有Firefox互操作性);适用于安卓 29 及更高版本的 Chrome 浏览器
  • Opera 18及更高版本中的稳定版本(以及Firefox互操作性);适用于安卓 20 及更高版本的 Opera
  • Firefox 22 及更高版本(默认启用)

有关 API 的跨平台支持(例如 和)的更多详细信息,请参阅 caniuse.com 和 Chrome 平台状态。getUserMediaRTCPeerConnection

本机 API 也可在有关 webrtc.org 的文档中找到。RTCPeerConnection

开始使用WebRTC相关推荐

  1. WebRTC框架中的硬件加速

    WebRTC框架中的硬件加速 典型缓冲流量 应用程序和单元测试设置 重要方法调用 WebRTC软件包 局限性 WebRTC是一个免费的开源项目,可为浏览器和移动应用程序提供实时通信功能. WebRTC ...

  2. webrtc android ndk,webrtc 针对 android 平台的编译和运行

    1环境准备 官方说明: 针对android构建需要Ubuntu64位机器,虚拟机也行. 1.1安装SVN 直接用apt-get安装 sudoapt-getinstallsubversion 1.2安装 ...

  3. 展望2018:WebRTC大规模商用元年

    历经6年长跑,WebRTC终于在去年迎来了1.0标准(candidate recommendation)的发布,而它也将成为2018年视频通信商业应用场景爆发的主要技术推动力.一站式WebRTC通信技 ...

  4. 使用WebRTC搭建前端视频聊天室——数据通道篇

    转自 使用WebRTC搭建前端视频聊天室--数据通道篇 在两个浏览器中,为聊天.游戏.或是文件传输等需求发送信息是十分复杂的.通常情况下,我们需要建立一台服务器来转发数据,当然规模比较大的情况下,会扩 ...

  5. Android IOS WebRTC 音视频开发总结(三八)-- tx help

    Android IOS WebRTC 音视频开发总结(三八)-- tx help 本文主要介绍帮一个程序员解决webrtc疑问的过程,文章来自博客园RTC.Blacker,支持原创,转载请说明出处(w ...

  6. WebRTC的拥塞控制技术转

    转载地址:http://www.jianshu.com/p/9061b6d0a901 1. 概述 对于共享网络资源的各类应用来说,拥塞控制技术的使用有利于提高带宽利用率,同时也使得终端用户在使用网络时 ...

  7. Android IOS WebRTC 音视频开发总结(七四)-- WebRTC开源5周年了,Google怎么看?

    本文最早发表在我们的微信公众号上(微信ID:blackerteam),支持原创,详见这里, 2016年6月9日是WebRTC开源5周年的日子,Google WebRTC负责人Harald在社区里面写了 ...

  8. WebRTC内置debug工具,详细参数解读

    为了确保这篇文章所写内容尽可能的准确,我决定请来Philipp Hancke来作为此篇文章的共同作者. 当你想要找到你WebRTC产品中的问题时,webrtc-internals是一个非常棒的工具,因 ...

  9. 一起来学习 WebRTC (篇一)| 掘金技术征文

    前言 作为一个认为啥都想懂一点的小开发,一直都对WebRTC很感兴趣,这个兴趣来源于几年前公司希望做一个即时通讯的小功能在APP上,不过最终由于项目最终需求更改而搁置.虽然如此,但是我还是了解了一些关 ...

  10. 专访WebRTC标准之父Daniel C. Burnett

    摘要:2010年5月,Google 以6,820万美元收购 VoIP 软件开发商 Global IP Solutions 的 GIPS 引擎,将其开源并改为名为"WebRTC".随 ...

最新文章

  1. 基于angular4+ng-bootstrap+bootstrap+scss的后台管理系统界面
  2. dedecms mysql 支持_安装dedecms MySQL 支持 不支持无法使用本系统 GD 支持Off解决办法...
  3. VTK:几何对象演示用法实战
  4. testlink自带java api_java如何连接testlink
  5. 发送http和https请求工具类 Json封装数据
  6. python字典类型写入文件_python 字典写入文件
  7. Android RadioGroup
  8. java infinity 处理_Java:如何执行向-Infinity而不是0的整数除法?
  9. 微软使用“钞能力”: 687 亿美元收购动视暴雪!
  10. cad批量打印_「批量打印」CAD图纸批量输出PDF及预览与输出不一致解决办法
  11. 单片机实验计数显示器C语言代码,单片机实验1-计数显示器.doc
  12. mysql 模糊查询 查询条件为多个
  13. html5好看的颜色代码,css好看的颜色配色.html
  14. LP-630K打印机无法插入放发票或纸张,只要一碰到就发出滴滴滴滴的声音的解决方法
  15. 天池数据竞赛docker提交操作学习
  16. 统计学中的第p百分位数的理解
  17. JqGrid 表格基本使用(一)
  18. 手机的发展史,手机未来的发展趋势
  19. 2009-2021系统架构设计师(高级)历年论文题目
  20. 把一个数据库的表导入到另一个数据库中的方法

热门文章

  1. 二进制安全:ptmalloc内存管理机制与堆块chunk源码分析
  2. 立志做中国市场TOP2,新华三云屏底气何来?
  3. Redis 3种集群方式,别傻傻分不清!
  4. Visual Graph常见问题回答(FAQ)
  5. linux简易教程 莫烦,安装 Ubuntu 17.10
  6. 竟然可以在Windows下用Nvim写代码?1.基础配置环境与安装nvim
  7. 移动架构11_建造者模式
  8. npm install XXX 报错:error An unexpected error occurred:
  9. 我要做-微处理器嵌入式系统设计师
  10. 做事先做人 做人先立德