Android 使用Camera2 API 和 GLSurfaceView实现相机预览
GLSurfaceView 和 SurfaceView 是 Android 中用于显示图像的两个视图类,它们在实现方式和使用场景上有一些区别。
- 实现方式:GLSurfaceView 基于 OpenGL ES 技术实现,可以通过 OpenGL ES 渲染图像。而 SurfaceView 则是通过基于线程的绘制方式,可以在独立的线程中进行绘制操作。
- 性能:由于 GLSurfaceView 使用了 OpenGL ES 技术,可以充分利用 GPU 进行图像渲染,因此在处理复杂图像和动画时通常具有更好的性能。相比之下,SurfaceView 使用 CPU 进行图像绘制,性能可能相对较低。
- 使用场景:如果你需要进行复杂的图形绘制、图像处理或者动画,那么 GLSurfaceView 是一个更好的选择,因为它提供了强大的 OpenGL ES 功能支持。另外,GLSurfaceView 还可以与其他 OpenGL ES 相关的库和工具进行集成。而 SurfaceView 在一些简单的图像展示场景中更常见,例如显示图片、播放视频等。
- 使用复杂度:由于 GLSurfaceView 使用了 OpenGL ES,因此它需要编写着色器程序来进行图像渲染,并且需要处理 OpenGL ES 相关的上下文管理。相对而言,SurfaceView 的使用相对简单,只需继承 SurfaceView 类并实现自定义的绘制逻辑即可。
需要注意的是,由于 GLSurfaceView 使用了 OpenGL ES 技术,它对开发者的要求更高,需要熟悉 OpenGL ES 相关的知识和编程技术。而 SurfaceView 在一些简单的场景中更易于使用和理解。
总之,GLSurfaceView 适用于需要进行复杂图形渲染和动画的场景,而 SurfaceView 适用于一般的图像展示和简单的绘制需求。选择哪个类取决于你的具体需求和技术能力。
-
在 AndroidManifest.xml 文件中添加相机权限:
<uses-permission android:name="android.permission.CAMERA" /> -
创建相机预览的布局
<RelativeLayout 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"><android.opengl.GLSurfaceViewandroid:id="@+id/glsurfaceview"android:layout_width="match_parent"android:layout_height="match_parent" /> </RelativeLayout> -
创建相机预览的 Activity,用于管理相机预览和 OpenGL 绘制、
package com.test.jnitestimport android.Manifestimport android.content.Contextimport android.content.pm.PackageManagerimport android.graphics.SurfaceTextureimport android.hardware.camera2.CameraCaptureSessionimport android.hardware.camera2.CameraDeviceimport android.hardware.camera2.CameraManagerimport android.hardware.camera2.CaptureRequestimport android.opengl.GLSurfaceViewimport android.os.Bundleimport android.util.Sizeimport android.view.Surfaceimport android.view.WindowManagerimport androidx.appcompat.app.AppCompatActivityimport androidx.core.app.ActivityCompatimport com.test.jnitest.databinding.ActivityCameraBindingimport java.util.*class CameraActivity : AppCompatActivity() {var mGLSurfaceView:GLSurfaceView?=nullvar mRenderer:CameraRenderer?=nullvar cameraManager:CameraManager?=nullvar mCameraDevice:CameraDevice?=nullvar mCaptureSession:CameraCaptureSession?=nullvar mRequestBuild:CaptureRequest.Builder?=nullvar size = Size(1920,1080)lateinit var mContext:Contextlateinit var binding:ActivityCameraBindingoverride fun onCreate(savedInstanceState: Bundle?) {super.onCreate(savedInstanceState)binding = ActivityCameraBinding.inflate(layoutInflater)setContentView(binding.root)// 设置状态栏透明window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_STATUS)//设置导航栏透明window.addFlags(WindowManager.LayoutParams.FLAG_TRANSLUCENT_NAVIGATION)mContext = thismGLSurfaceView = binding.glsurfaceviewmGLSurfaceView?.setEGLContextClientVersion(2)// 创建并设置相机渲染器mRenderer = CameraRenderer(mGLSurfaceView!!)mGLSurfaceView?.setRenderer(mRenderer)mGLSurfaceView?.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);// 获取摄像头管理器cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManagerif (ActivityCompat.checkSelfPermission(this, Manifest.permission.CAMERA) != PackageManager.PERMISSION_GRANTED) {this.requestPermissions(mutableListOf<String>(Manifest.permission.CAMERA).toTypedArray(),200)return}cameraManager?.openCamera("5",mCameraStateCallback,null)}override fun onResume() {super.onResume()mGLSurfaceView?.onResume()}override fun onDestroy() {super.onDestroy()closeCamera()}// 相机状态回调var mCameraStateCallback = object : CameraDevice.StateCallback() {override fun onOpened(p0: CameraDevice) {mCameraDevice = p0// 创建预览会话var surfaceTexture = mRenderer?.mSurfaceTexturesurfaceTexture?.setDefaultBufferSize(size.width,size.height)var surface = Surface(surfaceTexture)mRequestBuild = mCameraDevice?.createCaptureRequest(CameraDevice.TEMPLATE_PREVIEW)mRequestBuild?.addTarget(surface)val surfaces = Arrays.asList(surface)mCameraDevice?.createCaptureSession(surfaces,mCaptureCallback,null)}override fun onDisconnected(p0: CameraDevice) {p0.close()}override fun onError(p0: CameraDevice, p1: Int) {p0.close()}}// 捕获会话状态回调var mCaptureCallback = object : CameraCaptureSession.StateCallback() {override fun onConfigured(p0: CameraCaptureSession) {mCaptureSession = p0mRequestBuild?.build()?.let { mCaptureSession?.setRepeatingRequest(it,null,null) }}override fun onConfigureFailed(p0: CameraCaptureSession) {p0.close()mCaptureSession = null}}// 关闭相机private fun closeCamera() {mCaptureSession?.close()mCaptureSession = nullmCameraDevice?.close()mCameraDevice = null}} -
创建相机渲染器,创建一个继承自 GLSurfaceView.Renderer 的类,用于实现 OpenGL 绘制和与相机交互的逻辑
package com.test.jnitestimport android.content.Contextimport android.graphics.SurfaceTextureimport android.graphics.SurfaceTexture.OnFrameAvailableListenerimport android.opengl.GLES11Extimport android.opengl.GLES20import android.opengl.GLSurfaceViewimport java.nio.ByteBufferimport java.nio.ByteOrderimport java.nio.FloatBufferimport javax.microedition.khronos.egl.EGLConfigimport javax.microedition.khronos.opengles.GL10class CameraRenderer(var mGLSurfaceView: GLSurfaceView):GLSurfaceView.Renderer,OnFrameAvailableListener {//摄像头图像的纹理IDvar textureId:Int = 0var mSurfaceTexture:SurfaceTexture?=nullprivate val COORDS_PER_VERTEX = 2private val TEXTURE_COORDS_PER_VERTEX = 2//顶点着色器var vertexShaderCode = """attribute vec4 a_position;attribute vec2 a_textureCoord;varying vec2 v_textureCoord;void main() {gl_Position = a_position;v_textureCoord = a_textureCoord;}"""// 片段着色器var fragmentShaderCode = """#extension GL_OES_EGL_image_external : requireprecision mediump float;uniform samplerExternalOES u_texture;varying vec2 v_textureCoord;void main() {gl_FragColor = texture2D(u_texture, v_textureCoord);}"""//顶点坐标数据,表示预览图像的位置和大小。private val VERTEX_COORDS = floatArrayOf(-1.0f, -1.0f,1.0f, -1.0f,-1.0f, 1.0f,1.0f, 1.0f)//纹理坐标数据,表示摄像头图像在预览区域的映射关系。private val TEXTURE_COORDS = floatArrayOf(0f, 1f,1f, 1f,0f, 0f,1f, 0f)//着色器程序的IDprivate var programId = 0//顶点属性的句柄private var positionHandle = 0private var textureCoordHandle = 0init {textureId = createTexture()mSurfaceTexture = SurfaceTexture(textureId)mSurfaceTexture?.setOnFrameAvailableListener(this)}/*** 初始化OpenGL,并加载顶点着色器和片段着色器。通过编译和链接着色器,创建着色器程序,并获取顶点属性的句柄。*/override fun onSurfaceCreated(p0: GL10?, p1: EGLConfig?) {// 在此进行 OpenGL 环境初始化,如创建纹理、着色器程序等// 设置清空颜色缓冲区时的颜色值为黑色GLES20.glClearColor(0.0f, 0.0f, 0.0f, 1.0f)// 加载顶点着色器和片段着色器val vertexShader: Int = loadShader(GLES20.GL_VERTEX_SHADER, vertexShaderCode)val fragmentShader: Int = loadShader(GLES20.GL_FRAGMENT_SHADER, fragmentShaderCode)// 创建着色器程序并将顶点着色器和片段着色器绑定到该程序上programId = GLES20.glCreateProgram()GLES20.glAttachShader(programId, vertexShader)GLES20.glAttachShader(programId, fragmentShader)// 链接着色器程序并检查是否链接成功GLES20.glLinkProgram(programId)// 获取顶点坐标属性和纹理坐标属性的位置positionHandle = GLES20.glGetAttribLocation(programId, "a_position")textureCoordHandle = GLES20.glGetAttribLocation(programId, "a_textureCoord")// 使用着色器程序GLES20.glUseProgram(programId)}override fun onSurfaceChanged(p0: GL10?, p1: Int, p2: Int) {// 在此响应 GLSurfaceView 尺寸变化,如更新视口大小等GLES20.glViewport(0, 0, p1, p2);}/*** 绘制每一帧,在此进行实际的绘制操作,如清屏、绘制纹理等*/override fun onDrawFrame(p0: GL10?) {// 更新纹理图像mSurfaceTexture?.updateTexImage();// 清空颜色缓冲区GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT);// 设置顶点坐标属性并启用GLES20.glVertexAttribPointer(positionHandle, COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, floatBufferFromArray(VERTEX_COORDS));GLES20.glEnableVertexAttribArray(positionHandle);// 设置纹理坐标属性并启用GLES20.glVertexAttribPointer(textureCoordHandle, TEXTURE_COORDS_PER_VERTEX, GLES20.GL_FLOAT, false, 0, floatBufferFromArray(TEXTURE_COORDS));GLES20.glEnableVertexAttribArray(textureCoordHandle);// 激活纹理单元0,并将当前纹理绑定到外部OES纹理目标GLES20.glActiveTexture(GLES20.GL_TEXTURE0);GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureId);// 绘制三角带的图元GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, VERTEX_COORDS.size / COORDS_PER_VERTEX);}/*** 创建摄像头纹理*/private fun createTexture(): Int {// 创建一个用于存储纹理ID的数组val textureIds = IntArray(1)// 生成一个纹理对象,并将纹理ID存储到数组中GLES20.glGenTextures(1, textureIds, 0)// 将当前纹理绑定到OpenGL ES的纹理目标(外部OES纹理)GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureIds[0])// 设置纹理S轴的包裹模式为GL_CLAMP_TO_EDGE,即超出边界的纹理坐标会被截取到边界上的纹素GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE)// 设置纹理T轴的包裹模式为GL_CLAMP_TO_EDGEGLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE)// 设置纹理缩小过滤器为GL_NEAREST,即使用最近邻采样的方式进行纹理缩小GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_NEAREST)// 设置纹理放大过滤器为GL_NEARESTGLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_NEAREST)return textureIds[0]}/*** 加载着色器,接受着色器类型和着色器代码作为参数,并将编译后的着色器对象的ID返回* @param type 着色器类型,如GLES20.GL_VERTEX_SHADER或GLES20.GL_FRAGMENT_SHADER* @param shaderCode 着色器代码* @return 着色器的ID*/private fun loadShader(type: Int, shaderCode: String): Int {// 创建一个新的着色器对象val shader = GLES20.glCreateShader(type)// 将着色器代码加载到着色器对象中GLES20.glShaderSource(shader, shaderCode)// 编译着色器GLES20.glCompileShader(shader)return shader}private fun floatBufferFromArray(array: FloatArray): FloatBuffer? {val byteBuffer: ByteBuffer = ByteBuffer.allocateDirect(array.size * 4)byteBuffer.order(ByteOrder.nativeOrder())val floatBuffer: FloatBuffer = byteBuffer.asFloatBuffer()floatBuffer.put(array)floatBuffer.position(0)return floatBuffer}override fun onFrameAvailable(p0: SurfaceTexture?) {// 当相机有新的帧可用时回调,可以在这里进行一些处理mGLSurfaceView.requestRender()}}
通过以上步骤,你可以实现使用 Camera2 API 和 GLSurfaceView 预览相机的功能。在 CameraActivity 中,我们通过 Camera2 API 打开相机并创建相机预览会话,然后将相机预览的 SurfaceTexture 传递给 CameraRenderer,在 CameraRenderer 的 onDrawFrame() 方法中绘制相机预览帧的纹理内容。
相关文章:
Android 使用Camera2 API 和 GLSurfaceView实现相机预览
GLSurfaceView 和 SurfaceView 是 Android 中用于显示图像的两个视图类,它们在实现方式和使用场景上有一些区别。 实现方式:GLSurfaceView 基于 OpenGL ES 技术实现,可以通过 OpenGL ES 渲染图像。而 SurfaceView 则是通过基于线程的绘制方式…...
说说IO多路复用
分析&回答 IO多路复用 I/O multiplexing 这里面的 multiplexing 指的其实是在单个线程通过记录跟踪每一个Sock(I/O流)的状态(对应空管塔里面的Fight progress strip槽)来同时管理多个I/O流。直白点说:多路指的是多个socket连接,复用指的是复用一个…...
mysql 锁解决的办法
可以查看锁的信息,TRX_MYSQL_THREAD_ID 为processlist的表中的会话id,用于kill select trx_id,trx_state,trx_started,trx_requested_lock_id,trx_wait_started,trx_weight,trx_mysql_thread_id,trx_query from innodb_trx 可以查看锁的模式,类型,锁的表…...
C++零碎记录(五)
9. 静态成员 ① 静态成员就是在成员变量和成员函数前加上关键字static,称为静态成员。 ② 静态成员分为: 1. 静态成员变量 --所有对象共享同一份数据 --在编译阶段分配内存 --类内声明,类外初始化 2. 静态成员函数 --所有对象共享同一个函数…...
玩转Mysql系列 - 第16篇:变量详解
这是Mysql系列第16篇。 环境:mysql5.7.25,cmd命令中进行演示。 代码中被[]包含的表示可选,|符号分开的表示可选其一。 我们在使用mysql的过程中,变量也会经常用到,比如查询系统的配置,可以通过查看系统变…...
Windows云服务器 PHP搭建网站外网无法访问的问题
前言:本人在华为云上租了一台windows的云主机,可以远程访问桌面的那种,然后想搭个网站,最开始想到的是IIS,测试了下用html的文件,没有问题。但是,php文件却不能用,因为少了PHP环境。…...
TuyaOS Sensor Hub组件介绍
文章目录 Sensor Hub 设计思想分层设计Sensor Hub 层(tdl)Sensor Driver 层(tdd) 传感数据元素类型抽象传感器采集策略 Sensor Hub 对上数据与接口数据结构1. 数据读取的触发模式2. 元素型数据订阅规则3. 数据就绪通知回调4. 传感设备信息 应用接口1. 创建传感器实例2. 启动传感…...
【实战】React17+React Hook+TS4 最佳实践,仿 Jira 企业级项目(总结展望篇)
文章目录 一、项目起航:项目初始化与配置二、React 与 Hook 应用:实现项目列表三、TS 应用:JS神助攻 - 强类型四、JWT、用户认证与异步请求五、CSS 其实很简单 - 用 CSS-in-JS 添加样式六、用户体验优化 - 加载中和错误状态处理七、Hook&…...
Leetcode.321 拼接最大数
题目链接 Leetcode.321 拼接最大数 hard 题目描述 给定长度分别为 m m m 和 n n n 的两个数组,其元素由 0 ∼ 9 0 \sim 9 0∼9 构成,表示两个自然数各位上的数字。现在从这两个数组中选出 k k k ( k ≤ m n ) (k \leq m n) (k≤mn) 个数字拼接成…...
数学建模竞赛常用代码总结-PythonMatlab
数学建模过程中有许多可复用的基础代码,在此对 python 以及 MATLAB 中常用代码进行简单总结,该总结会进行实时更新。 一、文件读取 python (pandas) 文件后缀名(扩展名)并不是必须的,其作用主要一方面是提示系统是用…...
在Ubuntu上安装CUDA和cuDNN以及验证安装步骤
在Ubuntu上安装CUDA和cuDNN以及验证安装步骤 本教程详细介绍了如何在Ubuntu操作系统上安装CUDA(NVIDIA的并行计算平台)和cuDNN(深度神经网络库),以及如何验证安装是否成功。通过按照这些步骤操作,您将能够…...
SecureCRT ssh链接服务器
SecureCRT通过密钥进行SSH登录 说明: 一般的密码方式登录容易被密码暴力破解。所以一般我们会将 SSH 的端口设置为默认22以外的端口,或者禁用root账户登录。其实可以通过密钥登录这种方式来更好地保证安全。 密钥形式登录的原理是:利用密钥…...
linux之perf(3)top实时性能
Linux之perf(3)top实时性能 Author:Onceday Date:2023年9月3日 漫漫长路,才刚刚开始… 注:该文档内容采用了GPT4.0生成的回答,部分文本准确率可能存在问题。 参考文档: Tutorial - Perf Wiki (kernel.org)perf-to…...
【linux命令讲解大全】076.pgrep命令:查找和列出符合条件的进程ID
文章目录 pgrep补充说明语法选项参数实例 从零学 python pgrep 根据用户给出的信息在当前运行进程中查找并列出符合条件的进程ID(PID) 补充说明 pgrep 命令以名称为依据从运行进程队列中查找进程,并显示查找到的进程ID。每一个进程ID以一个…...
微信小程序开发---条件渲染和列表渲染
目录 一、条件渲染 (1)基本使用 (2)block (3)hidden 二、列表渲染 (1)基本使用 (2)手动指定索引和当前项的变量名 (3)wx:key的…...
【ES6】require、export和import的用法
在JavaScript中,require、export和import是Node.js的模块系统中的关键字,用于处理模块间的依赖关系。 1、require:这是Node.js中引入模块的方法。当你需要使用其他模块提供的功能时,可以使用require关键字来引入该模块。例如&…...
Vue + Element UI 前端篇(九):接口格式定义
接口请求格式定义 前台显示需要后台数据,我们这里先把前后端交互接口定义好,没有后台的时候,也方便用mock模拟。 接口定义遵循几个规范: 1. 接口按功能模块划分。 系统登录:登录相关接口 用户管理:用户…...
部署Django报错-requires SQLite 3.8.3 or higher
记一次CentOS7部署Django项目时的报错 问题出现 在部署测试环境时,有需要用到一个python的后端服务,要部署到测试环境中去 心想这不是so easy吗,把本地调试时使用的python版本及Django版本在服务器上对应下载好,然后直接执行命…...
什么是网络存储服务器
网络存储器就像一台只有存储功能的终端,独立地工作,里面带有固定的系统,但可以自己设置部分参数功能,可以接入服务器或者电脑进行设置,网络存储服务器实际上就是精简的、小型化的服务器,同样由主板、CPU&am…...
lv3 嵌入式开发-10 NFS服务器搭建及使用
目录 1 NFS服务器介绍 1.1 NFS服务器的介绍 1.2 NFS服务器的特点 1.3 NFS服务器的适用场景 2 NFS服务器搭建 2.1 配置介绍 2.2 常见错误 3 WINDOWS下NFS服务器搭建(扩展) 1 NFS服务器介绍 1.1 NFS服务器的介绍 nfs(Network File Sys…...
web vue 项目 Docker化部署
Web 项目 Docker 化部署详细教程 目录 Web 项目 Docker 化部署概述Dockerfile 详解 构建阶段生产阶段 构建和运行 Docker 镜像 1. Web 项目 Docker 化部署概述 Docker 化部署的主要步骤分为以下几个阶段: 构建阶段(Build Stage):…...
【杂谈】-递归进化:人工智能的自我改进与监管挑战
递归进化:人工智能的自我改进与监管挑战 文章目录 递归进化:人工智能的自我改进与监管挑战1、自我改进型人工智能的崛起2、人工智能如何挑战人类监管?3、确保人工智能受控的策略4、人类在人工智能发展中的角色5、平衡自主性与控制力6、总结与…...
【AI学习】三、AI算法中的向量
在人工智能(AI)算法中,向量(Vector)是一种将现实世界中的数据(如图像、文本、音频等)转化为计算机可处理的数值型特征表示的工具。它是连接人类认知(如语义、视觉特征)与…...
ardupilot 开发环境eclipse 中import 缺少C++
目录 文章目录 目录摘要1.修复过程摘要 本节主要解决ardupilot 开发环境eclipse 中import 缺少C++,无法导入ardupilot代码,会引起查看不方便的问题。如下图所示 1.修复过程 0.安装ubuntu 软件中自带的eclipse 1.打开eclipse—Help—install new software 2.在 Work with中…...
在web-view 加载的本地及远程HTML中调用uniapp的API及网页和vue页面是如何通讯的?
uni-app 中 Web-view 与 Vue 页面的通讯机制详解 一、Web-view 简介 Web-view 是 uni-app 提供的一个重要组件,用于在原生应用中加载 HTML 页面: 支持加载本地 HTML 文件支持加载远程 HTML 页面实现 Web 与原生的双向通讯可用于嵌入第三方网页或 H5 应…...
Java 二维码
Java 二维码 **技术:**谷歌 ZXing 实现 首先添加依赖 <!-- 二维码依赖 --><dependency><groupId>com.google.zxing</groupId><artifactId>core</artifactId><version>3.5.1</version></dependency><de…...
2025季度云服务器排行榜
在全球云服务器市场,各厂商的排名和地位并非一成不变,而是由其独特的优势、战略布局和市场适应性共同决定的。以下是根据2025年市场趋势,对主要云服务器厂商在排行榜中占据重要位置的原因和优势进行深度分析: 一、全球“三巨头”…...
Linux离线(zip方式)安装docker
目录 基础信息操作系统信息docker信息 安装实例安装步骤示例 遇到的问题问题1:修改默认工作路径启动失败问题2 找不到对应组 基础信息 操作系统信息 OS版本:CentOS 7 64位 内核版本:3.10.0 相关命令: uname -rcat /etc/os-rele…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
Sklearn 机器学习 缺失值处理 获取填充失值的统计值
💖亲爱的技术爱好者们,热烈欢迎来到 Kant2048 的博客!我是 Thomas Kant,很开心能在CSDN上与你们相遇~💖 本博客的精华专栏: 【自动化测试】 【测试经验】 【人工智能】 【Python】 使用 Scikit-learn 处理缺失值并提取填充统计信息的完整指南 在机器学习项目中,数据清…...
