Qt 通过OpenGL显示实时画面

1 篇文章 0 订阅

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

欢迎大家留言讨论!

 

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值