webRTC实战总结

媒介

前段时刻一直在忙一个基于WebRTC的PC和挪动端双向视频的项目。第一次打仗webRTC,不免遇到了许多题目,比方:webRTC挪动端兼容性检测,怎样设置MediaStreamConstraints, 信令(iceCandidate, sessionDescription)传输体式格局的挑选,iceCandidate和sessionDescription设置的先后顺序,STUN和TURN的观点,怎样完成截图及录制视频及上传图片和视频功用,怎样高效跟踪毛病等等。好记性不如烂笔头,特写此文以记之。

挪动端兼容性

对PC端来讲,webRTC早已被各大浏览器支撑了,Chrome 28,FF22,Edge…跟着不久之前宣告的IOS11也宣告支撑webRTC及getUserMedia,webRTC在挪动端的运用远景也使人向往。

详细到现实项目中,经由测试,各大国产安卓手机自带的浏览器基础不支撑webRTC,但这些安卓手机的微信内置浏览器均能优越地支撑webRTC,虽然Chrome及Firefox的挪动端版本也能优越的支撑webRTC,但国情决议了微信内置浏览器作为最好切入点。另一方面。IOS11中微信内置浏览器还不支撑webRTC(我深信不久的未来就会支撑),但在Safari中能够圆满支撑。因而本项目挑选了微信民众号为切入点,经由过程检测userAgent指导IOS11用户在Safari中翻开页面。

检测webRTC的可行性,主要从getUserMedia和webRTC自身来入手:

function detectWebRTC() {
  const WEBRTC_CONSTANTS = ['RTCPeerConnection', 'webkitRTCPeerConnection', 'mozRTCPeerConnection', 'RTCIceGatherer'];

  const isWebRTCSupported = WEBRTC_CONSTANTS.find((item) => {
    return item in window;
  });

  const isGetUserMediaSupported = navigator && navigator.mediaDevices && navigator.mediaDevices.getUserMedia;

  if (!isWebRTCSupported || typeof isGetUserMediaSupported === 'undefined' ) {
    return false;
  }

  return true;
}

假如返回false,再去检测userAgent赋予用户不支撑的详细提醒。

设置MediaStreamConstraints

所谓MediaStreamConstraints,就是navigator.mediaDevices.getUserMedia(constraints)传入的constraints,至于它的写法及功用,参考MDN,本文不做赘述。我在这里想要强调的是,关于挪动端来讲掌握好视频图象的大小是很主要的,比方本项目中想要对方的图象占有全屏,这不仅是转变video元素的款式或许属机能做到的,首先要做的是转变MediaStreamConstraints中的视频分辨率(width, height),使其长宽比例大抵与挪动端屏幕的相似,然后再将video元素的长和宽设置为容器的长和宽(比方100%)。

别的关于getUserMedia一定要捕捉能够涌现的毛病,假如是老的API,设置onErr回调,假如是新的(navigator.mediaDevices.getUserMedia),则catch异常。如许做的缘由:getUserMedia每每不会完整符合我们的预期,偶然纵然设置的是ideal的束缚,仍然会报错,假如不追踪毛病,每每一脸懵逼。这也是后文要提到的高效追踪毛病的要领之一。

搭建信令传输效劳

要传输的信令包含两个部份:sessionDescription和iceCandidate。为了便于传输可将其处置惩罚成字符串,另一端吸收时复原并用对应的组织函数组织对应的实例即可。

webRTC并没有划定信令的传输体式格局,而是完整由开发者自定义。罕见的体式格局有短轮询、webSocket(socket.io等),短轮询的长处无非是简朴,兼容性强,但在并发量较大时,效劳器负荷会很重。而webSocket就不存在这个题目,但webSocket搭建起来较为庞杂,并非一切的浏览器都支撑websocket。综合来讲socket.io是个不错的处置惩罚方案,事宜机制和自带的房间观点对拉拢视频会话都是自然有益的,而且当浏览器不支撑websocket时能够切换为轮询,也处置惩罚了兼容性的题目。

提议视频会话的流程

《webRTC实战总结》
能够看到无论是提议方照样接收方,第一步都是getUserMedia猎取当地媒体流,然后新建一个RTCPeerConnection实例,并指定好onicecandidate、onaddstream等回调:

// 指定TURN及STUN
const peerConnectionConfig = {
  'iceServers': [
    {
      'urls': 'turn:numb.viagenie.ca',
      'username': 'muazkh',
      'credential': 'webrtc@live.com'
    },

    {
      'urls': 'stun:stun.l.google.com:19302'
    }
  ],
  bundlePolicy: 'max-bundle',
};

const pc = new RTCPeerConnection(peerConnectionConfig);
pc.onicecandidate = ...;
pc.onaddstream = ...;

然后addTrack指定要传输的视频流

stream.getTracks().forEach((track) => { pc.addTrack(track, stream); });

提议方经由过程createOffer天生localDescription并传给pc.setLocalDescription(),pc猎取了当地的sdp后最先猎取candidate,这里的candidate指的是网络信息(ip、端口、协定),依据优先级从高到低分为三类:

  • host: 装备的ipv4或ipv6地点,即内网地点,平常会有两个,离别对应udp和tcp,ip雷同,端口差别;
  • srflx(server reflexive): STUN返回的外网地点;
  • relay: 当STUN不适用时(某些NAT会为每一个衔接分派差别的端口,致使猎取的端口和视频衔接端口并不一致),中继效劳器的地点;

三者当中只须要有一类衔接胜利即可,所以假如通讯两边在统一内网,不设置STUN和TURN也能够直接衔接。实在这里隐藏着机能优化的点:如上图所示,webRTC通讯两边在交流candidate时,首先由提议方先网络一切的candidate,然后在icegatheringstatechange事宜中检测iceGatheringState是不是为’complete’,再发送给吸收方。吸收方设置了发送方传来的sdp和candidate后,一样要网络完本身一切的candidate,再发送给对方。假如这些candidate中有一对能够衔接胜利,则P2P通讯竖立,不然衔接失利。

题目来了,接收端要等待提议方网络完一切的candidate以后才最先网络本身的candidate,这现实上是能够同时举行的;别的实在不一定须要一切的candidate才竖立衔接,这也是能够省下时刻的;末了假如网络,STUN或许TURN涌现题目,在上述传输形式下是异常致命的,会让衔接的时刻变得很长不可接收。

处置惩罚方案就是IETF提出的Trickle ICE。即提议方每猎取一个candidate便马上发送给吸收方,如许做的优点在于第一类candidate即host,会马上发送给吸收方,如许吸收方收到后能够马上最先网络candidate,也就是提议方和吸收方同时举行网络candidate的事情。别的,吸收方每收到一个candidate会马上去搜检它的有用性,假如有用直接接通视频,假如无效也不至于浪费时刻。概况能够拜见ICE always tastes better when it trickles.

至于sessionDescription及iceCandidate的传输,由于JavaScript没有处置惩罚sdp花样数据的要领,所以直接将其当作字符串处置惩罚,如许做的害处是难以转变sdp中的信息(假如非要改,经由过程正则婚配照样能改的)。

在挂断视频时,不仅要封闭peerConnection,也要住手当地及长途的媒体流:


const tracks = localStream.getTracks().concat(remoteStream.getTracks());
tracks.forEach((track) => {
  track.stop();
});

peerConnection.close();

截图&录制视频

截图实在并不算什么新颖的东西,无非是应用canvas的drawImage函数猎取video元素在某一帧的图象,获得的是图片的base64花样字符串,但要注重的是如许获得的base64码之前有如许一串文本:

data:image/png;base64,

这是对数据协定,花样,编码体式格局的声明,是给浏览器看的。所以在将drawImage获得的字符串上传给效劳器时,最好将这串文本去掉,防备后端在转换图片时涌现毛病。

录制视频运用的是MediaRecorder API 概况参考MDN MediaRecorder,如今仅支撑录制webm花样的视频。能够在新建MediaRecorder实例的时刻,设置mimeType、videoBitsPerSecond、audioBitsPerSecond:

const options = {
  mimeType: 'video/webm;codecs=vp8',     // 视频花样及编码花样
  videoBitsPerSecond: 2500000,           // 视频比特率,影响文件大小和质量 
  audioBitsPerSecond: 128000             // 音频比特率,影响文件大小和质量
};

const recorder = new MediaRecorder(options);

在recorder的ondataavailable事宜中拿到数据,将其转换为Blob对象,再经由过程Formdata异步上传至效劳器。

毛病追踪

全部双向视频涉及到的步骤较多,做好毛病追踪是异常主要的。像getUserMedia时,一定要catch能够涌现的异常。由于差别的装备,差别的浏览器或许说差别的用户每每不能完整满足我们设置的constraints。另有在实例化RTCPeerConnection时,每每会涌现不可预期的毛病,罕见的有STUN、TURN花样不对,另有createOffer时通报的offerOptions花样不对,准确的应该为:

const offerOptions = {
  'offerToReceiveAudio': true,
  'offerToReceiveVideo': true
};

CAVEAT

由于webRTC规范还在不断地更新中,所以相干的API常常会有修改。

  • navigator.getUserMeida(已烧毁),如今改成navigator.mediaDevices.getUserMedia;
  • RTCPeerConnection.addStream被RTCPeerConnection.addTrack庖代;
  • STUN,TURN设置里的url现被urls庖代;

别的,对video元素也要特别处置惩罚。设置autoPlay属性,对播放当地视频源的video还要设置muted属性以去除覆信。针对IOS播放视频自动全屏的特征,还要设置playsinline属性的值为true。

    原文作者:sakuragizhx
    原文地址: https://segmentfault.com/a/1190000012931944
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞