zhuiyuanqingya 2019-06-25
webRTC是Google在2010年收购GIP公司之后获得的一项技术。如下图所示,它提供了音视频的采集、处理(降噪,回声消除等)、编解码、传输等技术。
webRTC的目标是实现无需安装任何插件就可以通过浏览器进行P2P的实时音视频通话及文件传输,来看看Google的demo,是不是很酷?本文将带你分析webRTC的原理,并逐步编写一个简单的demo。
如图,浏览器之间媒体流的传输是P2P的,但是这并不意味着webRTC不需要服务器支持。建立P2P视频连接需要的信息,如用来初始化通信的session信息,双方的ip、端口,视频分辨率,编解码格式等等,还是需要通过服务器来传输的。webRTC没有规定这些信息传输的机制,XHR、webSocket、Socket.io等都是可以的,因为Socket.io自带了房间的概念,便于双向视频的撮合,所以我在demo里选择了Socket.io。
当然,连接建立的过程不会这么简单。首先,提到P2P就绕不开NAT(Network Address Translation),webRTC使用ICE(Interactive Connectivity Establishment)框架,ICE是一种综合性的NAT穿越技术,它整合了STUN、TURN。当穿越网络时,ICE会先尝试STUN,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口从而建立UDP连接,如果失败了ICE就会再尝试TCP(先尝试HTTP,再尝试HTTPS),如果仍然失败就使用中继的TURN服务器。
再来看看建立连接过程中的具体步骤:
var constraints = { audio: false, video: true }; navigator.mediaDevices.getUserMedia(constraints) .then(gotStream) .catch(function(e) { alert('getUserMedia() error: ' + e.name); }); function gotStream(stream) { localVideo.srcObeject = stream; localStream = stream; }
getUserMedia存在兼容性问题,需要在项目中引用webRTC官方给出的adapter.js。constraints还可以配置video的分辨率、帧率、对移动端还可以选择前后摄像头:
var constraints = { video: { width: { min:640, ideal: 1280, max: 1920 }, height: { min: 480 ideal: 720, max: 1080 }, facingMode: 'user' // 前置摄像头 } };
var serverConfig = { 'iceServers': [{ 'urls': 'stun:stun.l.google.com:19302' }] }; function createPeerConnection() { var pc = new RTCPeerConnection(serverConfig); pc.onicecandidate = function(e) { if (e.candidate) { pc.addIceCandidate(e.candidate); } }; // 添加对方的媒体流 pc.onaddstream = function(e) { remoteVideo.srcObeject = e.stream; remoteStream = stream; }; }
由STUN、TURN配置生成对应的RTCPeerConnection实例,再定义相关的事件处理函数,如onicecandidate、onaddstream、onremovestream等。
function start() { pc.addstream(localStream); if (isCaller) { pc.createOffer(function(sessionDescription) { pc.setLocalDescription(sessionDescription); send(sessionDescription); // 根据不同的Signaling方式实现 }); if (receiveAnswer) { pc.setRemoteDescription(answer.sessionDescription); } } else { if (receiveOffer) { pc.setRemoteDescription(offer.sessionDescription); } pc.createAnswer(function(sessionDescription) { pc.setLocalDescription(sessionDescription); send(sessionDescription); }); } }
必须先getUserMedia后才能生成sessionDescription,并且只有在setLocalDescription后onicecandidate事件才会触发。上面代码中的只是为了说明大致流程,实际项目中结合socket.io的事件更容易实现。
function stop() { pc.stop(); pc = null; }
关于socket.io有关的代码本文没有贴出,详情可参考socket.io的用法。
按照上面的步骤可以成功地搭建webRTC的小demo,但是能否将webRTC运用到实际项目中去呢?下面从浏览器兼容性和webRTC本身的性能两个方面去分析。
诚然webRTC在回声消除,图像编解码等方面已经做得十分出色,但它在性能上的问题还是不可忽视的:
综上所述,虽然webRTC具有不需安装插件或者客户端,开源免费,强大的网络穿透能力,出色的音视频处理技术等等优点,但由于兼容性及性能上的问题,要投入到生产中还需要时间,主要是IOS11的普及以及CPU占用率和延时的问题。
部署WebRTC 或 SIP p2p 方案时经常会遇到p2p 无法穿透的环境,这时就是TunServer 的用武之地了。添加完成后,就可以在webrtc 里面使用stun 和tun server 了。