在网上找到了一个非常适合入门的教程,分享给有缘人(稍微改了以下布局,并添加了一点内容o( ̄︶ ̄)o)
WebRtc处理过程
实现1v1的通话有4个部分,WebRtc终端(这里理解为浏览器端)、Signal(信令)服务器、STUN/TURN服务器
- WebRtc终端,负责音视频的采集、编解码、NAT穿越、音视频数据传输 (这里终端暂时看做浏览器,webRtc不止应用在浏览器)
- Signal服务器,负责信令处理,如有人加入房间、离开房间、媒体协商消息传递等。(类似聊天室的xx加入房间),一般采用WebSocket连接
- STUN/TURN服务,负责获取WebRtc终端在公网的ip地址,以及NAT穿越失败后的数据中转。()
用户A和用户B要语音通过大致过程:
- 用户A和用户B作为WebRtc终端(浏览器)检测你的设备是否支持音视频数据采集,
- 获取音视频数据后加入到信令信令服务器,这样2个用户都加入到一个房间
- 用户A会创建RTCPeerConnection对象,该对象将采集到的音视频数据进行编码和通过P2P传送给对方,P2P穿越失败,就使用TURN进行数据中转,有的公司架构是直接用后者进行传输
音视频采集
浏览器的getUserMedia方法
1 | navigator.mediaDevices.getUserMedia(constraints); |
constraints参数视频设置采集分辨率、帧率参数,音频可以设置开启降噪等参数;如设备具备音视频采集能力,它返回的成功的promise里,可以获取到MediaStream对象,并完成以下操作
- 本地操作视频流:MediaStream对象存放着采集到的音视频轨,直接赋值给video标签的srcObject属性,就可以本地实现看到摄像头和听到声音。
- 拍照:通过canvas的drawImage,将video标签传入
- 保存照片:通过canvas.toDataURL生成本地地址,通过a标签下载图片
1 | var constraints = { audio: true, video: true }; |
媒体协商
-
作用:
让双方找到共同支持的媒体能力,过程有点像 TCP 的三次握手。 -
知识点:
- 创建连接:
创建RTCPeerConnection对象,它负责端与端之间建立 P2P 连接。 - 信令:
客户端通过信令服务器交换 SDP(Session Description Protocol)信息。SDP 包含编解码方式、传输协议、IP 地址和端口等信息,确保双方能够找到共同支持的媒体能力并进行通信。 - Offer:
在双向通信时,呼叫方发送的 SDP 消息称为 Offer。 - Answer:
在双向通信时,被呼叫方发送的 SDP 消息称为 Answer。
- 创建连接:
-
媒体协商过程:
- 呼叫方创建 Offer 类型的 SDP 消息后,通过
setLocalDescription方法保存到本地的 Local 域,再通过信令将 Offer 发给被呼叫方。 - 被呼叫方收到 Offer 类型的 SDP 消息后,通过
setRemoteDescription保存到其 Remote 域。接着,它创建 Answer 类型的 SDP 消息,并通过setLocalDescription保存到本地,再将消息发给呼叫方。 - 呼叫方收到 Answer 类型的 SDP 消息后,通过
setRemoteDescription保存到其 Remote 域。
- 呼叫方创建 Offer 类型的 SDP 消息后,通过
-
总结:
媒体协商完成后,WebRTC 底层会收集 Candidate(WebRTC 与远端通信时使用的协议、IP 地址和端口),进行连通性测试,最终建立一条链路。
1 | // 呼叫方A |
连接建立
连接的基本原则
如果A和B连接,C作为服务器
场景一:双方处于同一个网段内(内网)
场景二:双方处于不同点
ICE Candidate(ICE候选者),它表示WebRtc与远端通信使用的协议、ip地址和端口,一般3种方式
host表示本机候选者,内网之间的联通性测试,优先级最高
srflx表示内网主机映射的外网地址和端口,让双发通过P2P进行连接,次优化级
relay表示中继候选者,低优先级
1 | { |
STUN协议
NAT是指网络地址转换,作用就是进行内外网的地址转换。
STUN(session traversal utilities for NAT),一种处理NAT传输的协议,它允许位于NAT后的客户端找出自己的公网地址。
srflx类型的Candidate实际上就是用的经NAT映射后的外网地址,进行P2P连接通信。
TURN协议
relay服务通过TURN协议实现。TURN协议描述了如何获取relay服务器(即TUNR协议)的Candidate过程。通过TURN服务器发送Allocation指令,relay服务就会在服务端分配一个新的relay端口,用于中转UDP数据报。
因为P2P场景有限,其实大部分还是采用relay方式来传输数据。
实现1v1音视频实时直播系统
用户A和用户B之间视频通话过程:
信令服务器
基于nodejs
1 | const ws = require('nodejs-websocket'); |
建立连接的具体流程
- 用户A和用户B,通过WebSocket,连接ws服务,监听回调函数
1 | function createWebScoket(url) { |
- 用户A和用户B先后点击加入房间,根据navigator.mediaDevices.getUserMedia获取本地的音视频流,再通过ws.send将roomId、userId等信息传递给信令服务器
1 | roomId = document.getElementById('roomBox').value // 输入框取房间号 |
- 信令服务器先后收到用户A和用户B消息,将该用户加入房间,当用户B加入的时候,就通知到房间里用户A
1 | socket.on('text', (data) => { |
- 用户A发现此时房间有其他人,会收到消息,type=“peer-join”,会先创建RTCPeerConnection对象,再监听onicecandidate(收到网络协商相关消息)和ontrack(收到远端音视频流),将本地音视频流添加到对象中
1 | ws.onmessage = (e) => { |
- 用户A再通过RTCPeerConnection的实例,创建offer(sdp信息用于协商音视频编码协议),setLocalDescription设置在本地,再发送到信令服务器,转给用户B
1 | ws.onmessage = (e) => { |
- 信令服务器收到消息,type=‘offer’时,转发给用户B
1 | socket.on('text', (data) => { |
- 用户B,收到信令服务器发到type=‘offer’消息,同样先创建RTCPeerConnection实例和监听,再监听onicecandidate和ontrack,将本地音视频流添加到实例中,代码同上面一致
同时用户B,会将用户A的sdp消息,setRemoteDescription到远程,自己创建answer的sdp信息,发送给服务端,转给用户A
1 | ws.onmessage = (e) => { |
- 最后用户A收到,type=‘answer’的消息,setRemoteDescription到远程
双方收到ontrack回调事件,获取到对方码流的对象句柄
双方都开始请求打洞,通过onicecandidate获取到打洞信息(candidate)并通过信令服务器发送给对方
如果p2p能成功则进行通话,不成功则进行中继转发通话(这里并没有配置服务器,所以只能进行内网的通信)
中继服务器的配置
这里我使用了开源的coturn服务器
添加pc = new RTCPeerConnection()这个方法的参数
1 | const configuration = { |
完整代码
前端代码
1 |
|
信令服务器完整代码
1 | const ws = require('nodejs-websocket'); |
最后
本文只是webrtc进行一个初步的了解,参考了<<WebRtc音视频实时互动技术原理>>书籍和b站视频,对于大家如果有所帮助,欢迎点赞~
作者:竹业
链接:https://juejin.cn/post/7218532789195702333
来源:稀土掘金
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
说些什么吧!