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()
      }
相关推荐
_祝你今天愉快12 小时前
UI核心 - UI绘制流程及原理
android
mmsx12 小时前
Android 一个设备多个 App 抢:跨进程共享服务的架构设计
android·kotlin·app
mmsx12 小时前
Android GIS系列 栅格怎么存、格网怎么归算:两个容易混淆的 GIS 链路
android·性能优化·app
陈柒吖12 小时前
安卓代码加固(1):加密DEX
android·前端
mmsx12 小时前
Android GIS系列 串口、蓝牙、USB、网络:一套统一抽象怎么扛住四种通道
android·app·产品
二流小码农12 小时前
鸿蒙开发:ArrayList,可不会让UI更新哦
android·ios·harmonyos
hai_android12 小时前
深入理解 Java 的四种引用:强引用、软引用、弱引用、虚引用
android·kotlin·android jetpack
恋猫de小郭12 小时前
Flutter Golden Tests:给 AI Agent 的 UI 测试系统
android·前端·flutter
知昂七昂12 小时前
AOSP编译:敲下 `lunch xxx-userdebug`,你知道系统偷偷干了什么吗?
android
千里马学框架6 天前
一起学 Android 14:ShellTransition 屏幕旋转过程深度剖析
android·智能手机·性能优化·framework·性能·屏幕旋转·rotation