Android 使用Camera2 API 和 GLSurfaceView实现相机预览

2023-11-16

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 适用于一般的图像展示和简单的绘制需求。选择哪个类取决于你的具体需求和技术能力。

  1. 在 AndroidManifest.xml 文件中添加相机权限:

    <uses-permission android:name="android.permission.CAMERA" />
    
  2. 创建相机预览的布局

     <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.GLSurfaceView
            android:id="@+id/glsurfaceview"
            android:layout_width="match_parent"
            android:layout_height="match_parent" />
    </RelativeLayout>
    
  3. 创建相机预览的 Activity,用于管理相机预览和 OpenGL 绘制、

     package com.test.jnitest
     
     import android.Manifest
     import android.content.Context
     import android.content.pm.PackageManager
     import android.graphics.SurfaceTexture
     import android.hardware.camera2.CameraCaptureSession
     import android.hardware.camera2.CameraDevice
     import android.hardware.camera2.CameraManager
     import android.hardware.camera2.CaptureRequest
     import android.opengl.GLSurfaceView
     import android.os.Bundle
     import android.util.Size
     import android.view.Surface
     import android.view.WindowManager
     import androidx.appcompat.app.AppCompatActivity
     import androidx.core.app.ActivityCompat
     import com.test.jnitest.databinding.ActivityCameraBinding
     import java.util.*
     
     class CameraActivity : AppCompatActivity() {
         var mGLSurfaceView:GLSurfaceView?=null
         var mRenderer:CameraRenderer?=null
         var cameraManager:CameraManager?=null
         var mCameraDevice:CameraDevice?=null
         var mCaptureSession:CameraCaptureSession?=null
         var mRequestBuild:CaptureRequest.Builder?=null
         var size = Size(1920,1080)
         lateinit var mContext:Context
         lateinit var binding:ActivityCameraBinding
         override 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 = this
             mGLSurfaceView = binding.glsurfaceview
             mGLSurfaceView?.setEGLContextClientVersion(2)
             // 创建并设置相机渲染器
             mRenderer = CameraRenderer(mGLSurfaceView!!)
             mGLSurfaceView?.setRenderer(mRenderer)
             mGLSurfaceView?.setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
             // 获取摄像头管理器
             cameraManager = getSystemService(Context.CAMERA_SERVICE) as CameraManager
             if (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?.mSurfaceTexture
                 surfaceTexture?.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 = p0
                 mRequestBuild?.build()?.let { mCaptureSession?.setRepeatingRequest(it,null,null) }
             }
     
             override fun onConfigureFailed(p0: CameraCaptureSession) {
                 p0.close()
                 mCaptureSession = null
             }
     
         }
     
         // 关闭相机
         private fun closeCamera() {
             mCaptureSession?.close()
             mCaptureSession = null
             mCameraDevice?.close()
             mCameraDevice = null
         }
     }
    
  4. 创建相机渲染器,创建一个继承自 GLSurfaceView.Renderer 的类,用于实现 OpenGL 绘制和与相机交互的逻辑

     package com.test.jnitest
     
     import android.content.Context
     import android.graphics.SurfaceTexture
     import android.graphics.SurfaceTexture.OnFrameAvailableListener
     import android.opengl.GLES11Ext
     import android.opengl.GLES20
     import android.opengl.GLSurfaceView
     import java.nio.ByteBuffer
     import java.nio.ByteOrder
     import java.nio.FloatBuffer
     import javax.microedition.khronos.egl.EGLConfig
     import javax.microedition.khronos.opengles.GL10
     
     class CameraRenderer(var mGLSurfaceView: GLSurfaceView):GLSurfaceView.Renderer,OnFrameAvailableListener {
         //摄像头图像的纹理ID
         var textureId:Int = 0
         var mSurfaceTexture:SurfaceTexture?=null
         private val COORDS_PER_VERTEX = 2
         private 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 : require
             precision 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
         )
         //着色器程序的ID
         private var programId = 0
         //顶点属性的句柄
         private var positionHandle = 0
         private var textureCoordHandle = 0
     
         init {
             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_EDGE
             GLES20.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_NEAREST
             GLES20.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() 方法中绘制相机预览帧的纹理内容。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android 使用Camera2 API 和 GLSurfaceView实现相机预览 的相关文章

随机推荐

  • 苏神文章解析(6篇)

    苏神文章解析 文章目录 苏神文章解析 1 浅谈Transformer的初始化 参数化与标准化 1 1采样分布 截尾正态分布 1 2 正交初始化 Xavier初始化 1 3 直接标准化 1 4 NTK参数化 1 5 残差连接 2 模型参数的初
  • 图像边缘算法——计算图像边缘(OpenCV)

    目录 一 算法描述 二 计算欧氏距离的Python代码 三 完整的代码 四 结果 一 算法描述 算法的基本原理是 将当前像素与邻接的下部和右部进行比较 如果相似 则将当前像素设置为白色 否则设置为黑色 如何判定像素相似呢 应用欧式距离算法
  • 吐血整理!Python程序员常见的几种变现方式!

    今天聊一个特俗但是大家都想的事情 那就是 赚钱 这件事 先说为什么这个事情 特俗 因为其实我发现我身边大部分程序员不爱谈钱 或者羞于谈钱 加上程序员工资普遍比较高 所以早期都没啥压力 但是随着年龄增大 薪资的涨薪幅度放缓 问题逐渐就暴露出来
  • n个人围成一圈 报数3 python

    n int input count 0 a list range 1 n 1 while len a gt 1 b a for i in range len a count 1 if count 3 0 a remove b i print
  • 不能使用clr编译c文件 怎么强制用clr_一名合格的 C/C++ 开发者拥有这些能力,你就可以去面试了!...

    首先你需要一个显得十分有 经验 的发型 然后拥有一身程序员的基本装备 比如 言归正传 在大多数开发人员的认知中 C C 是一门非常难学的编程语言 很多人知道它的强大 但因为 难 造成的恐惧让很多人放弃 在我看来 C C 一旦学成 其妙无穷
  • Hive简介和安装

    1 Hive是基于hadoop的数据仓库解决方案 由facebook贡献给Apache Hive出现的初衷是让不熟悉编程的数据分析人员也能够使用hadoop处理大数据 这是怎么实现的呢 2 我们先来看看Hive提供的接口 从下面Hive的架
  • Tesseract3.02训练生成新的识别语言库的详细步骤

    说明 本文参考了很多前辈的资料 主要是 tesseract OCR3 0语言库训练步骤 再结合自己的实践操作 个人感觉官网的教程是最权威的 耐着性子看完 收获很大 比网上到处看别人理解的更好 毕竟每个人理解的都是自己的 不全面 当然也包括本
  • Vue键盘事件

    一 keydown和keyup的区别 keydown 和 keyup 是JavaScript中用于捕获键盘按键事件的两个事件类型 它们有以下区别 触发时机 keydown 事件在按下键盘上的键时触发 无论是否已释放 keyup 事件在释放键
  • 深度学习之自编码器(2)Fashion MNIST图片重建实战

    深度学习之自编码器 2 Fashion MNIST图片重建实战 1 Fashion MNIST数据集 2 编码器 3 解码器 4 自编码器 5 网络训练 6 图片重建 完整代码 自编码器算法原理非常简单 实现方便 训练也较稳定 相对于PCA
  • 知识图谱基础代码构建(医疗向)

    今天上线发现自己竟然涨粉了 也给了我更大的动力将这一方面继续记录下去 这里是对另外一个项目代码的解读 个人认为是对前面连续几篇中文医疗知识图谱的解读的一个补充 有着拨云见日的作用 项目来源是GitHub上面刘老师做的一个基于知识医疗图谱的问
  • 【linux】——动静态库

    目录 动静态的比较 扩展名 编译操作 执行的状态 生成静态库 生成动态库 总结 在linux操作系统中 函数库是一个非常重要的的东西 因为很多软件之间都会互相使用彼此提供的函数来使用其特殊的功能 例如 我们在写c语言的时候 但我们要使用pr
  • 【人工智能】5.不确定性推理

    一 不确定推理预备知识 1 不确定性推理的含义 不确定性推理实际上是一种从不确定的初始证据出发 通过运用不确定性知识 最终推出具有一定程度的不确定性但却又是合理或基本合理的结论的思维过程 2 不确定推理基本问题 1 不确定性的表示 知识的不
  • 带环单链表及链表相交问题的分析及代码实现

    什么样的是带环单链表 什么样的是不带环单链表 带环单链表就是单链表的尾结点有连接到单链表自身的某一个结点构成了环 首先 我们怎样检测一个单链表是否带环 1 检测单链表是否带环 如果链表不带环 可以通过遍历链表找到尾节点 那如果单链表带环 就
  • iOS进阶_密码学进阶(三.非对称加密算法-RSA公钥私钥的生成)

    应用场景 集成支付宝SDK 会看见 p12文件也有 非对称加密算法 一 运行终端 生成私钥 1 cd Users mac Desktop iOS 加密 2 openssl genrsa out private pem 512 生成私钥 注意
  • [MATLAB]线性方程组求解(雅可比迭代和高斯迭代源码实现)

    本试验取材于中南大学 科学计算与MATLAB语言 直接解法 高斯消去法 列主元消去法 矩阵的三角分解法 1 利用左除运算符的直接解法 Ax b gt x a b 注意 如果矩阵A是奇异的或接近奇异的 则MATLAB会给出警告信息 gt gt
  • 队列的基本操作

    一 定义一个queue的接口 其中包含队列用到的方法 package p1 接口 public interface Queue
  • 路由器02---PandoraBox配置

    1 安装libc 什么是libc 这里贴一个gnu官方的解释 Any Unix like operating system needs a C library the library which defines the system cal
  • BootstrapTable 中使用checkbox

    BootstrapTable中使用CheckBox 添加checkbox列即可 columns checkbox true checkbox 默认为false 不显示 为true时显示 获取所以选中的列 var ids var rows t
  • 微信小程序wx.getUserProfile授权及页面展示

    微信wx getUserProfile授权及页面展示 微信小程序wx getUserProfile授权及页面展示 小程序授权页面样式和wx getUserProfile方法使用 话不多说 直接看图看代码 app js 代码片 app js
  • Android 使用Camera2 API 和 GLSurfaceView实现相机预览

    GLSurfaceView 和 SurfaceView 是 Android 中用于显示图像的两个视图类 它们在实现方式和使用场景上有一些区别 实现方式 GLSurfaceView 基于 OpenGL ES 技术实现 可以通过 OpenGL