在《安卓使用SurfaceView绘制ffmpeg解码的视频数据》虽然我们成功地实现了视频的渲染,
但是在YUV转换成RGB的时候,我们调用了ffmpeg内部的转换函数,这里面包含大量的计算转换,
所以是很耗费CPU性能的。

今天我们来学习一下如何将YUV转换RGB的功能转换到GPU中去执行,减少CPU的计算工作量,达到性能优化的目的。

解决方案是使用OpenGL渲染,将YUV转换RGB的功能交由着色器去处理。

Android中引入OpenGL

下面以OpenGL 2.0为例。

1、 引入库文件

CMakeLists.txt引入GLESv2EGL库(在ndk中内置)。
直接target_link_libraries加入即可:

target_link_libraries(
                      ........
                       #引入opengl的相关库
                       GLESv2
                       EGL
                          )

2、 引入相关的头文件

需要引入的头文件

#include

至此,我们的安卓OpenGL环境就算引入成功了。

OpenGL渲染环境搭建

1、 EGLDisplay  
OpenGL(移动端称作的是EGL)要知道把目标内容绘制在哪里。这就是EGLDisplay所需的功能。
EGLDisplay是一个封装系统物理屏幕的数据类型(可以理解为绘制目标的一个抽象),通常会调用eglGetDisplay方法返回EGLDisplay来作为OpenGL ES渲染的目标。然后通过eglInitialize初始化显示设备。

代码如下:

//1 EGL display创建和初始化

2、 EGLConfig  
EGL有了Display之后,它就可以将OpenGL ES的输出和设备的屏幕桥接起来,但是需要指定一些配置项,这时候EGLConfig就闪亮登场啦。

//输出配置
    EGLConfig config;
    EGLint configNum;
    EGLint configSpec[] = {
            EGL_RED_SIZE, 8,
            EGL_GREEN_SIZE, 8,
            EGL_BLUE_SIZE, 8,
            EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE
    };
    if (EGL_TRUE != eglChooseConfig(display, configSpec, &config, 1, &configNum)) {
        LOGE("eglChooseConfig failed!");
        return;
    }

3、 创建EGLSurface
有了显示设备,那么如何将设备的屏幕与EGL链接起来呢?EGLSurface粉墨登场。

//获取原始窗口

4、 EGLContext
OpenGL所创建的资源, 其实对程序员可见的仅仅是ID而已, 要操作其中内容就需要依赖于EGLContext上下文。

//context 创建关联的上下文

4、 着色器
所有的渲染都需要位置信息与色彩信息这两个信息才能成功渲染处理。顶点着色器和片元着色器就完成了这两个功能。

顶点着色器:

//顶点坐标,在外部获取传递进来

片元着色器:

float;

着色器编写好之后如何使用呢?我们看一张图:



Android 执行 ffmpeg android ffmpeg opengl_小程序 mathjs渲染公式

着色器编译关联

根据图片流程,我们很方便就创建编译并且链接好着色器。


const char *code, GLint

5、 传递数据
到了这一步,我们的渲染环境算是搭建完毕了,下面就是通过CPU传递数据到GPU进行渲染。简单地说就是获取在着色器中定义的变量并且对其赋值,然后调用绘制的API即可。 
主要代码:

// 解码得到YUV数据

至此,我们的渲染过程就完成了,与前面的ffmpeg解码为YUV数据联系起来,实现一边解码一边渲染数据。通过对比笔者很明显觉察到使用OpenGL渲染的视频画面流畅很多。而且对比两种渲染方式的CPU使用率也发现OpenGL的渲染方式确实使得CPU的使用率大大降低。

最后贴一下结合ffmpeg从解码到渲染全过程的完整代码:

#include

遇到的问题

笔者测试了两个不同的视频发现一个能播放,另外一个花屏,看不出图像。

查找了一些资料至今仍找不到问题所在,两个视频使用SurfaceView渲染都是可以的,说明可能不是解码的问题,估计是渲染程序的问题。而且两个视频解码出来的YUV数据格式不一样,一个yuvj420p,这个可以正常使用OpenGL渲染,一个是yuv420p,这个就不能渲染,花屏。

恳亲大神不吝赐教。