Qt 通過OpenGL顯示實時畫面

“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就可以正常顯示。

歡迎大家留言討論!

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章