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 文件中添加相机权限:

  2. 创建相机预览的布局

     
  3. 创建相机预览的 Activity,用于管理相机预览和 OpenGL 绘制、

     package com.test.jnitestimport 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(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.jnitestimport 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.GL10class 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 = 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_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() 方法中绘制相机预览帧的纹理内容。