GLSurfaceView 和 SurfaceView 是 Android 中用于显示图像的两个视图类,它们在实现方式和使用场景上有一些区别。
需要注意的是,由于 GLSurfaceView 使用了 OpenGL ES 技术,它对开发者的要求更高,需要熟悉 OpenGL ES 相关的知识和编程技术。而 SurfaceView 在一些简单的场景中更易于使用和理解。
总之,GLSurfaceView 适用于需要进行复杂图形渲染和动画的场景,而 SurfaceView 适用于一般的图像展示和简单的绘制需求。选择哪个类取决于你的具体需求和技术能力。
在 AndroidManifest.xml 文件中添加相机权限:
创建相机预览的布局
创建相机预览的 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(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
}
}
创建相机渲染器,创建一个继承自 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() 方法中绘制相机预览帧的纹理内容。