Blog
Blog copied to clipboard
WebRTC 学习笔记
前言
本想用这篇文章在Coding上挣两个码币,可是失败了,好了,那就写在这里,错误之处欢迎指出,在下不胜感激。
正文
WebRTC 的全称为 Web Real-Time Communication,是一个由 Google 主导的通信协议和编程接口。Web 浏览器可以从计算机硬件获取视频或音频信息,也可以通过从其他用户浏览器获取实时数据,即可以实现视频或音频的通信。这项技术是 2010 年 Google 以 6820 万美元收购了 Global IP Solutions 公司,2011 年却将其开源了(也只有 Google 能干出这事),2013 年在 Google IO 大会上推广。由于 Google 的积极推动和标准的制定,已经得到了业界的支持,称为新一代视频通话的标准。
平台通用性
WebRTC 在不同浏览器上(包括 Chrome,Firfox,Opera 和 Microsoft Edge 等),不同的平台上(PC,Android,iOS 和 TV)都有统一的标准。借用并改编 Sam Dutton 的一句话:想象一下,在你的手机、电视和电脑都可以相互通信,想象一下很容易的你就可以与他人进行 P2P 的音视频的通信,多么美好,这都是 WebRTC 干的好事。
确实是个好事,在浏览器上,点击输入一个网址,点击同意网站使用计算机话筒和摄像头,然后就可以与别人进行语音和视频通信了,不需要安装任何软件,也无需任何插件,因为在浏览器中已经内置了 WebRTC 了。
同样,在移动端 Android 和 iOS 上,也有对应的 AppRTCMobile 可以安装并试用(在2016年9月28日前还叫 AppRTCDemo )。与浏览器类似的操作,同样的方便。
发展情况
从 Google IO 大会上第一次提到 WebRTC 到现在,Google 的 Duo,Apple,微软的 Edge,Skype,Facebook 等都对 WebRTC 进行了支持,并且有流行的趋势。WebRTC 到 2016 年已经过了五年,虽然不能说全部完善了,还有 bug,但是,已经可以使用,应该给予一定关注了。
到这里,大致解决了 WebRTC 是什么和是否应该关注一下 WebRTC 的问题了。接下来就看看怎么使用吧,主要针对 Android 来进行讨论。
尝试一下
傲视天下的 Linus Torvalds 已经说过了,最好的学习程序的方法是“Reading The Fucking Source Code”,这里也一样,送源代码下载 传送门 ,当然,还要先看看 官网 。这里插播一条广告, WebRTC 代码更新的真的太快了,包括节假日每天都有多次新的提交,并且偶尔某次的提交会完全更改以前的设计,这一点真的让关注 WebRTC 的人吐血,也就是说很难说你已经下到了最新的代码,因为在说这句话的同时,可能又有新的提交了,这也让网上很多的资料过时的异常迅速,但是只能忍,人家是 Google。
由于跟 Chrome 有关,下载代码如果仅仅下载了主分支的代码,其实是不好用的。需要下载 depot_tools ,配置环境变量,然后用 fetch
和 gclient
命令下载,这一部分可以参考官网的 Development。官网也包含了编译和运行方法。当然,如果你只想了解一下,没必要下代码,下代码至少需要几个小时,你可以直接跳过这步, Google 的这些 例子 你可以试试。最后一点需要注意的就是WebRTC不可以本地运行,需要服务器,换一句话说,例子下载了直接用浏览器打开,是无法运行的,可以使用 Chrome 的插件,相当于在服务器上运行。
上面讨论的是浏览器部分,我承认我只是试了试官网 Start,还算好玩,接下来重点讨论 Android 部分。
关于 Android 部分,代码下载略有不同,我写过笔记,这里只送 传送门 ,就不赘述了。对于Android项目,同样送个 传送门 ,这个代码下载后,用 Android Studio 打开,可以直接运行,也有编译好的 apk
文件,可能这样会有更多的人会真的试一试吧。服务器是官网 Demo 服务器,需要手机科学上网。
技术分析——浅尝辄止
下面讨论一下 WebRTC 的技术实现和几个关键的 API ,但我自知技术实在是太弱,只能浅尝辄止,抛砖引玉。
先看几张图——全部图片来自网络
浏览器部分,封装了绝大部分功能,包括音视频编解码,网络通信、音视频数据捕捉和 P2P 相关等,用 C++ 向上层提供接口,W3C 制定标准,我们就可以使用浏览器的 API 了,网页就充当 APP 的角色。可能有人已经发现,这里有 P2P ,是的,看下面这张图。
两个浏览器进行通信,可以直接与对方建立 P2P 连接,交换媒体数据,中心网络服务器用来发送控制信息,包括断开连接,重连等,但是这时网络媒体数据并没有通过服务器,服务器压力也减轻了。但是又有问题了,建立P2P连接不成功了怎么办,长城,哦不,防火墙那么强大,哪有那么容易建立 P2P 连接的,看下图。
我们发现了,中间多了一个 Relay Server 。如果一切正常的情况下,通信的两端,可以穿透防火墙,建立 P2P 连接,如果没有穿透防火墙,那就只能通过中间 Relay Server 进行数据转发了。这样一来,信令的发送和多媒体数据的传输,都可以找到合适的方式进行传输。
刚刚说了,与对方建立 P2P 连接,但是建立 P2P 连接都需要知道对方和自己的公网 IP 和端口,这个交给了 STUN Server 。看下面两张图片。
看了几张图,大致过程了解了,接下来是代码 API 时间。本文主要列举几个主要的类,水平有限,抛砖引玉。浏览器相关代码介绍看这里。
MediaStream
数据流。浏览器可以直接通过 getUserMedia 创建, Android 则通过工厂类创建。MediaStream 主要包含本地的和远程的两种,也就是要发送的数据和要接收的数据两种。同时,由于这是隐私敏感数据,浏览器和 Android 都会弹出提示窗口,请求用户确认权限。
public class MediaStream {
public MediaStream(long nativeStream) {
this.nativeStream = nativeStream;
}
public boolean addTrack(AudioTrack track) {
// 调用本地C++方法
if(nativeAddAudioTrack(this.nativeStream, track.nativeTrack)) {
this.audioTracks.add(track);
return true;
} else {
return false;
}
}
public boolean addTrack(VideoTrack track) {
// 调用本地C++方法
if(nativeAddVideoTrack(this.nativeStream, track.nativeTrack)) {
this.videoTracks.add(track);
return true;
} else {
return false;
}
}
}
PeerConnection 和 PeerConnectionFactory
加载 JNI,创建连接,Android 中同时提供向连接中添加 VideoTrack 和 AudioTrack 方法,即向连接中添加数据流。这里应该提一下, WebRTC 对于数据是先将原数据 Capture 封装成 Source,在将 Source 封装成 Track ,再使用 MediaStream 的 addTrack 方法添加 Track,使用 PeerConnection 的 addStream 方法添加 mediaStream,而 PeerConnection 又是由 PeerConnectionFactory 创建的,因此,就是:
// 忽略方法参数
PeerConnectionFactory factory = new PeerConnectionFactory();
PeerConnection peerConnection = factory.createPeerConnection();
mediaStream = factory.createLocalMediaStream();
mediaStream.addTrack(createVideoTrack(videoCapturer));
peerConnection.addStream(mediaStream);
同样看一下源代码,其中就包含了上述提到的方法。
public class PeerConnectionFactory {
public PeerConnectionFactory(PeerConnectionFactory.Options options) {
this.nativeFactory = nativeCreatePeerConnectionFactory(options);
if(this.nativeFactory == 0L) {
throw new RuntimeException("Failed to initialize PeerConnectionFactory!");
}
}
public PeerConnection createPeerConnection(RTCConfiguration rtcConfig, MediaConstraints constraints, Observer observer) {
long nativeObserver = nativeCreateObserver(observer);
if(nativeObserver == 0L) {
return null;
} else {
long nativePeerConnection = nativeCreatePeerConnection(this.nativeFactory, rtcConfig, constraints, nativeObserver);
return nativePeerConnection == 0L?null:new PeerConnection(nativePeerConnection, nativeObserver);
}
}
public MediaStream createLocalMediaStream(String label) {
return new MediaStream(nativeCreateLocalMediaStream(this.nativeFactory, label));
}
public VideoSource createVideoSource(VideoCapturer capturer) {
Context eglContext = this.localEglbase == null?null:this.localEglbase.getEglBaseContext();
long nativeAndroidVideoTrackSource = nativeCreateVideoSource(this.nativeFactory, eglContext, capturer.isScreencast());
AndroidVideoTrackSourceObserver capturerObserver = new AndroidVideoTrackSourceObserver(nativeAndroidVideoTrackSource);
nativeInitializeVideoCapturer(this.nativeFactory, capturer, nativeAndroidVideoTrackSource, capturerObserver);
return new VideoSource(nativeAndroidVideoTrackSource);
}
public VideoTrack createVideoTrack(String id, VideoSource source) {
return new VideoTrack(nativeCreateVideoTrack(this.nativeFactory, id, source.nativeSource));
}
}
VideoCapturer
如下图,为类的继承关系,可以使用 CameraCapture 简化相机操作。
当然,Android部分,还有比较重要的有一下几个类,但是相比对于了解 WebRTC 就不应该在这里介绍了:
网络相关:NetworkMonitor
编解码相关:MediaCodecVideo
数据格式相关:DataChannel
如果读者感兴趣自行阅读。
后记
WebRTC 还在发展,斗胆写了此文,抛砖引玉。错误之处,敬请指正,在下不胜感激。
参考(以下链接可能需要科学上网)
官网:https://webrtc.org/
Demo:https://github.com/webrtc/samples
IO Docs:https://goo.gl/p8fnYl
STUN:https://goo.gl/kvxWge
Duo:https://goo.gl/mdy8Ca
Chromium:https://goo.gl/yU5W4g
RWebRTC:https://goo.gl/aKweJt
Blog:https://goo.gl/f9Vh2N
Ninja:https://goo.gl/RB4JVZ
WebRTC on Android:https://goo.gl/ek5pi3