如何使用TextureView+OpenGL绘制相机预览

2020-06-23 16:16:22 浏览数 (1)

使用Camera2 API实现相机预览样板代码太多了,偷一波懒,CV大法发动。。。

Google官方的Camera2BasicKotlin工程到手(该工程使用TextureView显示相机预览)

TextureView显示相机预览

Camera2启动相机预览需要三个步骤:

打开Camera--创建Session--启动预览

在创建Session的时候,传入的surface关联了textureView持有的SurfaceTexture:

代码语言:javascript复制
val texture = mTextureView!!.surfaceTexture
// We configure the size of default buffer to be the size of camera preview we want.
texture!!.setDefaultBufferSize(mPreviewSize!!.width, mPreviewSize!!.height)
//This is the output Surface we need to start preview.
val surface = Surface(texture)
// Here, we create a CameraCaptureSession for camera preview.
mCameraDevice!!.createCaptureSession(Arrays.asList(surface, mImageReader!!.surface),
        object : CameraCaptureSession.StateCallback() {
              // ...
        }, null)

这样相机预览会不停的更新到这个surface上,最后显示到屏幕上(至于是如何显示到window上的,此处不细究

)

关联OpenGL

  1. 创建EGL环境,绑定输出到textureView持有的SurfaceTexture上;
  2. 创建Session的时候传递一个关联了OES纹理的surface,当预览数据更新时,将数据更新到纹理上;
  3. GL操作OES纹理,绘制到绑定的输出即可.

1. 创建EGl环境

EGL的创建参考GLSurfaceVIew内部定义的EglHelper类

代码语言:javascript复制
// 1. Get an EGL instance.
mEgl = EGLContext.getEGL() as EGL10

// 2. Get to the default display
mEglDisplay = mEgl!!.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)

// 3. Initialize display
if (!mEgl!!.eglInitialize(mEglDisplay, version)) {
    throw RuntimeException("eglInitialize failed! "   mEgl!!.eglGetError())
}

// 4. Create an EGL context.
mEglContext = mEgl!!.eglCreateContext(mEglDisplay, mEglConfig[0], EGL10.EGL_NO_CONTEXT, contextAttributes)

// 5. Create an EGL surface we can render into.
mEglSurface = mEgl!!.eglCreateWindowSurface(mEglDisplay, mEglConfig[0], textureView.surfaceTexture, null)

// 6. Before we can issue GL commands, we need to make sure
//   the context is current and bound to a surface.
if (!mEgl!!.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)) {
    throw RuntimeException("eglMakeCurrent failed! "   mEgl!!.eglGetError())
}

2. 配置Session

首先需要创建一个OES纹理,因为SurfaceTexture的构造需要它:

代码语言:javascript复制
fun createOESTextureObject(): Int {
    val tex = IntArray(1)
    GLES20.glGenTextures(1, tex, 0)
    if (tex[0] == 0) {
        throw RuntimeException("create oes texture failed")
    }

    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, tex[0])
    GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 
              GL10.GL_TEXTURE_MIN_FILTER, GL10.GL_NEAREST.toFloat())
    GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 
              GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR.toFloat())
    GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
              GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE.toFloat())
    GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 
              GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE.toFloat())
    GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0)

    return tex[0]
}

然后构造Surface,并配置到Camera中:

代码语言:javascript复制
val surfaceTexture = SurfaceTexture(oesId)
val surface = Surface(surfaceTexture)
mCameraDevice!!.createCaptureSession(...)

3. 绘制

当SurfaceTexture的onFrameAvailable回调,也就是有新的预览数据生成时,将图像数据更新到OES纹理上,然后使用GL采样纹理绘制即可

代码语言:javascript复制
// Update image starem to texture
surfaceTexture!!.updateTexImage()

// Update matrix
surfaceTexture!!.getTransformMatrix(mTransformMatrix)

// draw
mEgl!!.eglMakeCurrent(mEglDisplay, mEglSurface, mEglSurface, mEglContext)
mCameraRender!!.drawTexture(mTransformMatrix, mOesTextureId)
mEgl!!.eglSwapBuffers(mEglDisplay, mEglSurface)

Demo

当TextureView引入GL环境后,我们就可以添加一些有趣的东西了,比如引入一个简单的粒子系统:

传送门:

https://github.com/sifutang/Camera2BasicKotlin.git

0 人点赞