波煮的实习公司主要是音视频业务,所以最近在补习WebRTC的相关内容,会不定期给大家分享学习心得和笔记。
WebRTC通话原理
WebRTC(Web 实时通信 )是一种使 Web 应用程序和站点能够捕获和选择性地流式传输音频或视频媒体,以及在浏览器之间交换任意数据的而无需中间件的技术。WebRTC 的一系列标准使得在不需要用户安装插件或任何其他第三方软件的情况下,可以实现点对点数据共享 和电话会议。
进行媒体协商:彼此要了解对方支持的媒体格式
注:有一个专门的协议,称为Session Description Protocol
(SDP),可用于描述上述这类信息,在WebRTC中,参与视频通讯的双方必须先交换SDP信息 ,这样双方才能知根知底,而交换SDP的过程 ,也称为“媒体协商”
网络协商:彼此要了解对方的网络情况,这样才有可能找到一条相互通讯的链路
candidate
获取外网IP地址映射
通过信令服务器(signal server)交换网络信息。
实际情况是:我们的电脑和电脑之间或大或小都是在某个局域网中,需要NAT(Network Address Translation,网络地址转换)
在解决WebRTC使用过程中的上述问题的时候,我们需要用到STUN和TURN
STUN
(Session Traversal Utilities for NAT,NAT会话穿越应用程序)是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internel端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间创建UDP通信,该信息由RFC 5389定义。
STUN做的事情就是:告诉我你的公网IP地址+端口是什么,搭建STUN服务器很简单,媒体流传输室按照P2P的方式。
TURN
TURN的全称为(Traversal Using Relays around NAT),是STUN/RFC5389的一个拓展,主要添加了Relay功能,如果终端在NAT之后,那么在特定的情景下,有可能使得终端无法和其对等端(peer)进行直接的通信,这是就需要公网的服务器作为一个中继,对来往的数据进行转发,这个转发的协议就被定义为TURN。
媒体协商+网络协商数据的交换通道
需要一个信令服务器(Signal server)转发彼此的媒体信息和网络信息。
信令服务器
信令服务器不只是交互,媒体信息sdp和网络信息candidate,比如:
WebRTC APIs
MediaStream
:MediaStream用来表示一个媒体数据流 (通过getUserMedia
接口获取),允许你访问输入设备,如麦克风和Web摄像机,该API允许从其中任意一个获取媒体流。 - RTCPeerConnection
:RTCPeerConnection对象允许用户在两个浏览器之间直接通讯 ,你可以通过网络将捕获的音频和视频流实时发送到另一个WebRTC端点,使用这些API,你可以在本地机器和远程对等点之间创建连接。它提供了连接到远程对等点、维护和监视连接以及在不再需要连接时关闭连接的方法。
打开摄像头
初始化button、video组件
绑定“打开摄像头”响应事件onOpenCamera
如果要打开摄像头则点击“打开摄像头”按钮,以触发onOpenCanmer
事件的调用
当触发onOpenCanmera
调用时:
设置约束条件,即是getUserMedia
函数的入参 2. getUserMedia
有两种情况:一种是正常打开摄像头,使用handleSuccess
处理;一种是打开摄像头失败,使用handleError
处理
当正常打开摄像头时,则将getUserMedia
返回的stream对象赋值给video空间的srcObject即可将视频显示出来
web端的websocket
为了建立一个WebSocket连接,客户端浏览器首先要向服务器发起一个HTTP请求,这个请求和通常的HTTP请求不同,包含了一些附加头信息,其中附加头信息Upgrade:WebSocket表明这是一个申请协议升级的HTTP请求,服务器端解析这些附加的头信息然后产生应答信息返回给客户端,客户端和服务器端的WebSocket连接就建立起来了,双方就可以通过这个连接通道自由的传递信息,并且这个连接会持续存在知道客户端或者服务器端的某一方主动的关闭连接。
打开摄像头和麦克风代码案例
<! DOCTYPE html >
< html lang = " en" >
< head>
< meta charset = " UTF-8" />
< meta name = " viewport" content = " width=device-width, initial-scale=1.0" />
< title> 摄像头与麦克风测试</ title>
</ head>
< body>
< video id = " localVideo" src = " " autoplay controls > </ video>
< button id = " showVideo" > 打开摄像头</ button>
< button id = " showAudio" > 打开麦克风</ button>
< audio id = " localAudio" controls autoplay > </ audio>
< script>
function onOpenCamera ( ) {
navigator. mediaDevices
. getUserMedia ( {
video : {
width : 640 ,
height : 480 ,
} ,
audio : false ,
} )
. then ( onhandleSuccess)
. catch ( onhandleError) ;
}
function onOpenMic ( ) {
navigator. mediaDevices
. getUserMedia ( {
video : false ,
audio : true ,
} )
. then ( onhandleAudioSuccess)
. catch ( onhandleAudioError) ;
}
function onhandleSuccess ( stream ) {
console. log ( "打开摄像头成功" ) ;
const video = document. querySelector ( "#localVideo" ) ;
video. srcObject = stream;
}
function onhandleAudioSuccess ( stream ) {
console. log ( "打开麦克风成功" ) ;
const audio = document. querySelector ( "#localAudio" ) ;
audio. srcObject = stream;
}
function onhandleError ( error ) {
console. error ( "打开摄像头失败" , error) ;
}
function onhandleAudioError ( error ) {
console. error ( "打开麦克风失败" , error) ;
}
const showVideo = document. querySelector ( "#showVideo" ) ;
showVideo. addEventListener ( "click" , onOpenCamera) ;
const showAudio = document. querySelector ( "#showAudio" ) ;
showAudio. addEventListener ( "click" , onOpenMic) ;
</ script>
</ body>
</ html>