您现在的位置是:首页 > 文章详情

35.FFmpeg+OpenGLES+OpenSLES播放器实现(九.OpenGLES播放视频)

日期:2018-10-21点击:527

项目源码
OpenGL ES 2.0 中文文档

完整代码,一切尽在注释中

extern "C" JNIEXPORT void JNICALL Java_com_rzm_ffmpegplayer_FFmpegPlayer_initOpenGL(JNIEnv *env, jobject instance, jstring url_, jobject surface) { const char *url = env->GetStringUTFChars(url_, 0); FILE *fp = fopen(url, "rb"); if (!fp) { LOGE("open file %s failed!", url); return; } /**************************EGL初始化********************************************/ //1.创建渲染窗口 ANativeWindow *aNativeWindow = ANativeWindow_fromSurface(env, surface); //2.EGL Display创建 //EGL提供了平台无关类型EGLDisplay表示窗口。定义EGLNativeDisplayType是为了匹配原生窗口系统的显示类型, // 对于Windows,EGLNativeDisplayType被定义为HDC, // 对于Linux系统,被定义为Display*类型, // 对于Android系统,定义为ANativeWindow *类型, // 为了方便的将代码转移到不同的操作系统上,应该传入EGL_DEFAULT_DISPLAY,返回与默认原生窗口的连接。 // 如果连接不可用,则返回EGL_NO_DISPLAY EGLDisplay eglDisplay = eglGetDisplay(EGL_DEFAULT_DISPLAY); if (eglDisplay == EGL_NO_DISPLAY) { LOGE("eglGetDisplay failed%d", eglGetError()); return; } //3.初始化Display //创建与本地原生窗口的连接后需要初始化EGL,使用函数eglInitialize进行初始化操作。如果 EGL 不能初始化, // 它将返回EGL_FALSE,并将EGL错误码设置为EGL_BAD_DISPLAY表示制定了不合法的EGLDisplay, // 或者EGL_NOT_INITIALIZED表示EGL不能初始化。使用函数eglGetError用来获取最近一次调用EGL函数出错的错误代码 //参数 //EGLDisplay display 创建的EGL连接 //EGLint *majorVersion 返回EGL主板版本号 //EGLint *minorVersion 返回EGL次版本号 if (EGL_TRUE != eglInitialize(eglDisplay, 0, 0)) { LOGE("eglInitialize failed"); return; } //3. surface窗口配置 //窗口配置有两种方式:一种方式是使用eglGetConfigs函数获取底层窗口系统支持的所有EGL表面配置(Config),然后再使用 // eglGetConfigAttrib依次查询每个EGLConfig相关的信息,Config有众多的Attribute,这些Attribute决定FrameBuffer // 的格式和能力,通过eglGetConfigAttrib ()来读取,但不能修改。EGLConfig包含了渲染表面的所有信息,包括可用颜色、 // 缓冲区等其他特性。 // 另一种方式是指定我们需要的渲染表面配置,让EGL自己选择一个符合条件的EGLConfig配置。eglChooseChofig // 调用成功返回EGL_TRUE,失败时返回EGL_FALSE,如果attribList包含了未定义的EGL属性,或者属性值不合法, // EGL代码被设置为EGL_BAD_ATTRIBUTR //Config实际就是FrameBuffer的参数 EGLConfig eglConfig; //attribList参数在EGL函数中可以为null或者指向一组以EGL_NONE结尾的键对值 //通常以id,value依次存放,对于个别标识性的属性可以只有id,没有value EGLint configAttr[] = { EGL_RED_SIZE, 8, EGL_GREEN_SIZE, 8, EGL_BLUE_SIZE, 8, EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE }; EGLint eglConfigNum; //参数 //EGLDisplay dpy 创建的和本地窗口系统的连接 //const EGLint *attrib_list, 指定渲染表面的参数列表,可以为null //EGLConfig *configs 调用成功,返会符合条件的EGLConfig列表 //EGLint config_size, 最多返回的符合条件的EGLConfig个数 //EGLint *num_config 实际返回的符合条件的EGLConfig个数 if (EGL_TRUE != eglChooseConfig(eglDisplay, configAttr, &eglConfig, 1, &eglConfigNum)) { LOGE("eglChooseConfig failed"); return; } //4. 创建surface // 有了符合条件的EGLConfig后,就可以通过eglCreateWindowSurface函数创建渲染表面。使用这个函数的前提是要使 // 用原生窗口系统提供的API创建一个窗口。eglCreateWindowSurface中attribList一般可以使用null即可。 // 函数调用失败会返回EGL_NO_SURFACE,并设置对应的错误码 //使用eglCreateWindowSurface函数创建在窗口上的渲染表面,此外还可以使用eglCreatePbufferSurface创建 // 屏幕外渲染表面(Pixel Buffer 像素缓冲区)。使用Pbuffer一般用于生成纹理贴图,不过该功能已经被 // FrameBuffer替代了,使用帧缓冲对象的好处是所有的操作都由OpenGL ES来控制。使用Pbuffer的方法和前面创建 // 窗口渲染表面一样,需要改动的地方是在选取EGLConfig时,增加EGL_SURFACE_TYPE参数使其值包含 // EGL_PBUFFER_BIT。而该参数默认值为EGL_WINDOW_BIT。 //EGLSurface eglCreatePbufferSurface( EGLDisplay display, EGLConfig config, EGLint const * attrib_list // 指定像素缓冲区属性列表 ); //Surface实际上就是一个FrameBuffer EGLSurface eglSurface = eglCreateWindowSurface(eglDisplay, eglConfig, aNativeWindow, 0); if (eglSurface == EGL_NO_SURFACE) { LOGE("eglCreateWindowSurface failed"); return; } //4 创建关联的上下文 //使用eglCreateContext为当前的渲染API创建EGL渲染上下文,返回一个上下文,当前的渲染API是由函数eglBindAPI // 设置的。OpenGL ES是一个状态机,用一系列变量描述OpenGL ES当前的状态如何运行,我们通常使用如下途径去更改 // OpenGL状态:设置选项,操作缓冲。最后,我们使用当前OpenGL上下文来渲染。比如我想告诉OpenGL ES接下来要绘制 // 三角形,可以通过一些上下文变量来改变OpenGL ES的状态,一旦改变了OpenGL ES的状态为绘制三角形,下一个命令 // 就会画出三角形。通过这些状态设置函数就会改变上下文,接下来的操作总会根据当前上下文的状态来执行,除非再次 // 重新改变状态。 const EGLint ctxAttr[] = { EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE }; EGLContext eglContext = eglCreateContext(eglDisplay, eglConfig, EGL_NO_CONTEXT, ctxAttr); if (eglContext == EGL_NO_CONTEXT) { LOGE("eglCreateContext failed!"); return; } //5.指定某个EGLContext为当前上下文。使用eglMakeCurrent函数进行当前上下文的绑定。一个程序可能创建多个EGLContext, // 所以需要关联特定的EGLContext和渲染表面,一般情况下两个EGLSurface参数设置成一样的。 if (EGL_TRUE != eglMakeCurrent(eglDisplay, eglSurface, eglSurface, eglContext)) { LOGE("eglMakeCurrent failed!"); return; } LOGI("EGL Init Success!"); /**************************EGL初始化********************************************/ /**************************shader初始化********************************************/ //定点shader初始化 GLint vshader = initShader(vertexShader, GL_VERTEX_SHADER); //片元yuv420 shader初始化 GLint fshader = initShader(fragYUV420P, GL_FRAGMENT_SHADER); /**************************shader初始化********************************************/ /**************************渲染程序初始化********************************************/ //7.使用OpenGL相关的API进行绘制操作。..... //创建渲染程序 GLint program = glCreateProgram(); if (program == 0) { LOGE("glCreateProgram failed!"); return; } //渲染程序中加入着色器代码 glAttachShader(program, vshader); glAttachShader(program, fshader); //链接程序 glLinkProgram(program); GLint status = 0; glGetProgramiv(program, GL_LINK_STATUS, &status); if (status != GL_TRUE) { LOGE("glLinkProgram failed!"); return; } glUseProgram(program); LOGI("glLinkProgram success!"); ///////////////////////////////////////////////////////////// //加入三维顶点数据 两个三角形组成正方形 //顶点坐标系描述了GopenGL的绘制范围,他以绘制中心为原点,在2D图形下,左边界为到x -1,右边界到x 1,上边界到y 1 //下边界到y -1,3D下同样道理。定点坐标系就是OpenGL的绘制区间 static float vers[] = { 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 = (GLuint) glGetAttribLocation(program, "aPosition"); glEnableVertexAttribArray(apos); //传递顶点 取3个数据,跳转12个字节位(3个数据)再取另外3个数据,这是实现块状数据存储的关键,很多函数里都有这个参数,通常写作int stride glVertexAttribPointer(apos, 3, GL_FLOAT, GL_FALSE, 12, vers); //加入纹理坐标数据 //纹理坐标的坐标系以纹理左下角为坐标原点,向右为x正轴方向,向上为y轴正轴方向。他的总长度是1。即纹理图片的 // 四个角的坐标分别是:(0,0)、(1,0)、(0,1)、(1,1),分别对应左下、右下、左上、右上四个顶点。 static float txts[] = { 1.0f, 0.0f, //右下 0.0f, 0.0f, //左下 1.0f, 1.0f, //右上 0.0, 1.0 //左上 }; GLuint atex = (GLuint) glGetAttribLocation(program, "aTexCoord"); glEnableVertexAttribArray(atex); glVertexAttribPointer(atex, 2, GL_FLOAT, GL_FALSE, 8, txts); LOGI("glVertexAttribPointer success"); /**************************渲染程序传递数据********************************************/ /**************************纹理设置********************************************/ int width = 176; int height = 144; width = 352; height = 288; //材质纹理初始化 //设置纹理层 //对于纹理第1层 glUniform1i(glGetUniformLocation(program, "yTexture"), 0); //对于纹理第2层 glUniform1i(glGetUniformLocation(program, "uTexture"), 1); //对于纹理第3层 glUniform1i(glGetUniformLocation(program, "vTexture"), 2); //创建opengl纹理 GLuint texts[3] = {0}; //创建三个纹理对象 //在纹理资源使用完毕后(一般是程序退出或场景转换时),一定要删除纹理对象,释放资源。 //glDeleteTextures(Count:Integer;TexObj:Pointer); glGenTextures(3, texts); //使用glBindTexture将创建的纹理绑定到当前纹理。这样所有的纹理函数都将针对当前纹理。 glBindTexture(GL_TEXTURE_2D, texts[0]); //设置缩小滤镜 /** * 第一个参数表明是针对何种纹理进行设置 * 第二个参数表示要设置放大滤镜还是缩小滤镜 * * 在纹理映射的过程中,如果图元的大小不等于纹理的大小,OpenGL便会对纹理进行缩放以适应图元的尺寸。 * 我们可以通过设置纹理滤镜来决定OpenGL对某个纹理采用的放大、缩小的算法。 * * 第三个参数表示使用的滤镜 * * 第三个参数可选项如下: * * GL_NEAREST 取最邻近像素 * GL_LINEAR 线性内部插值 * GL_NEAREST_MIPMAP_NEAREST 最近多贴图等级的最邻近像素 * GL_NEAREST_MIPMAP_LINEAR 在最近多贴图等级的内部线性插值 * GL_LINEAR_MIPMAP_NEAREST 在最近多贴图等级的外部线性插值 * GL_LINEAR_MIPMAP_LINEAR 在最近多贴图等级的外部和内部线性插值 * */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //设置放大滤镜 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //glTexImage2D函数将Pixels数组中的像素值传给当前绑定的纹理对象,于是便创建了纹理,Pixels是最后一个参数 glTexImage2D( //纹理的类型 GL_TEXTURE_2D, //纹理的等级 0默认 级的分辨率最大 0, //gpu内部格式 亮度,灰度图 GL_LUMINANCE, //纹理图像的宽度和高度 拉升到全屏 width, height, //边框大小 0, //像素数据的格式 亮度,灰度图 要与上面一致 GL_LUMINANCE, //像素值的数据类型 GL_UNSIGNED_BYTE, //纹理的数据(像素数据) NULL ); //使用glBindTexture将创建的纹理绑定到当前纹理。这样所有的纹理函数都将针对当前纹理。 glBindTexture(GL_TEXTURE_2D, texts[1]); //调用glTexParameter来设置纹理滤镜 //设置缩小滤镜 /** * 第一个参数表明是针对何种纹理进行设置 * 第二个参数表示要设置放大滤镜还是缩小滤镜 * 第三个参数表示使用的滤镜 * * 第三个参数可选项如下: * * GL_NEAREST 取最邻近像素 * GL_LINEAR 线性内部插值 * GL_NEAREST_MIPMAP_NEAREST 最近多贴图等级的最邻近像素 * GL_NEAREST_MIPMAP_LINEAR 在最近多贴图等级的内部线性插值 * GL_LINEAR_MIPMAP_NEAREST 在最近多贴图等级的外部线性插值 * GL_LINEAR_MIPMAP_LINEAR 在最近多贴图等级的外部和内部线性插值 * * 多贴图纹理(Mip Mapping)为一个纹理对象生成不同尺寸的图像。在需要时,根据绘制图形的大小来决定采用的纹理 * 等级或者在不同的纹理等级之间进行线性内插。使用多贴图纹理的好处在于消除纹理躁动。这种情况在所绘制的景物 * 离观察者较远时常常发生(如图6.6-1和6.6-2)。由于多贴图纹理现在的渲染速度已经很快,以至于和普通纹理没有 * 什么区别,我们现在一般都使用多贴图纹理。 */ glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); //设置放大滤镜 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //glTexImage2D函数将Pixels数组中的像素值传给当前绑定的纹理对象,于是便创建了纹理,Pixels是最后一个参数 glTexImage2D( //纹理的类型 GL_TEXTURE_2D, //纹理的等级 0默认 级的分辨率最大 0, //gpu内部格式 亮度,灰度图 GL_LUMINANCE, //纹理图像的宽度和高度 拉升到全屏 width, height, //边框大小 0, //像素数据的格式 亮度,灰度图 要与上面一致 GL_LUMINANCE, //像素值的数据类型 GL_UNSIGNED_BYTE, //纹理的数据(像素数据) NULL ); //使用glBindTexture将创建的纹理绑定到当前纹理。这样所有的纹理函数都将针对当前纹理。 glBindTexture(GL_TEXTURE_2D, texts[2]); //缩小的过滤器 glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); //glTexImage2D函数将Pixels数组中的像素值传给当前绑定的纹理对象,于是便创建了纹理,Pixels是最后一个参数 glTexImage2D( //纹理的类型 GL_TEXTURE_2D, //纹理的等级 0默认 级的分辨率最大 0, //gpu内部格式 亮度,灰度图 GL_LUMINANCE, //纹理图像的宽度和高度 拉升到全屏 width, height, //边框大小 0, //像素数据的格式 亮度,灰度图 要与上面一致 GL_LUMINANCE, //像素值的数据类型 GL_UNSIGNED_BYTE, //纹理的数据(像素数据) NULL ); LOGI("glTexImage2D success"); /**************************纹理设置********************************************/ unsigned char *buf[3] = {0}; buf[0] = new unsigned char[width * height]; buf[1] = new unsigned char[width * height / 4]; buf[2] = new unsigned char[width * height / 4]; for (int i = 0; i < 10000; i++) { //memset(buf[0],i,width*height); // memset(buf[1],i,width*height/4); //memset(buf[2],i,width*height/4); //420p yyyyyyyy uu vv if (feof(fp) == 0) { //yyyyyyyy fread(buf[0], 1, width * height, fp); fread(buf[1], 1, width * height / 4, fp); fread(buf[2], 1, width * height / 4, fp); } //激活第1层纹理,绑定到创建的opengl纹理 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, texts[0]); //替换纹理内容 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[0]); //激活第2层纹理,绑定到创建的opengl纹理 glActiveTexture(GL_TEXTURE0 + 1); glBindTexture(GL_TEXTURE_2D, texts[1]); //替换纹理内容 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[1]); //激活第2层纹理,绑定到创建的opengl纹理 glActiveTexture(GL_TEXTURE0 + 2); glBindTexture(GL_TEXTURE_2D, texts[2]); //替换纹理内容 glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[2]); //三维绘制 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); //窗口显示 eglSwapBuffers(eglDisplay, eglSurface); } /**************************纹理显示********************************************/ LOGI("eglSwapBuffers success"); env->ReleaseStringUTFChars(url_, url); } 
原文链接:https://yq.aliyun.com/articles/657296
关注公众号

低调大师中文资讯倾力打造互联网数据资讯、行业资源、电子商务、移动互联网、网络营销平台。

持续更新报道IT业界、互联网、市场资讯、驱动更新,是最及时权威的产业资讯及硬件资讯报道平台。

转载内容版权归作者及来源网站所有,本站原创内容转载请注明来源。

文章评论

共有0条评论来说两句吧...

文章二维码

扫描即可查看该文章

点击排行

推荐阅读

最新文章