准备工作
我们先要添加 WebRTC 依赖,在这篇文章中我们直接引用 WebRTC 官方编好的包即可,即直接在 build.gradle 中添加:
关于信令交换方式及信令服务器,不管是官方还是开源社区会有一大堆的开源项目,可以选择各种例如 WebSocket、XMPP 等方式进行信令通讯以交换相关信息创建连接。具体在此系列文章不进行叙述,可在文章末尾链接下载一整系列的代码(来自公司里我很敬佩的一位前辈)。
0、创建 PeerConnectionFactory
PeerConnectionFactory 是创建连接以及创建在连接中传输采集的音视频流数据的非常重要的一个类,且能在此处定义所使用的编解码器,本篇文章对编解码器不做深入描述,直接使用默认的 DefaultVideoEncoderFactory 和 DefaultVideoDecoderFactory。创建 PeerConnectionFactory 方式如下:
1、采集
1.1、视频采集
一个视频通话需要进行视频采集和音频采集。做过 Android 相机应用开发的朋友都知道,Android 提供了一系列的上层相机操作接口,能够让我们很方便地去进行相机采集的操作,Android 在 API 21 时废弃了 Camera1 的接口推荐使用新的 Camera2,但是由于兼容问题几乎大多数开发者仍需要使用 Camera1,且要做使用 Camera1 还是 Camera2 的选择和适配。WebRTC 视频采集需要创建一个 VideoCapturer,WebRTC 提供了 CameraEnumerator 接口,分别有 Camera1Enumerator 和 Camera2Enumerator 两个实现,能够快速创建所需要的 VideoCapturer,通过 Camera2Enumerator.isSupported 判断是否支持 Camera2 来选择创建哪个 CameraEnumerator,选择好即可快速创建 VideoCapturer 了:
其中 deviceName 可通过 cameraEnumerator.getDeviceNames 获取,进而选择前置还是后置。然后我们创建一个 VideoSource 来拿到 VideoCapturer 采集的数据,并且创建在 WebRTC 的连接中能传输的 VideoTrack 数据:
1.2 音频采集
音频采集则没有视频采集那么麻烦,仅需要创建 AudioSource 则可直接得到音频采集数据。同样最后创建一个 AudioTrack 即可在 WebRTC 的连接中传输。
2、渲染本地视频
无论是本地还是远端的视频渲染,都是通过 WebRTC 提供的 SurfaceViewRenderer (继承于 SurfaceView) 进行渲染的。
视频的数据需要 VideoTrack 绑定一个 VideoSink 的实现然后将数据渲染到 SurfaceViewRenderer 中,具体实现如下:
3、创建连接
创建连接即为创建 PeerConnection,PeerConnection 是 WebRTC 非常重要的一个东西,是多人音视频通话连接的关键。我们在最开始创建了 PeerConnectionFactory,通过此工厂类即可非常简单地创建一个 PeerConnection。
其中 PeerConnectionObserver 是用来监听这个连接中的事件的监听者,可以用来监听一些如数据的到达、流的增加或删除等事件,其接口如下:
具体哪些回调会在什么时候回调、需要做什么将在下文详细介绍。
通过 PeerConnectionFactory 创建好 PeerConnection 之后即可将之前创建的两个 Track 加入连接中了:
4、交换相关信息
当需要通话时,那么就需要交换相关信息了,信令服务器的作用就体现出来了。我们先看一张图:
意思就是说,两端通过信令交换一些相关信息,对于自己来说,先要创建一个 Offer,并且通过 setLocalDescription 设置为本地的信息,然后远端会将你的 Offer 通过 setRemoteDescription 设置到他那边,然后他那边创建一个 Answer 发送到你这边,你也要通过 setRemoteDescription 将他的 Answer 设置到自己这里,然后就会开始走 PeerConnectionObserver 内的事件了。
上面第三条线就是在我们上面讲的 PeerConnectionObserver 中的 onIceCandidate 里进行处理的。我们在 setLocalDescription 和 setRemoteDescription 后即会触发 onIceCandidate 回调生成一个 IceCandidate,IceCandidate 就是一个包装类,里边放了一些相关信息比如 sdp,通过信令服务器将这个 IceCandidate 发送到远端,远端通过 PeerConnection 的 addIceCandidate 方法将这个 IceCandidate 加到连接中,连接打通后会将远端的流通过 onAddTrack 回调传到本端进行处理。
4.1 创建 Offer
创建 Offer 时需要传入一些配置参数,可通过 MediaConstraints 传入。代码如下:
其中 SdpObserver 含有以下回调:
我们在创建 sdp 成功时将其设为 LocalDescription 并通过信令发给远端即可。远端在接收到 sdp 时通过以下代码设置 RemoteDescription:
4.2 创建 Answer
远端设置 RemoteDescription 完毕后,就要创建 Answer:
Answer 也是作为远端的 LocalDescription,然后通过信令发送给本地,本地将其设为 RemoteDescription。
4.3 发送 IceCandidate
双方设置 {Local,Remote}Description 成功后,则开始了 IceCandidate 的发送和接收,此步需要把双方在 PeerConnectionObserver 的 onIceCandidate 回调中回调的 IceCandidate 通过信令发送到远端:
接收到双方给对方发送的 IceCandidate 之后通过以下方法添加到连接中:
此时即可在 onAddTrack 中进行远端流渲染的处理了。
5、渲染远端画面
前文提到远端画面的信息会在 PeerConnectionObserver 中的 onAddTrack 回调中回调出来,此时我们在这个回调中像渲染本地画面一样渲染远端画面即可:
至此,整个通话的流程就跑通了。当然不要忘了在 Activity 销毁时对 PeerConnection、VideoCapturer、SurfaceTextureHelper、PeerConnectionFactory、SurfaceViewRenderer 进行关闭/释放。