You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何将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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.28 09:45:19