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

这篇具有很好参考价值的文章主要介绍了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 适用于一般的图像展示和简单的绘制需求。选择哪个类取决于你的具体需求和技术能力。

  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() 方法中绘制相机预览帧的纹理内容。文章来源地址https://www.toymoban.com/news/detail-704347.html

到了这里,关于Android 使用Camera2 API 和 GLSurfaceView实现相机预览的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 嵌入式安卓开发:使用Camera2获取相机

    从 Android 5.0 开始, Google 引入了一套全新的相机框架 Camera2(android.hardware.camera2) ,并且废弃了旧的相机框架 Camera1(android.hardware.Camera) 。 Camera2相比于Camera的API不仅大幅提高了Android系统拍照的功能,还能支持RAW照片输出,甚至允许程序调整相机的对焦模式、曝光模式、快

    2024年02月09日
    浏览(68)
  • 十分钟实现 Android Camera2 视频录制

    因为工作中要使用 Android Camera2 API ,但因为 Camera2 比较复杂,网上资料也比较乱,有一定入门门槛,所以花了几天时间系统研究了下,并在 CSDN 上记录了下,希望能帮助到更多的小伙伴。 上两篇文章使用 Camera2 实现了相机预览和拍照的功能,这篇文章我们接着上文,来实现

    2024年02月11日
    浏览(35)
  • Android studio Camera2实现的详细流程

    前提 TextureView.SurfaceTextureListener是一个接口,用于监听TextureView中的SurfaceTexture的状态更改。在使用相机时,您可以使用TextureView来显示相机预览。通过实现SurfaceTextureListener接口,您可以在SurfaceTexture准备好时开始相机预览,并在SurfaceTexture销毁时停止预览。 注意 : 必须是在

    2024年02月05日
    浏览(33)
  • 【Camera2 教程二】Camera2相机打开和关闭接口调用详细说明

    上一章《Camera2教程一》里我们介绍了一些 Camera2 的基础知识,但是并没有涉及太多的 API,从本章开始我们会开发一个具有完整相机功能的应用程序,并且将相机知识分成多个篇章进行介绍,而本章所要介绍的就是相机的开启流程。 阅读本章之后,你将学会以下几个知识点:

    2024年04月25日
    浏览(24)
  • android camera系列(Camera1、Camera2、CameraX)的使用以及输出的图像格式

    1.1.1、布局 1.1.2、实现预览 Camera.open() 打开摄像头 setPreviewDisplay 设置预览展示的控件 startPreview 开始预览 发现预览是横着的,需要使用 setDisplayOrientation 调整预览图像的方向 1.1.3、获取摄像头的原始数据 setPreviewCallback 设置预览数据的回调 2560*1440 默认返回图像的分辨率 Image

    2024年02月21日
    浏览(35)
  • Android 11.0 MTK Camera2 设置默认拍照尺寸功能实现

    在11.0的系统rom定制化开发中,在mtk平台的camera2关于拍照的一些功能修改中,在一些平台默认需要设置最大的分辨率 来作为拍照的分辨率,所以就需要了解拍照尺寸设置流程,然后来实现相关的功能 如图: Camera API中主要涉及以下几个关键类 CameraManager:相机的实际管理者,调

    2024年01月21日
    浏览(43)
  • Android 12.0 MTK Camera2 设置默认拍照尺寸功能实现

    在12.0的系统rom定制化开发中,在mtk平台的camera2关于拍照的一些功能修改中,在一些平台默认需要设置最大的分辨率 来作为拍照的分辨率,所以就需要了解拍照尺寸设置流程,然后来实现相关的功能 如图:

    2024年02月20日
    浏览(50)
  • Android 使用Camera1实现相机预览、拍照、录像

    本文介绍如何从零开始,在 Android 中实现 Camera1 的接入,并在文末提供 Camera1Manager 工具类,可以用于快速接入 Camera1 。 Android Camera1 API 虽然已经被 Google 废弃,但有些场景下不得不使用。 并且 Camera1 返回的帧数据是 NV21 ,不像 Camera2 、 CameraX 那样,需要自己再转一层,才能得

    2024年02月08日
    浏览(36)
  • Android Camera2(1)-Camera2在textureView中的预览和拍照

    解释上诉示意图,假如想要同时拍摄两张不同尺寸的图片,并且在拍摄过程中闪光灯必须亮起来。整个拍摄流程如下: 创建一个用于从 Pipeline 获取图片的 CaptureRequest。 修改 CaptureRequest 的闪光灯配置,让闪光灯在拍照过程中亮起来。 创建两个不同尺寸的 Surface 用于接收图片

    2024年02月05日
    浏览(46)
  • Android 高通Camera2 Camera Device Close

     1、很多人看到这个日志第一感觉可能觉得哪里没有合理释放,于是带着这个思路去进行百度探索 2、一开始我去寻找 ImageReader.OnImageAvailableListener 这个问题 3、后面网上去寻找因为  Camera2最大连拍限制是 2 网上很多数包括Google相机源码 需要单独开个线程去处理图片的逻辑

    2023年04月09日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包