前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
社区首页 >专栏 >WebRTC 如何在安卓系统上采集视频数据

WebRTC 如何在安卓系统上采集视频数据

作者头像
liuzhen007
发布于 2022-02-23 12:08:22
发布于 2022-02-23 12:08:22
2.7K00
代码可运行
举报
文章被收录于专栏:流媒体音视频流媒体音视频
运行总次数:0
代码可运行

目录

前言

正文

  • 摄像头1.0和2.0接口对比
  • Camera1Capturer 接口类
  • Camera2Capturer 接口类

结论

前言

WebRTC 作为一个开源的实时音视频通讯方案,经过多年的发展基本上已经支持了所有的常用终端,比如 windows、mac、AndroidiOS等。我们都知道音视频通讯的前提是采集本地的音频和视频数据信息。今天,我们就来先了解一下 WebRTC 在安卓端是如何采集视频信号的。

正文

安卓设备和苹果iOS设备都属于移动端,在音视频处理的很多地方都是类似的。比如,视频画面的采集和本地预览都会涉及到横屏显示和竖屏显示问题,视频编码时都需要考虑画面角度(0度、90度、180度、270度)问题。

为此,WebRTC 为安卓端和 iOS 端的 SDK 都提供了非常好用的 API 接口类。其中,安卓端的视频采集类是 CameraCapturer,注意,目前安卓端的摄像头采集有两种方案,一种是使用比较传统的 Camera1Capturer 类,另一种是使用比较新的 Camera2Capturer 类。接下来,分别介绍一下。

之所以会出现 Camera1Capturer 类和 Camera2Capturer 类两套不同的API方案,主要是因为谷歌在开发 Android 5.0 时,对摄像头API进行了全新的颠覆性设计,新增了全新的 Camera V2 接口,这些API不仅大幅提高了 Android 系统拍照的功能,还能支持 RAW 照片输出,甚至允许程序调整相机的对焦模式、曝光模式、快门等。

摄像头1.0和2.0接口对比

下面通过一张对比表格来简单了解一下摄像头1.0和2.0接口的不同。

Camera1Capturer 接口类

Camera1Capturer 接口类是如何采集摄像头视频画面的,下面结合代码介绍一下。大致流程如下:

步骤一、打开安卓本地前置摄像头,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
final android.hardware.Camera camera;
try {
  camera = android.hardware.Camera.open(CameraInfo.CAMERA_FACING_FRONT);
} catch (RuntimeException e) {
  callback.onFailure(FailureType.ERROR, e.getMessage());
  return;
}

步骤二、设置本地预览画面的显示图层,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try {
  camera.setPreviewTexture(surfaceTextureHelper.getSurfaceTexture());
} catch (IOException | RuntimeException e) {
  camera.release();
  callback.onFailure(FailureType.ERROR, e.getMessage());
  return;
}

步骤三、设置摄像头参数信息。根据前置摄像头支持的采集参数和系统设置的采集参数进行匹配,计算出最佳且支持的采集参数,其中采集参数涉及画面宽、画面高、画面帧率等,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
final CaptureFormat captureFormat;
try {
  final android.hardware.Camera.Parameters parameters = camera.getParameters();
  captureFormat = findClosestCaptureFormat(parameters, width, height, framerate);
  final Size pictureSize = findClosestPictureSize(parameters, width, height);
  updateCameraParameters(camera, parameters, captureFormat, pictureSize, captureToTexture);
} catch (RuntimeException e) {
  camera.release();
  callback.onFailure(FailureType.ERROR, e.getMessage());
  return;
}

步骤四、设置摄像头采集角度。这是一个预设参数,一般在实际使用过程中会根据当前手机的旋转角度动态变化,可选数值有0度、90度、180度、270度,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
camera.setDisplayOrientation(0 /* degrees */);

步骤五、设置本地视图,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
eglBase = EglBase.create(null /* sharedContext */, EglBase.CONFIG_PLAIN);
localRenderer = (SurfaceViewRenderer) findViewById(R.id.local_renderer);

localRenderer.init(eglBase.getEglBaseContext(), null /* rendererEvents */, EglBase.CONFIG_PLAIN, new GlRectDrawer());

videoCapturerSurfaceTextureHelper =
    SurfaceTextureHelper.create("VideoCapturerThread", eglBase.getEglBaseContext());

步骤六、设置采集数据回调方法,参考代码如下:

oesTextureId = GlUtil.generateTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES); surfaceTexture = new SurfaceTexture(oesTextureId); setOnFrameAvailableListener(surfaceTexture, (SurfaceTexture st) -> { hasPendingTexture = true; tryDeliverTextureFrame(); }, handler); 通过上面的六个简单步骤,我们就可以完成在安卓系统上摄像头采集和本地画面预览的效果。接下来,我们看一下 Camera2Capturer 接口类如何完成相同的功能。

Camera2Capturer 接口类

Camera2Capturer 接口类基于安卓系统的 Camera V2 接口开发封装的,原因是谷歌在 Android 5.0 中对摄像头API进行了全新的颠覆性设计,不仅大幅提高了 Android 系统拍照的功能,还能支持 RAW 照片输出,甚至允许程序调整相机的对焦模式、曝光模式、快门等。

那么,WebRTC 中又是如何利用 Camera2Capturer 接口类采集安卓系统的摄像头画面的呢?下面也结合代码分步骤介绍一下。

步骤一、根据安卓设备的相机ID打开本地摄像头,同时设置 CameraStateCallback 回调方法,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
try {
  cameraManager.openCamera(cameraId, new CameraStateCallback(), cameraThreadHandler);
} catch (CameraAccessException e) {
  reportError("Failed to open camera: " + e);
  return;
}

步骤二、设置本地预览画面的显示图层,根据步骤一中设置的摄像头回调事件 onOpened 进行设置,从而绑定图层和摄像头的关系,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
  surfaceTextureHelper.setTextureSize(captureFormat.width, captureFormat.height);
  surface = new Surface(surfaceTextureHelper.getSurfaceTexture());
  try {
    camera.createCaptureSession(
        Arrays.asList(surface), new CaptureSessionCallback(), cameraThreadHandler);
  } catch (CameraAccessException e) {
    reportError("Failed to create capture session. " + e);
    return;
  }

步骤三、设置摄像头相关的采集参数,同样是根据上一步中设置的回调事件,不过这次是 onConfigured 进行设置,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   try {
    final CaptureRequest.Builder captureRequestBuilder =
        cameraDevice.createCaptureRequest(CameraDevice.TEMPLATE_RECORD);
    captureRequestBuilder.set(CaptureRequest.CONTROL_AE_TARGET_FPS_RANGE,
        new Range<Integer>(captureFormat.framerate.min / fpsUnitFactor,
            captureFormat.framerate.max / fpsUnitFactor));
    captureRequestBuilder.set(
        CaptureRequest.CONTROL_AE_MODE, CaptureRequest.CONTROL_AE_MODE_ON);
    captureRequestBuilder.set(CaptureRequest.CONTROL_AE_LOCK, false);
    chooseStabilizationMode(captureRequestBuilder);
    chooseFocusMode(captureRequestBuilder);
    captureRequestBuilder.addTarget(surface);
    session.setRepeatingRequest(
        captureRequestBuilder.build(), new CameraCaptureCallback(), cameraThreadHandler);
  } catch (CameraAccessException e) {
    reportError("Failed to start capture request. " + e);
    return;
  }

步骤四、设置视频采集数据回调方法,通过监听渲染图层中的 startListening 方法回调的视频帧得到视频数据,然后通知其他模块,参考代码如下:

代码语言:javascript
代码运行次数:0
运行
AI代码解释
复制
   surfaceTextureHelper.startListening((VideoFrame frame) -> {
    checkIsOnCameraThread();

    if (state != SessionState.RUNNING) {
      Logging.d(TAG, "Texture frame captured but camera is no longer running.");
      return;
    }

    if (!firstFrameReported) {
      firstFrameReported = true;
      final int startTimeMs =
          (int) TimeUnit.NANOSECONDS.toMillis(System.nanoTime() - constructionTimeNs);
      camera2StartTimeMsHistogram.addSample(startTimeMs);
    }
    final VideoFrame modifiedFrame =
        new VideoFrame(CameraSession.createTextureBufferWithModifiedTransformMatrix(
                           (TextureBufferImpl) frame.getBuffer(),
                           /* mirror= */ isCameraFrontFacing,
                           /* rotation= */ -cameraOrientation),
            /* rotation= */ getFrameOrientation(), frame.getTimestampNs());
    events.onFrameCaptured(Camera2Session.this, modifiedFrame);
    modifiedFrame.release();
  });

再后续的流程就和 Camera1Capturer 接口类相同了,这里就不再赘述了。

需要注意的是,安卓系统采集完摄像头的视频画面后,处理逻辑一般会一分为二,一部分数据流用来本地预览显示,一部分数据流送到编码模块,进行数据组包并发送给对端。因此,我们在使用过程中经常会遇到本地预览画面没有问题,但是传输到远端的视频画面出现问题,或者是本地预览画面有问题,但是传输到远端的视频却是正常的,类似的问题有花屏、显示比例、裁剪等。

结论

本文基本上已经介绍了 WebRTC 是如何在安卓系统上采集本地摄像头画面的,但是,这仅仅是众多流程中一个小环节,后续还有预览、编码、组包、传输、解包、解码、渲染等过程。关于别的部分的内容,我们在后续章节再继续介绍。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2022-02-07,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 玩转音视频 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
JavaScript笔记
因为IE浏览器下不支持年月日日之间有横线连接的形式,所以在IE浏览器下做日期格式化时,需要用下面的正则表达式先将 横线转换成斜线,例如将2019-12-31 10:48:30 转成 2019/12/31 10:48:30
用户3880999
2023/04/13
2590
JavaScript笔记
ASP.NET AJAX(12)__浏览器兼容功能判断浏览器的类型和版本Sys.Browser针对DOM元素的兼容操作针对DOM事件的兼容操作
目前,常见的浏览器IE(6,8,9),chrome,firefox,safari等,还有国内的一些曾经靠恐吓用户来提高使用率的某浏览器(河蟹社会),这些浏览器对于Javascript的语言特性实现大致是相同的,但是对于DOM操作方式却大相径庭,所以我们通常需要自己对不同浏览器对于DOM的操作方式进行分而治之,或者我们往往是使用一些Javascript框架提供的兼容功能,当然也有我们的Microsoft AJAX Library 判断浏览器的类型和版本 浏览器兼容层的优势在于,我们可以使用同样的编码方式,让相
小白哥哥
2018/03/07
1.2K0
ASP.NET AJAX(12)__浏览器兼容功能判断浏览器的类型和版本Sys.Browser针对DOM元素的兼容操作针对DOM事件的兼容操作
JavaScript基础学习--零碎
1、如果WINDOW对象是常规HTML页面,TOP就是SELF       var top = document.getElementById('top');      top.innerHTML  
用户1148399
2018/01/09
1K0
DOM事件第二弹(UIEvent事件)
此文章主要总结UIEvent相关的事件,如有不对的地方,欢迎指正。 一、uitls.js(绑定事件公共类) var fixs = { 'focusin': { standard: 'focus', ie: 'focusin' }, 'focusout':{ standard: 'blur', ie: 'foucsout' }, 'input': { standard: 'input',
sam dragon
2018/01/17
2.9K0
DOM事件第二弹(UIEvent事件)
java实现靠边隐藏窗口
说明: 由于个人精力有限,现将部分研究的代码开源出来, 代码或思路有部分来源于网络,有些代码还没来得及整理, 如果您对这其中的部分代码、思路整理出了一些文档,希望您能够联系我,分享您的成果 我将在下一版中更新您提供的一些文档. 开源不是靠一个人的坚持能完成的事,希望在不涉及版权问题的情况下,贡献您一份力量 版权归原作者所有,如果您有什么好的想法或建议,欢迎联系我 github: https://github.com/darknessitachi/swing-autoHiddenFrame AutoHiddenFrame.java
全栈程序员站长
2022/07/19
2.5K0
java实现靠边隐藏窗口
大佬日常必备的JS工具函数大全
前面我们分享过一篇:前端常用的60多种JavaScript工具方法,很多人觉得有用,今天再分享一篇类似的东西,如果文章和笔记能带您一丝帮助或者启发,请不要吝啬你的赞和收藏,你的肯定是我前进的最大动力
前端开发博客
2020/11/04
1.5K0
「JavaScript 」动画基础 - 02
请注意,本文编写于 2085 天前,最后修改于 173 天前,其中某些信息可能已经过时。
曼亚灿
2023/05/17
3730
前端常见技术点-HTML扫盲(17问)
根据 <!DOCTYPE> 是否存在选择呈现模式,被称为 <!DOCTYPE> 切换或 <!DOCTYPE> 侦测。
用户5997198
2019/08/09
5990
html左侧浮动广告代码,jQuery 浮动广告实现代码[通俗易懂]
$(this).scroll(function() { // 页面发生scroll事件时触发
全栈程序员站长
2022/11/05
4.6K0
浅谈JavaScript的事件(事件模拟)
  事件经常由操作或者通过浏览器功能触发,通过JavaScript也可以触发元素的事件。通过JavaScript触发事件,也称为事件的模拟。 DOM中事件模拟   可以document的createEvent方法创建event对象。这个方法接收一个参数,即表示要创建的事件类型的字符串。在DOM2级中,所有这些字符串都使用英文复数形式,在DOM3级中都变成了单数。这几个字符串如下:UIEvents,一般化的ui事件,鼠标事件和键盘事件都继承于该事件,在DOM3级中是UIEvent;MouseEvents,一般
水击三千
2018/02/27
2K0
常见网页特效案例
节流阀目的:当上一个函数动画内容执行完毕,再去执行下一个函数动画,让事件无法连续触发。
清出于兰
2020/10/26
2.3K0
「JavaScript 」动画基础 - 01
请注意,本文编写于 2086 天前,最后修改于 174 天前,其中某些信息可能已经过时。
曼亚灿
2023/05/17
5100
「JavaScript 」动画基础 - 01
前端-如何精确统计页面停留时长
页面停留时间(Time on Page)简称 Tp,是网站分析中很常见的一个指标,用于反映用户在某些页面上停留时间的长短,传统的Tp统计方法会存在一定的统计盲区,比如无法监控单页应用,没有考虑用户切换Tab、最小化窗口等操作场景。 基于上述背景,重新调研和实现了精确统计页面停留时长的方案,需要 兼容单页应用和多页应用,并且不耦合或入侵业务代码。
grain先森
2019/03/29
9.9K0
前端-如何精确统计页面停留时长
深入理解 DOM 事件机制
本文主要介绍 DOM 事件级别、DOM 事件模型、事件流、事件代理和 Event 对象常见的应用,希望对你们有些帮助和启发!
小生方勤
2019/06/02
2.8K1
前端day15-JS(WebApi)学习笔记(三大家族、事件对象、getComputedStyle)
offsetWidth、offsetHeight、offsetParent、offsetLeft、offsetTop
帅的一麻皮
2020/05/04
7030
前端day15-JS(WebApi)学习笔记(三大家族、事件对象、getComputedStyle)
DOM的事件模拟
只有根据DOM2级事件实现这些事件的浏览器才返回true,以非标准方式支持这些事件的浏览器会返回false;
meteoric
2018/11/15
1K0
JavaScript 鼠标拖拽div 改变其大小
转自: http://www.cnblogs.com/yushang/archive/2013/03/19/2968782.html
acoolgiser
2019/01/17
1.7K0
DOM、BOM一些兼容性问题
汇集了许多关于DOM和BOM的兼容性问题,主要是关于 IE 浏览器的,考虑到浏览器迭代,这里主要列出了 IE8 以及之后的浏览器版本。 IE8 浏览器在 2008年推出,距现在(2019)已有11年之久,已经是很老的一款浏览器了。但是在一些项目中,可能仍需要考虑到兼容性,如果兼容到 IE8 已经是很兼容了,毕竟该浏览器也几乎没多少市场份额了。多是一些机构或政府部门在使用。而有些兼容性问题也可能是其它浏览器之间的差异,比如 Chrome 和 FireFox 对于鼠标滚轮事件对象的滚轮方向判断方式不同,Chrome使用 wheelDelta,而FireFox 则采用 detail 做判断。下面将一一说明或做补充实现来尽量弥补浏览器之间的差异。其实大部分就是为了兼容 IE 早期浏览器。
多云转晴
2019/10/23
1.6K0
DOM、BOM一些兼容性问题
JS魔法堂:定义页面的Dispose方法——[before]unload事件启示录
前言  最近实施的同事报障,说用户审批流程后直接关闭浏览器,操作十余次后系统就报用户会话数超过上限,咨询4A同事后得知登陆后需要显式调用登出API才能清理4A端,否则必然会超出会话上限。  即使在页面上增添一个登出按钮也无法保证用户不会直接关掉浏览器,更何况用户已经习惯这样做,增加功能好弄,改变习惯却难啊。这时想起N年用过的window.onbeforeunload和window.onunload事件。  本文记录重拾这两个家伙的经过,以便日后用时少坑。 为网页写个Dispose方法  C#中我们会将释
^_^肥仔John
2018/01/18
2.4K0
前端成神之路-WebAPIs05
offset 翻译过来就是偏移量, 我们使用 offset系列相关属性可以动态的得到该元素的位置(偏移)、大小等。
海仔
2021/01/05
1.5K0
前端成神之路-WebAPIs05
相关推荐
JavaScript笔记
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档
查看详情【社区公告】 技术创作特训营有奖征文