通过OpenGL预览并录制视频
Camera将预览的画面通过SurfaceTexture输出给屏幕的GLSurfaceView以及视频编码的MediaCodec的Surface上。
将渲染的结果输出到不同的目标,我们需要使用一种称为离屏渲染的技术。
离屏渲染:就是让OpenGL不将渲染的结果直接输出到屏幕上,而是输出到一个中间缓冲区(一块GPU空间),然后再将中间缓冲区的内容输出到屏幕或编码器等目标上,这就称为离屏渲染。
在Android系统下可以使用三种方法实现同时将OpenGL ES的内容输出给多个目标(屏幕和编码器):
- 二次渲染法 收到数据后调用Shader程序进行渲染;将渲染结果输出到SurfaceView中显示到屏幕;然后我们需要切换当前渲染的EGLSurface,通过调用EGL的eglMakeCurrent方法,将默认SurfaceView的EGLSurface切换到MediaCodec的EGLSurface上,然后再次调用Shader程序进行渲染,并将渲染结果输出给MediaCodec的Surface进行编码。
因为调用了两次Shader程序进行渲染,每次渲染后的结果输送给不同的Surface,因此称为二次渲染法。
- FBO
二次渲染会调用两次滤镜程序的draw方法,将同一个纹理绘制到不同的Surface上,但是如果要对图像进行一些美颜、高斯模糊等变换操作,那么我们就要对同一个纹理进行两次计算,这无疑是对GPU的浪费,且效率低下。
那如何只计算一次,把结果输出给不同的Surface呢?
OpenGL提供了一种高效的方法,即FBO(FrameBufferObject)。
FBO法中我们操作步骤如下:
- 将渲染结果绘制到FBO中
- 将FBO数据输送到屏幕中
- 将FBO数据输送到编码器
FBO法中,我们不直接将OpenGL ES的渲染结果输送给不同的Surface,而是将结果输出到FBO中,FBO可以理解为一块显存区域,用于存放OpenGL ES的渲染结果。 我们知道CameraFilter着色器程序是将SurfaceTexture纹理渲染到EGLSurface中的,如何将纹理渲染到FBO帧缓冲区中呢,我们需要一个渲染到FBO中的着色器程序。 渲染结果输出到FBO后,我们可以将FBO结果分别输出给不同的目标,FBO->屏幕,FBO->MediaCodec。而FBO输出到不同的目标也需要一个新的着色器去绘制。
FBO高效的原理在于,纹理渲染到FBO中的前置操作。如果需要对纹理进行美颜、高斯模糊等复杂的计算,FBO确实能提高效率。如果没有复杂的计算,那么FBO和二次渲染法效率差不多。
- glBlitFramebuffer: copy a block of pixels from the read framebuffer to the draw framebuffer
该方式不再使用FBO做缓存,而是像二次渲染法一样,先将渲染的内容输出到当前Surface中,但并不展示到屏幕上。我们来看下这种方式的流程:
- 先将渲染结果输送给SurfaceView的Surface
- 切换Surface为MediaCodec的Surface,此时当前Surface为MediaCodec的
- 利用OpenGL3.0提供的API glBlitFramebuffer从原来的Surface拷贝数据到当前Surface中,再调用EGL的eglSwapBuffers将Surface中的内容送编码器编码
- 最后将当前Surface切回原来的Surface,也就是SurfaceView的Surface,同样调用EGL的eglSwapBuffers方法,将其内容显示到屏幕上
该方式的效率是最高的,一次渲染输出给多个目标,但是只有OpenGL3.0才有该方法