今天要实现的效果:
一. 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 添加权限
<uses-permission android:name="android.permission.CAMERA" />
<uses-permission android:name="android.permission.RECORD_AUDIO" />
三. 实现相机功能
以前我们使用相机,都是通过 camera1/2/x 去打开相机,再设置 Surface ,这样相机采集的数据就会在 Surface 显示了。
如果你想使用原生相机采集,可参考历史文章。
Android 音视频开发(二) – Camera1 实现预览、拍照功能
Android 音视频开发(三) – Camera2 实现预览、拍照功能
Android 音视频开发(四) – CameraX 实现预览、拍照功能
3.1 创建 Surface
所以,在使用相机时,需要一个承载画面的组建,在WebRTC 中,我们使用 SufaceViewRender ,它是 SurfaceView 的子类。如下:
<?xml version="1.0" encoding="utf-8"?>
<androidx.appcompat.widget.LinearLayoutCompat xmlns:android="http://schemas.android.com/apk/res/android"
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">
<org.webrtc.SurfaceViewRenderer
android:id="@+id/viewRender"
android:layout_width="match_parent"
android:layout_height="match_parent"/>
</androidx.appcompat.widget.LinearLayoutCompat>
它是 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/
转载:https://blog.csdn.net/u011418943/article/details/127108642