OpenGL播放yuv数据流(着色器SHADER)-windows(一)

来源:互联网 发布:山东大学网络教育学费 编辑:程序博客网 时间:2024/06/08 06:33

OpenGL播放yuv数据流(着色器SHADER)-windows(一)


在写这篇文章之前首先要感谢老雷,http://blog.csdn.net/leixiaohua1020/article/details/40379845这篇文章,可以老雷英年早逝,在此致敬...


下面是代码,具体看注释

//Lvs_OpenGl_Interface.h

/** Copyright (c/c++) <2016.11.22> <zwg/>* Function  * Opanal for video rendering related implementation and definition, etc.* OpanAl 用于视频渲染相关实现及定义,等*/#ifndef __LVS_OPENGL_INTERFACE_H__#define __LVS_OPENGL_INTERFACE_H__#include <stdio.h>#include <stdlib.h>#include <malloc.h>#include <string.h>//windows#ifdef WIN32//opengl库#include "glew.h"#include "glut.h"#pragma comment(lib,"glew32.lib")//ios#elif __APPLE__//opengl库#include "glew.h"#include "glut.h"//ANDROID平台  #elif __ANDROID__  //opengl库#include "glew.h"#include "glut.h"//linux#else//opengl库#include "glew.h"#include "glut.h"#endif//到处宏定义//windows#ifdef WIN32#define LVS_DLLEXPORT __declspec(dllexport)//ios#elif __APPLE__#define LVS_DLLEXPORT//linux#else#define LVS_DLLEXPORT#endif//着色器用的顶点属性索引 position是由3个(x,y,z)组成,#define ATTRIB_VERTEX 3//着色器用的像素,纹理属性索引 而颜色是4个(r,g,b,a)#define ATTRIB_TEXTURE 4//是否旋转图像(纹理)#define TEXTURE_ROTATE    0//显示图像(纹理)的一半#define TEXTURE_HALF      0//窗口消息函数指针typedef void (*WindowRepaintCK)(void);//回调读取数据函数指针,数据及时间戳typedef int (*DisplayDataCK)(void ** data,int * timer_millis);//接口初始化int lvs_opengl_interface_init(int screen_width,int screen_height,int window_x, int window_y,int yuvdata_width,int uvdata_height,char * shader_vsh_pathname,char * shader_fsh_pathname,DisplayDataCK displaydatack,WindowRepaintCK windowrepaintcallback);//接口释放void lvs_opengl_interface_uninit();//接口渲染数据(定时器,渲染时间,毫秒),数据及渲染定时时间在回调里面做处理void lvs_opengl_interface_write(int value);//接口opengl消息循环void lvs_opengl_interface_messageloop();//渲染数据(定时器,渲染时间,毫秒),数据及渲染定时时间在回调里面做处理void TimerFunc1(int value);   //这里如果有可能则调成类的成员函数,以后处理,暂时不知道怎么解决类成员函数递归??using namespace std;class cclass_opengl_interface;class cclass_opengl_interface{public:cclass_opengl_interface();virtual ~cclass_opengl_interface();//初始化int initopengl(int screen_width,int screen_height,int window_x, int window_y,int yuvdata_width,int uvdata_height,char * shader_vsh_pathname,char * shader_fsh_pathname,DisplayDataCK displaydatack,WindowRepaintCK windowrepaintcallback);//初始化着色器,类似于告GPU当传进去数据的时候采用什么样的规则。void InitShaders();//具体显示图像的函数int DisplayImage(void * parm);//opengl消息循环void messageloop();private:public:DisplayDataCK m_displaydatack;                                   //用于显示回调函数,参数数据及时间戳char * m_yuvbuf; //存放yuv数据的buf指针,申请buffer在外面int m_millis_realtime;                                           //实时的时间戳,每次回调会更新private:int m_screen_width; //窗口宽int m_screen_height; //窗口高int m_window_x; //窗口的x坐标int m_window_y; //窗口的y坐标int m_yuvdata_width; //数据宽int m_yuvdata_height; //数据高WindowRepaintCK m_windowrepaintcallback; //窗口重绘的时候,例如最大化最小化窗口,缩放窗口等让窗口重绘的时候调用。//从而接收消息循环 char m_shader_vsh_pathname[256]; //shader的vsh源码位置char m_shader_fsh_pathname[256]; //shader的fsh源码位置GLuint m_textureid_y, m_textureid_u, m_textureid_v;              //纹理的名称,并且,该纹理的名称在当前的应用中不能被再次使用。GLuint m_textureUniformY, m_textureUniformU,m_textureUniformV;   //用于纹理渲染的变量 };#endif


//Lvs_OpenGl_Interface.cpp

#include "Lvs_OpenGl_Interface.h"static cclass_opengl_interface * copengl_interface = NULL;int lvs_opengl_interface_init(int screen_width,int screen_height,int window_x, int window_y,int yuvdata_width,int uvdata_height,char * shader_vsh_pathname,char * shader_fsh_pathname,DisplayDataCK displaydatack,WindowRepaintCK windowrepaintcallback){int ret = 0;printf("Device : lvs_opengl_interface_init\n");if(copengl_interface == NULL){copengl_interface = new cclass_opengl_interface();//初始化copengl_interface->initopengl(screen_width,screen_height,window_x,window_y,yuvdata_width,uvdata_height,shader_vsh_pathname,shader_fsh_pathname,displaydatack,windowrepaintcallback);//初始化着色器,类似于告GPU当传进去数据的时候采用什么样的规则。copengl_interface->InitShaders();}return ret;}void lvs_opengl_interface_uninit(){printf("Device : lvs_opengl_interface_uninit\n");if(copengl_interface){delete copengl_interface;copengl_interface = NULL;}return ;}void lvs_opengl_interface_write(int value){ //这里如果有可能则调成类的成员函数,以后处理,暂时不知道怎么解决类成员函数递归TimerFunc1(value);}void lvs_opengl_interface_messageloop(){copengl_interface->messageloop();}void TimerFunc1(int value){int ret = 0;//调用回调函数获取数据copengl_interface->m_displaydatack((void **)&copengl_interface->m_yuvbuf,&copengl_interface->m_millis_realtime);//这里做具体的处理ret = copengl_interface->DisplayImage(NULL);//因为glut的定时器是调用一次才产生一次定时,所以如果要持续产生定时的话,//在定时函数末尾再次调用glutTimerFunc//这里如果有可能则调成类的成员函数,以后处理,暂时不知道怎么解决类成员函数递归glutTimerFunc(copengl_interface->m_millis_realtime, TimerFunc1, 0);}cclass_opengl_interface::cclass_opengl_interface(){m_screen_width = 0; m_screen_height = 0; m_window_x = 0;m_window_y = 0;m_yuvdata_width = 0;m_yuvdata_height = 0; memset(m_shader_vsh_pathname,0,256);memset(m_shader_fsh_pathname,0,256);m_windowrepaintcallback = NULL;m_textureid_y = 0;m_textureid_u = 0;m_textureid_v = 0;m_textureUniformY = 0;m_textureUniformU = 0;m_textureUniformV = 0;m_displaydatack = NULL;m_yuvbuf = NULL;m_millis_realtime = 0;}cclass_opengl_interface::~cclass_opengl_interface(){m_screen_width = 0; m_screen_height = 0; m_window_x = 0;m_window_y = 0;m_yuvdata_width = 0;m_yuvdata_height = 0; memset(m_shader_vsh_pathname,0,256);memset(m_shader_fsh_pathname,0,256);m_windowrepaintcallback = NULL;m_textureid_y = 0;m_textureid_u = 0;m_textureid_v = 0;m_textureUniformY = 0;m_textureUniformU = 0;m_textureUniformV = 0;m_displaydatack = NULL;m_yuvbuf = NULL;m_millis_realtime = 0;}int cclass_opengl_interface::initopengl(int screen_width,int screen_height,int window_x, int window_y,int yuvdata_width,int uvdata_height,char * shader_vsh_pathname,char * shader_fsh_pathname,DisplayDataCK displaydatack,WindowRepaintCK windowrepaintcallback){int ret = 0;m_screen_width = screen_width; m_screen_height = screen_height; m_window_x = window_x;m_window_y = window_y;m_yuvdata_width = yuvdata_width;m_yuvdata_height = uvdata_height; sprintf(m_shader_vsh_pathname,"%s",shader_vsh_pathname);sprintf(m_shader_fsh_pathname,"%s",shader_fsh_pathname);m_windowrepaintcallback = windowrepaintcallback;m_displaydatack = displaydatack;//初始化 GLUT opengl函数库int zwg_argc=1;//添加函数库名称char* zwg_argv[]={"ZWG_GLUT"};  glutInit(&zwg_argc, zwg_argv); //设置显示模型glutInitDisplayMode(GLUT_DOUBLE | GLUT_RGBA /*| GLUT_STENCIL | GLUT_DEPTH*/);//设置在屏幕上的起始位置glutInitWindowPosition(m_window_x, m_window_y);//设置显示窗口大小,宽高glutInitWindowSize(m_screen_width, m_screen_height);//设置显示窗口名称glutCreateWindow("Lvs_OpenGl");//OpenGL扩展库是个简单的工具,opengl纹理程序用的着色程序初始化GLenum lvs_glew = glewInit();//输出版本号printf("OpenGl Version: %s\n", glGetString(GL_VERSION));//设置绘制窗口时候接收消息的回调函数glutDisplayFunc(m_windowrepaintcallback);ret = 1;return ret;}void cclass_opengl_interface::InitShaders(){GLint vertCompiled, fragCompiled;  //调试 shader的返回值,如果一切正常返回GL_TRUE代,否则返回GL_FALSE。GLuint p;      //Program着色器程序的idGLint linked;  //调试 param的返回值,如果一切正常返回GL_TRUE代,否则返回GL_FALSE。GLint v, f;  //shader的id;char vs[1024 *10] = {0}; //shader源码的字串vsh 是Vertex Shader(顶点着色器)char fs[1024 *10] = {0}; //shader源码的字串fsh 是Fragment Shader(片元着色器)  const char * vs_buf = vs;const char * fs_buf = fs;//shader的处理类似于将OpenGL Shader Language,简称GLSL的源码编译成2进制程序//“vsh负责搞定像素位置,填写gl_Posizion;fsh负责搞定像素外观,填写 gl_FragColor。”//Shader: step1 创建两个shader 实例。v = glCreateShader(GL_VERTEX_SHADER);f = glCreateShader(GL_FRAGMENT_SHADER);//着色器源码//penGL的着色器有.fsh和.vsh两个文件。这两个文件在被编译和链接后就可以产生可执行程序与GPU交互。//shader.vsh 是Vertex Shader(顶点着色器),用于顶点计算,可以理解控制顶点的位置,在这个文件中我们通常会传入当前顶点的位置,和纹理的坐标。//shader.fsh 是Fragment Shader(片源着色器),在这里面我可以对于每一个像素点进行重新计算。//将Vertex Shader和Fragment Shader源码读取到字符串中。FILE * infile_vsh = fopen(m_shader_vsh_pathname, "rb");int len_vsh = fread((char *)vs, 1, 1024 *10, infile_vsh);fclose(infile_vsh);infile_vsh = NULL;vs[len_vsh] = 0;FILE * infile_fsh = fopen(m_shader_fsh_pathname, "rb");int len_fsh = fread((char *)fs, 1, 1024 *10, infile_fsh);fclose(infile_fsh);infile_fsh = NULL;vs[len_fsh] = 0;//Shader: step2 给Shader实例指定源码。glShaderSource(v, 1, &vs_buf,NULL);glShaderSource(f, 1, &fs_buf,NULL);//Shader: step3 在线编译Shader源码。glCompileShader(v);//Shader: step4 调试一个Shader//void glGetShaderiv(GLuint shader,GLenum pname,GLint *params);//params:返回值,如果一切正常返回GL_TRUE代,否则返回GL_FALSE。glGetShaderiv(v, GL_COMPILE_STATUS, &vertCompiled);glCompileShader(f);glGetShaderiv(f, GL_COMPILE_STATUS, &fragCompiled);//Program有点类似于一个程序的链接器。program对象提供了把需要做的事连接在一起的机制。在一个program中,shader对象可以连接在一起。//Program 这个类似于运行OpenGL Shader Language,简称GLSL的源码编译成2进制程序的执行环境,链接器//Program: Step1 创建programp = glCreateProgram(); //Program: Step2 绑定shader到programglAttachShader(p,v);glAttachShader(p,f); //通过glBindAttribLocation()把“顶点属性索引”绑定到“顶点属性名” glBindAttribLocation(p, ATTRIB_VERTEX, "vertexIn");//通过glBindAttribLocation()把“像素纹理属性索引”绑定到“像素纹理属性名” glBindAttribLocation(p, ATTRIB_TEXTURE, "textureIn");//Program: Step3 链接programglLinkProgram(p);//void glGetProgramiv (int program, int pname, int[] params, int offset)   //参数含义: //program:一个着色器程序的id; //pname:GL_LINK_STATUS; //param:返回值,如果一切正常返回GL_TRUE代,否则返回GL_FALSE。glGetProgramiv(p, GL_LINK_STATUS, &linked);  //Program: Step4 在链接了程序以后,我们可以使用glUseProgram()函数来加载并使用链接好的程序glUseProgram(p);//获取片源着色器源码中的变量,用于纹理渲染m_textureUniformY = glGetUniformLocation(p, "tex_y");m_textureUniformU = glGetUniformLocation(p, "tex_u");m_textureUniformV = glGetUniformLocation(p, "tex_v"); //顶点数组(物体表面坐标取值范围是-1到1,数组坐标:左下,右下,左上,右上)#if TEXTURE_ROTATEstatic const GLfloat vertexVertices[] = {-1.0f, -0.5f,0.5f, -1.0f,-0.5f,  1.0f,1.0f,  0.5f,};    #elsestatic const GLfloat vertexVertices[] = {-1.0f, -1.0f,1.0f, -1.0f,-1.0f,  1.0f,1.0f,  1.0f,};    #endif//像素,纹理数组(纹理坐标取值范围是0-1,坐标原点位于左下角,数组坐标:左上,右上,左下,右下,如果先左下,图像会倒过来)#if TEXTURE_HALFstatic const GLfloat textureVertices[] = {0.0f,  1.0f,0.5f,  1.0f,0.0f,  0.0f,0.5f,  0.0f,}; #elsestatic const GLfloat textureVertices[] = {0.0f,  1.0f,1.0f,  1.0f,0.0f,  0.0f,1.0f,  0.0f,}; #endif//定义顶点数组glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices);//启用属性数组glEnableVertexAttribArray(ATTRIB_VERTEX); //定义像素纹理数组glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices);//启用属性数组glEnableVertexAttribArray(ATTRIB_TEXTURE);//初始化纹理glGenTextures(1, &m_textureid_y); //绑定纹理glBindTexture(GL_TEXTURE_2D, m_textureid_y);    //设置该纹理的一些属性glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glGenTextures(1, &m_textureid_u);glBindTexture(GL_TEXTURE_2D, m_textureid_u);   glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);glGenTextures(1, &m_textureid_v); glBindTexture(GL_TEXTURE_2D, m_textureid_v);    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);}int cclass_opengl_interface::DisplayImage(void * parm){int ret = 0;unsigned char * yuvplaner[3] = {0};                             //存放yuv数据的分量数组(y,u,v) //关联到yuv数据的分量数组yuvplaner[0] = (unsigned char *)m_yuvbuf;yuvplaner[1] = yuvplaner[0] + m_yuvdata_width*m_yuvdata_height;yuvplaner[2] = yuvplaner[1] + m_yuvdata_width*m_yuvdata_height/4;//Clear//清除颜色设为黑色,把整个窗口清除为当前的清除颜色,glClear()的唯一参数表示需要被清除的缓冲区。glClearColor(0.0,0.0,0.0,0.0);glClear(GL_COLOR_BUFFER_BIT);//显卡中有N个纹理单元(具体数目依赖你的显卡能力),每个纹理单元(GL_TEXTURE0、GL_TEXTURE1等)都有GL_TEXTURE_1D、GL_TEXTURE_2D等//Y//选择当前活跃的纹理单元glActiveTexture(GL_TEXTURE0);//允许建立一个绑定到目标纹理的有名称的纹理glBindTexture(GL_TEXTURE_2D, m_textureid_y);//根据指定的参数,生成一个2D纹理(Texture)。相似的函数还有glTexImage1D、glTexImage3D。glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_yuvdata_width, m_yuvdata_height, 0, GL_RED, GL_UNSIGNED_BYTE, yuvplaner[0]); glUniform1i(m_textureUniformY, 0);     //设置纹理,按照前面设置的规则怎样将图像或纹理贴上(参数和选择的活跃纹理单元对应,GL_TEXTURE0)//UglActiveTexture(GL_TEXTURE1);glBindTexture(GL_TEXTURE_2D, m_textureid_u);glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_yuvdata_width/2, m_yuvdata_height/2, 0, GL_RED, GL_UNSIGNED_BYTE, yuvplaner[1]);       glUniform1i(m_textureUniformU, 1);//VglActiveTexture(GL_TEXTURE2);glBindTexture(GL_TEXTURE_2D, m_textureid_v);glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_yuvdata_width/2, m_yuvdata_height/2, 0, GL_RED, GL_UNSIGNED_BYTE, yuvplaner[2]);    glUniform1i(m_textureUniformV, 2); // 绘制glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//双缓冲显示glutSwapBuffers();//单缓冲显示//glFlush();return 1;}void cclass_opengl_interface::messageloop(){//glutMainLoop进入GLUT事件处理循环,让所有的与“事件”有关的函数调用无限循环。开始时间循环glutMainLoop();}

//main.cpp

#include "Lvs_OpenGl_Interface.h"//要显示的yuv文件路径及名称#define YUV_STREAM_PATH_NAME  "../yuv_stream/352_288_yuv420p.yuv"//yuv数据宽#define YUVDATA_WIDTH  352 //yuv数据高#define YUVDATA_HEIGHT 288//shader的vsh源码位置#define SHADER_VSH_SOURCE  "../opengl_win32/Shader.vsh"//shader的fsh源码位置#define SHADER_FSH_SOURCE  "../opengl_win32/Shader.fsh"static FILE * m_inyuvfile = NULL;                                       //yuv文件句柄static unsigned char m_yuvbuf[YUVDATA_WIDTH*YUVDATA_HEIGHT*3/2];        //存放yuv数据的bufstatic unsigned char * m_yuvplane[3] = {0};                             //存放yuv数据的分量数组(y,u,v) static int m_timer_realtime = 40;                                       //每一次回调渲染数据定时器时间,可根据时间戳变化,毫秒//窗口重绘的时候,例如最大化最小化窗口,缩放窗口等让窗口重绘的时候调用。//从而接收消息循环void WindowRepaintCallback();//回调读取数据函数,参数数据及时间戳int DisplayDataCallback(void * data,int * timer_millis);//窗口重绘的时候,例如最大化最小化窗口,缩放窗口等让窗口重绘的时候调用。//从而接收消息循环void WindowRepaintCallback(){//可以做一些处理printf("窗口重绘了...\n");}int DisplayDataCallback(void ** data,int * timer_millis){int ret = 0;//循环读取文件ret = fread(m_yuvbuf, 1, YUVDATA_WIDTH*YUVDATA_HEIGHT*3/2, m_inyuvfile);if (ret != YUVDATA_WIDTH*YUVDATA_HEIGHT*3/2){//seek到文件开头fseek(m_inyuvfile, 0, SEEK_SET);fread(m_yuvbuf, 1,YUVDATA_WIDTH*YUVDATA_HEIGHT*3/2, m_inyuvfile);}//将数据返回去*data = m_yuvbuf;*timer_millis = m_timer_realtime;return ret;}int main(){int ret = 0;//打开 YUV420P 文件if((m_inyuvfile = fopen(YUV_STREAM_PATH_NAME, "rb")) == NULL){printf("filed open file : %s\n",YUV_STREAM_PATH_NAME);return getchar();}else{printf("success open file : %s\n",YUV_STREAM_PATH_NAME);}//初始化ret = lvs_opengl_interface_init(500,500,100,100,YUVDATA_WIDTH,YUVDATA_HEIGHT,SHADER_VSH_SOURCE,SHADER_FSH_SOURCE,DisplayDataCallback,WindowRepaintCallback);//渲染,带定时器,数据回调,及渲染时间回调lvs_opengl_interface_write(m_timer_realtime);//glutMainLoop进入GLUT事件处理循环,让所有的与“事件”有关的函数调用无限循环。开始时间循环lvs_opengl_interface_messageloop();//关闭yuv420p文件if (m_inyuvfile != NULL){fclose(m_inyuvfile);m_inyuvfile = NULL;}return 1;}


程序运行效果:



暂时不知道怎么解决类成员函数递归,以后待解决。


本demo还需完善。

如有错误请指正:

交流请加QQ群:62054820
QQ:379969650.




0 0