Android Camera2 + OpenGL离屏渲染示例

  1. 初始化EGL环境

    kotlin 复制代码
    // 创建带 EGL 环境的 GL 线程
    class GLThread : HandlerThread("GLThread") {
        private lateinit var eglDisplay: EGLDisplay
        private lateinit var eglContext: EGLContext
        private lateinit var handler: Handler
    
        override fun onLooperPrepared() {
            super.onLooperPrepared()
            initEGL()
            handler = Handler(looper)
        }
    
        private fun initEGL() {
            // 1. 获取 EGL Display
            eglDisplay = EGL14.eglGetDisplay(EGL14.EGL_DEFAULT_DISPLAY)
            
            // 2. 初始化 EGL
            val version = IntArray(2)
            EGL14.eglInitialize(eglDisplay, version, 0, version, 1)
            
            // 3. 选择配置
            val configs = arrayOfNulls<EGLConfig>(1)
            val configAttribs = intArrayOf(
                EGL14.EGL_RENDERABLE_TYPE, EGL14.EGL_OPENGL_ES2_BIT,
                EGL14.EGL_RED_SIZE, 8,
                EGL14.EGL_GREEN_SIZE, 8,
                EGL14.EGL_BLUE_SIZE, 8,
                EGL14.EGL_NONE
            )
            EGL14.eglChooseConfig(eglDisplay, configAttribs, 0, configs, 0, 1, intArrayOf(0), 0)
            
            // 4. 创建 EGL Context
            val contextAttribs = intArrayOf(
                EGL14.EGL_CONTEXT_CLIENT_VERSION, 2,
                EGL14.EGL_NONE
            )
            eglContext = EGL14.eglCreateContext(
                eglDisplay, configs[0], EGL14.EGL_NO_CONTEXT,
                contextAttribs, 0
            )
            
            // 5. 创建虚拟 Surface (离屏渲染)
            val surfaceAttribs = intArrayOf(EGL14.EGL_WIDTH, 1, EGL14.EGL_HEIGHT, 1, EGL14.EGL_NONE)
            val eglSurface = EGL14.eglCreatePbufferSurface(eglDisplay, configs[0], surfaceAttribs, 0)
            
            // 6. 绑定上下文
            EGL14.eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)
        }
    
        fun runOnGLThread(runnable: Runnable) {
            handler.post {
                // 确保在当前线程绑定上下文
                EGL14.eglMakeCurrent(eglDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, eglContext)
                runnable.run()
            }
        }
    }
  2. 在 GL 线程中初始化 SurfaceTexture

    kotlin 复制代码
    // 创建 GL 线程
    val glThread = GLThread().apply { start() }
    
    // 等待线程准备就绪
    Thread.sleep(300)  // 简化的等待机制,实际应用中应使用回调
    
    // 在 GL 线程中创建 SurfaceTexture
    lateinit var mSurfaceTexture: SurfaceTexture
    
    glThread.runOnGLThread {
        // 在有效的 GL 环境中创建纹理
        val textureIds = IntArray(1)
        GLES30.glGenTextures(1, textureIds, 0)
        GLES30.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, textureIds[0])
        
        // 创建绑定到纹理的 SurfaceTexture
        mSurfaceTexture = SurfaceTexture(textureIds[0]).apply {
            setDefaultBufferSize(width, height)
            
            // 注意:监听器内部操作将在 GL 线程执行
            setOnFrameAvailableListener({ surfaceTexture ->
                Log.i(TAG, "onFrameAvailable in GL context")
                try {
                    surfaceTexture.updateTexImage()  // 现在可以安全调用
                    
                    // 可选:获取变换矩阵
                    val transformMatrix = FloatArray(16)
                    surfaceTexture.getTransformMatrix(transformMatrix)
                    
                    // 处理帧数据...
                } catch (e: Exception) {
                    Log.e(TAG, "Error updating texture", e)
                }
            }, null)  // 不指定 Handler,使用当前线程
        }
    }
  3. 在 Camera 配置中使用 Surface

    kotlin 复制代码
    // 创建 Surface 供相机使用
    val mSurface = Surface(mSurfaceTexture)
    
    // 配置相机输出目标
    mCaptureRequestBuild?.addTarget(mSurface)
    outputConfiguration.add(OutputConfiguration(mSurface))
  4. 注意事项

    • SurfaceTexture 的创建和销毁必须在同一个线程

    • updateTexImage() 必须在创建 SurfaceTexture 的线程调用

    • 资源释放

      kotlin 复制代码
      fun release() {
          glThread.runOnGLThread {
              mSurfaceTexture.release()
              // 释放 EGL 资源
              EGL14.eglDestroyContext(glThread.eglDisplay, glThread.eglContext)
          }
          glThread.quitSafely()
      }
相关推荐
努力努力再努力wz1 小时前
【MySQL入门系列】掌握表数据的 CRUD:DML 核心语法与执行逻辑解析
android·开发语言·数据结构·数据库·c++·b树·mysql
zh_xuan3 小时前
Android gradle任务
android·gradle构建
Grackers4 小时前
Android Perfetto 系列 10:Binder 调度与锁竞争
android·binder
李白你好4 小时前
Android 自动化渗透测试指令生成
android·自动化
CeshirenTester5 小时前
Claude Code 不只是会写代码:这 10 个 Skills,才是效率分水岭
android·开发语言·kotlin
朝星7 小时前
Android开发[2]:Flow
android·kotlin
zzb15807 小时前
Android Activity 与 Intent 学习笔记
android·笔记·学习
studyForMokey7 小时前
【Android面试】动画 & Bitmap
android·面试·职场和发展
黑牛儿7 小时前
面试高频问题:从浏览器请求到PHP响应:完整流程拆解
android·后端·面试·php
y小花9 小时前
安卓USB服务概述
android·usb