http://blog.youkuaiyun.com/gh_home/article/details/52399959
1. 概述
这篇文章所做的事情是这样的:
1. 从一个.mp4文件中解码视频流到surface上
2. 利用OpenGL ES渲染改变视频流中每一帧的内容
3. 将改变后的视频流重新编码输出到一个新的.mp4文件
所有代码可在此处下载:https://github.com/GH-HOME/DecodeEncodeMP4
2. 数据流
图像的数据流按照以下方式传递。
3. 工作流
1. 初始化MediaEncoder(视频编码)、MediaDecoder(视频解码)、MediaMux(生成MP4文件合成音频)、MediaExtractor(分割视频与音频)。在这一步中,encoder和decoder需要分别绑定一个surface。
核心代码如下:
1)初始化编码器与MediaMux
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
2)初始化解码器与MediaExtractor
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
- 1
- 2
- 3
- 4
- 5
- 6
- 7
- 8
- 9
- 10
- 11
- 12
- 13
- 14
- 15
- 16
- 17
- 18
- 19
- 20
- 21
- 22
- 23
- 24
- 25
- 26
- 27
- 28
2. 初始化EGL,配置OpenGL的环境
这一步的相关概念可以参照 http://www.cnitblog.com/zouzheng/archive/2011/05/30/74326.html
主要是配置以下几个内容
数据类型 | 取值 |
---|---|
EGLDisplay | (系统显示 ID 或句柄) |
EGLConfig | (Surface 的 EGL 配置) |
EGLSurface | (系统窗口或 frame buffer 句柄) |
EGLContext | (OpenGL ES 图形上下文) |
在这里我们需要建立两个EGLContext ,一个用于控制接收从mp4文件中传过来的数据,一个用于控制将EGLSurface上的数据经过OPENGL ES渲染传递到encoder中的surface。在初始化第二个EGLContext 的时候需要将其与第一个EGLContext 绑定,这样两者可以共享一个Texture ID(也就是实际的图像数据)。
这部分代码较多:主要是CodecOutputSurface类中的eglsetup函数
关键代码是这一句
- 1
- 2
- 3
- 4
- 5
- 6
- 1
- 2
- 3
- 4
- 5
- 6
保证了两个EGLContext共享Texture id
3. OPENGL渲染的准备工作
这一步主要是分以下步骤:
1. 写shader language的程序,包含VERTEX_SHADER和FRAGMENT_SHADER
2. 编译链接以及加载上述程序
3. 获取VERTEX_SHADER以及FRAGMENT_SHADER中的变量的句柄,创建Texture ID
在参考的Big Flake示例代码中发现在shader language中可以直接去渲染YUV420编码的数据,需要加以下标志声明
- 1
- 1
这一部分的程序主要是在CodecOutputSurface类中的setup函数以及STextureRender类中的一些成员函数
4. 绘图程序的运行过程
在初始化编解码器后,将解码器对应的surface和一个SurfaceTexture绑定起来,同时SurfaceTexture的另外一边与OPRNGL ES中初始化建立的一个Texture ID绑定。这样就建立了一条由解码的mp4数据到OPENGL ES的Texture的数据流。 其中SurfaceTexture充当中介,在上述工作准备好后,开启SurfaceTexture内容侦听,即回调函数onFrameAvailable。 一旦SurfaceTexture内容发生变化(有新的编码数据流流入),系统会自动调用onFrameAvailable表明SurfaceTexture中有可用数据,之后我们调用SurfaceTexture的成员函数updateTexImage将当前的图像流传递到OPENGL ES中的texture。
在掉用GLES20绘图函数之前需要先调用
- 1
- 1
这个使得我们通知GPU以及OPENGL ES在执行绘图指令的时候,是在当前mEGLContext这个上下文绘制在mEGLSurface上的。所以我们在最后绘图的时候需要makecurrent到与Encoder绑定的surface对应的那个EGLSurface上:之前我们需要这样绑定这两个量
- 1
- 2
- 1
- 2
4. 总结
对于OpenGL ES与编解码的结合主要可以参考以下两个网站
http://bigflake.com/mediacodec/
https://github.com/google/grafika
个人觉得grafika的模块化写的更好一点。总体来说,这个流程就是Encoder和Decoder把数据流弄到surface上,然后与OpenGL中的Texture id绑定,之后调用OPENGL ES的绘图指令渲染的过程,其中EGL就是给OPENGL ES方便移植做中间层的,它在程序中的作用就是为OPENGL ES做了初始化的工作,同时它也与mediacodec留有交互接口,因此说它是一个中间层