测试

425 阅读24分钟

WebRTC简介

WebRTC是什么

WebRTC名称源自网页即时通信(Web Real-Time Communication)的缩写,是一个支持Web浏览器进行音视频对话的解决方案,它于2011年6月1号由Google等开源并被纳入W3C推荐标准。它通过简单的API为Web浏览器和移动应用程序提供实时通信(RTC)功能

WebRTC优缺点

WebRTC主要应用于实时通信方面,优点主要如下:

  • Google开源框架、免费
  • 跨平台,可以在Web、Android、IOS、Window、MacOS、LInux运行、主流浏览器支持,Chrome、Safari、Firefox,免插件,打开网页即可使用
  • 实时传输:低延迟传输、毫秒级别延迟,适合实时性要求较高的应用场景
  • 强大的P2P打洞能力,WebRTC提供了STUN、ICE、TURN的关键NAT和防火墙穿透技术

然而,WebRTC也有缺点,缺点主要如下

  • 某些情况下P2P不能工作,需要维护STUN/TURN服务器
  • 参与者不能过多,过多的P2P连接,网络压力很大的

WebRTC应用场景

WebRTC的应用场景十分广泛,尤其是在疫情的背景下,音视频会议、在线教育、即时通讯等各种交互性场景需求下,已经得到爆发似的发展,其主要应用领域如下

  • 音视频会议
  • 在线教育
  • 共享远程桌面,典型的ToDesk就是采用RTC方案
  • P2P网络通信
  • 聊天室
  • 等等

WebRTC原理

WebRTC的主要原理就是在两个客户端搭建起端到端的通信通道,那么两个不同的网路环境的客户端,要实现点对点的实时音视频通话,有如下几个难点

  • 怎么才能发现对方?
  • 怎么知道对方的音视频编解码?
  • 如何交换数据?

媒体协商

如下图,Peer A支持H264和H265、Peer B支持H264、VP8,那么要保证两端可以正常通信,必须保证支持的编解码,最好的做法就是取两者的交集H264编解码。

媒体协商一般采用 SDP会话描述协议 来进行媒体协商

交换SDP的过程叫做媒体协商

SDP会话描述协议

SDP协议介绍

RFC4566

SDP会话描述协议(Session Description Protocol (SDP))是一个描述多媒体连接内容的协议。例如分辨率、格式、编码、加密算法等。所以在传输时两端能够彼此知道对方的数据,本质上,这些描述内容的元数据并不是媒体流本身。

从技术上讲,SDP并不是一个真正的协议,而是一种数据格式,用于描述在设备之间共享媒体的连接

SDP的主要目的是为了解决参与会话的各成员之间能力不对等的问题

SDP协议格式

SDP描述由许多文本行组成,文本行的格式为

<类型>=<值>

=[CRLF]

类型是一个字母、值是结构化的文本串,其格式依类型而定

SDP的文本信息包括

  • 会话名称和意图
  • 会话持续时间
  • 会话的媒体信息
  • 接受媒体的信息
会话名称和意图描述

v = (协议版本)
o = (所有者/创建者和会话标识符)
s = (会话名称)
i = * (会话信息)
u = * (URI 描述)
e = * (Email 地址)
p = * (电话号码)
c = * (连接信息 ― 如果包含在所有媒体中,则不需要该字段)
b = * (带宽信息)

时间描述

t = (会话活动时间)
r = * (0或多次重复次数)

媒体描述

m = (媒体名称和传输地址)
i = * (媒体标题)
c = * (连接信息 — 如果包含在会话层则该字段可选)
b = * (带宽信息)
k = * (加密密钥)
a = * (0 个或多个会话属性行)

SDP例子
v=0
o=- 4894328260005960092 2 IN IP4 127.0.0.1
s=-
t=0 0
a=group:BUNDLE 0 1 2
a=extmap-allow-mixed
a=msid-semantic: WMS ARDAMS


//m=video 表示本会话包括视频,9代表该视频使用端口9传输,使用UDP传输RTP包,并TSL加密
//SAVPF代表使用srtcp的反馈机制来控制通信过程
//96 97 98 99 127 125 104 124 106 表示支持的编码
//与后面的  a=rtpmap:96 VP8/90000 对应
m=video 9 UDP/TLS/RTP/SAVPF 96 97 98 99 127 125 104 124 106
//表示你要使用来传输数据的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:LBA1
a=ice-pwd:LjClFvTfdgXouZW0BQWM64Qh
a=ice-options:trickle renomination
a=fingerprint:sha-256 09:3F:B5:7D:D2:5F:D4:34:3E:8C:EA:41:93:32:C7:88:61:E7:34:7F:5A:CF:F1:6A:2C:D8:1F:BE:F2:0E:3D:D8
a=setup:actpass
a=mid:0
a=extmap:1 urn:ietf:params:rtp-hdrext:toffset
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:3 urn:3gpp:video-orientation
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:5 http://www.webrtc.org/experiments/rtp-hdrext/playout-delay
a=extmap:6 http://www.webrtc.org/experiments/rtp-hdrext/video-content-type
a=extmap:7 http://www.webrtc.org/experiments/rtp-hdrext/video-timing
a=extmap:8 http://www.webrtc.org/experiments/rtp-hdrext/color-space
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
a=extmap:10 urn:ietf:params:rtp-hdrext:sdes:rtp-stream-id
a=extmap:11 urn:ietf:params:rtp-hdrext:sdes:repaired-rtp-stream-id
//当前客户端既接受数据、又发送数据,recvonly、sendonly、inactive、sendrecv
a=sendrecv
a=msid:ARDAMS ARDAMSv0
a=rtcp-mux
a=rtcp-rsize
//96,代表vp8编码
a=rtpmap:96 VP8/90000
a=rtcp-fb:96 goog-remb
a=rtcp-fb:96 transport-cc
a=rtcp-fb:96 ccm fir
a=rtcp-fb:96 nack
a=rtcp-fb:96 nack pli
a=rtpmap:97 rtx/90000
a=fmtp:97 apt=96
a=rtpmap:98 VP9/90000
a=rtcp-fb:98 goog-remb
a=rtcp-fb:98 transport-cc
a=rtcp-fb:98 ccm fir
a=rtcp-fb:98 nack
a=rtcp-fb:98 nack pli
a=rtpmap:99 rtx/90000
a=fmtp:99 apt=98
a=rtpmap:127 H264/90000
a=rtcp-fb:127 goog-remb
a=rtcp-fb:127 transport-cc
a=rtcp-fb:127 ccm fir
a=rtcp-fb:127 nack
a=rtcp-fb:127 nack pli
a=fmtp:127 level-asymmetry-allowed=1;packetization-mode=1;profile-level-id=42e01f
a=rtpmap:125 rtx/90000
a=fmtp:125 apt=127
a=rtpmap:104 red/90000
a=rtpmap:124 rtx/90000
a=fmtp:124 apt=104
a=rtpmap:106 ulpfec/90000
a=ssrc-group:FID 792617043 3635631891
a=ssrc:792617043 cname:WE5JhPAQ/QvE6AJD
a=ssrc:792617043 msid:ARDAMS ARDAMSv0
a=ssrc:3635631891 cname:WE5JhPAQ/QvE6AJD
a=ssrc:3635631891 msid:ARDAMS ARDAMSv0


m=audio 9 UDP/TLS/RTP/SAVPF 111 63 103 9 102 0 8 105 13 110 113 126
c=IN IP4 0.0.0.0
a=rtcp:9 IN IP4 0.0.0.0
a=ice-ufrag:LBA1
a=ice-pwd:LjClFvTfdgXouZW0BQWM64Qh
a=ice-options:trickle renomination
a=fingerprint:sha-256 09:3F:B5:7D:D2:5F:D4:34:3E:8C:EA:41:93:32:C7:88:61:E7:34:7F:5A:CF:F1:6A:2C:D8:1F:BE:F2:0E:3D:D8
a=setup:actpass
a=mid:1
a=extmap:14 urn:ietf:params:rtp-hdrext:ssrc-audio-level
a=extmap:2 http://www.webrtc.org/experiments/rtp-hdrext/abs-send-time
a=extmap:4 http://www.ietf.org/id/draft-holmer-rmcat-transport-wide-cc-extensions-01
a=extmap:9 urn:ietf:params:rtp-hdrext:sdes:mid
a=sendrecv
a=msid:ARDAMS ARDAMSa0
a=rtcp-mux
a=rtpmap:111 opus/48000/2
a=rtcp-fb:111 transport-cc
a=fmtp:111 minptime=10;useinbandfec=1
a=rtpmap:63 red/48000/2
a=fmtp:63 111/111
a=rtpmap:103 ISAC/16000
a=rtpmap:9 G722/8000
a=rtpmap:102 ILBC/8000
a=rtpmap:0 PCMU/8000
a=rtpmap:8 PCMA/8000
a=rtpmap:105 CN/16000
a=rtpmap:13 CN/8000
a=rtpmap:110 telephone-event/48000
a=rtpmap:113 telephone-event/16000
a=rtpmap:126 telephone-event/8000
a=ssrc:582649583 cname:WE5JhPAQ/QvE6AJD
a=ssrc:582649583 msid:ARDAMS ARDAMSa0

//以下是对DataChannel的描述
m=application 9 UDP/DTLS/SCTP webrtc-datachannel
c=IN IP4 0.0.0.0
a=ice-ufrag:LBA1
a=ice-pwd:LjClFvTfdgXouZW0BQWM64Qh
a=ice-options:trickle renomination
a=fingerprint:sha-256 09:3F:B5:7D:D2:5F:D4:34:3E:8C:EA:41:93:32:C7:88:61:E7:34:7F:5A:CF:F1:6A:2C:D8:1F:BE:F2:0E:3D:D8
a=setup:actpass
a=mid:2
a=sctp-port:5000
a=max-message-size:262144

SDP描述中有描述到IP和端口,但是WebRTC并没有采用这个IP和端口,WebRTC的客户端通信是通过ICE来完成的

网络协商

ICE

RFC8445

ICE(Interactive Connectivity Establishment)、交互式连接设施,是一个允许你的浏览器和对端浏览器建立连接的协议框架

在实际的网络当中,有很多原因能导致简单的从A端到B端直接连接不能如愿完成。这需要绕过阻止建立连接的防火墙,给你设备分配一个唯一可见的地址(通常情况下,我们大部分设备没有一个固定的公网位置),如果路由器不允许主机直连,还得通过一台服务器转发数据。

ICE和STUN和TURN不一样,ICE 不是协议,而是一个框架,它整合了STUN和TURN

每个Peer都可以收集到3种服务器地址:

  • 一个是自己网卡绑定的IP地址,也叫LocalAddress;
  • 第二个是STUN Server告诉自己的外网地址,比如路由器绑定的外网IP地址,叫做ServerReflexiveAddress
  • 第三个TURN Server给自己创建的中转IP地址,叫做RelayedAddress
  • ICE Candidate

ICE候选人,简单理解为每个Peer收集到的IP地址和端口

搜集过程,叫做ICE Candidate Gathering

NAT

网络地址转换协议(Network Address Translation)用来给你的设备映射一个公网的IP地址的协议。

实际上就是一种在IP数据包通过路由器或者防火墙时修改数据包源IP或者目标IP

NAT的存在的目的主要是为了解决ipv4地址不足的问题,首先,我们先了解下,网络主要分为以下两种

  • 公网
  • 私网

一般情况下,路由器的WAN口有一个公网IP,所有连接这个路由器的LAN口的设备都会分配一个私有网段的IP地址。

私有设备的IP被映射成路由器的公网IP和唯一的端口,通过这种方式不需要为每一个私有网络设备分配不同的公网IP,但是依然能被外网设备发现。

私网的IP地址主要三类,分别A类、B类、C类

A类:10.0.0.0~10.255.255.255

B类:172.16.0.0~172.31.255.255

C类: 192.168.0.0~192.168.255.255

公网的IP地址:除了私网范围以外的ABC类地址

现实中 一般的最简单的网络结构如下:

要在公司A的主机A 要想和 家庭B的主机A 建立一个连接,需要知道对方的IP地址和端口号,在私网环境下,一台路由器可能连接非常多的路由器、防火墙设备,如公司A的主机A的时候,运营商会分配一个公网IP地址(假如是10.1.1.1),所有连接路由器A的设备都共用这个公网IP地址,如果 公司A的主机A公司A的主机B 都使用10.1.1.1:8080发送请求,这样服务器返回数据,就不知道转发给哪个主机了,所以啊,路由器里需要重写IP:Port进行区分,这就是NAT协议里的映射表,NAT设备通常会自动设置各个各个主机的映射关系,也可以手动设置。

以上还是最简单的网络模型,由于NAT有很多类型,又考虑到网络安全问题,NAT的映射也会有所不同,更多的可查看NAT维基百科,以下对NAT类型做个简单的归纳,NAT的主要四种类型有以下四种

  • 完全圆锥形NAT(Full Cone NAT)
  • 一旦内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自 iAddr:iPort 的数据包都经由 eAddr:ePort 向外发送。
  • 任意外部主机都能经由发送数据包给 eAddr:ePort 到达 iAddr:iPort

私网的主机A向主机C发送一个请求后,主机A(192.168.1.16:8080)数据包通过NAT后,会形成一个公网IP的映射表(10.1.1:8081),形象的描述就是它会在NAT服务上打了一个洞,形成了外网的IP地址和端口Port后(映射表),其他的主机B、D在知道这个IP和Port后,也可以顺利的向主机A发送数据!很不安全!

这种情况下,NAT映射表很简单,只有

- 内网的IP:Port
- 公网的IP:Port
  • 受限圆锥形NAT(Address-Restricted cone)
  • 一旦内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自 iAddr:iPort 的数据包都经由 eAddr:ePort 向外发送。
  • 唯 iAddr:iPort 曾经发送数据包到外部主机(nAddr:any),外部主机才能经由发送数据包给eAddr:ePort 到达 iAddr:iPort。(注:any 指外部主机源端口不受限制。)

在上述情况下,NAT映射表里有

    -   内网的IP:Port
    -   公网的IP:Port
    -   请求的主机C的IP

由于IP地址受限,主机B或者主机D发送数据包给主机A,NAT一看到IP地址不对,就会丢掉这个数据包。

  • 端口受限圆锥形NAT(Port-Restricted Cone )
  • 一旦内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自 iAddr:iPort 的数据包都经由 eAddr:ePort 向外发送。
  • 受限圆锥型NAT基础上增加了外部主机源端口必须是固定的。

端口受限型比受限圆锥形NAT还严格,除了IP地址,还会对端口做限制,NAT映射表多个主机C的Port,如下

    - 内网的IP:Port
    - 公网的IP:Port
    - 请求的主机C的IP、Port

在主机A发送请求后,不光有内网的IP地址和端口以及公网的IP地址和端口, 还有你请求的主机C的IP地址和端口

  • 对称型NAT(Symmetric NAT)
  • 每一个来自相同内部 IP 与端口,到一个特定目的地 IP 和端口的请求,都映射到一个独特的外部 IP 和端口。
  • 同一内部 IP 与端口发到不同的目的地和端口的信息包,都使用不同的映射
  • 只有曾经收到过内部主机数据的外部主机,才能够把数据包发回

对称型是最复杂的,上面三个在打通之后,公网IP地址是不变的,但是对于对称型NAT来说,他就发生了变化,对于每一台发送请求的私网主机,都创建了不同的IP和Port。如上图,主机A给主机C发送请求,主机C只能通过10.1.1.19:8086回消息,其他端口不行。主机A给主机B发送请求,主机B只能通过10.1.1.17:8082回消息,通过10.1.1.16:8081不行。

如何判断自己网络NAT类型

推荐一款Python下的工具

pip install pystun3
pystun3

NAT0: OpenInternet,没有经过NAT地址转换,公网IP
NAT1: 完全圆锥形NAT
NAT2: 受限圆锥形NAT
NAT3: 端口受限圆锥形NAT
NAT4: 对称型NAT

STUN

NAT的会话穿越功能(Session Traversal Utilities For NAT (STUN))是一个允许位于NAT后的客户端找出自己的公网地址,判断路由器组织直连的限制方法的协议。

客户端通过给公网的STUN服务器发送请求获取自己的公网地址信息,以及是否能够被访问。

客户端通过STUN服务器获取公网IP地址后,也不一定能建立连接,因为NAT的不同类型处理传入的UDP分组的方式是不同的。

其中对称型NAT则不能使用STUN进行穿透,需要使用TURN中转服务器

TURN

全称 Traversal Using Relays Around NAT

一些路由器严格地限定了部分私有网络设备的对外连接。这种情况下,即使STUN服务器识别了该私有网络设备的公网IP和端口的映射,依然无法和这个私有网络设备简历连接,这个情况下就需要TRUN协议

一些路由器使用了一种对称型NAT的NAT模型。这意味着路由器只接受和对端先前建立的连接

NAT的中继穿越方式通过TURN服务器中继所有数据的方式来绕过对称型NAT。

通过STUN获取自己的公网Ip Demo

sudo apt-get install coturn
brew install coturn

//-L 绑定IP
//-a 使用长期凭证,turn中继转发模式,WebRTC中必须使用长期凭证机制
//-u 用户名:密码
//-v 日志输出级别
//-f 指定turn消息使用fingerprint
//-r 制定域名
sudo turnserver -L 0.0.0.0 --min-port 30000 --max-port 60000  -a -u leo:11235813 -v -f

使用ICE客户端,即可获取当前网络环境的信息

如果返回有srflx即说明stun服务器已经能正常工作了。

如果返回有relay即说明turn服务器已经能正常工作了。

类型别名如何传给对端用法
主机候选项host信令服务器从网卡中获取到的本地传输地址,如果此地址位于NAT后,则为内网地址
服务器反射候选项srfix信令服务器从发送给STUN服务器的Binding检查中获取传输地址。如果此地址位于NAT之后,则为最外层的NAT公网地址
对端反射候选项prfixStun Binding请求从对端发送的STUN Binding请求获取的传输地址。这是一种在连接检查期间新发生的候选项
中继候选项relay信令服务器媒体中继服务器传输地址。通过使用STUN Allocate请求获取

STUN/TURN测试地址

NAT打洞方式

NAT的不同类型,其打洞的的方式也不同

  • 如果NAT是完全圆锥形的,那么双方的任何一方都可以发起通信
  • 如果NAT是受限圆锥型或端口受限圆锥形,双方必须一起向对方发送请求,这样NAT映射表上有了记录后,就能连通,若有一方在对称型NAT后,则不能打洞成功,NAT穿透失败!
  • 如果有对称型NAT,客户端A向STUN服务器发数据包,客户端A打洞成功了,但是客户端B无法使用客户端A打好的洞,所以只能使用Turn中转方案

WebRTC打洞流程

WebRTC使用了ICE框架,实现了NAT打洞能力,ICE整合了STUN和TURN,只要双方交换了网络IP:Port后,WebRTC就会自行打洞,STUN是用于探测NAT类型,IP,端口的服务,WebRTC获取NAT类型,IP,端口后会触发candidate事件,然后双方交换IP和端口,开始打洞,如果打洞失败,就会用TURN服务转发数据包。

WebRTC源码整体框架

WebRTC整体架构从上到下分为三层

  • API层

暴露给应用开发人员用于开发WebRTC应用的API层,包括Javascript、Android、IOS等

该部分接口的具体实现均使用WebRTC中的C++ API

  • WebRTC关键核心层,一共包括三个模块
    • 音频引擎

负责音频采集、编解码、降噪、回声消除等

    • 视频引擎

负责视频采集、编解码、传输等功能

    • 网络传输

负责P2P打洞、RTP/SRTP实时传输、多路复用等

  • 底层

由各厂商自主研发一层,用于实现音视频的采集、网络传输功能

WebRTC源码目录结构

WebRTC目录结构大致如下,其中核心模块是modules层,这部分类似于音视频工具箱,涵盖了大部分的音视频组件。

.
├── api               //对外标准API
├── audio							//音频相关
├── base			
├── build
├── build_overrides
├── buildtools
├── call							//数据流管理
├── common_audio			//音频相关算法
├── common_video			//视频相关算法
├── data
├── docs
├── examples
├── logging
├── media							//媒体逻辑处理
├── modules
│   ├── audio_coding            //音频编解码
│   ├── audio_device						//音频设备采集播放
│   ├── audio_mixer							//音频混音
│   ├── audio_processing				//音频3A处理算法
│   ├── congestion_controller		//拥塞控制
│   ├── desktop_capture					//桌面采集
│   ├── include
│   ├── pacing									//码率控制平滑处理
│   ├── remote_bitrate_estimator //远程码率估算
│   ├── rtp_rtcp								 //RTP/RTCP协议
│   ├── third_party							 //三方库
│   ├── utility				
│   ├── video_capture						//视频采集
│   ├── video_coding						//视频编解码
│   └── video_processing				//视频处理
├── out
├── p2p								//端到端、STUN、TURN
├── pc								//PeerConnection链接相关逻辑
├── resources
├── rtc_base					//基础组件:线程、锁等等
├── rtc_tools					//音视频分析工具
├── sdk								//android ios层代码,采集、渲染等等
├── stats
├── style-guide
├── system_wrappers		//操作系统相关代码
├── test
├── testing
├── third_party
├── tools
├── tools_webrtc			//测试相关工具
└── video							//视频相关逻辑

WebRTC核心组件基本如下

  • 音视频引擎:OPUS、H264、VP8/VP9

  • 传输层协议: UDP

  • 媒体协议: SRTP/SRTCP

  • 数据协议: DTLS/SCTP

  • P2P内网穿透:STUN/TURN

  • 信令与SDP协商:HTTP/WEBSOCKET/SIP/SDP

WebRTC协议栈

WebRTC里涉及的协议,如下图

  • IP(RFC 791)
  • TCP (RFC 793)
  • UDP(RFC 768)
  • 内网穿透相关
    • ICE:互动式连接建立(RFC 5245
    • STUN:用于NAT的会话遍历实用程序(RFC 5389
    • TURN:在NAT周围使用继电器进行遍历(RFC 5766
  • SDP:会话描述协议(RFC 4566
  • DTLS:用于数据报传输层安全性(RFC 6347
  • SCTP:流控制传输协议(RFC 4960
  • SRTP/SRTCP:安全实时传输协议,传输控制协议(RFC 3711

WebRTC源码编译

工欲善其事,必先利其器,首先我们先把sdk编译出来

编译WebRTC,官方说明只能Ubuntu平台编译,个人经验,建议使用docker构建编译环境,如果您使用虚拟机Ubuntu,有可能会出现莫名其妙的问题。记得科学上网

# 1.配置WebRTC编译环境 depot_tools
cd
export WORKSPACE=`pwd`
cd $WORKSPACE
mkdir webrtc
cd webrtc
# 需要翻墙 建议 https://ikuuu.dev/auth/register?code=3d2I
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git
export PATH=$WORKSPACE/webrtc/depot_tools:$PATH

# 2. 下载webRTC源码
fetch --nohook webrtc_android
# fetch --nohook webrtc_ios
# fetch --nohook webrtc

# 同步数据,可能因为网络情况多次失败,只要多次执行gclient sync
# 直到看到 100% 为止
gclient sync

# android需要执行以下命令
./build/install-build-deps.sh
./build/install-build-deps-android.sh


# 3.编译
gn gen out/android_arm_release --args='is_debug=false target_os="android" target_cpu="arm"'
autoninja -C out/android_arm_release

gn gen out/android_arm64_release --args='is_debug=false target_os="android" target_cpu="arm64"'
autoninja -C out/android_arm64_release

运行一段时间后,不出意外的话,会看到以下信息,编译成功

提取编译产物

webrtc编译会输出许多产物,Android端SDK所需要的目前只用到了两个libwebrtc.jar 、libjingle_peerconnection_so.so,其路径输出如下

# jar包输出路径
$WORKSPACE/webrtc/src/out/android_arm_release/lib.java/sdk/android/libwebrtc.jar
# so包输出路径
$WORKSPACE/webrtc/src/out/android_arm_release/libjingle_peerconnection_so.so

jar对应的源码路径

webrtc/src/sdk/android/api/org/webrtc
webrtc/src/sdk/android/src/java/org/webrtc
webrtc/src/modules/audio_device/android/java/src/org/webrtc/voiceengine
webrtc/src/rtc_base/java/src/org/webrtc
webrtc/src/sdk/android/api/org/webrtc

生成AS工程

生成AS工程,这里生成的工程基本是Java代码,并不是我想要的Cmake管理的Native代码

build/android/gradle/generate_gradle.py --output-directory ./out/android_arm_release \
--target "//examples:AppRTCMobile" --use-gradle-process-resources \
--split-projects --canary

WebRTC重要接口

Webrtc的接口已经是一种规范,可以查看 RFC8829中文版RFC8829

音视频源的捕获与录制

  • VideoCapturer
    • FileVideoCapturer
    • ScreenCapturerAndroid
    • CameraEnumerator
  • VideoSink
  • JavaAudioDeviceModule.SamplesReadyCallback

RTCPeerConnection

RTCPeerConnection 用来建立和维护端到端连接,并提供高效的音视频流传输

  • PeerConnectionFactory

peerConnectionFactory实例,可以从用户设备获取视频和音频,最终将其渲染到屏幕上。

    • createPeerConnection
    • createLocalMediaStream
    • createVideoSource
    • createAudioSource
  • MediaConstraints

MediaConstraints是支持不同约束的WebRTC库方式的类,可以加载到MediaStream中的音频和视频

轨道,以及PeerConnection中

  • MediaStream

表示一个媒体流,内部包括了多条音频轨、视频轨,即 MediaStreamTrack

  • MediaStreamTrack

媒体源,音频源AudioTrack或者视频源VideoTrack

  • PeerConnection
    • addTrack
    • removeTrack
    • addStream
    • addIceCandidate
    • addTransceiver
    • createOffer
    • createAnswer
    • setLocalDescription
    • setRemoteDescription
    • PeerConnection.Observer 事件
      • onSignalingChange
      • onIceConnectionChange
      • onStandardizedIceConnectionChange
      • onConnectionChange
      • onIceConnectionReceivingChange
      • onIceGatheringChange
      • onIceCandidate
      • onIceCandidateError
      • onIceCandidatesRemoved
      • onSelectedCandidatePairChanged
      • onAddStream
      • onRemoveStream
      • onDataChannel
      • onRenegotiationNeeded
      • onAddTrack
      • onRemoveTrack
      • onTrack

RTCDataChannel

RTCDataChannel 用来支持端到端的任意二进制数据传输,是双工通道,中间木有服务器中转,使用SCTP协议。


基于WebRTC实现的应用架构方案

基于WebRTC的原理,我们一般可以实现以下几种具体的方案

  • 1v1音视频通话
    • 1v1语音通话
    • 1v1音视频通话
  • 多对多音视频会议
    • 音视频会议
    • 语音房
  • 一对多互动直播方案
  • 聊天室
  • ........

实现音视频1v1通话方案

Google官方提供的AppRTC就是1v1的解决方案

AppRTC依赖3个服务

  • Collider

WebSocket服务,提供信令交互能力

  • Coturn

Stun/Turn服务,提供P2P转发能力

  • AppRTC

Web服务

搭建AppRTC Server的基本步骤

  1. 搭建Room Server(房间服务器),也就是AppRTC
  2. 搭建信令服务器,AppRTC源码中的src/Collider下
  3. 搭建Coturn服务器,用于音视频数据转发
  4. 搭建获取Coturn信息的服务器,也就是ICE REST API服务

AppRTC里重要的库

├─build
├─src
│  ├─app_engine        // AppRTC 房间与信令服务器
│  │  └─bigquery
│  ├─collider          // 信令服务器
│  │  ├─collider
│  │  ├─collidermain
│  │  └─collidertest
│  ├─third_party        
│  │  ├─apiclient
│  │  ├─google_api_python_client-1.2.egg-info
│  │  ├─httplib2
│  │  ├─httplib2-0.9.egg-info
│  │  ├─oauth2client
│  │  └─uritemplate     
│  └─web_app            // 后台管理
│      ├─css
│      ├─html
│      ├─images
│      └─js
└─tools                 
    └─turn-prober
  • AppRTC

房间与信令服务器,该服务器包括了房间的业务逻辑和信令处理器,其中AppRTC

  • Collider

信令服务器、基于go语言实现的WebSocket的信令服务器

  • Coturn

Coturn是一种TURN服务器(中转服务器),该服务器用于数据流的NAT穿越和数据转发

实现音视频多方通信方案

WebRTC的多方通信架构有以下三种

  • Mesh方案
  • MCU方案
  • SFU方案

Mesh方案

即多个终端进行两两连接,形成一个网状。如下图,该方案对各终端的带宽要求比较高,5v5的情况下,对于某个客户端来说,需要5个上行带宽、5个下行带宽

MCU方案

MCU(Multipoint Conferencing Unit)方案,

该方案由一个服务器加上多个终端组成一个星型结构。

MCU服务器会将该房间里所有的终端的音视频进行混合,最终生成一个混合后的音视频再发送给各个

终端。

实际上,MCU服务器就是一个音视频混合器,这种方案非常考验服务器的压力。

5v5的情况下,对于某个客户端来说,需要1个上行带宽、1个下行带宽,对于MCU服务器来说,5个上行带宽,5个下行带宽

Licode

SFU方案

SFU(Selective Forwarding Unit)方案,该方案和MCU差不多,但是SFU服务器不做音视频混流,当

SFU收到一个端的音视频流后,就直接转发给其他终端。

实际上,SFU就是个音视频转发器。

SFU在音视频会议中应用非常广泛

5v5的情况下,对于某个客户端来说,需要1个上行带宽、4个下行带宽

Janus

  • Simulcast模式

视频的共享者可以同时向 SFU 发送多路不同分辨率的视频流(一般为三

路,如 1080P、720P、360P)。而 SFU 可以将接收到的三路流根据各终端的情况而选择其中某一路

发送出去。如下图,由于 PC 端网络特别好,给 PC 端发送 1080P 分辨率的视频;而移动网络较差,

就给 Phone 发送 360P 分辨率的视频

  • SVC模式

SVC 是可伸缩的视频编码模式。与 Simulcast 模式的同时传多路流不同,SVC 模式是在视频编码时将视频分 成多层——核心层、中间层和扩展层。上层依赖于底层,而且越上层越清晰,

越底层越模糊。在带宽不好的情况下,可以只传输底层,即核心层,在带宽充足的情况下,可以将三层

全部传输过去

I# 一对多直播方案(广播模式)