“OpenGLDrag.h”頭文件
#ifndef OPENGLDRAG_H
#define OPENGLDRAG_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QOpenGLShaderProgram>
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 <QtGlobal>
#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()<< "生成紋理 "<<id_y;
}else{
id_y = globalData::getInstance()->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<GLuint> 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 <QApplication>
#include <QDebug>
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 <QMainWindow>
#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就可以正常顯示。
歡迎大家留言討論!