“OpenGLDrag.h”头文件
#ifndef OPENGLDRAG_H
#define OPENGLDRAG_H
#include
#include
#include
#include
class OpenGLDrag : public QOpenGLWidget,protected QOpenGLFunctions
{
Q_OBJECT
public:
OpenGLDrag(QString ipAddr, QWidget *parent = 0);
~OpenGLDrag();
public Q_SLOTS:
void PlayOneFrame(int width,int height,unsigned char* inBuf,int bufSize);
private Q_SLOTS:
void initializeGL();
void resizeGL(int width, int height);
void paintGL();
private:
GLuint textureUniformY; //y纹理数据位置
GLuint textureUniformU; //u纹理数据位置
GLuint textureUniformV; //v纹理数据位置
GLuint id_y; //y纹理对象ID
GLuint id_u; //u纹理对象ID
GLuint id_v; //v纹理对象ID
QOpenGLShaderProgram *m_pShaderProgram; //着色器程序容器
unsigned char* m_pBufYuv420p;//实时数据
int m_nVideoW; //视频分辨率宽
int m_nVideoH; //视频分辨率高
};
#endif // OPENGLDRAG_H
“OpenGLDrag.cpp”文件
#include "OpenGLDrag.h"
#include "GlobalData.h"
#include
#define ATTRIB_VERTEX 3
#define ATTRIB_TEXTURE 4
// 顶点矩阵
static const GLfloat vertexVertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
//纹理矩阵
static const GLfloat textureVertices[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f,
};
OpenGLDrag::OpenGLDrag(QString ipAddr, QWidget *parent)
:QOpenGLWidget(parent)
{
textureUniformY = 0;
textureUniformU = 0;
textureUniformV = 0;
id_y = 0;
id_u = 0;
id_v = 0;
m_pShaderProgram = NULL;
m_nVideoH = 0;
m_nVideoW = 0;
m_pBufYuv420p = NULL;
}
OpenGLDrag::~OpenGLDrag()
{
makeCurrent();
globalData::getInstance()->g_GLList.append(id_y);//回收纹理
globalData::getInstance()->g_GLList.append(id_u);
globalData::getInstance()->g_GLList.append(id_v);
m_pShaderProgram->disableAttributeArray(ATTRIB_VERTEX);
m_pShaderProgram->disableAttributeArray(ATTRIB_TEXTURE);
m_pShaderProgram->release();
m_pShaderProgram->removeAllShaders();
delete m_pShaderProgram;
m_pShaderProgram=NULL;
delete [] m_pBufYuv420p;
m_pBufYuv420p=NULL;
doneCurrent();
}
void OpenGLDrag::initializeGL()
{
//初始化OpenGL函数
initializeOpenGLFunctions();
#ifdef USEGL
glEnable(GL_DEPTH_TEST);
#endif
glEnable(GL_TEXTURE_2D);
//创建着色器程序容器
m_pShaderProgram = new QOpenGLShaderProgram();
//将顶点着色器源码添加到程序容器
m_pShaderProgram->addShaderFromSourceCode(QOpenGLShader::Vertex,
"attribute highp vec4 vertexIn;"
"attribute highp vec2 textureIn;"
"varying highp vec2 textureOut;"
"void main(void) {"
"gl_Position = vertexIn;"
"textureOut = textureIn;}");
//将片段着色器添加到程序容器
m_pShaderProgram->addShaderFromSourceCode(QOpenGLShader::Fragment,
"varying highp vec2 textureOut;"
"uniform highp sampler2D tex_y;"
"uniform highp sampler2D tex_u;"
"uniform highp sampler2D tex_v;"
"void main(void){"
"highp vec3 yuv;"
"highp vec3 rgb;"
"yuv.x = texture2D(tex_y, textureOut).r;"
"yuv.y = texture2D(tex_u, textureOut).r - 0.5;"
"yuv.z = texture2D(tex_v, textureOut).r - 0.5;"
"rgb = mat3( 1, 1, 1,"
"0, -0.39465, 2.03211,"
"1.13983, -0.58060, 0) * yuv;"
"gl_FragColor = vec4(rgb, 1);}" );
//绑定属性vertexIn到指定位置ATTRIB_VERTEX,该属性在顶点着色源码其中有声明
m_pShaderProgram->bindAttributeLocation("vertexIn", ATTRIB_VERTEX);
//绑定属性textureIn到指定位置ATTRIB_TEXTURE,该属性在顶点着色源码其中有声明
m_pShaderProgram->bindAttributeLocation("textureIn", ATTRIB_TEXTURE);
//链接所有所有添入到的着色器程序
m_pShaderProgram->link();
//激活所有链接
m_pShaderProgram->bind();
//读取着色器中的数据变量tex_y, tex_u, tex_v的位置,这些变量的声明可以在
//片段着色器源码中可以看到
textureUniformY = m_pShaderProgram->uniformLocation("tex_y");
textureUniformU = m_pShaderProgram->uniformLocation("tex_u");
textureUniformV = m_pShaderProgram->uniformLocation("tex_v");
//设置属性ATTRIB_VERTEX的顶点矩阵值以及格式
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices);
glEnableVertexAttribArray(ATTRIB_VERTEX);
//设置属性ATTRIB_TEXTURE的纹理矩阵值以及格式
glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices);
glEnableVertexAttribArray(ATTRIB_TEXTURE);
if(globalData::getInstance()->g_GLList.size()==0){
glGenTextures(1,&id_y); //生成纹理
//qDebug()<< "生成纹理 "<g_GLList.first();
globalData::getInstance()->g_GLList.removeFirst();
// glDeleteTextures(1,&id_y);
}
glBindTexture(GL_TEXTURE_2D,id_y);//绑定纹理
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,(GLfloat)GL_NEAREST);//设置纹理参数
glTexParameterf(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,(GLfloat)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);
if(globalData::getInstance()->g_GLList.size()==0)
glGenTextures(1,&id_u); //生成纹理
else{
id_u = globalData::getInstance()->g_GLList.first();
globalData::getInstance()->g_GLList.removeFirst();
// glDeleteTextures(1,&id_u);
}
glBindTexture(GL_TEXTURE_2D, id_u);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)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);
if(globalData::getInstance()->g_GLList.size()==0)
glGenTextures(1,&id_v); //生成纹理
else{
id_v = globalData::getInstance()->g_GLList.first();
globalData::getInstance()->g_GLList.removeFirst();
// glDeleteTextures(1,&id_v);
}
glBindTexture(GL_TEXTURE_2D, id_v);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, (GLfloat)GL_NEAREST);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, (GLfloat)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);
glClearColor(0.3,0.3,0.3,0.0);//设置背景色
}
void OpenGLDrag::resizeGL(int width, int height)
{
glViewport(0,0, width,height);
}
void OpenGLDrag::paintGL()
{
if(m_pBufYuv420p!=NULL){
//清理屏幕
glClear(GL_COLOR_BUFFER_BIT|GL_DEPTH_BUFFER_BIT);
//加载y数据纹理
glActiveTexture(GL_TEXTURE0);//激活纹理单元GL_TEXTURE0
glBindTexture(GL_TEXTURE_2D, id_y);
#ifdef USEGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW, m_nVideoH, 0, GL_RED, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p);//使用内存中m_pBufYuv420p数据创建真正的y数据纹理
#endif
#ifdef USED3D
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW, m_nVideoH, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p);
#endif
//只能用0,1,2等表示纹理单元的索引,这是opengl不人性化的地方
//0对应纹理单元GL_TEXTURE0,1对应纹理单元GL_TEXTURE1,2对应纹理的单元GL_TEXTURE2
glUniform1i(textureUniformY, 0); //指定y纹理要使用新值
//加载u数据纹理
glActiveTexture(GL_TEXTURE1);//激活纹理单元GL_TEXTURE1
glBindTexture(GL_TEXTURE_2D, id_u);
#ifdef USEGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW/2, m_nVideoH/2, 0, GL_RED, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p+m_nVideoW*m_nVideoH);
#endif
#ifdef USED3D
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p+m_nVideoW*m_nVideoH);
#endif
glUniform1i(textureUniformU, 1);//指定u纹理要使用新值
//加载v数据纹理
glActiveTexture(GL_TEXTURE2);//激活纹理单元GL_TEXTURE2
glBindTexture(GL_TEXTURE_2D, id_v);
#ifdef USEGL
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW/2, m_nVideoH/2, 0, GL_RED, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p+m_nVideoW*m_nVideoH*5/4);
#endif
#ifdef USED3D
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p+m_nVideoW*m_nVideoH*5/4);
#endif
glUniform1i(textureUniformV, 2);//指定v纹理要使用新值
#ifdef USEGL
glDisable(GL_DEPTH_TEST);//should be put before glDrawArrays
#endif
//使用顶点数组方式绘制图形
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}
}
//在其他线程接收数据,解码一帧后通过信号槽传递过来
void OpenGLDrag::PlayOneFrame(int width,int height,unsigned char* inBuf,int bufSize)
{
if(inBuf==NULL || bufSize==0){
//qDebug() << "-------PlayOneFrame inBuf==NULL";
return;
}
if(m_pBufYuv420p==NULL){//如果正在运行中改变摄像头的分辨率有可能导致内存不足而奔溃
// qDebug() << "-------new buf";
int bufSize = 1920*1080*1.5;
m_pBufYuv420p = new unsigned char[bufSize];
}
m_nVideoW = width;
m_nVideoH = height;
memcpy(m_pBufYuv420p,inBuf, bufSize);
//刷新界面,触发paintGL接口
update();
}
“GlobalData.h"头文件 存放一些全局变量
#ifndef GLOBALDATE_H
#define GLOBALDATE_H
#include "OpenGLDrag.h"
class globalData
{
private:
globalData();
static globalData* _instance;
public:
static globalData* getInstance();
public:
QList g_GLList;//回收利用纹理
};
#endif // GLOBALDATE_H
“GlobalData.cpp"
#include "GlobalData.h"
globalData* globalData::_instance = NULL;
globalData* globalData::getInstance(){
if(_instance==NULL){
_instance = new globalData();
}
return _instance;
}
globalData::globalData()
{
g_GLList.clear();
}
“main.cpp”
#include "GlobalData.h"
#include "MainWindow.h"
#include
#include
int main(int argc, char *argv[])
{
#ifdef USED3D
qputenv("QT_ANGLE_PLATFORM", "d3d9");
QCoreApplication::setAttribute(Qt::AA_UseOpenGLES);
#endif
QtSingleApplication a(argc, argv);
MainWindow* gMainwin = new MainWindow() ;
gMainwin->show();
int ret = a.exec();
return ret;
}
"MainWindow.h" 这个根据实际情况调整
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include
#include "OpenGLDrag.h"
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
void Init();
public:
OpenGLDrag* m_dragWin;
private:
Ui::MainWindow *m_ui;
};
#endif // MAINWINDOW_H
"MainWindow.cpp"
#include "MainWindow.h"
#include "ui/ui_mainwindow.h"//这个是UI界面生成的
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
m_ui(new Ui::MainWindow)
{
this->setWindowFlags(Qt::FramelessWindowHint|Qt::WindowSystemMenuHint |Qt::WindowMinMaxButtonsHint);
m_ui->setupUi(this);
Init();
}
MainWindow::~MainWindow()
{
}
void MainWindow::Init()
{
m_dragWin = new OpenGLDrag(QString(this);//一般是需要的时候才创建,可以多个,根据实际需求而定
m_dragWin->show();
//其他线程接收数据,再通过信号槽传递给m_dragWin窗口就要自己实现了
}
pro文件
#-------------------------------------------------
#
# Project created by QtCreator 2017-10-27T16:10:04
#
#-------------------------------------------------
QT += core gui network opengl websockets
greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
TARGET = openglWin
TEMPLATE = app
HEADERS += MainWindow.h\
.
.
.
SOURCES += main.cpp\
MainWindow.cpp\
.
.
.
FORMS += mainwindow.ui
DEFINES += QT_DEPRECATED_WARNINGS\
USED3D
#USEGL
LIBS += -lws2_32 lopengl32
关于纹理回收使用还需要大家给点建议。
因为之前我在删除多个opengl窗口时,在析构的时候调用glDeleteTextures(1,&GLuint), 但实际只有第一个窗口的会有效果,后面的虽然调用了但没效果,还是会造成内存泄漏,没办法才用的现在这种方式进行回收利用。
关于USEGL和USED3D
主要是在电脑休眠再唤醒的时候,也是多个窗口的情况,直接用opengl会显示有问题,通过d3d就可以正常显示。
欢迎大家留言讨论!