【项目实战】基于 WebRTC 的音视频在线监考模块的设计与实现(上)
前言
最近在做关于考试系统的项目,其中有一项需求分析是要做在线监考模块,因为之前没有做过这方面的东西,还是比较迷茫的,在查阅了大量的资料之后,再结合系统是以 H5 的形式展示的,最后选用了 WebRTC 框架为主体来实现这一模块,本文会介绍其基本理论;
什么是 WebRTC?
WebRTC,名称源自网页即时通信(英语:Web Real-Time Communication)的缩写,是一个支持网页浏览器进行实时语音对话或视频对话的 API。与直播常用的 RTMP 协议相比,WebRTC 拥有极低的延迟,并且整合了大量的终端多媒体问题和传输问题的应对方案的实现,包括音视频的编解码、同步、带宽预测、QoS,AEC等,因此使用支持 WebRTC 的设备和浏览器可以轻松实现 P2P 实时语音通话的功能。
WebRTC 是一个由 Google 发起的实时通讯解决方案,其中包含视频音频采集,编解码,数据传输,音视频展示等功能,我们可以通过技术快速地构建出一个音视频通讯应用。 虽然其名为 WebRTC,但是实际上它不光支持 Web 之间的音视频通讯,还支持 Android 以及 IOS 端,此外由于该项目是开源的,我们也可以通过编译 C++ 代码,从而达到全平台的互通。
WebRTC 架构
Your Web App
Web 开发者开发的程序,Web 开发者可以基于集成 WebRTC 的浏览器提供的 web API开发基于视频、音频的实时通信应用。
Web API
面向第三方开发者的 WebRTC 标准 API(Javascript),使开发者能够容易地开发出类似于网络视频聊天的 web 应用,需要注意的是可能在不同浏览器中 API 接口名会不太一样, 所以推荐使用这个 JS 适配器来协调各个浏览器的不同接口。 这些 API可 分成Media API、 RTCPeerConnection、Peer-to-peer Data API 三类;
Media API
MediaStream:用来表示一个媒体数据流。 MediaStreamTrack:在浏览器中表示一个媒体源。
RTCPeerConnection
RTCPeerConnection:一个 RTCPeerConnection 对象允许用户在两个浏览器之间直接通讯。 SDP: 用来描述当前连接者想要传输的内容,支持的协议类型,支持的编解码类型等。 RTCIceCandidate:表示一个 ICE 协议的候选者,简单的说,就是目标节点的 IP 以及端口。 RTCIceServer:表示一个 ICE Server,其主要用于当前主机的 IP 发现,通过和 ICE Server 通讯,我们会得到一组可供连接使用的 IP:Port 候选值,双方通过交换 ICE 候选值来建立起连接。
Peer-to-peer Data API
DataChannel:数据通道(DataChannel)接口,表示一个在两个节点之间的双向的数据通道,该通道可以设置成可靠传输或非可靠传输 。
WebRTC Native C++ API
本地 C++ API 层,使浏览器厂商容易实现 WebRTC 标准的 Web API,抽象地对数字信号过程进行处理。
Transport / Session
传输部分可基于 TCP/UDP,会话层组件采用了 libjingle 库的部分组件实现。
AudioEngine
音频引擎是包含一系列音频多媒体处理的框架,包括从视频采集卡到网络传输端等整个解决方案。
VideoEngine
视频引擎是包含一系列视频处理的整体框架,从摄像头采集视频到视频信息网络传输再到视频显示整个完整过程的解决方案。
WebRTC 通讯内容
首先,两个客户端想要创建连接,一般来说需要有一个双方都能访问的服务器来帮助他们交换连接所需要的信息。有了交换数据的中间人之后,他们首先要交换的数据是SessionDescription(SD),这里面描述了连接双方想要建立怎样的连接。
SD 从哪来
一般来说,在建立连接之前连接双方需要先通过 AP I来指定自己要传输什么数据(Audio,Video,DataChannel),以及自己希望接受什么数据,然后 客户端 A 调用 CreateOffer()
方法,获取 offer 类型的 SessionDescription,通过公共服务器传递给客户端 B,同样地客户端 B 通过调用 CreateAnswer()
,获取 answer 类型的 SessionDescription,通过公共服务器传递给客户端 A。 在这个过程中无论是哪一方创建 Offer(Answer)都无所谓,但是要保证连接双方创建的 SessionDescription 类型是相互对应的。客户端 A = Answer 客户端 B = Offer | 客户端 A = Offer 客户端 B = Answer;
SD 包含什么内容
//版本
v=0
//<username> <sess-id> <sess-version> <nettype> <addrtype> <unicast-address>
o=- 3089712662142082488 2 IN IP4 127.0.0.1
//会话名
s=-
//会话的起始时间和结束时间,0代表没有限制
t=0 0
//表示音频传输和data channel传输共用一个传输通道传输的媒体,通过id进行区分不同的流
a=group:BUNDLE audio data
//WebRTC Media Stream
a=msid-semantic: WMS
//m=audio说明本会话包含音频,9代表音频使用端口9来传输,但是在webrtc中现在一般不使用,如果设置为0,代表不传输音频
//使用UDP来传输RTP包,并使用TLS加密, SAVPF代表使用srtcp的反馈机制来控制通信过程
//111 103 104 9 0 8 106 105 13 110 112 113 126表示支持的编码,和后面的a=rtpmap对应
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 110 112 113 126
//表示你要用来接收或者发送音频使用的IP地址, webrtc使用ice传输,不使用这个地址, 关于ICE是什么后面会讲到
c=IN IP4 0.0.0.0
//用来传输rtcp的地址和端口,webrtc中不使用
a=rtcp:9 IN IP4 0.0.0.0
//ice协商过程中的安全验证信息
a=ice-ufrag:ubhd
a=ice-pwd:l82NnsGm5i7pucQRchNdjA6B
//支持trickle,即sdp里面只描述媒体信息, ice候选项的信息另行通知
a=ice-options:trickle
//dtls协商过程中需要的认证信息
a=fingerprint:sha-256 CA:83:D0:0F:3B:27:4C:8F:F4:DB:34:58:AC:A6:5D:36:01:07:9F:2B:1D:95:29:AD:0C:F8:08:68:34:D8:62:A7
a=setup:active
//前面BUNDLE行中用到的媒体标识
a=mid:audio
//指出要在rtp头部中加入音量信息
a=extmap:1 urn:ietf:params:rtp-hdrext:ssrc-audio-level
//当前客户端只接受数据,不发送数据,recvonly,sendonly,inactive,sendrecv
a=recvonly
//rtp,rtcp包使用同一个端口来传输
a=rtcp-mux
//下面都是对m=audio这一行的媒体编码补充说明,指出了编码采用的编号,采样率,声道等
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
//对opus编码可选的补充说明,minptime代表最小打包时长是10ms,useinbandfec=1代表使用opus编码内置fec特性
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
a=rtpmap:9 G722/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:106 CN/32000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:112 telephone-event/32000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
//下面就是对Data Channel的描述,基本和上面的audio描述类似,使用DTLS加密,使用SCTP传输
m=application 9 DTLS/SCTP 5000
c=IN IP4 0.0.0.0
//可以是CT或AS,CT方式是设置整个会议的带宽,AS是设置单个会话的带宽。缺省带宽是千比特每秒
b=AS:30
a=ice-ufrag:ubhd
a=ice-pwd:l82NnsGm5i7pucQRchNdjA6B
a=ice-options:trickle
a=fingerprint:sha-256 CA:83:D0:0F:3B:27:4C:8F:F4:DB:34:58:AC:A6:5D:36:01:07:9F:2B:1D:95:29:AD:0C:F8:08:68:34:D8:62:A7
a=setup:active
//前面BUNDLE行中用到的媒体标识
a=mid:data
//使用端口5000,一个消息的大小是1024比特
a=sctpmap:5000 webrtc-datachannel 1024
以上就是一个 SessionDescription 的例子,虽然没有 video 的描述,但是 video 和 audio 的描述是十分类似的。 SDP 中有关于 IP 和端口的描述,但是 WebRTC 技术并没有使用这些内容,那么双方是怎么建立 "直接" 连接的呢?建立起连接最关键的 IP 和端口是从哪里来的呢?且看接下来的 WebRTC 协议介绍;
WebRTC 通讯协议
ICE
交互式连接建立(ICE,Interactive Connectivity Establishment)是一中框架,允许你的网络浏览器与同行连接。有很多原因导致从对等体 A 到对等体 B 的直接连接无法工作。它需要绕过会阻止打开连接的防火墙,如果像大多数情况下你的设备没有公共 IP 地址,就给你一个唯一的地址,如果你的路由器不允许你直接与对等体连接,就通过一个服务器中转数据。ICE 使用 STUN 或 TURN 服务器来实现这一目的。
STUN
Session Traversal Utilities for NAT(STUN)是一个协议,用于发现你的公共地址,并确定你的路由器中的任何限制,这些限制会阻止与同行的直接连接。
客户端将向互联网上的 STUN 服务器发送一个请求,该服务器将回复客户的公共地址以及客户是否可以在路由器的 NAT 后面访问。
NAT
Network Address Translation(NAT)是用来给你的设备一个公共 IP 地址。路由器将有一个公共 IP 地址,而连接到路由器的每个设备将有一个私人 I P地址。请求将从设备的私有 IP 翻译到路由器的公共 IP,并有一个唯一的端口。这样,你不需要为每个设备提供一个独特的公共 IP,但仍然可以在互联网上被发现。
一些路由器会对谁可以连接到网络上的设备有限制。这可能意味着,即使我们有 STUN 服务器发现的公共 IP 地址,也不是任何人都可以建立连接。在这种情况下,我们需要求助于 TURN。
TURN
一些使用 NAT 的路由器采用一种叫做 "对称 NAT " 的限制,这意味着路由器将只接受来自你以前连接过的对等体的连接。
Traversal Using Relays around NAT(TURN)是为了绕过对称 NAT 的限制,通过与 TURN 服务器建立连接并通过该服务器转发所有信息。你将创建一个与 TURN 服务器的连接,并告诉所有对等体将数据包发送到该服务器,然后转发给你。这显然会带来一些开销,所以只有在没有其他选择的情况下才会使用它。
SDP
Session Description Protocol (SDP)是一个描述连接的多媒体内容的标准,如分辨率、格式、编解码器、加密等,以便在数据传输后,对等双方能够相互理解。从本质上讲,这是描述内容的元数据,而不是媒体内容本身。
那么,从技术上讲,SDP 不是真正的协议,而是一种用于描述设备间共享媒体的连接的数据格式。
记录 SDP 远远超出了本文档的范围;
WebRTC 连接建立过程
- 连接双方(Peer)通过第三方服务器来交换(Signalling)各自的 SessionDescription 数据。
- 连接双方(Peer)通过 STUN 协议从 STUN Server 那里获取到自己的 NAT 结构,子网 IP 和公网 IP,端口,这里的 IP 和端口对我们称之为 ICE Candidate。
- 连接双方(Peer)通过第三方服务器来交换(Signalling)各自 ICE Candidates,如果连接双方在同一个 NAT 下那他们仅通过内网 Candidate 就能建立起连接,反之如果他们处于非对称型 NAT 下,就需要 STUN Server 识别出的公网 Candidate 进行通讯。
- 如果仅通过 STUN Server 发现的公网 Candidate 仍然无法建立连接,换句话说就是连接双方(Peer)中至少有一方处于对称 NAT 下,这就需要处于对称 NAT 下的客户端(Peer)去寻求 TURN Server 提供的转发服务,然后将转发形式的Candidate共享(Signalling)给对方(Peer)。
- 连接双方(Peer)向目标 IP 端口发送报文,通过 SessionDescription 中涉及的密钥以及期望传输的内容,建立起加密长连接。
后记
这篇博文以介绍 WebRTC 为主,下一篇博文将进入实战,【复】基于 WebRTC 的音视频在线监考模块的设计与实现(下);
参考:
转载自:https://juejin.cn/post/7143406030980907022