0


WebRTC入门

1、WebRTC入门

1、以下内容来自 零声学院 ,把PDF放到博客上面来,方便以后查看

1.1 什么是WebRTC

WebRTC(Web Real­Time Communication)是 Google于 2010 以 6829 万美元从 Global IP Solutions 公司购买,并于2011 年将其开源,旨在建立一个互联网浏览器间的实时通信的平台,让 WebRTC技术成为 H5标准之一。我们看 官网 的介绍

从官网上的描述我们可以知道,WebRTC是一个免费的开放项目,它通过简单的API为浏览器和移动应用程序提供实时通信(RTC)功能。

1.2 WebRTC框架

在这里插入图片描述
上图的框架对于不同的开发人员关注点不同:

( 1 )紫色部分是Web应用开发者API层
( 2 )蓝色实线部分是面向浏览器厂商的API层
( 3 )蓝色虚线部分浏览器厂商可以自定义实现

特别是图中的 PeerConnection 为 Web 开发人员提供了一个抽象,从复杂的内部结构中抽象出来。我们只需要关注PeerConnection这个对象即可以开发音视频通话应用内。

WebRTC架构组件介绍

  • Your Web App Web开发者开发的程序,Web开发者可以基于集成WebRTC的浏览器提供的web API开发基于视频、音频的实时通信应用。
  • Web API 面向第三方开发者的WebRTC标准API(Javascript),使开发者能够容易地开发出类似于网络视频聊天的web应用, 最新的标准化进程可以查看这里。
  • WebRTC Native C++ API 本地C++ API层,使浏览器厂商容易实现WebRTC标准的Web API,抽象地对数字信号过程进行处理。
  • Transport / Session 传输/会话层 会话层组件采用了libjingle库的部分组件实现,无须使用xmpp/jingle协议
  • VoiceEngine 音频引擎是包含一系列音频多媒体处理的框架。 PS:VoiceEngine是WebRTC极具价值的技术之一,是Google收购GIPS公司后开源的。在VoIP上,技术业界领先。 Opus:支持从6 kbit/s到510 kbit/s的恒定和可变比特率编码,帧大小从2.5 ms到60 ms,各种采样率从8 kHz(4 kHz带宽)到48 kHz(20 kHz带宽,可复制人类听觉系统的整个听力范围)。由IETF RFC 6176定义。NetEQ模块是Webrtc语音引擎中的核心模块 ,一种动态抖动缓冲和错误隐藏算法,用于隐藏网络抖动和数据包丢失的负面影响。保持尽可能低的延迟,同时保持最高的语音质量。
  • VideoEngine WebRTC视频处理引擎VideoEngine是包含一系列视频处理的整体框架,从摄像头采集视频到视频信息网络传输再到视频显示整个完整过程的解决方案。 VP8 视频图像编解码器,是WebRTC视频引擎的默认的编解码器 VP8适合实时通信应用场景,因为它主要是针对低延时而设计的编解码器。

1.2.1 WebRTC发展前景

WebRTC虽然冠以“web”之名,但并不受限于传统互联网应用或浏览器的终端运行环境。实际上无论终端运行环境是浏览器、桌面应用、移动设备(Android或iOS)还是IoT设备,只要IP连接可到达且符合WebRTC规范就可以互通。

这一点释放了大量智能终端(或运行在智能终端上的app)的实时通信能力,打开了许多对于实时交互性要求较高的应用场景的想象空间,譬如在线教育、视频会议、视频社交、远程协助、远程操控等等都是其合适的应用领域。

全球领先的技术研究和咨询公司Technavio最近发布了题为“全球网络实时通讯(WebRTC)市场,2017­2021”的报告。报告显示,2017­2021年期间,全球网络实时通信(WebRTC)市场将以34.37%的年均复合增长率增长,增长十分迅速。增长主要来自北美、欧洲及亚太地区。

1.2.2 国内方案厂商

声网、即构科技、环信、融云等公司都在基于WebRTC二次开发自己的音视频通话方案。

1.2.3 WebRTC通话原理

首先思考的问题:两个不同网络环境的(具备摄像头/麦克风多媒体设备的)浏览器,要实现点对点 的实时音视频对话,难点在哪里?

  1. 媒体协商 彼此要了解对方支持的媒体格式

在这里插入图片描述

比如:Peer­A端可支持VP8、H264多种编码格式,而Peer­B端支持VP9、H264,要保证二端都正确的编解码,最简单的办法就是取它们的交集H264

注:有一个专门的协议 ,称为**Session Description Protocol (SDP)**,可用于描述上述这类信息,在WebRTC中,参与
视频通讯的双方必须先交换SDP信息,这样双方才能知根知底,而交换SDP的过程,也称为"媒体协商"。

  1. 网络协商彼此要了解对方的网络情况,这样才有可能找到一条相互通讯的链路****先说结论:(1)获取外网IP地址映射;( 2 )通过信令服务器(signal server)交换"网络信息"

理想的网络情况是每个浏览器的电脑都是私有公网IP,可以直接进行点对点连接。

在这里插入图片描述

实际情况是:我们的电脑和电脑之前或大或小都是在某个局域网中,需要NAT(Network Address Translation,网络地址转换),显示情况如下图:

在这里插入图片描述

在这里插入图片描述
在解决WebRTC使用过程中的上述问题的时候,我们需要用到STUN和TURN。

1.2.4 STUN

STUN(Session Traversal Utilities for NAT,NAT会话穿越应用程序)是一种网络协议,它允许位于NAT(或多重NAT)后的客户端找出自己的公网地址,查出自己位于哪种类型的NAT之后以及NAT为某一个本地端口所绑定的Internet端端口。这些信息被用来在两个同时处于NAT路由器之后的主机之间创建UDP通信。该协议由RFC 5389定义。

在遇到上述情况的时候,我们可以建立一个STUN服务器,这个服务器做什么用的呢?主要是给无法在公网环境下的视频通话设备分配公网IP用的。这样两台电脑就可以在公网IP中进行通话。

在这里插入图片描述
使用一句话说明STUN做的事情就是:告诉我你的公网IP地址+端口是什么。搭建STUN服务器很简单,媒体流传输是按照P2P的方式。

那么问题来了,STUN并不是每次都能成功的为需要NAT的通话设备分配IP地址的,P2P在传输媒体流时,使用的本地带宽,在多人视频通话的过程中,通话质量的好坏往往需要根据使用者本地的带宽确定。那么怎么办?TURN可以很好的解决这个问题。

1.2.5 TURN

TURN的全称为Traversal Using Relays around NAT,是STUN/RFC5389的一个拓展,主要添加了Relay功能。如果终端在NAT之后, 那么在特定的情景下,有可能使得终端无法和其对等端(peer)进行直接的通信,这时就需要公网的服务器作为一个中继, 对来往的数据进行转发。这个转发的协议就被定义为TURN。

在上图的基础上,再架设几台TURN服务器:

在这里插入图片描述

在STUN分配公网IP失败后,可以通过TURN服务器请求公网IP地址作为中继地址。这种方式的带宽由服务器端承担,在多人视频聊天的时候,本地带宽压力较小,并且,根据Google的说明,TURN协议可以使用在所有的环境中。(单向数据200kbps 一对一通话)

以上是WebRTC中经常用到的 2 个协议,STUN和TURN服务器我们使用coturn开源项目来搭建。

补充:ICE跟STUN和TURN不一样,ICE不是一种协议,而是一个框架(Framework),它整合了STUN和TURN。coturn开源项目集成了STUN和TURN的功能。

1.2.6 STUN&TURN 概括

STUN(Session Traversal Utilities for NAT)和 TURN(Traversal Using Relays around NAT)是两种用于解决网络地址转换(NAT)环境下通信问题的协议。

一、STUN

  1. 作用:- STUN 的主要目的是帮助位于 NAT 后面的客户端发现自己的公网 IP 地址和端口号。这对于建立直接的点对点通信非常重要,尤其是在使用实时通信协议如 WebRTC 时。- 通过向 STUN 服务器发送请求,客户端可以获取到自己在公网中的可见地址信息,然后将这些信息分享给通信的另一方,以便尝试直接建立连接。
  2. 工作原理:- 客户端向 STUN 服务器发送请求消息,其中包含一些特定的信息,如客户端的内网 IP 地址和端口号等。- STUN 服务器收到请求后,会回复一个响应消息,其中包含客户端的公网 IP 地址和端口号,以及一些其他的属性信息,如 NAT 的类型等。- 客户端根据收到的响应消息,确定自己的公网地址信息,并可以尝试与其他客户端建立直接连接。
  3. 应用场景:- 在 WebRTC 应用中,STUN 服务器通常被用于帮助客户端发现自己的公网地址,以便进行点对点通信。如果两个客户端位于不同的 NAT 后面,但它们的 NAT 类型允许直接建立连接,那么就可以通过 STUN 发现的公网地址进行通信,无需经过中间服务器转发。- 对于一些简单的 NAT 环境,STUN 可能足以解决通信问题。但在某些情况下,如 NAT 类型限制或网络限制,仅靠 STUN 可能无法实现直接通信。

二、TURN

  1. 作用:- 当直接的点对点连接无法建立时,TURN 服务器可以作为中继,转发通信双方的数据。这在 NAT 环境非常严格或者存在防火墙等限制的情况下非常有用。- TURN 服务器接收来自一个客户端的数据,然后将其转发给另一个客户端,确保通信的持续进行。
  2. 工作原理:- 客户端首先尝试通过 STUN 发现自己的公网地址并尝试直接建立连接。如果直接连接失败,客户端可以向 TURN 服务器申请中继服务。- 客户端将数据发送到 TURN 服务器指定的端口,TURN 服务器收到数据后,根据目标地址将其转发给另一个客户端。同样,另一个客户端也将数据发送到 TURN 服务器,由服务器转发给第一个客户端。- TURN 服务器在转发数据的过程中,通常会对数据进行一些处理,如封装、加密等,以确保数据的安全和可靠传输。
  3. 应用场景:- 在复杂的网络环境中,如企业网络、移动网络等,TURN 服务器可以作为一种可靠的通信解决方案。当直接连接不可行时,通过 TURN 服务器的中继服务,仍然可以实现实时通信。- 对于一些对通信质量要求较高的应用,如视频会议、在线游戏等,TURN 服务器可以提供稳定的通信路径,确保数据的及时传输。

三、STUN 和 TURN 的关系

  1. 通常情况下,在尝试建立通信时,会先使用 STUN 服务器来发现公网地址并尝试直接连接。如果直接连接失败,再使用 TURN 服务器作为中继。
  2. STUN 和 TURN 可以结合使用,为不同的网络环境提供灵活的通信解决方案。例如,在 WebRTC 中,通常会同时配置 STUN 和 TURN 服务器,以确保在各种情况下都能实现通信。
  3. 两者都是为了解决 NAT 带来的通信问题,但 STUN 更侧重于发现公网地址和尝试直接连接,而 TURN 则是在直接连接不可行时提供可靠的中继服务。

总之,STUN 和 TURN 是在 NAT 环境下实现通信的重要协议,它们为各种实时通信应用提供了必要的技术支持。

1.3、 媒体协商+网络协商数据的交换通道

在WebRTC中用来描述 网络信息的术语叫candidate。

  • 媒体协商 sdp
  • 网络协商 candidate

从上面1/2点我们知道了 2 个客户端协商媒体信息和网络信息,那怎么去交换?是不是需要一个中间商去做交换?所以我们需要一个信令服务器(Signal server)转发彼此的媒体信息和网络信息。

在这里插入图片描述
如上图,我们在基于WebRTC API开发应用(APP)时,可以将彼此的APP连接到信令服务器(Signal Server,一般搭建在公网,或者两端都可以访问到的局域网),借助信令服务器,就可以实现上面提到的SDP媒体信息及Candidate网络信息交换。

信令服务器不只是交互 媒体信息sdp和网络信息candidate,比如:

  • 房间管理
  • 人员进出房间

1.3.1、WebRTC APIs

  1. MediaStream — MediaStream用来表示一个媒体数据流(通过getUserMedia接口获取),允许你访问输入设备,如麦克风和 Web摄像机,该 API 允许从其中任意一个获取媒体流。
  2. RTCPeerConnection — RTCPeerConnection 对象允许用户在两个浏览器之间直接通讯 ,你可以通过网络将捕获的音频和视频流实时发送到另一个 WebRTC 端点。使用这些 Api,你可以在本地机器和远程对等点之间创建连接。它提供了连接到远程对等点、维护和监视连接以及在不再需要连接时关闭连接的方法。

1.3.2、 一对一通话

在这里插入图片描述

在一对一通话场景中,每个 Peer均创建有一个 PeerConnection 对象,由一方主动发 Offer SDP,另一方则应答AnswerSDP,最后双方交换 ICE Candidate 从而完成通话链路的建立。但是在中国的网络环境中,据一些统计数据显示,至少 1 半的网络是无法直接穿透打通,这种情况下只能借助TURN服务器中转。

1.3.3、 NAT知识补充

具体NAT打洞的知识在本课程不做进一步的讲解,这里提供些链接给大家做参考:

2、WebRTC开发环境

1、安装vscode或者idea,等等开发工具
2、安装 nodejs

2.1、第一个nodejs教程

nodejs教程:https://www.runoob.com/nodejs/nodejs­tutorial.html

# 查看是否安装,安装正常则打印版本号
node ‐v
npm ‐v

在这里插入图片描述

以上代码我们完成了一个可以工作的 HTTP 服务器。使用 node 命令执行以上的代码:接下来,打开浏览器访问 http://127.0.0.1:8888/,你会看到一个写着 "Hello World"的网页。

分析Node.js 的 HTTP 服务器:

第一行请求(require)Node.js 自带的 http 模块,并且把它赋值给 http 变量。
接下来我们调用 http 模块提供的函数: createServer 。这个函数会返回 一个对象,这个对象有一个叫做 listen 的方法,这个方法有一个数值参数, 指定这个 HTTP 服务器监听的端口号。

2.2、coturn穿透和转发服务器

1、这部分直接参考 官网就好了,也可以使用docker安装

cotun github

2.3、分别测试stun和turn

Coturn是集成了stun+turn协议。

测试网址: 在线测试地址

这里要注意下trickle­ice中的­在PDF转换后变为全角­。

3、音视频采集和播放

有三个案例:

  • 打开摄像头并将画面显示到页面;
  • 打开麦克风并在页面播放捕获的声音;
  • 同时打开摄像头和麦克风,并在页面显示画面和播放捕获的声音

3.1、 打开摄像头

实战:打开摄像头并将画面显示到页面,效果展示
在这里插入图片描述

3.1.1、代码流程

  1. 初始化button、video控件
  2. 绑定“打开摄像头”响应事件onOpenCamera
  3. 如果要打开摄像头则点击 “打开摄像头”按钮,以触发onOpenCamera事件的调用
  4. 当触发onOpenCamera调用时 a. 设置约束条件,即是getUserMedia函数的入参 b. getUserMedia有两种情况,一种是正常打开摄像头,使用handleSuccess处理;一种是打开摄像头失败,使用handleError处理 c. 当正常打开摄像头时,则将getUserMedia返回的stream对象赋值给video控件的srcObject即可将视频显示出来

3.1.2、示例代码

在这里插入图片描述

3.2、打开麦克风

实战:打开麦克风并在页面播放捕获的声音,效果展示

在这里插入图片描述

3.2.1、代码流程

  1. 初始化button、audio控件
  2. 绑定“打开麦克风”响应事件onOpenMicrophone
  3. 如果要打开麦克风则点击 “打开麦克风”按钮,以触发onOpenMicrophone事件的调用
  4. 当触发onOpenCamera调用时 a. 设置约束条件,即是getUserMedia函数的入参 b. getUserMedia有两种情况,一种是正常打开麦克风,使用handleSuccess处理;一种是打开麦克风失败,使用handleError处理 c. 当正常打开麦克风时,则将getUserMedia返回的stream对象赋值给audio控件的srcObject即可将声音播放出来

3.2.2、 示例代码

在这里插入图片描述

3.3、打开摄像头和麦克风

同时打开摄像头和麦克风,范例可以参考3.1,只是在约束条件中把
在这里插入图片描述
改为
在这里插入图片描述

3.3.1、具体代码

在这里插入图片描述

3.4、补充讲解

3.4.1、getUserMedia API简介

HTML5的getUserMedia API为用户提供访问硬件设备媒体(摄像头、视频、音频、地理位置等)的接口,基于该接口,开发者可以在不依赖任何浏览器插件的条件下访问硬件媒体设备。

getUserMedia API最初是navigator.getUserMedia,目前已被最新Web标准废除,变更为navigator.mediaDevices.getUserMedia(),但浏览器支持情况不如旧版API普及。

MediaDevices.getUserMedia()方法提示用户允许使用一个视频和/或一个音频输入设备,例如相机或屏幕共享和/或麦克风。如果用户给予许可,就返回一个Promise对象,MediaStream对象作为此Promise对象的Resolved[成功]状态的回调函数参数,相应的,如果用户拒绝了许可,或者没有媒体可用的情况下PermissionDeniedError或者NotFoundError作为此Promise的Rejected[失败]状态的回调函数参数。注意,由于用户不会被要求必须作出允许或者拒绝的选择,所以

返回的Promise对象可能既不会触发resolve也不会触发 reject。

3.4.2、浏览器兼容性

在这里插入图片描述

3.4.3、语法&参数

在这里插入图片描述
成功回调函数seccessCallback的参数stream:stream是MediaStream的对象,表示媒体内容的数据流,可以通过URL.createObjectURL转换后设置为Video或Audio元素的src属性来使用,部分较新的浏览器也可以直接设置为srcObject属性来使用(Darren注:目前大部分浏览器都是使用srcObject)。

失败回调函数errorCallback的参数error,可能的异常有:

  • AbortError:硬件问题
  • NotAllowedError:用户拒绝了当前的浏览器实例的访问请求;或者用户拒绝了当前会话的访问;或者用户在全局范围内拒绝了所有媒体访问请求。
  • NotFoundError:找不到满足请求参数的媒体类型。
  • NotReadableError:操作系统上某个硬件、浏览器或者网页层面发生的错误导致设备无法被访问。
  • OverConstrainedError:指定的要求无法被设备满足。
  • SecurityError:安全错误,在getUserMedia() 被调用的 Document上面,使用设备媒体被禁止。这个机制是否开启或者关闭取决于单个用户的偏好设置。TypeError:类型错误,constraints对象未设置[空],或者都被设置为false。

3.4.4、HTML 5调用媒体设备摄像头

这个例子中,请求访问用户硬件设备的摄像头,并把视频流通过Video元素显示出来。网页中提供一个"拍照"的按钮,

通过Canvas将Video的画面截取并绘制,核心代码如下:

  • HTML

在这里插入图片描述

  • JavaScript

在这里插入图片描述

3.4.5、WebRTC检测音视频设备

1、API说明

webrtc获取电脑所有音视频设备的API:enumerateDevices。获取成功后走then的方法,获取失败走catch的方法。

在这里插入图片描述

获取到的音视频设备信息包括:
在这里插入图片描述

2、代码示例完整代码范例

  • html在这里插入图片描述
  • js

在这里插入图片描述

4、WebRTC基础前站

1、该章节主要是对WebRTC相关的基础知识做补充。

2、一般情况下,在实时音视频通话传输音视频数据流时,我们并不直接将音视频数据流交给 UDP 传输,而是先 给音视频数据加个 RTP 头,然后再交给 UDP 进行传输。为什么要这样做呢?

3、我们以视频帧为例,一个 I 帧的数据量是非常大的,最少也要几十 K。而以太网的最大传输单元是多少呢?1.5K,所以要传输一个 I 帧需要几十个包。并且这几十个包传到对端后,还要重新组装成 I 帧,这样才能进行解码还原出一幅幅的图像。如果是我们自己实现的话,要完成这样的过程,至少需要以下几个标识。

  • 序号:用于标识传输包的序号,这样就可以知道这个包是第几个分片了。
  • 起始标记:记录分帧的第一个 UDP 包。
  • 结束标记:记录分帧的最后一个 UDP 包。

有了上面这几个标识字段,我们就可以在发送端进行拆包,在接收端将视频帧重新再组装起来了。

4.1、 RTP 协议

其实,这样的需求在很早之前就已经有了。因此,人们专门定义了一套规范,它就是RTP协议。下面让我们来详细看一下 RTP 协议吧。

在这里插入图片描述

如图所示,RTP 协议非常简单,我这里按字段的重要性从高往低的顺序讲解一下。

  • sequence number:序号,用于记录包的顺序。这与上面我们自己实现拆包、组包是同样的道理。
  • imestamp:时间戳,同一个帧的不同分片的时间戳是相同的。这样就省去了前面所讲的起始标记和结束标记。一定要记住,不同帧的时间戳肯定是不一样的。
  • PT:Payload Type,数据的负载类型。音频流的 PT 值与视频的 PT 值是不同的,通过它就可以知道这个包存放的是什么类型的数据。

这里,我并没有将 RTP 协议头中的所有字段的详细说明都列在这儿,如果你想了解所有字段的含义,可以到参考一节查看其他字段的含义。需要注意的是,这里没有将它们列出来并不代表它们不重要。恰恰相反,如果 你想做音视频传输相关的工作,RTP 头中的每个字段的含义你都必须全部清楚。

知道了上面这些字段的含义后,下面我们还是来看一个具体的例子吧!假设你从网上接收到一组音视频数据,如下:

在这里插入图片描述
假设 PT=98 是视频数据,PT=111 是音频数据,那么按照上面的规则你是不是很容易就能将视频帧组装起来呢?

4.1.1、字段详解

RTP 协议头:

在这里插入图片描述
RTP有效负载(载荷)类型,RTP Payload Type具体见: 《RTP有效负载(载荷)类型,RTP PayloadType》https://blog.csdn.net/caoshangpa/article/details/53008018

PT从 96 到 127 是动态值,有些负载类型由于诞生的较晚,没有具体的PT值,只能使用动态(dynamic)PT值。

4.2、RTCP 协议

在使用 RTP 包传输数据时,难免会发生丢包、乱序、抖动等问题,下面我们来看一下使用的网络一般都会在什么情况下出现问题:

  • 网络线路质量问题引起丢包率高;
  • 传输的数据超过了带宽的负载引起的丢包问题;
  • 信号干扰(信号弱)引起的丢包问题;
  • 跨运营商引入的丢包问题 ;

WebRTC 对这些问题在底层都有相应的处理策略,但在处理这些问题之前,它首先要让各端都知道它们自己的网络
质量到底是怎样的,这就是 RTCP 的作用。

RTCP 有两个最重要的报文:RR(Reciever Report)和 SR(Sender Report)。通过这两个报文的交换,各端就知道自己的网络质量到底如何了。

RTCP 支持的所有报文及其含义可以查看文章最后所附的参考一节。这里我们以SR 报文为例,看看 SR 报文中都包括哪些信息。

在这里插入图片描述

下面我就简要说明一下该报文中字段的含义:

  • V=2,指报文的版本。
  • P,表示填充位,如果该位置 1 ,则在 RTCP 报文的最后会有填充字节(内容是按字节对齐的)。
  • RC,全称 Report Count,指 RTCP 报文中接收报告的报文块个数。
  • PT=200,Payload Type,也就是说 SR 的值为 200 。

与 RTP 协议头一样,上面只介绍了 RTCP 头字段的含义,至于其他每个字段的含义请查看参考一节。同样的,对于RTCP 头中的每个字段也必须都非常清楚,只有这样以后你在看 WebRTC 带宽评估相关的代码时,才不至于晕头转向。

从上图中我们可以了解到,SR 报文分成三部分:Header、Sender info和Report block。在 NTP 时间戳之上的部分为 SR 报文的 Header 部分,SSRC_1 字段之上到 Header 之间的部分为 Sender info 部分,剩下的就是一个一个的Report Block 了。那这每一部分是用于干什么的呢?

  • Header 部分用于标识该报文的类型,比如是 SR 还是 RR。
  • Sender info 部分用于指明作为发送方,到底发了多少包。
  • Report block 部分指明发送方作为接收方时,它从各个 SSRC 接收包的情况。

通过以上的分析,你可以发现SR 报文并不仅是指发送方发了多少数据,它还报告了作为接收方,它接收到的数据的情况。当发送端收到对端的接收报告时,它就可以根据接收报告来评估它与对端之间的网络质量了,随后再根据网络质量做传输策略的调整。

SR 报文与RR 报文无疑是 RTCP 协议中最重要的两个报文,不过 RTCP 中的其他报文也都非常重要的,如果你想学好 WebRTC ,那么 RTCP 中的每个报文你都必须掌握。

比如,RTCP 类型为 206 、子类型为 4 的 FIR 报文,其含义是 Full Intra Request (FIR) Command,即完整帧请求命令。它起什么作用?又在什么时候使用呢?

该报文也是一个特别关键的报文,我为什么这么说呢?试想一下,在一个房间里有 3 个人进行音视频聊天,然后又有一个人加入到房间里,这时如果不做任何处理的话,那么第四个人进入到房间后,在一段时间内很难直接看到其他三个人的视频画面了,这是为什么呢?

原因就在于解码器在解码时有一个上下文。在该上下文中,必须先拿到一个 IDR 帧之后才能将其后面的 P 帧、B 帧进行解码。也就是说,在没有 IDR 帧的情况下,对于收到的 P 帧、B 帧解码器只能干瞪眼了。

如何解决这个问题呢?这就引出了 FIR 报文。当第四个人加入到房间后,它首先发送 FIR 报文,当其他端收到该报文后,便立即产生各自的 IDR 帧发送给新加入的人,这样当新加入的人拿到房间中其他的 IDR 帧后,它的解码器就会解码成功,于是其他人的画面也就一下子全部展示出来了。所以你说它是不是很重要呢?

在这里插入图片描述

4.3、媒体协商之sdp

在这里插入图片描述

比如:Peer­A端可支持VP8、H264多种编码格式,而Peer-B端支持VP9、H264,要保证二端都正确的编解码,最简单的办法就是取它们的交集H264

注:有一个专门的协议 ,称为Session Description Protocol (SDP),可用于描述上述这类信息,在WebRTC中,参与视频通讯的双方必须先交换SDP信息,这样双方才能知根知底,而交换SDP的过程,也称为"媒体协商"。

SDP(Session Description Protocal)以文本描述各端(PC 端、Mac 端、Android 端、iOS 端等)的能力,这里的能力指的是各端所支持的:

  1. 音频编解码器是什么,这些编解码器设定的参数是什么
  2. 使用的传输协议是什么
  3. 以及包括的音视频媒体是什么等等。

一个sdp片段举例

在这里插入图片描述
如上面的 SDP 片段所示,该 SDP 中描述了一路音频流,即m=audio,该音频支持的 Payload ( 即数据负载 ),类型包括 111 、 103 、 104 等等。

在该 SDP 片段中又进一步对 111 、 103 、 104 等 Payload 类型做了更详细的描述,如 a=rtpmap:111,opus/48000/2 表示 Payload 类型为 111 的数据是 OPUS 编码的音频数据,并且它的采样率是 48000 ,使用双声道。以此类推,你也就可以知道 a=rtpmap:104 ISAC/32000 的含义是音频数据使用 ISAC 编码,采样频率是 32000 ,使用单声道。

交换 SDP 信息,下面是 1 对 1 WebRTC 处理过程图:

在这里插入图片描述
如上图所示,两个客户端 / 浏览器进行 1 对 1 通话时,首先要进行信令交互,而交互的一个重要信息就是SDP 的交换。

交换 SDP 的目的是为了让对方知道彼此具有哪些能力,然后根据双方各自的能力进行协商,协商出大家认可的音视频编解码器、编解码器相关的参数(如音频通道数,采样率等)、传输协议等信息。

如上图所示,Amy 与 Bob进行通讯,它们先各自在 SDP 中记录自己支持的音频参数、视频参数、传输协议等信息,然后再将自己的 SDP 信息通过信令服务器发送给对方。当一方收到对端传来的 SDP 信息后,它会将接收到的 SDP 与自己的 SDP 进行比较,并取出它们之间的交集,这个交集就是它们协商的结果,也就是它们最终使用的音视频参数及传输协议了。

4.3.1、标准 SDP 规范

了解了 SDP 是什么,接下来我们看一下 SDP 规范。其实单论 SDP 规范它并不复杂,但 WebRTC 使用时又对其做了不少修改,所以当你初见完整的 WebRTC 的 SDP 时,可能会一脸茫然。

标准 SDP 规范主要包括SDP 描述格式和SDP 结构,而 SDP 结构由会话描述和媒体信息描述两个部分组成。其中,媒体信息描述是整个 SDP 规范中最重要的知识,它又包括了:

  • 媒体类型
  • 媒体格式
  • 传输协议
  • 传输的 IP 和端口

下面我们就以上这些知识逐一进行讲解。

1、SDP 的格式
SDP 是由多个 = 这样的表达式组成的。其中,是一个字符,是一个字符串。需要特别注意的是,“=” 两边是不能有空格的。如下所示:

在这里插入图片描述
SDP 由一个会话级描述(session level description)和多个媒体级描述(media level description)组成。

  • 会话级(session level)的作用域是整个会话,其位置是从 v= 行开始到第一个媒体描述为止。
  • 媒体级(media level)是对单个的媒体流进行描述,其位置是从 m= 行开始到下一个媒体描述(即下一个m=)为止。

另外,除非媒体部分重新对会话级的值做定义,否则会话级的值就是各个媒体的缺省默认值。让我们看个例子吧。

在这里插入图片描述
面是一个特别简单的例子,每一行都是以一个字符开头,后面紧跟着等于号(=),等于号后面是一串字符。

从“v=”开始一直到“m=audio”,这之间的描述是会话级的;而后面的两个“m=”为媒体级。从中可以看出,在该SDP 描述中有两个媒体流,一个是音频流,另一个是视频流。

4.3.2、SDP 的结构

了解了 SDP 的格式,下面我们来看一下 SDP 的结构,它由会话描述和媒体描述两部分组成。

4.3.2.1、会话描述

会话描述的字段比较多,下面四个字段比较重要,我们来重点介绍一下。
第一个,v=(protocol version,必选)。例子:v=0 ,表示 SDP 的版本号,但不包括次版本号。

第二个,o=(owner/creator and session identifier,必选)。例子:

o = <username> <session id><version> <network type> <address type> <address>

,该例子是对一个会话发起者的描述。其中,

  • o= 表示的是对会话发起者的描述;
  • <username>:用户名,当不关心用户名时,可以用 “-” 代替 ;
  • <session id> :数字串,在整个会话中,必须是唯一的,建议使用 NTP 时间戳;
  • <version>:版本号,每次会话数据修改后,该版本值会递增;
  • <network type> :网络类型,一般为“IN”,表示“internet”;
  • <address type>:地址类型,一般为 IP4;
  • <address>:IP 地址。

第三个,Session Name(必选)。例子:s=

<session name>

,该例子表示一个会话,在整个 SDP 中有且只 有一个会话,也就是只有一个 s=。

第四个,t=(time the session is active,必选)。例子:

t=<start time> <stop time>

,该例子描述了会话的开始时间和结束时间。其中,

 <start time>

<stop time>

为 NTP 时间,单位是秒;当

<start time>

 <stop time>

均为零时,表示持久会话。

4.3.2.2、媒体描述

在这里插入图片描述
在这里插入图片描述

从上面的例子中,你可以清楚地看到在这段 SDP 片段里包括会话信息与媒体信息。在媒体信息中又包括了音频流信息和视频流信息。

在音频流和视频流信息中,通过 rtpmap 属性对它们做了进一步的说明。如音频流支持 OPUS 和 ISAC 编码,OPUS 编码的采样率是 48000 ,双声道,而 ISAC 编码的采样率可以是 16000 或 32000 , 它们都是单声道。

视频流支持 VP8,采样率是 90000 (实质是指时钟信息) 。

4.3.3、 WebRTC 中的 SDP

WebRTC 对标准 SDP 规范做了一些调整,更详细的信息可以看这里,它将 SDP 按功能分成几大块:

  • Session Metadata,会话元数据
  • Network Description,网络描述
  • Stream Description,流描述
  • Security Descriptions,安全描述
  • Qos Grouping Descriptions, 服务质量描述

下面这张图清晰地表达了它们之间的关系:

在这里插入图片描述

通过上图我们可以看出,WebRTC 按功能将 SDP 划分成了五部分,即会话元数据、网络描述、流描述、安全 描述以及服务质量描述。WebRTC SDP 中的会话元数据(Session Metadata)其实就是 SDP 标准规范中的 会话层描述;流描述、网络描述与 SDP 标准规范中的媒体层描述是一致的;而安全描述与服务质量描述都是新增的一些属性描述。下图我们来看一个具体的例子:

在这里插入图片描述

上面的 SDP 片段是摘取的 WebRTC SDP 中的安全描述与服务质量描述,这两块描述在标准 SDP 规范中没有明确定义,它更多属于 WebRTC 业务的范畴。
其中,安全描述起到两方面的作用,一方面是进行网络连通性检测时,对用户身份进行认证;另一方面是收发数据时,对用户身份的认证,以免受到对方的攻击。从中可以看出 WebRTC 对安全有多重视了服务质量描述指明启动哪些功能以保证音视频的质量,如启动带宽评估,当用户发送数据量太大超过评估的带宽时,要及时减少数据包的发送;启动防拥塞功能,当预测到要发生拥塞时,通过降低流量的方式防止拥塞的发生等等,这些都属于服务质量描述的范畴。

为便于你更好地理解和使用 SDP,接下来我再分享一个真实的例子。

下面这段 SDP 是我从一个真实的 1 对 1 场景中截取出来的 WebRTC SDP 的片段。并对 SDP 上做了详细的注释,通过上面知识的学习,现在你应该也可以看懂这段 SDP 的内容了。

在这里插入图片描述
从上面这段 SDP 中你应该可以总结出:SDP 是由一个会话层和多个媒体层组成的;而对于每个媒体层,WebRTC 又将其细划为四部分,即媒体流、网络描述、安全描述和服务质量描述。并且在上面的例子中有两个媒体层——音频媒体层和视频媒体层,而对于每个媒体层,也都有对应的媒体流描述、网络描述、安全描述及服务质量描述

补充引申

RFC3551(RTP/AVP)在RFC3550的基础上针对RTP档次进行补充形成RTP/APVP档次,被用在具有最小会话控制的音视频会议中,是其它扩展档次的基础。该档次在没有参数协商和成员控制的会话中非常有用。该档次也为音视频定义一系列编码和负载格式。对于具体的流媒体负载格式,IETF也定义一系列协议详细描述,如VP8视频负载格式和H264视频负载格式,等等

4.4、Candidate

在讲解 WebRTC 建立连接的过程之前,你有必要先了解一个基本概念,即 ICE Candidate (ICE 候选者)。
它表示 WebRTC 与远端通信时使用的协议、IP 地址和端口,一般由以下字段组成:

  • 本地 IP 地址
  • 本地端口号
  • 候选者类型,包括 host、srflx 和 relay
  • 优先级
  • 传输协议
  • 访问服务的用户名

如果用一个结构表示,那么它就如下面所示的样子:

在这里插入图片描述

其中,候选者类型中的:

  • host 表示本机候选者
  • srflx 表示内网主机映射的外网的地址和端口
  • relay 表示中继候选者。

当 WebRTC 通信双方彼此要进行连接时,每一端都会提供许多候选者,比如你的主机有两块网卡,那么每块网卡的不同端口都是一个候选者。

WebRTC 会按照上面描述的格式对候选者进行排序,然后按优先级从高到低的顺序进行连通性测试,当连通性测试成功后,通信的双方就建立起了连接。

在众多候选者中,host 类型的候选者优先级是最高的。在 WebRTC 中,首先对 host 类型的候选者进行连通性检测,如果它们之间可以互通,则直接建立连接。其实,host 类型之间的连通性检测就是内网之间的连通性检测。WebRTC 就是通过这种方式巧妙地解决了大家认为很困难的问题。

同样的道理,如果 host 类型候选者之间无法建立连接,那么 WebRTC 则会尝试次优先级的候选者,即 srflx类型的候选者。也就是尝试让通信双方直接通过 P2P 进行连接,如果连接成功就使用 P2P 传输数据;如果失败,就最后尝试使用 relay 方式建立连接。

4.4.1、收集 Candidate

了解了什么是 Candidate 之后,接下来,我们再来看一下端对端的连接是如何建立的吧。

实际上,端对端的建立更主要的工作是Candidate 的收集。WebRTC 将 Candidate 分为三种类型:

  • host 类型,即本机内网的 IP 和端口;
  • srflx 类型, 即本机 NAT 映射后的外网的 IP 和端口;
  • relay 类型,即中继服务器的 IP 和端口。

其中,host 类型优先级最高,srflx 次之,relay 最低(前面我们已经说明过了)。在以上三种 Candidate 类型中,host 类型的 Candidate 是最容易收集的,因为它们都是本机的 IP 地址和端口。对于 host 类型的 Candidate 这里就不做过多讲解了,下面我们主要讲解一下 srflx 和 relay 这两种类型的 Candidate 的收集。

4.4.1.1、STUN 协议

srflx 类型的 Candidate 实际上就是内网地址和端口经NAT映射后的外网地址和端口。如下图所示:

在这里插入图片描述

你应该知道,如果主机没有公网地址,是无论如何都无法访问公网上的资源的。例如你要通过百度搜索一些信息,如
果你的主机没有公网地址的话,百度搜索到的结果怎么传给你呢?

而一般情况下,主机都只有内网 IP 和端口,那它是如何访问外网资源的呢?实际上,在内网的网关上都有 NAT (Net Address Transport) 功能,NAT 的作用就是进行内外网的地址转换。这样当你要访问公网上的资源时,NAT 首先会将该主机的内网地址转换成外网地址,然后才会将请求发送给要访问的服务器;服务器处理好后将结果返回给主机的公网地址和端口,再通过 NAT 最终中转给内网的主机。

知道了上面的原理,你要想让内网主机获得它的外网 IP 地址也就好办了,只需要在公网上架设一台服务器,并向这台服务器发个请求说: “Hi!伙计,你看我是谁?”对方回: “你不是那 xxxx 吗?”这样你就可以知道自己的公网 IP了,是不是很简单?

实际上,上面的描述已经被定义成了一套规范,即 RFC5389 ,也就是 STUN 协议,我们只要遵守这个协议就可以拿到自己的公网 IP 了。

这里我们举个例子,看看通过 STUN 协议,主机是如何获取到自己的外网 IP 地址和端口的。

  • 首先在外网搭建一个 STUN 服务器,现在比较流行的 STUN 服务器是coturn,你可以到 GitHub 上自己下载源码编译安装。
  • 当 STUN 服务器安装好后,从内网主机发送一个 binding request 的 STUN 消息到 STUN 服务器。
  • STUN 服务器收到该请求后,会将请求的 IP 地址和端口填充到 binding response 消息中,然后顺原路将该消息返回给内网主机。此时,收到 binding response 消息的内网主机就可以解析 binding response 消息了,并可以从中得到自己的外网 IP 和端口。
4.4.1.2、TURN 协议

这里需要说明一点,relay 服务是通过 TURN 协议实现的。所以我们经常说的 relay 服务器或 TURN 服务器它们是同一个意思,都是指中继服务器。

咱们言归正转,知道了内网主机如何通过 STUN 协议获取到 srflx 类型的候选者后,那么中继类型候选者,即 relay
型的 Candidate 又是如何获取的呢?下面我们就来看一下。

首先你要清楚,relay 型候选者的优先级与其他类型相比是最低的,但在其他候选者都无法连通的情况下,relay 候选者就成了最好的选择。因为它的连通率是所有候选者中连通率最高的。

其实,relay 型候选者的获取也是通过 STUN 协议完成的,只不过它使用的 STUN 消息类型与获取 srflx 型候选者的STUN 消息的类型不一样而已。

RFC5766 的 TURN 协议描述了如何获取 relay 服务器(即 TURN 服务器)的 Candidate 过程。其中最主要的是Allocation 指令。通过向 TURN 服务器发送 Allocation 指令,relay 服务就会在服务器端分配一个新的 relay 端口,用于中转 UDP 数据报。

不过这里我只是简要描述了下,如果你对这块感兴趣的话,可以直接查看 RFC5766 以了解更多的细节。

4.4.2、NAT 打洞 /P2P 穿越

当收集到 Candidate 后,WebRTC 就开始按优先级顺序进行连通性检测了。它首先会判断两台主机是否处于同一个局域网内,如果双方确实是在同一局域网内,那么就直接在它们之间建立一条连接。

但如果两台主机不在同一个内网,WebRTC 将尝试NAT 打洞,即 P2P 穿越。在 WebRTC 中,NAT 打洞是极其复杂的过程,它首先需要对 NAT 类型做判断,检测出其类型后,才能判断出是否可以打洞成功,只有存在打洞成功的可能性时才会真正尝试打洞。

WebRTC 将 NAT 分类为 4 种类型,分别是:

  • 完全锥型 NAT
  • IP 限制型 NAT
  • 端口限制型 NAT
  • 对称型 NAT

而每种不同类型的 NAT 的详细介绍我们将在下一篇关于 NAT 穿越原理一文中进行讲解,现在你只要知道 NAT 分这 4 种类型就好了。另外,需要记住的是,对称型 NAT 与对称型 NAT 是无法进行 P2P 穿越的;而对称型 NAT 与端口限制型 NAT 也是无法进行 P2P 连接的。

4.4.3、ICE

了解了上面的知识后,你再来看 ICE 就比较简单了。其实 ICE 就是上面所讲的获取各种类型 Candidate 的过程,也就是:

  1. 在本机收集所有的 host 类型的 Candidate
  2. 通过 STUN 协议收集 srflx 类型的 Candidate
  3. 通过 TURN 协议收集 relay 类型的 Candidate

在整个过程中,WebRTC 使用优先级的方法去建立连接,即局域网内的优先级最高,其次是 NAT 穿越,再次是通过中继服务器进行中转,这样就巧妙地实现了“既要高效传输,又能保证连通率”这个目标。

5、 实现音视频一对一通话

5.1、一对一通话原理

对于我们WebRTC应用开发人员而言,主要是关注RTCPeerConnection类,我们以( 1 )信令设计;( 2 )媒体协商;( 3 )加入Stream/Track;( 4 )网络协商 四大块继续讲解通话原理

在这里插入图片描述

5.2、信令协议设计

采用json封装格式

  1. join 加入房间
  2. resp­join 当join房间后发现房间已经存在另一个人时则返回另一个人的uid;如果只有自己则不返回
  3. leave 离开房间,服务器收到leave信令则检查同一房间是否有其他人,如果有其他人则通知他有人离开
  4. new­peer 服务器通知客户端有新人加入,收到new­peer则发起连接请求
  5. peer­leave 服务器通知客户端有人离开
  6. offer 转发offer sdp
  7. answer 转发answer sdp
  8. candidate 转发candidate sdp

在这里插入图片描述

5.3、媒体协商

在这里插入图片描述
首先,呼叫方创建 Offer 类型的 SDP 消息。创建完成后,调用 setLocalDescriptoin 方法将该 Offer 保存到本地 Local 域,然后通过信令将 Offer 发送给被呼叫方。

被呼叫方收到 Offer 类型的 SDP 消息后,调用 setRemoteDescription 方法将 Offer 保存到它的 Remote 域。作为应答,被呼叫方要创建 Answer 类型的 SDP 消息,Answer 消息创建成功后,再调用 setLocalDescription方法将 Answer 类型的 SDP 消息保存到本地的 Local 域。最后,被呼叫方将 Answer 消息通过信令发送给呼叫方。至此,被呼叫方的工作就完部完成了。

接下来是呼叫方的收尾工作,呼叫方收到 Answer 类型的消息后,调用 RTCPeerConnecton 对象的setRemoteDescription 方法,将 Answer 保存到它的 Remote 域。

至此,整个媒体协商过程处理完毕。

当通讯双方拿到彼此的 SDP 信息后,就可以进行媒体协商了。媒体协商的具体过程是在 WebRTC 内部实现的,我们就不去细讲了。你只需要记住本地的 SDP 和远端的 SDP 都设置好后,协商就算成功了。

createOffer 创建Offer

在这里插入图片描述

显示远端媒体流

5.4、RTCPeerConnection补充

在这里插入图片描述

5.5、实现WebRTC音视频通话

这篇博客已经完成了,大家可以参考一下 websocket 局域网 webrtc 一对一 多对多 视频通话 的示例

在这里插入图片描述

在这里插入图片描述

上图的命令是

# sar  命令在 sysstat 包下
yum install sysstat -y 
sar -n DEV 1

5.6、关于只能使用 localhots或者127.0.0.1问题

1、直接参考 关于使用通过ip访问网站无法使用多媒体,MediaDevices.getUserMedia()为undefined的解决方案–(亲测可用)。

标签: webrtc

本文转载自: https://blog.csdn.net/qq_38263083/article/details/143108378
版权归原作者 灵泽~ 所有, 如有侵权,请联系我们删除。

“WebRTC入门”的评论:

还没有评论