前言,最近在搞网页投屏,发现 WebRTC 的Android 版本较少,这里的话,参考了一些优秀的博客,主要是这个大佬的 https://www.jianshu.com/p/eb5fd116e6c8 博客来整理,然后加一些自己的理解。权当记录

Android WebRTC 入门教程(一) – 使用相机

Android WebRTC 入门教程(二) – 模拟p2p本地视频传输

源码工程: https://github.com/LillteZheng/WebRTCDemo 今天要实现的效果:

一. WebRTC 简介

WebRTC (Web Real-Time Communications) 是一项实时通讯技术,它允许网络应用或者站点,在不借助中间媒介的情况下,建立浏览器之间点对对(Peer-to-Peer) 的连接,实现视频流和视频流或者任意数据的传输。

目前,WebRTC 的应用已经不局限在浏览器和浏览器之间,通过官网提供的 SDK ,我们可以实现本地应用的音视频传输。 用法也非常简单,可以用非常简介的代码实现强大可靠的音视频功能。

这一章,我们先实现相机的数据获取和渲染。

二. 关联 WebRTC 官方aar

官方已经打包好了 so 和 java 代码,可直接关联:

implementation 'org.webrtc:google-webrtc:1.0.32006'

当然,如果你想改动源码或者 so ,可在 官方源码src/tools_webrtc/android/中build_aar.py与release_aar.py中有相关生成本地aar与发布aar到maven仓库的脚本。

2.1 添加权限

三. 实现相机功能

以前我们使用相机,都是通过 camera1/2/x 去打开相机,再设置 Surface ,这样相机采集的数据就会在 Surface 显示了。 如果你想使用原生相机采集,可参考历史文章。 Android 音视频开发(二) – Camera1 实现预览、拍照功能 Android 音视频开发(三) – Camera2 实现预览、拍照功能 Android 音视频开发(四) – CameraX 实现预览、拍照功能

3.1 创建 Surface

所以,在使用相机时,需要一个承载画面的组建,在WebRTC 中,我们使用 SufaceViewRender ,它是 SurfaceView 的子类。如下:

xmlns:app="http://schemas.android.com/apk/res-auto"

xmlns:tools="http://schemas.android.com/tools"

android:layout_width="match_parent"

android:layout_height="match_parent"

tools:context=".CameraActivity">

android:id="@+id/viewRender"

android:layout_width="match_parent"

android:layout_height="match_parent"/>

它是 VideoSink 的子类,后面会讲,它内部是通过 opengl 去渲染的。

3.2 使用相机

WebRTC 有自己一套使用规则,使用相机也是如此,因此想在脑子把 Camera 相关的 api 遗忘,WebRTC 有自己的 API,而它的使用步骤基本不变。

3.2.1 初始化 PeerConnectionFactory

在初次使用 PeerConnectionFactory 时,需要先初始化 PeerConnectionFactory ,调用静态方法 initialize() 进行全局初始化和资源加载,通过Builder 模式。可对 Tracer,Logger 等进行配置。

// step 1 创建PeerConnectionFactory

PeerConnectionFactory.initialize(

PeerConnectionFactory.InitializationOptions.builder(this)

.setEnableInternalTracer(true)

.createInitializationOptions()

)

3.2.2 创建 PeerConnectionFactory 对象

在全局初始化之后,就可以创建 PeerConnectionFactory 对象了。这个工厂类非常重要,后续的音视频采集、编解码中,会为我们生成各种重要的组建,如 VideoSource,VideoTrack 等。 当然我们现在还不需要编解码相关的配置,只需要简单的创建对象即可:

val peerConnectionFactory =

PeerConnectionFactory.builder()

.createPeerConnectionFactory()

3.2.3 创建 AudioSource

创建了 PeerConnectionFactory 后,就可以把音视频的源丢进去,音频的创建,使用 peerConnectionFactory.createAudioSource(MediaConstraints()),其中 MediaConstraints() 为媒体描述符,我们可以它的 mandatory 去添加一些特殊的配置。然后通过 createAudioTrack 添加音频源。 音频创建:

// create AudioSource and AudioTrack

val audioSource = peerConnectionFactory.createAudioSource(MediaConstraints())

peerConnectionFactory.createAudioTrack(AUDIO_TRACK_ID, audioSource)

其他 AUDIO_TRACK_ID 可以为任意字符串。

3.3.3 创建 VideoSource

对应音频来说,在创建 AUdioSource 的时候,就开始捕获设备音频数据了。对于视频流说来, WebRTC 定义了 VideoCaturer 抽象接口,并实现了三种实现: ScreenCapturerAndroid、CameraCapturer和FileVideoCapturer,分别为从录屏、摄像头及文件中获取视频流,调用startCapture()后将开始获取数据。 因为我们用到相机,所以是 CamreaCapturer ,它有两个子类,Camera1Enumerator 和 Camera2Enumerator ,我们使用 Camera1Enumerator 即可,创建也非常简单:

/**

* 或者 CameraCapture

*/

private fun createCameraCapture(): VideoCapturer? {

val enumerator = Camera1Enumerator(false)

val deviceNames = enumerator.deviceNames

//使用后置摄像头

for (deviceName in deviceNames) {

if (enumerator.isBackFacing(deviceName)) {

val capturer = enumerator.createCapturer(deviceName, null)

if (capturer != null) {

return capturer

}

}

}

return null

}

拿到 VideoCapturer 之后,就可以使用 PeerConnectionFactory 创建视频源了。

createCameraCapture()?.let { camera->

val videoSource = peerConnectionFactory.createVideoSource(camera.isScreencast)

}

3.2.4 VideoSource 与 SurfaceViewRender 配合,实现数据渲染

上面,已经创建了音视频源,接着,我们把 SurfaceViewRender 给到 CamreaCapturer ,实现画面渲染。 viewRender.holder.addCallback(object : SurfaceHolder.Callback { override fun surfaceCreated(holder: SurfaceHolder) { //创建并启动VideoCapturer // 用PeerConnectionFactory创建VideoSource // 用PeerConnectionFactory和VideoSource创建VideoTrack createCameraCapture()?.let { camera-> val videoSource = peerConnectionFactory.createVideoSource(camera.isScreencast)

val eglBaseContext = EglBase.create().eglBaseContext

//拿到 surface工具类,用来表示camera 初始化的线程

val surfaceTextureHelper = SurfaceTextureHelper.create("caputerTHread", eglBaseContext)

//用来表示当前初始化 camera 的线程,和 application context,当调用 startCapture 才会回调。

camera.initialize(surfaceTextureHelper, application, videoSource.capturerObserver)

//开始采集

camera.startCapture(

viewRender.width,

viewRender.height,

30

)

//是否镜像

//viewRender.setMirror(true)

// 初始化 SurfaceViewRender ,这个方法非常重要,不初始化黑屏

viewRender.init(eglBaseContext,null)

//添加视频轨道

val videoTrack =

peerConnectionFactory.createVideoTrack(AUDIO_TRACK_ID, videoSource)

// 添加渲染接收端器到轨道中,画面开始呈现

videoTrack.addSink(viewRender)

}

}

效果如下:

参考: https://webrtc.org.cn/ https://www.jianshu.com/p/eb5fd116e6c8 https://webrtc.googlesource.com/src https://webrtc.org.cn/20190419_tutorial3_webrtc_android/

查看原文