Android Camera系列(二):TextureView+Camera
两岸猿声啼不住,轻舟已过万重山—李白
-
Android Camera系列(一):SurfaceView+Camera
-
Android Camera系列(二):TextureView+Camera
-
Android Camera系列(三):GLSurfaceView+Camera
本系列主要讲述Android开发中Camera的相关操作、预览方式、视频录制等,项目结构代码耦合性低,旨在帮助大家能从中有所收获(方便copy :) ),对于个人来说也是一个总结的好机会
本章我们来讲解TextureView进行Camera预览,基于上一篇Android Camera系列(一):SurfaceView+Camera的成果,我们已经对Camera进行了封装,CameraManager拿来直接使用就好
一.TextureView使用
优点:
支持复杂的视图变换:与SurfaceView不同,TextureView支持包括缩放、旋转在内的各种变换操作,这些操作在视图层次中进行,使得TextureView更加灵活和适应复杂的用户界面需求。
缺点:
性能不如SurfaceView:在低端设备或高GPU负荷情况下,可能会出现掉帧或卡顿现象,低性能可以给一个参考指标大概就是10年前的设备,或者是给欠发达国家提供的低性能的海外设备
TextureView作为Camera的预览视图与SurfaceView不同,TextureView要获取SurfaceTexture,并传递给Camera作为预览容器
CameraManager针对SurfaceTexture的预览接口
/*** 使用TextureView预览Camera** @param surface*/@Overridepublic synchronized void startPreview(SurfaceTexture surface) {Logs.i(TAG, "startPreview...");if (isPreviewing) {return;}if (mCamera != null) {try {mCamera.setPreviewTexture(surface);if (!mPreviewBufferCallbacks.isEmpty()) {mCamera.addCallbackBuffer(new byte[mPreviewWidth * mPreviewHeight * 3 / 2]);mCamera.setPreviewCallbackWithBuffer(mPreviewCallback);}mCamera.startPreview();onPreview(mPreviewWidth, mPreviewHeight);} catch (Exception e) {onPreviewError(CAMERA_ERROR_PREVIEW, e.getMessage());}}}
- 自定义CameraTextureView继承TextureView
- 实现
TextureView.SurfaceTextureListener
接口,并在CameraTextureView初始化时设置回调 - 实现自定义CameraCallback接口,监听Camera状态
- 一定要实现
onResume
和onPause
接口,并在对应的Activity生命周期中调用。这是所有使用Camera的bug的源头
/*** 摄像头预览TextureView** @author xiaozhi* @since 2024/8/22*/
public abstract class BaseTextureView extends TextureView implements TextureView.SurfaceTextureListener, CameraCallback, BaseCameraView {private static final String TAG = BaseTextureView.class.getSimpleName();private Context mContext;private SurfaceTexture mSurfaceTexture;private boolean isMirror;private boolean hasSurface; // 是否存在摄像头显示层private ICameraManager mCameraManager;private int mRatioWidth = 0;private int mRatioHeight = 0;private int mTextureWidth;private int mTextureHeight;public BaseTextureView(Context context) {super(context);init(context);}public BaseTextureView(Context context, AttributeSet attrs) {super(context, attrs);init(context);}public BaseTextureView(Context context, AttributeSet attrs, int defStyleAttr) {super(context, attrs, defStyleAttr);init(context);}private void init(Context context) {mContext = context;mCameraManager = createCameraManager(context);mCameraManager.setCameraCallback(this);setSurfaceTextureListener(this);}public abstract ICameraManager createCameraManager(Context context);/*** 获取摄像头工具类** @return*/public ICameraManager getCameraManager() {return mCameraManager;}/*** 是否镜像** @return*/public boolean isMirror() {return isMirror;}/*** 设置是否镜像** @param mirror*/public void setMirror(boolean mirror) {isMirror = mirror;requestLayout();}private void setAspectRatio(int width, int height) {if (width < 0 || height < 0) {throw new IllegalArgumentException("Size cannot be negative.");}mRatioWidth = width;mRatioHeight = height;requestLayout();}@Overrideprotected void onMeasure(int widthMeasureSpec, int heightMeasureSpec) {super.onMeasure(widthMeasureSpec, heightMeasureSpec);int width = MeasureSpec.getSize(widthMeasureSpec);int height = MeasureSpec.getSize(heightMeasureSpec);if (0 == mRatioWidth || 0 == mRatioHeight) {setMeasuredDimension(width, width * 4 / 3);} else {if (width < height * mRatioWidth / mRatioHeight) {setMeasuredDimension(width, width * mRatioHeight / mRatioWidth);} else {setMeasuredDimension(height * mRatioWidth / mRatioHeight, height);}}if (isMirror) {Matrix transform = new Matrix();transform.setScale(-1, 1, getMeasuredWidth() / 2, 0);setTransform(transform);} else {setTransform(null);}}/*** 获取SurfaceTexture** @return*/@Overridepublic SurfaceTexture getSurfaceTexture() {return mSurfaceTexture;}@Overridepublic void onSurfaceTextureAvailable(SurfaceTexture surfaceTexture, int width, int height) {Logs.i(TAG, "onSurfaceTextureAvailable.");mTextureWidth = width;mTextureHeight = height;mSurfaceTexture = surfaceTexture;hasSurface = true;openCamera();}@Overridepublic void onSurfaceTextureSizeChanged(SurfaceTexture surfaceTexture, int width, int height) {Logs.i(TAG, "onSurfaceTextureSizeChanged.");}@Overridepublic boolean onSurfaceTextureDestroyed(SurfaceTexture surfaceTexture) {Logs.v(TAG, "onSurfaceTextureDestroyed.");closeCamera();hasSurface = false;return true;}@Overridepublic void onSurfaceTextureUpdated(SurfaceTexture surfaceTexture) {}/*** 打开摄像头并预览*/@Overridepublic void onResume() {if (hasSurface) {// 当activity暂停,但是并未停止的时候,surface仍然存在,所以 surfaceCreated()// 并不会调用,需要在此处初始化摄像头openCamera();}}/*** 停止预览并关闭摄像头*/@Overridepublic void onPause() {closeCamera();}@Overridepublic void onDestroy() {}/*** 初始化摄像头,较为关键的内容*/private void openCamera() {if (mSurfaceTexture == null) {Logs.e(TAG, "mSurfaceTexture is null.");return;}if (mCameraManager.isOpen()) {Logs.w(TAG, "Camera is opened!");return;}mCameraManager.openCamera();}private void closeCamera() {mCameraManager.releaseCamera();}@Overridepublic void onOpen() {mCameraManager.startPreview(mSurfaceTexture);}@Overridepublic void onOpenError(int error, String msg) {}@Overridepublic void onPreview(int previewWidth, int previewHeight) {if (mTextureWidth > mTextureHeight) {setAspectRatio(previewWidth, previewHeight);} else {setAspectRatio(previewHeight, previewWidth);}}@Overridepublic void onPreviewError(int error, String msg) {}@Overridepublic void onClose() {}
}
1.Camera操作时机
- 在
onSurfaceTextureAvailable
回调中打开Camera,在onSurfaceTextureDestroyed
中关闭摄像头 - 一定要记得在
onResume
中也打开摄像头,onPause
中关闭摄像头。
再次强调
onResume
和onPuase
中一定也要对Camera进行打开关闭操作。SurfaceTexture的回调和SurfaceHolder不同,页面不显示时SurfaceHolder会destroy,而SurfaceTexture并不会回调onSurfaceTextureDestroyed
。如果我们按Home键回到桌面打开系统相机,然后再次进入我们的应用你会发现预览黑屏,这就是没有正确在生命周期中关闭Camera导致。这也是很多别的开源项目正常用没问题,随便退出再进来总有Bug的源头。
2.TextureView计算大小
基本上和CameraSurfaceView一样,我们在onPreview
回调中设置TextureView的大小和比例即可
二.最后
本文介绍了Camera+TextureView
的基本操作及关键代码。本章内容不是很多,这得益于我们上一章定义好了CameraManager
的功劳。下一章介绍GLSurfaceView同样也是用第一章的CameraManager,嘻嘻嘻。
lib-camera库包结构如下:
包 | 说明 |
---|---|
camera | camera相关操作功能包,包括Camera和Camera2。以及各种预览视图 |
encoder | MediaCdoec录制视频相关,包括对ByteBuffer和Surface的录制 |
gles | opengles操作相关 |
permission | 权限相关 |
util | 工具类 |
每个包都可独立使用做到最低的耦合,方便白嫖
github地址:https://github.com/xiaozhi003/AndroidCamera,https://gitee.com/xiaozhi003/android-camera
参考:
- https://github.com/afei-cn/CameraDemo
- https://github.com/saki4510t/UVCCamera
- https://github.com/google/grafika
相关文章:

Android Camera系列(二):TextureView+Camera
两岸猿声啼不住,轻舟已过万重山—李白 Android Camera系列(一):SurfaceViewCamera Android Camera系列(二):TextureViewCamera Android Camera系列(三):GLS…...

DFS算法专题(一)——二叉树中的深搜【回溯与剪枝的初步注入】
目录 1、DFS算法简介 2、算法实战应用【leetcode】 2.1 计算布尔二叉树的值 2.1.1 算法原理 2.1.2 算法代码 2.2 求根节点到叶节点数字之和 2.2.1 算法原理 2.2.2 算法代码 2.3 二叉树剪枝 2.3.1 算法原理 2.3.2 算法代码 2.4 验证二叉搜索树 2.4.1 算法原理 …...

AWS SES服务 Golang接入教程(排坑版)
因为刚来看的时候 也迷迷糊糊的 所以 先讲概念 再上代码 一 基础设置 这里需要完成两个最基础的设置任务 1 是验证至少一个收件电子邮箱 2 【很关键】是验证发送域。即身份里的域类型的身份。(可以理解为配置你的域名邮箱服务器(SMPT)为亚马…...

Vite + Vue3 +Vant4出现Toast is not a function
今天写前端的时候出现了这个问题搞了我一会 搜集原因: 1:是vant版本的问题,Toast()的方法是vant3版本的写法,而我用的是vant4,vant4中的写法改成了showToast()方法,改正过来 import {showToast} from "vant"; 发现还是…...

【MATLAB】模拟退火算法
模拟退火算法的MATLAB实现 模拟退火算法简介模拟退火算法应用实例关于计算结果 模拟退火算法简介 1982年,Kirkpatrick 将退火思想引入组合优化领域,提出了一种能够有效解决大规模组合优化问题的算法,尤其对 NP 完全问题表现出显著优势。模拟…...
什么是Kubernetes RBAC?
什么是Kubernetes RBAC? 1、什么是RBAC?2、核心组件3、优势💖The Begin💖点点关注,收藏不迷路💖 在Kubernetes集群中,RBAC(基于角色的访问控制)是保障系统安全的关键。它通过角色和绑定管理不同实体对资源的访问权限,具有显著优势: 1、什么是RBAC? RBAC是Kube…...
在Spring Boot中通过自定义注解、反射以及AOP(面向切面编程)
在Spring Boot中,通过自定义注解、反射以及AOP(面向切面编程)来动态修改请求参数是一种高级且强大的技术组合,它允许开发者在不修改原始方法实现的情况下,对方法的执行过程进行干预和定制。这种技术通常用于日志记录、…...

安防监控视频平台LntonAIServer视频智能分析平台新增视频质量诊断功能
随着安防行业的快速发展,视频监控系统已经成为维护公共安全和个人隐私的重要工具。然而,由于各种因素的影响,视频流的质量可能会受到影响,从而导致监控效果不佳。为了解决这一问题,LntonAIServer推出了全新的视频质量诊…...

vscode从本地安装插件
1. 打开VSCode。 2. 点击左侧菜单中的“扩展”(或按CtrlShiftX)。 3. 点击“更多操作”(三个点)> “从VSIX安装”。 4. 选择下载的.vsix文件。 5. 点击“安装”即可安装插件。...

Superset二次开发之新增复选框Checkbox筛选器
一. 背景 Superset目前支持的筛选类型:值、数值范围、时间列、时间粒度、时间范围 5种类型,显然无法满足业务需求。根据产品需要,需要支持复选框、单选框、级联选择等类型的筛选器。本文探讨复选框、单选框的技术实现方式。 二. 效果预览 三. 实现思路 复用 值 筛选器模块,…...
PromQL 语法
什么是 PromQL PromQL (Prometheus Query Language) 是 Prometheus 监控系统中用于查询时间序列数据的语言。它允许用户编写查询,以从 Prometheus 中检索并处理监控数据。 PromQL 的基础概念 1. 时间序列 Prometheus 中的时间序列由以下几个部分组成:…...
掌握Go语言中的时间与日期操作
Go语言中的时间与日期操作 在编写程序时,处理时间和日期看似是一项无关紧要的任务,但在需要同步多个任务或从文本文件中读取时间时,它的重要性便凸显出来。Go语言中的time包为我们提供了丰富的时间与日期操作功能。本文将详细介绍如何在Go语…...

4G模块、WIFI模块、NBIOT模块通过AT指令连接华为云物联网服务器(MQTT协议)
MQTT协议概述 MQTT(Message Queuing Telemetry Transport)是一种轻量级的消息传输协议,它被设计用来提供一对多的消息分发和应用之间的通讯,尤其适用于远程位置的设备和高延迟或低带宽的网络。MQTT协议基于客户端-服务器架构&…...
spring数据校验Validation
文章目录 需要的依赖创建校验对象Validator 需要的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-validation</artifactId> </dependency>创建校验对象Validator 测试的实体类 //创建…...

Uniapp基于uni拦截器+Promise的请求函数封装
最近在学Uniapp,到封装请求的时候本来还想用axios,但是看到一些教学视频有更简单的方法, 基于uni的拦截器和Promise封装的请求函数 但是他们是用TS写的,还没学到TS,我就把JS写了,最终也是请求成功 // src/…...

【工具】使用 Jackson 实现优雅的 JSON 格式化输出
说明 在 Java 开发中,我们经常需要处理 JSON 数据。无论是从服务器端返回的数据,还是本地存储的数据,JSON 格式都因其轻量级和易于解析的特点而被广泛使用。当我们需要查看或调试 JSON 数据时,优雅、格式化的输出将大大提高我们的…...

ApacheKafka中的设计
文章目录 1、介绍1_Kafka&MQ场景2_Kafka 架构剖析3_分区&日志4_生产者&消费者组5_核心概念总结6_顺写&mmap7_Kafka的数据存储形式 2、Kafka的数据同步机制1_高水位(High Watermark)2_LEO3_高水位更新机制4_副本同步机制解析5_消息丢失问…...
.NET 自定义过滤器 - ActionFilterAttribute
这个代码片段定义了一个自定义的 ASP.NET Core 过滤器(GuardModelStateAttribute),用于在控制器动作执行之前验证模型状态(ModelState)。如果模型状态无效,则构造一个 ProblemDetails 对象来描述错误&#…...

VMware Fusion Pro 13 for Mac虚拟机软件
Mac分享吧 文章目录 效果一、下载软件二、开始安装安装完成!!! 效果 一、下载软件 下载软件 地址:www.macfxb.cn 二、开始安装 安装完成!!!...

HarmonyOS应用开发环境搭建
本文主要讲述的是HarmonyOS应用开发环境的搭建,HUAWEI DevEco Studio是基于IntelliJ IDEA Community开源版本打造,为运行在HarmonyOS系统上的应用和服务提供一站式的开发平台。具体下载链接DevEco Studio 一、下载 DevEco Studio 只需要下载对应的版本&…...

视频字幕质量评估的大规模细粒度基准
大家读完觉得有帮助记得关注和点赞!!! 摘要 视频字幕在文本到视频生成任务中起着至关重要的作用,因为它们的质量直接影响所生成视频的语义连贯性和视觉保真度。尽管大型视觉-语言模型(VLMs)在字幕生成方面…...
聊一聊接口测试的意义有哪些?
目录 一、隔离性 & 早期测试 二、保障系统集成质量 三、验证业务逻辑的核心层 四、提升测试效率与覆盖度 五、系统稳定性的守护者 六、驱动团队协作与契约管理 七、性能与扩展性的前置评估 八、持续交付的核心支撑 接口测试的意义可以从四个维度展开,首…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...

AI+无人机如何守护濒危物种?YOLOv8实现95%精准识别
【导读】 野生动物监测在理解和保护生态系统中发挥着至关重要的作用。然而,传统的野生动物观察方法往往耗时耗力、成本高昂且范围有限。无人机的出现为野生动物监测提供了有前景的替代方案,能够实现大范围覆盖并远程采集数据。尽管具备这些优势…...

毫米波雷达基础理论(3D+4D)
3D、4D毫米波雷达基础知识及厂商选型 PreView : https://mp.weixin.qq.com/s/bQkju4r6med7I3TBGJI_bQ 1. FMCW毫米波雷达基础知识 主要参考博文: 一文入门汽车毫米波雷达基本原理 :https://mp.weixin.qq.com/s/_EN7A5lKcz2Eh8dLnjE19w 毫米波雷达基础…...
c# 局部函数 定义、功能与示例
C# 局部函数:定义、功能与示例 1. 定义与功能 局部函数(Local Function)是嵌套在另一个方法内部的私有方法,仅在包含它的方法内可见。 • 作用:封装仅用于当前方法的逻辑,避免污染类作用域,提升…...

自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
WEB3全栈开发——面试专业技能点P4数据库
一、mysql2 原生驱动及其连接机制 概念介绍 mysql2 是 Node.js 环境中广泛使用的 MySQL 客户端库,基于 mysql 库改进而来,具有更好的性能、Promise 支持、流式查询、二进制数据处理能力等。 主要特点: 支持 Promise / async-await…...
CppCon 2015 学习:REFLECTION TECHNIQUES IN C++
关于 Reflection(反射) 这个概念,总结一下: Reflection(反射)是什么? 反射是对类型的自我检查能力(Introspection) 可以查看类的成员变量、成员函数等信息。反射允许枚…...

【版本控制】GitHub Desktop 入门教程与开源协作全流程解析
目录 0 引言1 GitHub Desktop 入门教程1.1 安装与基础配置1.2 核心功能使用指南仓库管理日常开发流程分支管理 2 GitHub 开源协作流程详解2.1 Fork & Pull Request 模型2.2 完整协作流程步骤步骤 1: Fork(创建个人副本)步骤 2: Clone(克隆…...