最近需要搭建一个在线课堂的直播平台,考虑到清晰度和延迟性,我们一致认为使用 WebRTC 最合适。
原因有两点:首先是“点对点通信”非常吸引我们,不需要中间服务器,客户端直连,通信非常方便;再者是 WebRTC 浏览器原生支持,其他客户端支持也很好,不像传统直播用 flv.js 做兼容,可以实现标准统一。
然而令我非常尴尬的是,社区看了好几篇文章,理论架构写了一堆,但没一个能跑起来。WebRTC 里面概念很新也很多,理解它的通信流程
才是最关键,这点恰恰很少有描述。
于是我就自己捣鼓吧。捣鼓了几天,可算是整明白了。下面我结合自己的实践经验,按照我理解的关键步骤,带大家从应用场景的角度认识这个厉害的朋友 —— WebRTC
。
大纲预览
本文介绍的内容包括以下方面:
- 什么是 WebRTC?
- 获取媒体流
- 对等连接流程
- 本地模拟通信源码
- 局域网两端通信
- 一对多通信
- 我想学更多
什么是 WebRTC?
WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对点(Peer-to-Peer)的连接,实现视频流和音频流或者其他任意数据的传输。
简单的说,就是 WebRTC 可以不借助媒体服务器,通过浏览器与浏览器直接连接(点对点),即可实现音视频传输。
如果你接触过直播技术,你就会知道“没有媒体服务器”多么令人惊讶。以往的直播技术大多是基于推流/拉流的逻辑实现的。要想做音视频直播,则必须有一台流媒体服务器做为中间站做数据转发。但是这种推拉流的方案有两个问题:
- 较高的延迟
- 清晰度难以保证
因为两端通信都要先过服务器,就好比本来是一条直路,你偏偏“绕了半个圈”,这样肯定会花更多的时间,因此直播必然会有延迟,即使延迟再低也要 1s 以上。
清晰度高低的本质是数据量的大小。你想象一下,每天乘地铁上班,早高峰人越多,进站的那条道就越容易堵,堵你就会走走停停,再加上还绕了路,是不是到公司就更晚了。
把这个例子联系到高清晰度的直播:因为数据量大就容易发生网络拥堵,拥堵就会导致播放卡顿,同时延迟性也会更高。
但是 WebRTC 就不一样了,它不需要媒体服务器,两点一线直连,首先延迟性一定大大缩短。再者因为传输路线更短,所以清晰度高的数据流也更容易到达,相对来说不易拥堵,因此播放端不容易卡顿,这样就兼顾了清晰度与延迟性。
当然 WebRTC 也是支持中间媒体服务器的,有些场景下确实少不了服务器转发。我们这篇只探讨点对点的模式,旨在帮助大家更容易的了解并上手 WebRTC。
获取媒体流
点对点通信的第一步,一定是发起端获取媒体流。
常见的媒体设备有三种:摄像机,麦克风 和 屏幕。其中摄像机和屏幕可以转化为视频流,而麦克风可转化为音频流。音视频流结合起来就组成了常见的媒体流。
以 Chrome 浏览器为例,摄像头和屏幕的视频流获取方式不一样。对于摄像头和麦克风,使用如下 API 获取:
var stream = await navigator.mediaDevices.getUserMedia()
对于屏幕录制,则会用另外一个 API。限制是这个 API 只能获取视频,不能获取音频:
var stream = await navigator.mediaDevices.getDisplayMedia()
注意:这里我遇到过一个问题,编辑器里提示 navigator.mediaDevices == undefined,原因是我的 typescript 版本小于 4.4,升级版本即可。
这两个获取媒体流的 API 有使用条件,必须满足以下两种情况之一:
- 域名是 localhost
- 协议是 https
如果不满足,则 navigator.mediaDevices
的值就是 undefined。
以上方法都有一个参数 constraints
,这个参数是一个配置对象,称为 媒体约束。这里面最有用的是可以配置只获取音频或视频,或者音视频同时获取。
比如我只要视频,不要音频,就可以这样:
let stream = await navigator.mediaDevices.getDisplayMedia({
audio: false,
video: true
})
除了简单的配置获取视频之外,还可以对视频的清晰度,码率等涉及视频质量相关的参数做配置。比如我需要获取 1080p 的超清视频,我就可以这样配:
var stream = await navigator.mediaDevices.getDisplayMedia({
audio: false,
video: {
width: 1920,
he