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

AR Core集成Agora 4.2.0遇OpenGL视频转换问题求助

解决方案

一、解决“生成帧缓冲失败”问题

1. 必须在GL线程执行GL操作

所有OpenGL API调用必须在GLSurfaceView的渲染线程(即Renderer.onDrawFrame所在线程)执行,你当前在Main线程调用glGenFramebuffers等方法,属于跨线程操作,必然失败。

修改方式:

  • 直接在Renderer.onDrawFrame方法内调用转换逻辑
  • 或者通过GLSurfaceView.queueEvent将转换任务提交到GL线程:
localSurfaceView.queueEvent {
    val i420Buffer = GLSurfaceViewToI420Converter.convertGLSurfaceViewToI420(width, height)
    // 处理转换后的数据
}

2. 删除冗余的帧缓冲创建逻辑

AR Core已经维护了自己的帧缓冲,你无需额外创建新帧缓冲。直接读取当前绑定的帧缓冲即可,简化convertGLSurfaceViewToI420方法:

fun convertGLSurfaceViewToI420(width: Int, height: Int): ByteBuffer? {
    val rgbaBuffer = ByteBuffer.allocateDirect(width * height * 4)
    rgbaBuffer.order(ByteOrder.nativeOrder())

    // 直接读取当前帧缓冲数据
    GLES30.glReadPixels(0, 0, width, height, GLES30.GL_RGBA, GLES30.GL_UNSIGNED_BYTE, rgbaBuffer)
    
    // 检查GL错误
    val error = GLES30.glGetError()
    if (error != GLES30.GL_NO_ERROR) {
        Timber.e("glReadPixels error code: $error")
        return null
    }

    val i420Buffer = ByteBuffer.allocateDirect(width * height * 3)
    i420Buffer.order(ByteOrder.nativeOrder())
    convertRGBAToI420(rgbaBuffer, i420Buffer, width, height)
    return i420Buffer
}

3. 修正常量定义错误

BYTES_PER_PIXEL应该直接定义为4(RGBA每个像素4字节),避免依赖Float.SIZE_BYTES造成混淆:

private const val BYTES_PER_PIXEL = 4 // RGBA格式,每个像素4字节

二、解决接收端绿屏问题

1. 处理OpenGL坐标翻转问题

OpenGL的帧缓冲原点在左下角,而视频编码的原点在右上角,直接读取的像素会上下颠倒,导致解码异常显示绿屏。添加垂直翻转逻辑:

private fun flipRGBAVertically(buffer: ByteBuffer, width: Int, height: Int) {
    val rowSize = width * 4
    val tempRow = ByteArray(rowSize)
    for (i in 0 until height / 2) {
        val topPos = i * rowSize
        val bottomPos = (height - 1 - i) * rowSize

        // 读取顶部行
        buffer.position(topPos)
        buffer.get(tempRow)
        // 读取底部行
        buffer.position(bottomPos)
        val bottomRow = ByteArray(rowSize)
        buffer.get(bottomRow)

        // 交换两行位置
        buffer.position(topPos)
        buffer.put(bottomRow)
        buffer.position(bottomPos)
        buffer.put(tempRow)
    }
    buffer.position(0)
}

在glReadPixels之后调用该方法:

GLES30.glReadPixels(...)
flipRGBAVertically(rgbaBuffer, width, height)

2. 匹配Agora的视频格式

  • 若发送RGBA数据,需确保Agora设置的视频格式为VIDEO_FORMAT_RGBA
  • 优先使用I420格式发送,这是视频传输的标准格式,兼容性更好

3. 确保宽高为偶数

I420格式要求宽高必须是偶数,否则UV分量计算会出错。转换前调整尺寸:

val adjustedWidth = if (width % 2 != 0) width - 1 else width
val adjustedHeight = if (height % 2 != 0) height - 1 else height

三、优化建议

  • 避免在onDrawFrame内频繁分配ByteBuffer,提前初始化并复用缓冲区,减少GC开销
  • 所有GL操作后调用glGetError检查错误,快速定位问题
  • 使用Agora官方提供的VideoSource接口自定义视频源,比直接发送字节流更稳定

内容的提问来源于stack exchange,提问作者Siddhesh

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.03 21:00:27