摘要:使用了集成了的来进行双方的数据通信。四加入视频流现在我们已经有了一个可靠的视频数据传输通道了,下一步只需要向这个通道加入数据流即可。
欢迎关注我的知乎专栏:https://zhuanlan.zhihu.com/starkwang
在传统的 Web 应用中,浏览器与浏览器之间是无法直接相互通信的,必须借助服务器的帮助,但是随着 WebRTC 在各大浏览器中的普及,这一现状得到了改变。
WebRTC(Web Real-Time Communication,Web实时通信),是一个支持网页浏览器之间进行实时数据传输(包括音频、视频、数据流)的技术,谷歌于2011年5月开放了工程的源代码,目前在各大浏览器的最新版本中都得到了不同程度的支持。
这篇文章里我们采用 WebRTC 来构建一个简单的视频传输应用。
一、关于 WebRTC 的一些基本概念传统的视频推流的技术实现一般是这样的:客户端采集视频数据,推流到服务器上,服务器再根据具体情况将视频数据推送到其他客户端上。
但是 WebRTC 却截然不同,它可以在客户端之间直接搭建基于 UDP 的数据通道,经过简单的握手流程之后,可以在不同设备的两个浏览器内直接传输任意数据。
这其中的流程包括:
采集视频流数据,创建一个 RTCPeerConnection
创建一个 SDP offer 和相应的回应
为双方找到 ICE 候选路径
成功创建一个 WebRTC 连接
下面我们介绍这其中涉及到的一些关键词:
RTCPeerConnection 对象是 WebRTC API 的入口,它负责创建、维护一个 WebRTC 连接,以及在这个连接中的数据传输。目前新版本的浏览器大都支持了这一对象,但是由于目前 API 还不稳定,所以需要加入各个浏览器内核的前缀,例如 Chrome 中我们使用 webkitRTCPeerConnection 来访问它。
为了连接到其他用户,我们必须要对其他用户的设备情况有所了解,比如音频视频的编码解码器、使用何种编码格式、使用何种网络、设备的数据处理能力,所以我们需要一张“名片”来获得用户的所有信息,而 SDP 为我们提供了这些功能。
一个 SDP 的握手由一个 offer 和一个 answer 组成。
通信的两侧可能会处于不同的网络环境中,有时会存在好几层的访问控制、防火墙、路由跳转,所以我们需要一种方法在复杂的网络环境中找到对方,并且连接到相应的目标。WebRTC 使用了集成了 STUN、TURN 的 ICE 来进行双方的数据通信。
二、创建一个 RTCPeerConnection首先我们的目标是在同一个页面中创建两个实时视频,一个的数据直接来自你的摄像头,另一个的数据来自本地创建的 WebRTC 连接。看起来是这样的:
图图图。。。。。。。
首先我们创建一个简单的 HTML 页面,含有两个 video 标签:
下面我们创建一个 main.js 文件,先封装一下各浏览器的 userMedia 和 RTCPeerConnection 对象:
function hasUserMedia() { navigator.getUserMedia = navigator.getUserMedia || navigator.msGetUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; return !!navigator.getUserMedia; } function hasRTCPeerConnection() { window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.msRTCPeerConnection; return !!window.RTCPeerConnection; }
然后我们需要浏览器调用系统的摄像头 API getUserMedia 获得媒体流,注意要打开浏览器的摄像头限制。Chrome由于安全的问题,只能在 https 下或者 localhost 下打开摄像头。
var yourVideo = document.getElementById("yours"); var theirVideo = document.getElementById("theirs"); var yourConnection, theirConnection; if (hasUserMedia()) { navigator.getUserMedia({ video: true, audio: false }, stream => { yourVideo.src = window.URL.createObjectURL(stream); if (hasRTCPeerConnection()) { // 稍后我们实现 startPeerConnection startPeerConnection(stream); } else { alert("没有RTCPeerConnection API"); } }, err => { console.log(err); } ) }else{ alert("没有userMedia API") }
没有意外的话,现在应该能在页面中看到一个视频了。
下一步是实现 startPeerConnection 方法,建立传输视频数据所需要的 ICE 通信路径,这里我们以 Chrome 为例:
function startPeerConnection(stream) { //这里使用了几个公共的stun协议服务器 var config = { "iceServers": [{ "url": "stun:stun.services.mozilla.com" }, { "url": "stun:stunserver.org" }, { "url": "stun:stun.l.google.com:19302" }] }; yourConnection = new RTCPeerConnection(config); theirConnection = new RTCPeerConnection(config); yourConnection.onicecandidate = function(e) { if (e.candidate) { theirConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } theirConnection.onicecandidate = function(e) { if (e.candidate) { yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } }
我们使用这个函数创建了两个连接对象,在 config 里,你可以任意指定 ICE 服务器,虽然有些浏览器内置了默认的 ICE 服务器,可以不用配置,但还是建议加上这些配置。下面,我们进行 SDP 的握手。
由于是在同一页面中进行的通信,所以我们可以直接交换双方的 candidate 对象,但在不同页面中,可能需要一个额外的服务器协助这个交换流程。
三、建立 SDP Offer 和 SDP Answer浏览器为我们封装好了相应的 Offer 和 Answer 方法,我们可以直接使用。
function startPeerConnection(stream) { var config = { "iceServers": [{ "url": "stun:stun.services.mozilla.com" }, { "url": "stun:stunserver.org" }, { "url": "stun:stun.l.google.com:19302" }] }; yourConnection = new RTCPeerConnection(config); theirConnection = new RTCPeerConnection(config); yourConnection.onicecandidate = function(e) { if (e.candidate) { theirConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } theirConnection.onicecandidate = function(e) { if (e.candidate) { yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } //本方产生了一个offer yourConnection.createOffer().then(offer => { yourConnection.setLocalDescription(offer); //对方接收到这个offer theirConnection.setRemoteDescription(offer); //对方产生一个answer theirConnection.createAnswer().then(answer => { theirConnection.setLocalDescription(answer); //本方接收到一个answer yourConnection.setRemoteDescription(answer); }) }); }
和 ICE 的连接一样,由于我们是在同一个页面中进行 SDP 的握手,所以不需要借助任何其他的通信手段来交换 offer 和 answer,直接赋值即可。如果需要在两个不同的页面中进行交换,则需要借助一个额外的服务器来协助,可以采用 websocket 或者其它手段进行这个交换过程。
四、加入视频流现在我们已经有了一个可靠的视频数据传输通道了,下一步只需要向这个通道加入数据流即可。WebRTC 直接为我们封装好了加入视频流的接口,当视频流添加时,另一方的浏览器会通过 onaddstream 来告知用户,通道中有视频流加入。
yourConnection.addStream(stream); theirConnection.onaddstream = function(e) { theirVideo.src = window.URL.createObjectURL(e.stream); }
以下是完整的 main.js 代码:
function hasUserMedia() { navigator.getUserMedia = navigator.getUserMedia || navigator.msGetUserMedia || navigator.webkitGetUserMedia || navigator.mozGetUserMedia; return !!navigator.getUserMedia; } function hasRTCPeerConnection() { window.RTCPeerConnection = window.RTCPeerConnection || window.webkitRTCPeerConnection || window.mozRTCPeerConnection || window.msRTCPeerConnection; return !!window.RTCPeerConnection; } var yourVideo = document.getElementById("yours"); var theirVideo = document.getElementById("theirs"); var yourConnection, theirConnection; if (hasUserMedia()) { navigator.getUserMedia({ video: true, audio: false }, stream => { yourVideo.src = window.URL.createObjectURL(stream); if (hasRTCPeerConnection()) { startPeerConnection(stream); } else { alert("没有RTCPeerConnection API"); } }, err => { console.log(err); }) } else { alert("没有userMedia API") } function startPeerConnection(stream) { var config = { "iceServers": [{ "url": "stun:stun.services.mozilla.com" }, { "url": "stun:stunserver.org" }, { "url": "stun:stun.l.google.com:19302" }] }; yourConnection = new RTCPeerConnection(config); theirConnection = new RTCPeerConnection(config); yourConnection.onicecandidate = function(e) { if (e.candidate) { theirConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } theirConnection.onicecandidate = function(e) { if (e.candidate) { yourConnection.addIceCandidate(new RTCIceCandidate(e.candidate)); } } theirConnection.onaddstream = function(e) { theirVideo.src = window.URL.createObjectURL(e.stream); } yourConnection.addStream(stream); yourConnection.createOffer().then(offer => { yourConnection.setLocalDescription(offer); theirConnection.setRemoteDescription(offer); theirConnection.createAnswer().then(answer => { theirConnection.setLocalDescription(answer); yourConnection.setRemoteDescription(answer); }) }); }
文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。
转载请注明本文地址:https://www.ucloud.cn/yun/79888.html
摘要:最后,消息成功抵达并显示在页面上。在中,所有的数据都使用数据报传输层安全性。如果应用知识简单的一对一文件传输,使用不可靠的数据通道将需要设计一定的响应重传协议。目前建议的最大块大小为。 本文翻译自WebRTC data channels 在两个浏览器中,为聊天、游戏、或是文件传输等需求发送信息是十分复杂的。通常情况下,我们需要建立一台服务器来转发数据,当然规模比较大的情况下,会扩展成...
摘要:下面我们就看一下具体如何申请权限静态权限申请在项目中的中增加以下代码动态权限申请随着的发展,对安全性要求越来越高。其定义如下通过上面的代码我们就将显示视频的定义好了。当发送消息,并收到服务端的后,其状态变为。 作者:李超,如遇到相关问题,可以点击这里与作者直接交流。 前言 在学习 WebRTC 的过程中,学习的一个基本步骤是先通过 JS 学习 WebRTC的整体流程,在熟悉了整体流程之后,...
摘要:而现在我们可以利用多种工具框架进行跨平台开发。实现视频会议的几种思路如何利用实现一个视频会议应用这主要取决于使用什么技术来实现作为业务核心的部分。通过与技术结合,实现了网页端多方音视频通讯,可以快速实现部分的开发。 作者简介:张乾泽,声网 Agora Web 研发工程师 对于在线教育、医疗、视频会议等场景来讲,开发面向 Windows、Mac 的跨平台客户端是必不可少的一步。在过去,每...
摘要:为了使连接起作用,对等方必须获取元数据的本地媒体条件例如,分辨率和编解码器功能,并收集应用程序主机的可能网络地址,用于来回传递这些关键信息的信令机制并未内置到中。所有特定于多媒体的元数据都使用协议传递。 这是专门探索 JavaScript 及其所构建的组件的系列文章的第 18 篇。 想阅读更多优质文章请猛戳GitHub博客,一年百来篇优质文章等着你! 如果你错过了前面的章节,可以在这里...
阅读 3048·2023-04-25 15:44
阅读 1826·2019-08-30 13:11
阅读 2808·2019-08-30 11:11
阅读 2976·2019-08-29 17:21
阅读 1285·2019-08-29 15:38
阅读 868·2019-08-29 12:49
阅读 1773·2019-08-28 18:19
阅读 3205·2019-08-26 14:01