Android-音视频学习系列-(六)掌握视频基础知识并使用-OpenGL-ES-2-0-渲染-YUV-数据

GLESv2
#添加本地库
android

${log_lib}
)

至此,对于 OpenGL 的开发需要用到的头文件以及库文件就引入完毕了,下面再来看看如何使用 EGL 搭建出 OpenGL 的上下文环境以及渲染视频数据。

    1. 使用 EGL 首先必须创建,建立本地窗口系统和 OpenGL ES 的连接

//1.获取原始窗口
nativeWindow = ANativeWindow_fromSurface(env, surface);
//获取Display
display = eglGetDisplay(EGL_DEFAULT_DISPLAY);
if (display == EGL_NO_DISPLAY) {
LOGD(“egl display failed”);
showMessage(env, “egl display failed”, false);
return;
}

    1. 初始化 EGL

//初始化egl,后两个参数为主次版本号
if (EGL_TRUE != eglInitialize(display, 0, 0)) {
LOGD(“eglInitialize failed”);
showMessage(env, “eglInitialize failed”, false);
return;
}

    1. 确定可用的渲染表面( Surface )的配置。

//surface 配置,可以理解为窗口
EGLConfig eglConfig;
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, &eglConfig, 1, &configNum)) {
LOGD(“eglChooseConfig failed”);
showMessage(env, “eglChooseConfig failed”, false);
return;
}

    1. 创建渲染表面 surface(4/5步骤可互换)

//创建surface(egl和NativeWindow进行关联。最后一个参数为属性信息,0表示默认版本)
winSurface = eglCreateWindowSurface(display, eglConfig, nativeWindow, 0);
if (winSurface == EGL_NO_SURFACE) {
LOGD(“eglCreateWindowSurface failed”);
showMessage(env, “eglCreateWindowSurface failed”, false);
return;
}

    1. 创建渲染上下文 Context

//4 创建关联上下文
const EGLint ctxAttr[] = {
EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE
};
//EGL_NO_CONTEXT表示不需要多个设备共享上下文
context = eglCreateContext(display, eglConfig, EGL_NO_CONTEXT, ctxAttr);
if (context == EGL_NO_CONTEXT) {
LOGD(“eglCreateContext failed”);
showMessage(env, “eglCreateContext failed”, false);
return;
}

    1. 指定某个 EGLContext 为当前上下文, 关联起来

//将egl和opengl关联
//两个surface一个读一个写。第二个一般用来离线渲染
if (EGL_TRUE != eglMakeCurrent(display, winSurface, winSurface, context)) {
LOGD(“eglMakeCurrent failed”);
showMessage(env, “eglMakeCurrent failed”, false);
return;
}

    1. 使用 OpenGL 相关的 API 进行绘制操作

GLint vsh = initShader(vertexShader, GL_VERTEX_SHADER);
GLint fsh = initShader(fragYUV420P, GL_FRAGMENT_SHADER);

//创建渲染程序
GLint program = glCreateProgram();
if (program == 0) {
LOGD(“glCreateProgram failed”);
showMessage(env, “glCreateProgram failed”, false);
return;
}

//向渲染程序中加入着色器
glAttachShader(program, vsh);
glAttachShader(program, fsh);

//链接程序
glLinkProgram(program);
GLint status = 0;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == 0) {
LOGD(“glLinkProgram failed”);
showMessage(env, “glLinkProgram failed”, false);
return;
}
LOGD(“glLinkProgram success”);
//激活渲染程序
glUseProgram(program);

//加入三维顶点数据
static float ver[] = {
1.0f, -1.0f, 0.0f,
-1.0f, -1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
-1.0f, 1.0f, 0.0f
};

GLuint apos = static_cast(glGetAttribLocation(program, “aPosition”));
glEnableVertexAttribArray(apos);
glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE, 0, ver);

//加入纹理坐标数据
static float fragment[] = {
1.0f, 0.0f,
0.0f, 0.0f,
1.0f, 1.0f,
0.0f, 1.0f
};
GLuint aTex = static_cast(glGetAttribLocation(program, “aTextCoord”));
glEnableVertexAttribArray(aTex);
glVertexAttribPointer(aTex, 2, GL_FLOAT, GL_FALSE, 0, fragment);

//纹理初始化
//设置纹理层对应的对应采样器?

/**

  • //获取一致变量的存储位置
    GLint textureUniformY = glGetUniformLocation(program, “SamplerY”);
    GLint textureUniformU = glGetUniformLocation(program, “SamplerU”);
    GLint textureUniformV = glGetUniformLocation(program, “SamplerV”);
    //对几个纹理采样器变量进行设置
    glUniform1i(textureUniformY, 0);
    glUniform1i(textureUniformU, 1);
    glUniform1i(textureUniformV, 2);
    */
    //对sampler变量,使用函数glUniform1i和glUniform1iv进行设置
    glUniform1i(glGetUniformLocation(program, “yTexture”), 0);
    glUniform1i(glGetUniformLocation(program, “uTexture”), 1);
    glUniform1i(glGetUniformLocation(program, “vTexture”), 2);
    //纹理ID
    GLuint texts[3] = {0};
    //创建若干个纹理对象,并且得到纹理ID
    glGenTextures(3, texts);

//绑定纹理。后面的的设置和加载全部作用于当前绑定的纹理对象
//GL_TEXTURE0、GL_TEXTURE1、GL_TEXTURE2 的就是纹理单元,GL_TEXTURE_1D、GL_TEXTURE_2D、CUBE_MAP为纹理目标
//通过 glBindTexture 函数将纹理目标和纹理绑定后,对纹理目标所进行的操作都反映到对纹理上
glBindTexture(GL_TEXTURE_2D, texts[0]);
//缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
//放大的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//设置纹理的格式和大小
// 加载纹理到 OpenGL,读入 buffer 定义的位图数据,并把它复制到当前绑定的纹理对象
// 当前绑定的纹理对象就会被附加上纹理图像。
//width,height表示每几个像素公用一个yuv元素?比如width / 2表示横向每两个像素使用一个元素?
glTexImage2D(GL_TEXTURE_2D,
0,//细节基本 默认0
GL_LUMINANCE,//gpu内部格式 亮度,灰度图(这里就是只取一个亮度的颜色通道的意思)
width,//加载的纹理宽度。最好为2的次幂(这里对y分量数据当做指定尺寸算,但显示尺寸会拉伸到全屏?)
height,//加载的纹理高度。最好为2的次幂
0,//纹理边框
GL_LUMINANCE,//数据的像素格式 亮度,灰度图
GL_UNSIGNED_BYTE,//像素点存储的数据类型
NULL //纹理的数据(先不传)
);

//绑定纹理
glBindTexture(GL_TEXTURE_2D, texts[1]);
//缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
//设置纹理的格式和大小
glTexImage2D(GL_TEXTURE_2D,
0,//细节基本 默认0
GL_LUMINANCE,//gpu内部格式 亮度,灰度图(这里就是只取一个颜色通道的意思)
width / 2,//u数据数量为屏幕的4分之1
height / 2,
0,//边框
GL_LUMINANCE,//数据的像素格式 亮度,灰度图
GL_UNSIGNED_BYTE,//像素点存储的数据类型
NULL //纹理的数据(先不传)
);

//绑定纹理
glBindTexture(GL_TEXTURE_2D, texts[2]);
//缩小的过滤器
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER,Android-音视频学习系列-(六)掌握视频基础知识并使用-OpenGL-ES-2-0-渲染-YUV-数据_第1张图片
GL_LINEAR);
//设置纹理的格式和大小
glTexImage2D(GL_TEXTURE_2D,
0,//细节基本 默认0
GL_LUMINANCE,//gpu内部格式 亮度,灰度图(这里就是只取一个颜色通道的意思)
width / 2,
height / 2,//v数据数量为屏幕的4分之1
0,//边框
GL_LUMINANCE,//数据的像素格式 亮度,灰度图
GL_UNSIGNED_BYTE,//像素点存储的数据类型
NULL //纹理的数据(先不传)
);

unsigned char *buf[3] = {0};
buf[0] = new unsigned char[width * height];//y
buf[1] = new unsigned char[width * height / 4];//u
buf[2] = new unsigned char[width * height / 4];//v

showMessage(env, “onSucceed”, true);

FILE *fp = fopen(data_source, “rb”);
if (!fp) {
LOGD(“oepn file %s fail”, data_source);
return;
}

while (!feof(fp)) {
//解决异常退出,终止读取数据
if (!isPlay)
return;
fread(buf[0], 1, width * height, fp);
fread(buf[1], 1, width * height / 4, fp);
fread(buf[2], 1, width * height / 4, fp);

//激活第一层纹理,绑定到创建的纹理
//下面的width,height主要是显示尺寸?
glActiveTexture(GL_TEXTURE0);
//绑定y对应的纹理
glBindTexture(GL_TEXTURE_2D, texts[0]);
//替换纹理,比重新使用glTexImage2D性能高多
glTexSubImage2D(GL_TEXTURE_2D, 0,
0, 0,//相对原来的纹理的offset
width, height,//加载的纹理宽度、高度。最好为2的次幂
GL_LUMINANCE, GL_UNSIGNED_BYTE,
buf[0]);

//激活第二层纹理,绑定到创建的纹理
glActiveTexture(GL_TEXTURE1);
//绑定u对应的纹理
glBindTexture(GL_TEXTURE_2D, texts[1]);
//替换纹理,比重新使用glTexImage2D性能高
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_LUMINANCE,
GL_UNSIGNED_BYTE,
buf[1]);

//激活第三层纹理,绑定到创建的纹理
glActiveTexture(GL_TEXTURE2);
//绑定v对应的纹理
glBindTexture(GL_TEXTURE_2D, texts[2]);
//替换纹理,比重新使用glTexImage2D性能高
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_LUMINANCE,
GL_UNSIGNED_BYTE,
buf[2]);

glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
//8. 窗口显示,交换双缓冲区
eglSwapBuffers(display, winSurface);
}

    1. 交换 EGL 的 Surface 的内部缓冲和 EGL 创建的和平台无关的窗口 diaplay

//窗口显示,交换双缓冲区
eglSwapBuffers(display, winSurface);
复制代码

    1. 释放资源

/**

  • 销毁数据
    */
    void Gles_play::release() {
    if (display || winSurface || context) {
    //销毁显示设备
    eglDestroySurface(display, winSurface);
    //销毁上下文
    eglDestroyContext(display, context);
    //释放窗口
    ANativeWindow_release(nativeWindow);
    //释放线程
    eglReleaseThread();
    //停止
    eglTerminate(display);
    eglMakeCurrent(display, winSurface, EGL_NO_SURFACE, context);
    context = EGL_NO_CONTEXT;
    display = EGL_NO_SURFACE;
    winSurface = nullptr;
    Surface(display, winSurface);
    //销毁上下文
    eglDestroyContext(display, context);
    //释放窗口
    ANativeWindow_release(nativeWindow);
    //释放线程
    eglReleaseThread();
    //停止
    eglTerminate(display);
    eglMakeCurrent(display, winSurface, EGL_NO_SURFACE, context);
    context = EGL_NO_CONTEXT;
    display = EGL_NO_SURFACE;
    winSurface = nullptr;

你可能感兴趣的:(程序员,架构,移动开发,android)