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

Android中使用OpenGL ES渲染MediaCodec输出Image异常排查

问题分析:MediaCodec解码YUV通过EGL渲染异常

问题背景

使用MediaCodec解码视频时,因无法为Codec配置Surface,改用EGL手动渲染YUV数据。解码逻辑为从MediaCodec获取输出Image,传入YuvDrawer后触发渲染,但运行画面异常。

解码核心代码:

videoDecoder.setStateListener(object : DefDecoderStateListener {
    override fun decodeOneFrame(
        codec: MediaCodec, // 已配置格式为MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Flexible
        bufferInfo: MediaCodec.BufferInfo?,
        outputBuffers: Array<ByteBuffer>,
        index: Int
    ) {
        val image = codec.getOutputImage(index)!!
        yuvDrawer.setImage(image)
        mRenderer.notifySwap(bufferInfo?.presentationTimeUs ?: 0)
    }
})

YuvDrawer存在的核心问题

1. 纹理参数设置错误

在activateTexture()方法中,错误地为GLES11Ext.GL_TEXTURE_EXTERNAL_OES类型纹理设置参数,但实际使用的是普通GL_TEXTURE_2D纹理,导致纹理采样规则不生效,画面会出现拉伸或失真。

2. YUV转RGB公式错误

片段着色器中对U、V通道的采样和转换公式存在问题:

  • 采样U、V时错误取了g/b通道,而GL_LUMINANCE格式的纹理,r/g/b通道值完全相同,应统一取r通道
  • 转换系数不符合标准YUV420转RGB的公式,会导致颜色偏移、失真

3. 忽略Image缓冲区的行步长(rowStride)

MediaCodec输出的Image Plane存在rowStride(每行字节数),其值可能大于视频宽度,直接将原始ByteBuffer传入glTexImage2D会因数据不连续导致画面扭曲、错位。

4. 着色器属性与Java代码不匹配

  • 顶点着色器中纹理属性名为aTexture,但Java代码中获取的是aCoordinate,导致纹理坐标无法正确传递,画面可能全黑或错乱
  • 顶点着色器未使用uMatrix矩阵,导致投影矩阵完全无效,画面拉伸比例错误

5. Alpha属性处理错误

Java代码中将alpha作为attribute传递,但片段着色器未接收使用,且glVertexAttrib1f的调用属于无效操作;若需支持透明度,应将alpha改为uniform变量。

6. 纹理资源泄漏

release()方法中仅删除了mTextureId,但实际生成了3个纹理(textures数组),导致GL资源泄漏。

修复建议

针对上述问题,可逐一修正:

  1. 修正纹理参数:将activateTexture()中的GLES11Ext.GL_TEXTURE_EXTERNAL_OES替换为GLES20.GL_TEXTURE_2D
  2. 修正YUV转RGB公式:更新片段着色器代码为标准转换逻辑:
y = texture(textureY, vTexture).r;
u = texture(textureU, vTexture).r - 0.5;
v = texture(textureV, vTexture).r - 0.5;

rgb.r = y + 1.402 * v;
rgb.g = y - 0.344 * u - 0.714 * v;
rgb.b = y + 1.772 * u;
  1. 处理行步长:通过glPixelStorei(GLES20.GL_UNPACK_ROW_LENGTH, rowStride)指定非连续缓冲区的行步长,或复制数据到连续ByteBuffer中
  2. 对齐着色器属性:将顶点着色器中的aTexture改为aCoordinate,并添加矩阵应用逻辑:
uniform mat4 uMatrix;
void main() {
    gl_Position = uMatrix * a_Position;
    vTexture = aCoordinate;
}
  1. 修复Alpha处理:将alpha改为uniform变量,在片段着色器中控制透明度:
uniform float alpha;
// ...
FragColor = vec4(rgb, alpha);

Java代码中用glUniform1f(mAlphaHandler, mAlpha)设置值
6. 修复资源泄漏:在release()中删除全部3个纹理:

GLES20.glDeleteTextures(3, textures, 0);

替代方案

如果手动处理YUV渲染的成本过高,可考虑:

  • 使用SurfaceTexture中转:创建SurfaceTexture并将其对应的Surface传给MediaCodec,在EGL线程中更新SurfaceTexture并渲染,避免手动处理YUV缓冲区
  • 复用成熟渲染模块:直接使用ExoPlayer等开源库的渲染组件,或基于FFmpeg实现解码+渲染流程,减少手动实现的错误率

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.12 00:59:50