开始使用WebRTC
Get Started with WebRTC 机翻
原文:Get Started with WebRTC - HTML5 Rocks
无需插件即可进行实时通信
想象一下,在这样一个世界中,您的手机、电视和计算机可以在一个通用平台上进行通信。想象一下,将视频聊天和对等数据共享添加到 Web 应用很容易。这就是WebRTC的愿景。
想试试吗?WebRTC可在桌面和移动设备上使用Google Chrome,Safari,Firefox和Opera。一个好的起点是 appr.tc 的简单视频聊天应用程序:
- 在浏览器中打开 appr.tc。
- 单击"加入"以加入聊天室,并让应用使用您的网络摄像头。
- 在新选项卡中打开页面末尾显示的 URL,或者更好的是,在其他计算机上打开 URL。
快速入门
没有时间阅读本文或只想要代码?
要获得WebRTC的概述,请观看以下Google I / O视频或查看这些幻灯片:
- 如果尚未使用 API,请参阅在 HTML5 中捕获音频和视频,simpl.info getUserMedia。
getUserMedia
- 若要了解 API,请参阅以下示例和 RTCPeerConnection simpl.info。
RTCPeerConnection
- 要了解 WebRTC 如何使用服务器进行信令以及防火墙和 NAT 遍历,请参阅 appr.tc 中的代码和控制台日志。
- 迫不及待,现在只想尝试WebRTC?尝试 20 多个演示中的一些,这些演示演示了 WebRTC JavaScript API。
- 您的机器和 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
- 在浏览器中,导航到 WebRTC 示例 getUserMedia。
- 打开控制台。
- 检查全局范围内的变量。
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 的输入节点:
<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的应用程序和扩展程序也可以合并。通过向清单添加和/或权限,只能在安装时请求和授予一次权限。此后,不会要求用户获得相机或麦克风访问权限。getUserMedia
audioCapture
videoCapture
对于 , 只需授予一次权限。第一次,浏览器的信息栏中会显示"允许"按钮。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:为演示选择分辨率。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。
信令用于交换三种类型的信息:
- 会话控制消息:初始化或关闭通信并报告错误。
- 网络配置:对外界来说,你电脑的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 框架查找网络接口和端口的过程。
- Alice 使用处理程序创建一个对象,该处理程序在网络候选程序可用时运行。
RTCPeerConnection
onicecandidate
- Alice 通过 Bob 使用的任何信令通道(如 WebSocket 或其他机制)将序列化的候选数据发送给 Bob。
- 当 Bob 收到来自 Alice 的应聘者消息时,他会打电话将应聘者添加到远程对等描述中。
addIceCandidate
WebRTC 客户端(也称为对等体,在本例中称为 Alice 和 Bob)还需要确定和交换本地和远程音频和视频媒体信息,例如解析和编解码器功能。通过会话描述协议 (SDP) 交换产品/服务和答案来发出信号以交换媒体配置信息:
- Alice 运行该方法。此返回的传输路径为 —Alice 的本地会话描述。
RTCPeerConnection
createOffer()
RTCSessionDescription
- 在回调中,Alice 使用设置本地描述,然后通过其信令通道将此会话描述发送给 Bob。请注意,在被调用之前不会开始收集候选项。这在JSEP IETF草案中得到了编纂。
setLocalDescription()
RTCPeerConnection
setLocalDescription()
- Bob 使用 将 Alice 发送给他的描述设置为远程描述。
setRemoteDescription()
- Bob 运行该方法,将他从 Alice 那里获得的远程描述传递给它,以便可以生成与她的会话兼容的本地会话。回调传递一个 .Bob 将其设置为本地描述,并将其发送给 Alice。
RTCPeerConnection
createAnswer()
createAnswer()
RTCSessionDescription
- 当 Alice 获取 Bob 的会话描述时,她将其设置为带有 的远程描述。
setRemoteDescription
- 乒!
确保允许 在不再需要时通过调用来回收 垃圾回收。否则,线程和连接将保持活动状态。在WebRTC中可能会泄漏大量资源!RTCPeerConnection
close()
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的工作原理更加清晰,因为页面上的对象可以直接交换数据和消息,而无需使用中间信令机制。RTCPeerConnection
RTCPeerConnection
RTCPeerConnection
在此示例中,表示本地对等方(调用方)和远程对等方(被调用方)。pc1
pc2
访客
创建一个新流并从中添加流:
RTCPeerConnection
getUserMedia()
<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>
创建产品/服务并将其设置为 的本地描述和 远程描述。这可以直接在代码中完成,而无需使用信令,因为调用方和被调用方都在同一页面上:
pc1
pc2
<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>
被叫方
创建并在添加来自 的流时,将其显示在视频元素中:pc2
pc1
<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>
RTCPeerConnection
API 加服务器
在现实世界中,WebRTC需要服务器,无论多么简单,因此可能会发生以下情况:
- 用户发现彼此并交换真实世界的详细信息,例如名称。
- WebRTC客户端应用程序(对等体)交换网络信息。
- 对等方交换有关媒体的数据,例如视频格式和分辨率。
- WebRTC 客户端应用程序遍历 NAT 网关和防火墙。
换句话说,WebRTC需要四种类型的服务器端功能:
- 用户发现和通信
- 信号
- NAT/防火墙遍历
- 对等通信失败时的中继服务器
尝试WebRTC的好地方,包括使用STUN服务器的信令和NAT /防火墙穿越,是 appr.tc 的视频聊天演示。此应用使用适配器.js、填充程序将应用与规范更改和前缀差异隔离开来。
代码在其日志记录中故意冗长。检查控制台以了解事件的顺序。下面是代码的详细演练。
如果你觉得这有点令人困惑,你可能更喜欢 WebRTC codelab。本分步指南介绍了如何构建完整的视频聊天应用程序,包括在 Node 服务器上运行的简单信令 服务器。
网络拓扑
RTCDataChannel
应用程序接口
除了音频和视频,WebRTC还支持其他类型的数据的实时通信。
该 API 支持以低延迟和高吞吐量对等方式交换任意数据。有关单页演示以及如何构建简单的文件传输应用程序,请分别参阅 WebRTC 示例和 WebRTC 代码实验室。RTCDataChannel
该 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
有关 的更多信息,请查看 IETF 的协议规范草案。RTCDataChannel
安全
- WebRTC实现使用安全协议,如DTLS和SRTP。
- 加密对于所有WebRTC组件都是强制性的,包括信令机制。
- 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 平台状态。getUserMedia
RTCPeerConnection
本机 API 也可在有关 webrtc.org 的文档中找到。RTCPeerConnection
开始使用WebRTC相关推荐
- WebRTC框架中的硬件加速
WebRTC框架中的硬件加速 典型缓冲流量 应用程序和单元测试设置 重要方法调用 WebRTC软件包 局限性 WebRTC是一个免费的开源项目,可为浏览器和移动应用程序提供实时通信功能. WebRTC ...
- webrtc android ndk,webrtc 针对 android 平台的编译和运行
1环境准备 官方说明: 针对android构建需要Ubuntu64位机器,虚拟机也行. 1.1安装SVN 直接用apt-get安装 sudoapt-getinstallsubversion 1.2安装 ...
- 展望2018:WebRTC大规模商用元年
历经6年长跑,WebRTC终于在去年迎来了1.0标准(candidate recommendation)的发布,而它也将成为2018年视频通信商业应用场景爆发的主要技术推动力.一站式WebRTC通信技 ...
- 使用WebRTC搭建前端视频聊天室——数据通道篇
转自 使用WebRTC搭建前端视频聊天室--数据通道篇 在两个浏览器中,为聊天.游戏.或是文件传输等需求发送信息是十分复杂的.通常情况下,我们需要建立一台服务器来转发数据,当然规模比较大的情况下,会扩 ...
- Android IOS WebRTC 音视频开发总结(三八)-- tx help
Android IOS WebRTC 音视频开发总结(三八)-- tx help 本文主要介绍帮一个程序员解决webrtc疑问的过程,文章来自博客园RTC.Blacker,支持原创,转载请说明出处(w ...
- WebRTC的拥塞控制技术转
转载地址:http://www.jianshu.com/p/9061b6d0a901 1. 概述 对于共享网络资源的各类应用来说,拥塞控制技术的使用有利于提高带宽利用率,同时也使得终端用户在使用网络时 ...
- Android IOS WebRTC 音视频开发总结(七四)-- WebRTC开源5周年了,Google怎么看?
本文最早发表在我们的微信公众号上(微信ID:blackerteam),支持原创,详见这里, 2016年6月9日是WebRTC开源5周年的日子,Google WebRTC负责人Harald在社区里面写了 ...
- WebRTC内置debug工具,详细参数解读
为了确保这篇文章所写内容尽可能的准确,我决定请来Philipp Hancke来作为此篇文章的共同作者. 当你想要找到你WebRTC产品中的问题时,webrtc-internals是一个非常棒的工具,因 ...
- 一起来学习 WebRTC (篇一)| 掘金技术征文
前言 作为一个认为啥都想懂一点的小开发,一直都对WebRTC很感兴趣,这个兴趣来源于几年前公司希望做一个即时通讯的小功能在APP上,不过最终由于项目最终需求更改而搁置.虽然如此,但是我还是了解了一些关 ...
- 专访WebRTC标准之父Daniel C. Burnett
摘要:2010年5月,Google 以6,820万美元收购 VoIP 软件开发商 Global IP Solutions 的 GIPS 引擎,将其开源并改为名为"WebRTC".随 ...
最新文章
- 基于angular4+ng-bootstrap+bootstrap+scss的后台管理系统界面
- dedecms mysql 支持_安装dedecms MySQL 支持 不支持无法使用本系统 GD 支持Off解决办法...
- VTK:几何对象演示用法实战
- testlink自带java api_java如何连接testlink
- 发送http和https请求工具类 Json封装数据
- python字典类型写入文件_python 字典写入文件
- Android RadioGroup
- java infinity 处理_Java:如何执行向-Infinity而不是0的整数除法?
- 微软使用“钞能力”: 687 亿美元收购动视暴雪!
- cad批量打印_「批量打印」CAD图纸批量输出PDF及预览与输出不一致解决办法
- 单片机实验计数显示器C语言代码,单片机实验1-计数显示器.doc
- mysql 模糊查询 查询条件为多个
- html5好看的颜色代码,css好看的颜色配色.html
- LP-630K打印机无法插入放发票或纸张,只要一碰到就发出滴滴滴滴的声音的解决方法
- 天池数据竞赛docker提交操作学习
- 统计学中的第p百分位数的理解
- JqGrid 表格基本使用(一)
- 手机的发展史,手机未来的发展趋势
- 2009-2021系统架构设计师(高级)历年论文题目
- 把一个数据库的表导入到另一个数据库中的方法