如何将Android MediaCodec解码后的帧显示到GLSurfaceView上
实现步骤
1. 基础环境初始化
- 自定义GLSurfaceView,同时实现
GLSurfaceView.Renderer和SurfaceTexture.OnFrameAvailableListener接口 - 在Renderer的
onSurfaceCreated回调中完成以下操作:- 创建OES类型外部纹理,记为纹理ID
mTextureID - 用
mTextureID初始化SurfaceTexture实例,设置帧可用监听 - 初始化OpenGL着色器程序、顶点数组、矩阵变量等绘图资源
- 用初始化好的
SurfaceTexture创建Surface对象,作为MediaCodec的解码输出目标
- 创建OES类型外部纹理,记为纹理ID
2. MediaCodec解码配置调整
官方测试类默认是把解码输出直接输出到普通Surface或者缓存,你需要修改解码器配置逻辑:
在配置MediaCodec的时候,把你上面创建的Surface对象传入configure方法的第二个参数,示意代码如下:
MediaCodec decoder = MediaCodec.createDecoderByType("video/avc"); MediaFormat format = MediaFormat.createVideoFormat("video/avc", width, height); // 填入你的H264流相关配置,比如sps、pps、帧率等参数 decoder.configure(format, yourCreatedSurface, null, 0); decoder.start();
3. 帧渲染联动逻辑
- 当
SurfaceTexture.OnFrameAvailableListener的onFrameAvailable回调触发时,调用GLSurfaceView.requestRender()请求刷新画面 - 在Renderer的
onDrawFrame回调中,先调用surfaceTexture.updateTexImage()更新纹理内容,再调用你提供的drawFrame方法完成绘制 - 你提供的
drawFrame方法本身逻辑是正确的,不需要改动,只要保证用到的着色器符合OES纹理要求即可:
顶点着色器示例:
片段着色器示例:attribute vec4 aPosition; attribute vec4 aTextureCoord; varying vec2 vTextureCoord; uniform mat4 uMVPMatrix; uniform mat4 uSTMatrix; void main() { gl_Position = uMVPMatrix * aPosition; vTextureCoord = (uSTMatrix * aTextureCoord).xy; }#extension GL_OES_EGL_image_external : require precision mediump float; varying vec2 vTextureCoord; uniform samplerExternalOES sTexture; void main() { gl_FragColor = texture2D(sTexture, vTextureCoord); }
你提供的drawFrame参考代码如下:
public void drawFrame(SurfaceTexture st) { checkGlError("onDrawFrame start"); st.getTransformMatrix(mSTMatrix); GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f); GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT); GLES20.glUseProgram(mProgram); checkGlError("glUseProgram"); GLES20.glActiveTexture(GLES20.GL_TEXTURE0); GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID); mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET); GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); checkGlError("glVertexAttribPointer maPosition"); GLES20.glEnableVertexAttribArray(maPositionHandle); checkGlError("glEnableVertexAttribArray maPositionHandle"); mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET); GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false, TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices); checkGlError("glVertexAttribPointer maTextureHandle"); GLES20.glEnableVertexAttribArray(maTextureHandle); checkGlError("glEnableVertexAttribArray maTextureHandle"); Matrix.setIdentityM(mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0); GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0); GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4); checkGlError("glDrawArrays"); GLES20.glFinish(); }
常见注意点
- OES纹理必须在GL线程中创建,不能在主线程直接创建
- 如果出现画面旋转/拉伸,只需要调整
mMVPMatrix矩阵即可,不需要修改解码逻辑 - 解码结束后要按顺序释放MediaCodec、SurfaceTexture、GL纹理资源
内容的提问来源于stack exchange,提问作者Ali Has
相关产品推荐
相关产品推荐

