用 WebRTC 创建相机预览,不到 50 行核心代码就可以轻松搞定了。

WebRTC 依赖版本

直接使用官方给的版本就好了,不需要再去额外编译。

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

后面都会使用该版本做测试的。

相机权限申请

WebRTC 虽说功能强大,代码简洁,但是并没有封装一个应用权限申请的接口,这需要自己去操作了。

相机预览

有个段子是把大象放进冰箱有多少步骤,共三步,打开冰箱,塞进大象,关上冰箱。

用 WebRTC 创建相机预览和上面的段子步骤一样,打开相机设置接收开启预览

至于中间的繁琐步骤,比如相机创建的内部实现,预览绘制的内部实现都不用去关心了,调用好接口,设置好参数就行。

创建相机实例

在 WebRTC 中相机实例统一实现了 VideoCapturer 接口,不管是 Camera1 还是 Camera2 。

public interface VideoCapturer {void initialize(SurfaceTextureHelper var1, Context var2, CapturerObserver var3);void startCapture(int var1, int var2, int var3);void stopCapture() throws InterruptedException;void changeCaptureFormat(int var1, int var2, int var3);void dispose();boolean isScreencast();
}

该接口也比较简单,只需要相机实例对外提供一些简单的预览能力就好。

创建相机实例的代码如下:

private fun createVideoCapture(): VideoCapturer? {val enumerator = Camera1Enumerator(false)val deviceNames = enumerator.deviceNamesfor (deviceName in deviceNames) {if (enumerator.isFrontFacing(deviceName)) {val videoCapture = enumerator.createCapturer(deviceName, null)if (videoCapture != null) {return videoCapture}}}return null
}

Camera1Enumerator 是用来枚举设备上有多少摄像头的,一般只有前置和后置两种,,也可以用 Camera2Enumerator 来获取 Camera2 的相机调用。

deviceNames 对应 getDeviceNames 方法,只不过用了 kotlin 变成缩写了,它表示设备上的摄像头集合,这个接口其实就已经屏蔽了 Camera1 和 Camera2 内部检索不同摄像头的实现。

满足前后置条件时,调用 createCapturer 来创建相机实例就好了。

相机预览接收

需要有分别对应的组件去接收相机输出的画面并且显示到屏幕上。

显示到屏幕上的控件既不是 SurfaceView 也不是 TextureView ,而是 WebRTC 自己封装的控件 SurfaceViewRenderer

它其实就是继承了 SurfaceView ,并且内部有个 SurfaceEglRenderer 变量,用来将外界传递的 VideoFrame 绘制到屏幕上。

<org.webrtc.SurfaceViewRenderer android:id="@+id/localView"android:layout_width="match_parent"android:layout_height="match_parent"/>// SurfaceViewRenderer 的绘制方法
public void onFrame(VideoFrame frame) {this.eglRenderer.onFrame(frame);
}

SurfaceEglRenderer 也是走的 OpenGL 渲染进行预览,在创建 OpenGL 环境可以决定是否要以 ShareContext 的形式创建。

val eglBaseContext = EglBase.create().eglBaseContext
localView.init(eglBaseContext, null)

接收相机预览流的组件就是 SurfaceTexture ,只不过 WebRTC 将它包装到了 SurfaceTextureHelper 变量中。

创建 SurfaceTextureHelper 的方法如下:

val eglBaseContext = EglBase.create().eglBaseContext
val surfaceTextureHelper = surfaceTextureHelper.create("CaptureThread", eglBaseContext)

SurfaceTextureHelper 内部会创建一个线程,并且也可以通过外部传递 EGLContext 以决定是否要走 ShareContext 方式的调用。

有了相机实例 VideoCapturer 和接收预览的组件 SurfaceTextureHelper ,就可以将他们关联起来:

videoCapture?.initialize(surfaceTextureHelper, applicationContext, videoSource?.capturerObserver)
videoCapture?.startCapture(480, 640, 30)

videoCapture 调用 initialize 方法实现两者的关联,同时 startCapture 方法决定相机采集的宽高和帧率。

开启相机预览

在开启相机预览时,就需要涉及到和 WebRTC 相关内容了。

WebRTC 本身是用来做即时通信的,它将音频和视频流都抽象成了一个个轨道 MediaStreamTrack ,有音频轨 AudioTrack 也有视频轨 VideoTrack。

而轨道上的内容来源就对应 MedisSource ,有音频源 AudioSource 和视频源 VideoSource 。

相机输出就是提供视频源的,需要将 VideoCapturer 和 VideoSource 关联起来。

在上面代码中 initialize 方法实际上就建立了关联。

videoSource = videoCapture?.isScreencast?.let { factory.createVideoSource(it) }
videoCapture?.initialize(surfaceTextureHelper, applicationContext, videoSource?.capturerObserver)

initialize 方法的最后一个参数就是一个回调,典型的观察者模式,VideoCapturer 相关的状态都会通过 capturerObserver 通知到 VideoSource ,从而实现关联。

创建 videoSource 的 factory ,对应的就是一条即时通信端对端的连接,而 videoTrack 和 audioTrack 就是这条连接上的内容。

创建 factory 的代码比较固定:

val options = PeerConnectionFactory.InitializationOptions.builder(this).createInitializationOptions();
PeerConnectionFactory.initialize(options)
factory = PeerConnectionFactory.builder().createPeerConnectionFactory()

创建 VideoTrack 的代码如下,需要将视频源和视频轨道关联起来。

videoTrack = factory.createVideoTrack("101",videoSource)

完成了所有的创建和关联之后,就可以开启预览了。需要将视频轨道内容显示到画面上,也就是上面的 SurfaceViewRenderer 控件上。

videoTrack?.addSink(localView)

完整代码示例:

class CameraActivity : AppCompatActivity() {private lateinit var factory: PeerConnectionFactoryprivate var videoCapture:VideoCapturer? = nullprivate var videoSource: VideoSource? = nullprivate var videoTrack: VideoTrack? = nullprivate lateinit var localView:SurfaceViewRendereroverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)setContentView(R.layout.activity_camera)localView = findViewById(R.id.localView)val options = PeerConnectionFactory.InitializationOptions.builder(this).createInitializationOptions();PeerConnectionFactory.initialize(options)factory = PeerConnectionFactory.builder().createPeerConnectionFactory()val eglBaseContext = EglBase.create().eglBaseContextval surfaceTextureHelper = SurfaceTextureHelper.create("CaptureThread", eglBaseContext)videoCapture = createVideoCapture()videoSource = videoCapture?.isScreencast?.let { factory.createVideoSource(it) }videoCapture?.initialize(surfaceTextureHelper, applicationContext, videoSource?.capturerObserver)videoCapture?.startCapture(480, 640, 30)localView.setMirror(true)localView.init(eglBaseContext, null)videoTrack = factory.createVideoTrack("101",videoSource)videoTrack?.addSink(localView)}private fun createVideoCapture(): VideoCapturer? {val enumerator = Camera1Enumerator(false)val deviceNames = enumerator.deviceNamesfor (deviceName in deviceNames) {if (enumerator.isFrontFacing(deviceName)) {val videoCapture = enumerator.createCapturer(deviceName, null)if (videoCapture != null) {return videoCapture}}}return null}
}

不到 50 行代码就完成了相机预览,Github 仓库地址后续会给出。

这篇文章就先讲到这里,持续更新中~~

系列文章集合

WebRTC 系列文章:

  1. WebRTC & Android 开发学习环境搭建~

技术交流,欢迎加我微信:ezglumes ,拉你入技术交流群。

推荐阅读:

音视频面试基础题

OpenGL ES 学习资源分享

开通专辑 | 细数那些年写过的技术文章专辑

NDK 学习进阶免费视频来了

推荐几个堪称教科书级别的 Android 音视频入门项目

觉得不错,点个在看呗~

WebRTC 系列1--创建相机预览相关推荐

  1. Android Camera API/Camera2 API 相机预览及滤镜、贴纸等处理

    Android Lollipop 增加了Camera2 API,并将原来的Camera API标记为废弃了.相对原来的Camera API来说,Camera2是重新定义的相机 API,也重构了相机 A ...

  2. android surfaceview camera,android – 如何在SurfaceView上显示相机预览?

    要使用Camera2 API从相机显示预览,您应该执行以下步骤: >获得使用相机设备的权限 >使用CameraManager打开与相机的连接 准备表面预览 >使用打开的相机设备和所需 ...

  3. 【Camera相机开发】实现相机预览

    文章目录 认识 Parameters 设置预览尺寸 添加预览 Surface 开启和关闭预览 校正预览画面方向 自然方向 设备方向 局部坐标系 屏幕方向 摄像头传感器方向 画面方向校正 适配预览比例 ...

  4. Android用MediaCodec将相机预览帧编码成MP4视频

    文章目录 知识预备 实现思路 获取图像数据帧 编码视频 初始化编码器 编码转换 编码视频 问题记录 最近项目中,有一个在扫码同时录视频的需求.扫码框架是通过摄像头 onPreviewFrame方法获取 ...

  5. Android相机预览设置适配及显示方式

    Android相机的部分工作原理. 预览流程 相机预览是Android Camera最常用的功能之一,它是很多功能重要的输入,例如扫码.AR等. 一般而言,相机预览的整体流程,可以通过下图表示: 其中 ...

  6. Camera2 打开相机预览界面

    camera2 是21之后的api用于代替Camera,提供更加牛X的对相机hardware操作的api 参考资料:http://www.jcodecraeer.com/a/anzhuokaifa/a ...

  7. Android 相机方向传感,Nexus 5x反向横向传感器修复在Android相机预览...

    我是Android开发中的新手,所以如果我的问题很简单,我会提前道歉.在我的应用程序的一部分,我需要我的后置摄像头的实时预览,所以我创建了一个自定义类,扩展SurfaceView并实现SurfaceH ...

  8. qr码是二维码码_SwiftUI中的相机预览和QR码扫描仪

    qr码是二维码码 In this post, I will guide you through the creation of a QR-code scanner for iOS, using Swi ...

  9. Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解

    Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解 目录 Android AR开发实践之七:OpenGLES相机预览背景绘制源码详解 一.OpenGL ES渲染管线 1.基本处 ...

最新文章

  1. SpringBoot在Tomcat下面启动,访问路径
  2. 2亿美元投入+软硬件新服务!华为加速构建计算产业生态,侯金龙:要与开发者共成长...
  3. C#调用dll提示试图加载格式不正确的程序解决方法
  4. java 序列化概念和作用_结合代码详细解读Java序列化与反序列化概念理解
  5. Android --- Android Studio 内无法直接运行 main 方法
  6. Automatic IE Testing With Python
  7. webParts与Web部件
  8. mac用什么写python程序_mac下,有哪些python开发工具可用
  9. shell 除法 小数点
  10. C++malloc,calloc,realloc,free函数
  11. Vue生命周期钩子函数
  12. Hibernate 缓存机制详细分析
  13. cocos2d-x学习之旅(十一):制作TXM游戏地图,并加载到游戏场景中
  14. dnf时装补丁教程_dnf时装补丁怎么用?DNF时装补丁教程
  15. CocosCreator之KUOKUO带你做刚体移动与物品拾取到背包
  16. mysqloffset什么意思_MySQL中OFFSET和FETCH的详解
  17. Lambda表达式的省略
  18. vue spa php,在Vue中有关SPA首屏加载优化(详细教程)
  19. TypeScript 开发环境的搭建与数据类型
  20. 【Unity3D进阶4-14】Unity3D 连接MySQL数据库

热门文章

  1. 2022-2028全球与中国防弹轮胎市场现状及未来发展趋势
  2. 太阳光轨迹软件_太阳光自动跟踪设计_图文(精)
  3. win7计算机里不显示摄像头,揪出消失不见的Win7系统摄像头
  4. edge浏览器主页被hao123劫持解决办法
  5. 计算机科学技术与工程技术,科学技术与工程杂志
  6. 十大排序算法详解(一)冒泡排序、选择排序、插入排序、快速排序、希尔排序
  7. いもけんぴ 三作 汉化补丁
  8. 2维,3维向量单位化
  9. 数据库系统原理与应用教程(070)—— MySQL 练习题:操作题 101-109(十四):查询条件练习
  10. IPP图像处理常用函数说明