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
相关产品推荐
相关产品推荐

