如何将GL_TEXTURE_EXTERNAL_OES纹理复制到帧缓冲区 解决glx506报错
GLES帧缓冲读取触发0x506错误修复(ExoPlayer帧转储场景)
GL错误码0x506对应GL_INVALID_FRAMEBUFFER_OPERATION,触发原因是操作的帧缓冲对象(FBO)未处于完整可读写状态,你的代码存在接口混用、逻辑缺失等问题,具体修复方案如下:
核心问题点
- 接口混用:你用GLES3核心接口
glGenFramebuffers生成FBO,却用GLES1.1扩展包GLES11Ext下的OES版本绑定、挂载接口操作FBO,两者对象域不互通,绑定挂载关系不生效。 - 缺失FBO完整性校验:挂载纹理后没有校验FBO状态,直接执行
glReadPixels必然触发错误。 - 缓冲区指针错误:
glReadPixels写入数据后ByteBuffer的position指针停在缓冲区末尾,直接写文件会得到空文件。 - 冗余操作:连续两次绑定同一个FBO无实际意义,且没有在操作完成后删除临时生成的FBO,会造成显存泄漏。
- 目录未预创建:输出路径的父目录如果不存在,会直接触发文件找不到异常。
- 纹理类型限制:如果是ExoPlayer直接输出的
GL_TEXTURE_EXTERNAL_OES外部纹理,本身不支持直接作为FBO颜色附件,必须先转成普通2D纹理才能读取。
修正后实现代码
int[] tempFbo = new int[1]; int[] originFbo = new int[1]; // 保存当前默认绑定的帧缓冲 GLES32.glGetIntegerv(GLES32.GL_FRAMEBUFFER_BINDING, originFbo, 0); // GLES3原生支持FBO,不需要使用GLES11Ext下的旧OES扩展接口 GLES32.glGenFramebuffers(1, tempFbo, 0); GlUtil.checkGlError("glGenFramebuffers"); GLES32.glBindFramebuffer(GLES32.GL_FRAMEBUFFER, tempFbo[0]); GlUtil.checkGlError("glBindFramebuffer"); // 注意:此处传入的texture必须是GL_TEXTURE_2D类型,OES外部纹理需要先经过离屏绘制转换 GLES32.glFramebufferTexture2D( GLES32.GL_FRAMEBUFFER, GLES32.GL_COLOR_ATTACHMENT0, GLES32.GL_TEXTURE_2D, texture, 0 ); GlUtil.checkGlError("glFramebufferTexture2D"); // 必须校验FBO完整性,是定位0x506错误的核心步骤 int fboStatus = GLES32.glCheckFramebufferStatus(GLES32.GL_FRAMEBUFFER); if (fboStatus != GLES32.GL_FRAMEBUFFER_COMPLETE) { throw new RuntimeException("FBO不完整,状态码:0x" + Integer.toHexString(fboStatus)); } GLES32.glViewport(0, 0, width, height); GlUtil.checkGlError("glViewport"); ByteBuffer pixelBuffer = ByteBuffer.allocateDirect(width * height * 4); pixelBuffer.order(ByteOrder.nativeOrder()); GLES32.glReadPixels(0, 0, width, height, GLES32.GL_RGBA, GLES32.GL_UNSIGNED_BYTE, pixelBuffer); GlUtil.checkGlError("glReadPixels"); pixelBuffer.flip(); // 重置缓冲区读写指针,否则写入文件为空 File outFile = new File("/sdcard/Movies/frames/input_" + mediaTime + ".rgba"); // 提前创建父目录 File parentDir = outFile.getParentFile(); if (parentDir != null && !parentDir.exists()) { parentDir.mkdirs(); } try (FileOutputStream fos = new FileOutputStream(outFile); FileChannel writeChannel = fos.getChannel()) { writeChannel.write(pixelBuffer); } catch (IOException e) { e.printStackTrace(); } Log.d(TAG, "Saved input frame : Dimensions : " + width + "x" + height + " Frame saved as '" + outFile.getAbsolutePath() + "'"); // 恢复原始帧缓冲绑定和视口配置 GLES32.glBindFramebuffer(GLES32.GL_FRAMEBUFFER, originFbo[0]); GLES32.glViewport(0, 0, mWindowSurface.getWidth(), mWindowSurface.getHeight()); // 删除临时FBO避免显存泄漏 GLES32.glDeleteFramebuffers(1, tempFbo, 0);
额外注意事项
- ExoPlayer输出的OES外部纹理处理:这类纹理是GPU特殊优化的采样格式,不能直接挂FBO读取,需要先创建普通2D纹理作为FBO附件,编写全屏着色器采样OES纹理绘制到FBO上,再执行读像素操作。
- 存储权限适配:Android 10及以上版本默认禁止直接访问SD卡根目录,要么申请全文件访问权限,要么将文件存储到App私有目录、通过MediaStore接口写入公共目录,否则会写文件失败。
- 性能优化:
glReadPixels是CPU/GPU同步阻塞调用,频繁执行会严重拖慢渲染帧率,抽帧保存建议控制频率,不要逐帧读取。
内容的提问来源于stack exchange,提问作者Shri Hari
相关产品推荐
相关产品推荐

