WebRTC 学习

WebRTC 学习

Posted by wangdiqi on August 14, 2022

基础知识

协议

在 WebRTC 中, RTP/RTCP 只是众多协议中的比较重要的两个,还有 SRTP/SRTCP、DTLS、STUN/TURN 协议等

WebRTC 的 RTCDataChannel 使用的传输协议为 SCTP (Stream Control Transport Protocol)

SDP

//////////////////// 会话描述
// v=(protocol version) SDP 版本号,不包括次版本号
v=0
// o=(owner/creator and session identifier) 会话发起人者的描述
// o=<username> <session id> <version> <network type> <address type> <address>
// <username>:用户名,当不关心用户名时,可以用 “-” 代替 ;
// <session id> :数字串,在整个会话中,必须是唯一的,建议使用 NTP 时间戳;
// <version>:版本号,每次会话数据修改后,该版本值会递增;
// <network type> :网络类型,一般为“IN”,表示“internet”;
// <address type>:地址类型,一般为 IP4;
// <address>:IP 地址。
o=- 7017624586836067756 2 IN IP4 127.0.0.1
// s=<session name>,表示一个会话,在整个SDP中有且只有一个会话
s=-
// t=(time the session is active)。描述会话的开始时间和结束后时间。NTP 时间,单位是秒;当 <start time> 和 <stop time> 均为零时,表示持久会话
// t=<start time> <stop time>。
t=0 0

//////////////////// 媒体描述
//////////////////// 下面 m= 开头的两行,是两个媒体流:一个音频,一个视频。
// m=(media name and transport address,可选)。
// m=<media> <port> <transport> <fmt list>
// <media>:媒体类型,比如 audio/video 等;
// <port>:端口;
// <transport>:传输协议,有两种——RTP/AVP 和 UDP;
// <fmt list>:媒体格式,即数据负载类型 (Payload Type) 列表。
m=audio 9 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126
...
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 100 101 102 122 127 121 125 107 108 109 124 120 123 119 114 115 116
...
// a=*(zero or more media attribute lines,可选)。
// a=<TYPE> 或 a=<TYPE>:<VALUES>, 表示属性,用于进一步描述媒体信息;在例子中,指属性的类型, a= 有两个特别的属性类型,即下面要介绍的 rtpmap 和 fmtp
// rtpmap(可选)。例子:a=rtpmap:<payload type> <encoding name>/<sample rate>[/<encodingparameters>]。
// rtpmap 是 rtp 与 map 的结合,即 RTP 参数映射表。
// <payload type> :负载类型,对应 RTP 包中的音视频数据负载类型。
// <encoding name>:编码器名称,如 VP8、VP9、OPUS 等。
// <sample rate>:采样率,如音频的采样率频率 32000、48000 等。
// <encodingparameters>:编码参数,如音频是否是双声道,默认为单声道。
a=rtpmap:111 opus/48000/2
a=rtpmap:103 ISAC/16000
a=rtpmap:104 ISAC/32000
// fmtp。例子:a=fmtp:<payload type> <format specific parameters>。
// fmtp,格式参数,即 format  parameters;
// <payload type> ,负载类型,同样对应 RTP 包中的音视频数据负载类型;
// < format specific parameters>指具体参数。
a=fmtp:111 minptime=10;useinbandfec=1 //对格式参数的描述
...

webrtc sdp

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


//=============会话描述====================
v=0 
o=- 7017624586836067756 2 IN IP4 127.0.0.1
s=-
t=0 0
...

//================媒体描述=================
//================音频媒体=================
/*
 * 音频使用端口1024收发数据
 * UDP/TLS/RTP/SAVPF 表示使用 dtls/srtp 协议对数据加密传输
 * 111、103 ... 表示本会话音频数据的 Payload Type
 */
 m=audio 1024 UDP/TLS/RTP/SAVPF 111 103 104 9 0 8 106 105 13 126 

//==============网络描述==================
//指明接收或者发送音频使用的IP地址,由于WebRTC使用ICE传输,这个被忽略。
c=IN IP4 0.0.0.0
//用来设置rtcp地址和端口,WebRTC不使用
a=rtcp:9 IN IP4 0.0.0.0
...

//==============音频安全描述================
//ICE协商过程中的安全验证信息
a=ice-ufrag:khLS
a=ice-pwd:cxLzteJaJBou3DspNaPsJhlQ
a=fingerprint:sha-256 FA:14:42:3B:C7:97:1B:E8:AE:0C2:71:03:05:05:16:8F:B9:C7:98:E9:60:43:4B:5B:2C:28:EE:5C:8F3:17
...

//==============音频流媒体描述================
a=rtpmap:111 opus/48000/2
//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
...

//=================视频媒体=================
m=video 9 UDP/TLS/RTP/SAVPF 100 101 107 116 117 96 97 99 98
...
//=================网络描述=================
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
...
//=================视频安全描述=================
a=ice-ufrag:khLS
a=ice-pwd:cxLzteJaJBou3DspNaPsJhlQ
a=fingerprint:sha-256 FA:14:42:3B:C7:97:1B:E8:AE:0C2:71:03:05:05:16:8F:B9:C7:98:E9:60:43:4B:5B:2C:28:EE:5C:8F3:17
...

//================视频流描述===============
a=mid:video
...
a=rtpmap:100 VP8/90000
//================服务质量描述===============
a=rtcp-fb:100 ccm fir
a=rtcp-fb:100 nack //支持丢包重传,参考rfc4585
a=rtcp-fb:100 nack pli
a=rtcp-fb:100 goog-remb //支持使用rtcp包来控制发送方的码流
a=rtcp-fb:100 transport-cc
...

NAT

类型(A-B) 建立状况 完全锥型-完全锥型 A通过server获得B的IP:port开始通信 完全锥型-IP限制型 B通过server获得A的IP:port开始通信 完全锥型-port限制型 B通过server获得A的IP:port开始通信 完全锥型-对称型 B通过server获得A的IP:port开始通信 IP限制型-IP限制型 A通过server获得B的IP:port,A向B发送UDP包,数据通过自己的NAT时会为B建立NAT映射条目;B通过server获得A的IP:port,发送UDP包为A建立NAT映射条目,二者之后就可以开始通信。 IP限制型-port限制型 A通过server获得B的IP:port,A向B发送UDP包,数据通过自己的NAT时会为B建立NAT映射条目;B通过server获得A的IP:port,发送UDP包为A建立NAT映射条目,二者之后就可以开始通信。 IP限制型-对称型 A通过server获得B的IP:port1,A向B发送UDP包,数据通过自己的NAT时会为B建立NAT映射条目;B通过server获得A的IP:port,发送UDP包为A建立NAT映射条目,A收到B的UDP包,获得B新的IP:port2; A向B的新地址IP:port2发送数据,可以开始通信。 port限制型-对称型 A通过server获得B的IP:port1,A向B发送UDP包,数据通过自己的NAT时会为B建立NAT映射条目;B通过server获得A的IP:port,发送UDP包为A建立NAT映射条目,但由于B更换了新端口port2,A刚建立的映 射无法使用,A故此无法收到B为A通信使用的新端口,无法建立NAT映射,两者无法互通。 对称型-对称型 同上,对称型开启新端口对方无法获悉,无法建立链接。无法P2P就需要用TURN服务器转发数据了

音视频采集基本概念

摄像头。用于捕捉(采集)图像和视频。

帧率。现在的摄像头功能已非常强大,一般情况下,一秒钟可以采集 30 张以上的图像,一些好的摄像头甚至可以采集 100 张以上。我们把摄像头一秒钟采集图像的次数称为帧率。帧率越高,视频就越平滑流畅。然而,在直播系统中一般不会设置太高的帧率,因为帧率越高,占的网络带宽就越多。

分辨率。摄像头除了可以设置帧率之外,还可以调整分辨率。我们常见的分辨率有 2K、1080P、720P、420P 等。分辨率越高图像就越清晰,但同时也带来一个问题,即占用的带宽也就越多。所以,在直播系统中,分辨率的高低与网络带宽有紧密的联系。也就是说,分辨率会跟据你的网络带宽进行动态调整。

宽高比。分辨率一般分为两种宽高比,即 16:9 或 4:3。4:3 的宽高比是从黑白电视而来,而 16:9 的宽高比是从显示器而来。现在一般情况下都采用 16:9 的比例。

麦克风。用于采集音频数据。它与视频一样,可以指定一秒内采样的次数,称为采样率。每个采样用几个 bit 表示,称为采样位深或采样大小。

轨(Track)。WebRTC 中的“轨”借鉴了多媒体的概念。火车轨道的特性你应该非常清楚,两条轨永远不会相交。“轨”在多媒体中表达的就是每条轨数据都是独立的,不会与其他轨相交,如 MP4 中的音频轨、视频轨,它们在 MP4 文件中是被分别存储的。

流(Stream)。可以理解为容器。在 WebRTC 中,“流”可以分为媒体流(MediaStream)和数据流(DataStream)。其中,媒体流可以存放 0 个或多个音频轨或视频轨;数据流可以存 0 个或多个数据轨。

音频

音频有采样率和采样大小的概念

音频输入设备的主要工作是采集音频数据,而采集音频数据的本质就是模数转换(A/D),即将模似信号转换成数字信号。

模数转换使用的采集定理称为奈奎斯特定理,其内容如下: 在进行模拟 / 数字信号的转换过程中,当采样率大于信号中最高频率的 2 倍时,采样之后的数字信号就完整地保留了原始信号中的信息。

人类听觉范围的频率是 20Hz~20kHz 之间。对于日常语音交流(像电话),8kHz 采样率就可以满足人们的需求。但为了追求高品质、高保真,你需要将音频输入设备的采样率设置在 40kHz 以上,这样才能完整地将原始信号保留下来

采集到的数据再经过量化、编码,最终形成数字信号,这就是音频设备所要完成的工作。在量化和编码的过程中,采样大小(保存每个采样的二进制位个数)决定了每个采样最大可以表示的范围。如果采样大小是 8 位,则它表示的最大值是就是 28 -1,即 255;如果是 16 位,则其表示的最大数值是 65535。

视频

当实物光通过镜头进行到摄像机后,它会通过视频设备的模数转换(A/D)模块,即光学传感器, 将光转换成数字信号,即 RGB(Red、Green、Blue)数据。

获得 RGB 数据后,还要通过 DSP(Digital Signal Processer)进行优化处理,如自动增强、白平衡、色彩饱和等都属于这一阶段要做的事情。通过 DSP 优化处理后,你就得到了 24 位的真彩色图片。因为每一种颜色由 8 位组成,而一个像素由 RGB 三种颜色构成,所以一个像素就需要用 24 位表示,故称之为 24 位真彩色。

另外,此时获得的 RGB 图像只是临时数据。因最终的图像数据还要进行压缩、传输,而编码器一般使用的输入格式为 YUV I420,所以在摄像头内部还有一个专门的模块用于将 RGB 图像转为 YUV 格式的图像。

web 客户端

设置采集设备的参数 const mediaStreamContrains = { video: { frameRate: {min: 20}, width: {min: 640, ideal: 1280}, height: {min: 360, ideal: 720}, aspectRatio: 16/9 }, audio: { echoCancellation: true, noiseSuppression: true, autoGainControl: true } };

多对多通信

Mesh

即多个终端之间两两进行连接,形成一个网状结构。
比如 A、B、C 三个终端进行多对多通信,当 A 想要共享媒体(比如音频、视频)时,它需要分别向 B 和 C 发送数据。
同样的道理,B 想要共享媒体,就需要分别向 A、C 发送数据,依次类推。这种方案对各终端的带宽要求比较高

MCU (Multipoint Conferencing Unit)

该方案由一个服务器和多个终端组成一个星形结构。
各终端将自己要共享的音视频流发送给服务器,服务器端会将在同一个房间中的所有终端的音视频流进行混合,最终生成一个混合后的音视频流再发给各个终端,这样各终端就可以看到 / 听到其他终端的音视频了。
实际上服务器端就是一个音视频混合器,这种方案服务器的压力会非常大

SFU (Selective Forwarding Unit)

该方案也是由一个服务器和多个终端组成,但与 MCU 不同的是,SFU 不对音视频进行混流,收到某个终端共享的音视频流后,就直接将该音视频流转发给房间内的其他终端。它实际上就是一个音视频路由转发器。

开源项目

Licode (https://github.com/lynckia/licode)

既可以用作 SFU 类型的流媒体服务器,也可以用作 MCU 类型的流媒体服务器。

Licode 不仅仅是一个流媒体通信服务器,而且还是一个包括了媒体通信层、业务层、用户管理等功能的完整系统,并且该系统还支持分布式部署。

Licode 是由 C++ 和 Node.js 语言实现。其中,媒体通信部分由 C++ 语言实现,而信令控制、用户管理、房间管理用 Node.js 实现。包括客户端的 SDK 等

缺点:

  1. 在 linux 下目前只支持 ubuntu 版本,在其他版本上很难编译通过
  2. Licode 不仅包括了 SFU,而且包括了 MCU,所以它的代码结构比较重,学习和掌握它要花不少的时间
  3. Licode 的性能一般。

Janus-gateway (https://github.com/meetecho/janus-gateway)

Janus 是一个非常有名的 WebRTC 流媒体服务器,它是以 Linux 风格编写的服务程序,采用 C 语言实现,支持 Linux/MacOS 下编译、部署,但不支持 Windows 环境。

支持插件

传输层包括媒体数据传输和信令传输:

  1. 媒体数据传输层主要实现了 WebRTC 中需要有流媒体协议及其相关协议,如 DTLS 协议、ICE 协议、SDP 协议、RTP 协议、SRTP 协议、SCTP 协议等。

  2. 信令传输层用于处理 Janus 的各种信令,它支持的传输协议包括 HTTP/HTTPS、WebSocket/WebSockets、NanoMsg、MQTT、PfUnix、RabbitMQ。
    不过需要注意的是,有些协议是可以通过编译选项来控制是否安装的,也就是说这些协议并不是默认全部安装的。另外,Janus 所有信令的格式都是采用 Json 格式

缺点:

  1. 架构设计比较复杂,初学者难度较大

mediasoup (https://github.com/versatica/mediasoup/)

Mediasoup 是推出时间不长的 WebRTC 流媒体服务器开源库。

Mediasoup 由应用层和数据处理层组成。应用层是通过 Node.js 实现的;数据处理层由 C++ 语言实现,包括 DTLS 协议实现、ICE 协议实现、SRTP/SRTCP 协议实现、路由转发等。

Mediasoup 的实现逻辑非常清晰,它不关心上层应用该如何做,只关心底层数据的传输,并将它做到极致。
Mediasoup 底层使用 C++ 开发,使用 libuv 作为其异步 IO 事件处理库,所以保证了其性能的高效性。同时它支持了几乎所有 WebRTC 为了实时传输做的各种优化,所以说它是一个特别优秀的 WebRTC SFU 流媒体服务器。
它与 Janus 相比,它更聚焦于数据传输的实时性、高效性、简洁性,而 Janus 相比 Mediasoup 做的事儿更多,架构和逻辑也更加复杂.

Medooze (https://github.com/medooze/media-server)

Medooze 是一款综合流媒体服务器,它不仅支持 WebRTC 协议栈,还支持很多其他协议,如 RTP、RTMP 等

缺点:

  1. 尽管 Medooze 也是 C++ 开发的流媒体服务务器,使用了异步 IO 事件处理机制,但它使用的异步 IO 事件处理的 API 是 poll,poll 在处理异步 IO 事件时,与 Linux 下最强劲的异步 IO 事件 API epoll 相比要逊色不少,这导致它在接收 / 发送音视频包时性能比 Mediasoup 要稍差一些