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()
      }
相关推荐
LSL666_1 天前
5 Repository 层接口
android·运维·elasticsearch·jenkins·repository
alexhilton1 天前
在Jetpack Compose中创建CRT屏幕效果
android·kotlin·android jetpack
2501_940094021 天前
emu系列模拟器最新汉化版 安卓版 怀旧游戏模拟器全集附可运行游戏ROM
android·游戏·安卓·模拟器
下位子1 天前
『OpenGL学习滤镜相机』- Day9: CameraX 基础集成
android·opengl
参宿四南河三1 天前
Android Compose SideEffect(副作用)实例加倍详解
android·app
火柴就是我1 天前
mmkv的 mmap 的理解
android
没有了遇见1 天前
Android之直播宽高比和相机宽高比不支持后动态获取所支持的宽高比
android
shenshizhong1 天前
揭开 kotlin 中协程的神秘面纱
android·kotlin
vivo高启强1 天前
如何简单 hack agp 执行过程中的某个类
android
沐怡旸1 天前
【底层机制】 Android ION内存分配器深度解析
android·面试