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()
      }
相关推荐
小程故事多_805 小时前
从 Tokenizer 到 Next Token,穿透数据流,完整理解 Transformer 底层逻辑
android·深度学习·transformer
三少爷的鞋8 小时前
Android 协程实践:为什么不应将 `supervisorScope` 作为常规方案
android
承渊政道9 小时前
【从零开始大模型开发与微调:基于PyTorch与ChatGLM】(从ChatGLM本地化到LoRA、PEFT的高级微调)
android·人工智能·pytorch·chatglm·本地化部署·大模型开发与微调
2601_9628857211 小时前
如何用 Python 判断一只股票“追涨”还是“抄底”更有效?
android·开发语言·python
素师良码11 小时前
第10篇:屏ESD Check内核逻辑驱动代码完整拆解
android
高晶11 小时前
Android 短信收发模块从零到实战:权限配置、界面设计、发送接收与兼容性适配全攻略
android·程序员
随遇丿而安11 小时前
第18周:ViewModel 基础功能 + 架构优化
android
问心无愧051314 小时前
[GXYCTF2019]Ping Ping Ping
android·笔记
事圆则缓15 小时前
Kotlin 异步与并发:从一次搜索请求走到状态流
android·开发语言·kotlin
帅得不敢出门15 小时前
Android Framework定制卸载确认窗口
android