由于工作需要,需要完成一个p2p视频流预览的功能,经过预研后打算通过webrtc的方式实现,因为webrtc是一个开源并且成熟的技术,他有一套成熟的标准,以及大量的参考资料,这样遇到的坑的时候大概率能找到对应的解决方案,实在不行,还能通过查看框架的源代码进行排查。
这系列文章的目的就是将学习webrtc以及实现一套demo代码的过程进行记录,供有需要的网友参考。通过这篇文章,你可以了解到webrtc的总体流程,以及一个可以跑通的demo示例等。目前暂定的内容包括webrtc概念介绍,web网页版demo实现,安卓版本demo实现,webrtc源码解析等。
webrtc介绍
首先介绍下webrtc,WebRTC(Web Real-Time Communication)是一个由 Google 发起并成为 W3C 标准的开源项目,旨在实现浏览器之间(或设备之间)的实时音视频通信、数据传输,而无需安装插件或第三方软件。举个例子,比如两个人可以通过网页进行视频通话,这种需求就可以使用webrtc实现,当然,webrtc也不局限于网页,也可以实现为手机app和网页,手机app和设备之间的视频流交互等。
为了实现两个远程的客户端可以进行视频流交互,webrtc框架通过封装提供了以下几个核心能力:
- 音视频的采集和处理,包括支持音频降噪、回声消除、视频编解码等功能,应用端通过这些自带的功能可以通过简单的api调用实现高质量的音视频通话
- 信令交换的协议,包括sdp和ice等,定义了标准的通信流程和协议,应用端只要把sdp和ice正常交换,就能确保音视频通话成功
- 网络穿透(NAT/防火墙穿越),对于可以让两个远程端自己建立连接进行音视频传输的情况,就使用默认的网络穿透方式进行p2p打洞,让一个远端直接和另一个远端建立连接并传输数据,不需要服务器端进行流量中转,从而大大减小了服务器的流量使用。这个也是webrtc的重要特性之一,能极大降低服务器流量成本,不过也有无法打通的情况,这时候就只能通过服务器端中转流量,就会提升流量成本和通信延迟。
上述核心的功能都已经由webrtc封装为api进行提供,应用端只要调用api就可以,不需要了解内部复杂的实现逻辑,大大降低了开发难度。通过这几个核心能力,应用端可以在开发少量代码的情况下就能提供一个高质量的音视频传输服务。
webrtc的主要概念
NAT:网络地址转换,是一种将私有地址映射为公有地址的技术,比如在公司使用网络访问互联网时,其实所有公司内的人都是使用的公司的公网ip进行访问的,NAT技术就是将私有ip转换成公有ip的技术
打洞:NAT 打洞是一种穿透 NAT设备的技术,允许两个位于不同内网(如家庭或企业网络)的设备直接建立 P2P(点对点)连接,而无需依赖中心化服务器中转数据。比如两个公司内部的一台电脑,他们互相不知道对方的外网地址,那怎么建立连接进行数据交互呢? 就算a有办法知道了b公司的外网地址,也没办法主动连接到b公司内的指定一台电脑,而打洞就是通过一系列方式让两者建立连接的技术。
STUN: 是一种用于 NAT 穿透的协议,主要帮助设备发现自己的公网 IP 和端口,并判断 NAT 类型,从而为 P2P(点对点)通信(如 WebRTC、VoIP)提供支持。其中的检测NAT类型功能,是判断 NAT 是完全锥型、受限锥型、端口受限锥型还是对称型,以决定是否可打洞。这几种类型中对称性是无法打洞成功的。
STUN服务器:实现STUN协议的服务器
TURN:是一种通过中继服务器实现 NAT 穿透的协议,用于在直接 P2P(点对点)通信失败时(如对称型 NAT 环境),通过第三方服务器转发数据。
TURN服务器:实现TURN协议的服务器
信令服务器:是 WebRTC 通信的核心协调者,负责在通信双方(Peer)之间交换会话控制信息(如 IP 地址、媒体能力、ICE Candidate 等),帮助建立端到端连接。它不参与实际音视频数据传输,而是为 P2P 通信“铺路”。简单来说,就是负责帮忙将客户端的消息转发给另一个客户端的服务器,从而实现两个客户端之间的消息沟通。
SDP:SDP 是一种纯文本协议,用于描述媒体会话的配置信息,主要用途是通过描述本地客户端支持的音视频格式,让两个客户端协商好,使用一种两边都支持的音视频格式进行交互。
ICE:ICE 是一种自动选择最优网络路径的协议,用于解决 NAT 穿透问题。其核心是通过 ICE Candidate 收集所有可能的连接方式(直连、反射、中继),并测试连通性。主要用途是协助webrtc通过客户端的外网ip等信息进行打洞。
webrtc总体流程介绍
如图所示,webrtc的主要流程有4个步骤:
- 获取本地视频流,这一步主要是通过webrtc的api来进行,以网页版来举例,就是将摄像头的流绑定到html的一个div上,用于展示本地的流,同时也保存到一个变量用,用于后续传递给远程客户端
- 交换sdp信息,这个sdp信息是调用webrtc api生成的,主要包括了支持的视频格式,音频格式等,将两个客户端的sdp进行交换后,两边就知道用什么格式的音视频进行交互了,不过这里有几点需要注意:
- 交换sdp时,是客户端a先主动发起,通过信令服务器将sdp转发给客户端b后,客户端根据a的sdp生成一个本地的回复sdp,再通过信令服务器将b的sdp回复给a,交换时是有一方主动,一方被动
- 两个客户端交换信息需要依赖信令服务器,因为他们不知道对方的地址,无法给对方发送数据,就无法完成数据交换。 所以在这一步之前他们需要分别和信令服务器建立长连接,这样a发消息给b时,信令服务器才能通过长连接将消息推送给b。至于信令服务器的实现方式,可以有很多种,只要是和信令服务器建立长连接的,信令服务器在收到转发消息时,可以找到并反向推送消息给b客户端的就行。
- 交换ice信息,ice信息可以理解为网络穿透(打洞)所需要的一些信息,包括客户端的外网ip,客户端的内网ip等信息,因为这些信息客户端自己可能无法获取到,所以需要用到一个stun服务器,向这个服务器发起一个请求,stun服务器会返回客户端的外网ip。生成ice信息之后,也是通过信令服务器将消息发送到另外一个客户端。
- 发起NAT打洞,这一步的主要实现逻辑也被webrtc封装了,他的主要步骤就是发起打洞,尝试在两个客户端之间建立一个直接通信的连接,如果建立成功,就可以两个客户端互发音视频流,进行通信了。 如果打洞失败,则两个客户端都会和turn服务器建立连接,将音视频流先发给turn服务器,然后turn服务器再把流转发给对方,通过转发的方式完成交互。 但是这样就会额外消耗服务器的流量资源。
这几个步骤中,2和3是应用端开发需要编写的主要逻辑,后面会先简单完成一个网页版的demo,实现两个网页的音视频交互,先把流程跑通,然后深入了解实现。