前两篇描述了渲染方面的内容,昨天有写了点3D模型贴图以及全景影像效果的代码。感觉有必要把接口记录下,不然只是单纯的熟练使用,对其背后原理理解还不够深刻。
首先明确要完成的任务:通过OpenGL机制渲染视频。至于视频是本地、网络或者摄像头采集到的视频不关心。呈现方面是通过OpenGL机制,通过native层代码或者Android SDK里的控件都可以。
之前的实现是纯java即Android SDK里的GLSurfaceView控件,本地视频播放通过MediaPlayer方式播放。因此以下总结基于GLSurfaceView记录,总体可以分为GLSurfaceView模块以及OpenGl模块。
GLSurfaceView
继承自SurfaceView,使用SurfaceView中的surface来呈现OpenGL显示的内容。区别于SurfaceView,主要有以下增强:
1. 管理surface这块特殊的内存,这块内存可以合入Android的显示系统。
2. 管理EGL 的显示, 方便OpenGL渲染内容至前面提高的surface内存中。
3. 可以提供用户自定义Renderer渲染对象来执行真正的渲染实现。(这块没试过~~)
4. 在GLSurfaceView内置的子线程中渲染,不影响UI线程。
5. 提供不同的渲染模式,可以是按需刷新渲染或者不间断持续渲染。
不同于Android其他的View通过继承方式重写绘制接口,GLSurfaceView通过setRenderer接口将
渲染相关的接口代理给接口实现类去重写。而且,GLSurfaceView的使用非常简单,调用setRenderer以后就完成了所有的初始化以及启动子线程的操作。
特殊处理:
设置背景Translucent透明。因为GLSurface默认的Surface是PixelFormat.RGB_888格式,即没有透明度通过。但是可通过getHolder().setFormat(PixelFormat.TRANSLUCENT)方式修改,之后每个像素就是32位了,因为多了alpha通道的8位。
设置EGLConfig,上面说过GLSurfaceView的Surface默认是PixelForm.RGB_888格式,可以通过GLSurfaceView的setEGLConfigChooser接口设置对应的参数,修改默认的参数设置。
生命周期。GLSurfaceView有onResume/onPause函数,在相应的函数中会开启或暂停渲染。
手势事件处理。可以通过queueEvent接口发送事件交给GLThread中的事件队列,然后在Runnable的run方法中自定义操作即可,就像Handler那样。🤣
GLWrapper
代码文档中说法是当Renderer回调函数中的gl参数被调用时,可以通过在GLSurfaceView中设置的GLWrapper实例静态代理方法调用,添加测试或者监控代码,但目前也没遇到在Renderer的回调函数中调用gl的场景。
Renderer
Renderer接口是最重要的接口类,负责调用OpenGL的方法完成每一帧的渲染。GLSurfaceView提供了setRenderer的方法供调用方设置接口的实现类。Renderer的渲染操作都是独立的线程完成,如果要处理屏幕手势事件可以通过GLSurfaceView的queueEvent接口发送。最关键的是EGL Context丢失问题,例如系统被唤醒时就容易出问题,在系统休眠时,如果EGL Context丢失,与其关联的所有资源例如纹理这些都会被删除。因此,要合理利用Renderer#onSurfaceCreated回调方法,在其中初始化OpenGL的状态。
-
public
interface
Renderer {
-
/**
-
* Surface创建或重建成功后回调
-
* 在渲染线程中调用。在开始渲染之前,应该在这个函数中完成资源初始化的工作。
-
* @param gl the GL interface.可通过instance方法判断是否支持GL11以及更高版本。
-
* @param config EGLConfig,可用于离屏渲染相关配置。
-
*/
-
void
onSurfaceCreated
(GL10 gl, EGLConfig config);
-
-
/**
-
* 当surface创建成功或者尺寸变化时回调。
-
* 可用于设置OpenGL的视口变换,同时可以计算空间坐标变换时的MVP矩阵。
-
*/
-
void
onSurfaceChanged
(GL10 gl, int width, int height);
-
-
/**
-
* OpenGL渲染函数调用。用于绘制当前帧。
-
* 常见调用方式如下
-
* void onDrawFrame(GL10 gl) {
-
* gl.glClear(GL10.GL_COLOR_BUFFER_BIT | GL10.GL_DEPTH_BUFFER_BIT);
-
* //... other gl calls to render the scene ...
-
* }
-
*/
-
void
onDrawFrame
(GL10 gl);
-
}
EGLContext
EGL上下文。OpenGL是状态机,在EGLContext中保存了当前状态,以及OpenGL可执行的指令函数,并且ELGContext与Display和Surface进行了绑定,OpenGL通过上下文将Surface内容渲染至Display上去。在创建上下文eglCreateContext时,在底层代码中会有申请内存空间地址赋值给结构体中的成员。创建成功后执行eglMakeCurrent调用,将EGLContext与当前线程即GLThread绑定。
EGLSurface
EGLSurface是一块内存空间,实际上就是一个FrameBuffer,用户想要绘制的内容会首先绘制到EGLSurface中。创建EGLSurface需要绑定“窗口对象”的GLSurfaceView的surface,当EGLSurface中有待绘制的帧时,会将数据添加入之前绑定的“窗口对象”中的缓冲队列中供系统渲染机制进行渲染。
EGLConfig
绘制目标FrameBuffer的配置,主要配置RGBA通道的位宽,以及是否有深度缓冲区(默认有)和模版缓冲区(0表示没有,默认没有)。
-
private
class
SimpleEGLConfigChooser
extends
ComponentSizeChooser {
-
public
SimpleEGLConfigChooser
(boolean withDepthBuffer) {
-
super(
8,
8,
8,
0, withDepthBuffer ?
16 :
0,
0);
-
}
-
}
EGLDisplay
系统物理显示窗口的句柄。通过EGL10.eglGetDisplay(EGL10.EGL_DEFAULT_DISPLAY)获取。
GLThread
绘制线程。在run方法中循环回调Renderer#onDrawFrame接口,通知OpenGL渲染画面。
大致的调用流程