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协议介绍
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
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公网地址 |
| 对端反射候选项 | prfix | Stun 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)
- 内网穿透相关
- 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的基本步骤
- 搭建Room Server(房间服务器),也就是AppRTC
- 搭建信令服务器,AppRTC源码中的src/Collider下
- 搭建Coturn服务器,用于音视频数据转发
- 搭建获取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个下行带宽
SFU方案
SFU(Selective Forwarding Unit)方案,该方案和MCU差不多,但是SFU服务器不做音视频混流,当
SFU收到一个端的音视频流后,就直接转发给其他终端。
实际上,SFU就是个音视频转发器。
SFU在音视频会议中应用非常广泛
5v5的情况下,对于某个客户端来说,需要1个上行带宽、4个下行带宽
- Simulcast模式
视频的共享者可以同时向 SFU 发送多路不同分辨率的视频流(一般为三
路,如 1080P、720P、360P)。而 SFU 可以将接收到的三路流根据各终端的情况而选择其中某一路
发送出去。如下图,由于 PC 端网络特别好,给 PC 端发送 1080P 分辨率的视频;而移动网络较差,
就给 Phone 发送 360P 分辨率的视频
- SVC模式
SVC 是可伸缩的视频编码模式。与 Simulcast 模式的同时传多路流不同,SVC 模式是在视频编码时将视频分 成多层——核心层、中间层和扩展层。上层依赖于底层,而且越上层越清晰,
越底层越模糊。在带宽不好的情况下,可以只传输底层,即核心层,在带宽充足的情况下,可以将三层
全部传输过去
I# 一对多直播方案(广播模式)