使用QT的QOpenGLWidget显示yuv视频数据

191 篇文章 38 订阅
9 篇文章 0 订阅

QT5的QOpenGLWidget和QGLWidget有不同,将原有的继承自QGLWidget的改为QOpenGLWidget主要区别就是一些函数不能使用,要想使用opengl的一些函数需要继承QOpenGLFunctions,具体修改如下:

头文件

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QOpenGLShaderProgram>
 
 
typedef struct GLTexture_s {
    GLuint id;  // for glGenTextures
    HFrame frame;
} GLTexture;
//qt opengl的封装类
 
//这个类主要做的是界面显示的操作,不要将业务相关(开始播放,暂停,停止等)的添加进来
//具体做法是再写一个纯业务类来处理,要处理业务时调用这个业务类的方法即可
class HGLWidget :public QOpenGLWidget,protected QOpenGLFunctions
{
  Q_OBJECT
public:
     HGLWidget(QWidget* parent = NULL);
 
     void setVertices(double ratio);
     void setVertices(QRect rc);
 
     //画帧
     void drawFrame(HFrame *pFrame);
     //画纹理
     void drawTexture(HRect rc, GLTexture *tex);
     //画框
     void drawRect(HRect rc, HColor clr, int line_width = 1, bool bFill = false);
     //画文本
     void drawText(QPoint lb, const char* text, int fontsize, QColor clr);
 
protected:
    virtual void initializeGL();
    virtual void resizeGL(int w, int h);
    virtual void paintGL();
 
 
    static void loadYUVShader();
    //VAO(Vertex Array Object)顶点数组对象
    void initVAO();
    void initYUV();
 
    //画yuv数据
    void drawYUV(HFrame* pFrame);
 
protected:
    static std::atomic_flag s_init_flag;
    //无符号四字节整型,
    static GLuint prog_yuv;  // GLuint
    static GLuint texUniformY;// GLuint
    static GLuint texUniformU;//GLuint
    static GLuint texUniformV;//GLuint
 
static QOpenGLShaderProgram m_ShaderProgram;
    //编号,对应Y U V
    GLuint  tex_yuv[3];//GLuint
 
    GLfloat vertices[8];//GLfloat
    GLfloat textures[8];//GLfloat
 
 
 
    // NOTE: QPainter used 3 VertexAttribArray
    enum VER_ATTR{
        VER_ATTR_VER = 3,
        VER_ATTR_TEX,
    };
 
};

源文件:

std::atomic_flag HGLWidget::s_init_flag = ATOMIC_FLAG_INIT;
GLuint HGLWidget::prog_yuv;
GLuint HGLWidget::texUniformY;
GLuint HGLWidget::texUniformU;
GLuint HGLWidget::texUniformV;
QOpenGLShaderProgram HGLWidget::m_ShaderProgram;
 
 HGLWidget::HGLWidget(QWidget* parent)
     :QOpenGLWidget(parent)
 {
 
 }
 
 
 void  HGLWidget::setVertices(double ratio)
 {
     double w = 1.0, h = 1.0;
     if (ratio < 1.0) {
         w = ratio;
     }
     else {
         h = 1.0 / ratio;
     }
 
     GLfloat tmp[] = {
         -w, -h,
          w, -h,
         -w,  h,
          w,  h,
     };
 
     memcpy(vertices, tmp, sizeof(GLfloat)*8);
 }
 void  HGLWidget::setVertices(QRect rc)
 {
     int wnd_w = width();
     int wnd_h = height();
     if (wnd_w <= 0 || wnd_h <= 0) {
         return;
     }
     GLfloat left = (GLfloat)rc.left() * 2 / wnd_w - 1;
     GLfloat right = (GLfloat)(rc.right()+1) * 2 / wnd_w - 1;
     GLfloat top = 1 - (GLfloat)rc.top() * 2 / wnd_h;
     GLfloat bottom = 1 - (GLfloat)(rc.bottom()+1) * 2 / wnd_h;
     qDebug("l=%d r=%d t=%d b=%d", rc.left(), rc.right(), rc.top(), rc.bottom());
     qDebug("l=%f r=%f t=%f b=%f", left, right, top, bottom);
     GLfloat tmp[] = {
         left,  bottom,
         right, bottom,
         left,  top,
         right, top
     };
 
     memcpy(vertices, tmp, sizeof(GLfloat)*8);
 }
 
 //画帧
 void  HGLWidget::drawFrame(HFrame *pFrame)
 {
 
     if (pFrame->type == GL_I420 || pFrame->type == GL_YV12) {
         drawYUV(pFrame);
     }
     else {
//         glMatrixMode(GL_PROJECTION);
//         glLoadIdentity();
//         glRasterPos3f(-1.0f,1.0f,0);
//         glPixelZoom(width()/(float)pFrame->w, -height()/(float)pFrame->h);
//         glDrawPixels(pFrame->w, pFrame->h, pFrame->type, GL_UNSIGNED_BYTE, pFrame->buf.base);
     }
 }
 //画纹理
 void  HGLWidget::drawTexture(HRect rc, GLTexture *tex)
 {
     glActiveTexture(GL_TEXTURE0);
     glBindTexture(GL_TEXTURE_2D, tex->id);
 
     glEnable(GL_TEXTURE_2D);
     glEnable(GL_BLEND);
     glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
 
 
 
     glDisable(GL_TEXTURE_2D);
     glDisable(GL_BLEND);
 }
 //画框
 void  HGLWidget::drawRect(HRect rc, HColor clr, int line_width, bool bFill)
 {
 
 }
 //画文本
#include <QPainter>
void HGLWidget::drawText(QPoint lb, const char* text, int fontsize, QColor clr) {
    QPainter painter(this);
    QFont font = painter.font();
    font.setPointSize(fontsize);
    painter.setFont(font);
    painter.setPen(clr);
    painter.drawText(lb, text);
}
 
 
void  HGLWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
 
 
    if (!s_init_flag.test_and_set()) {
//        s_init_flag.clear();
        loadYUVShader();
    }
 
    initVAO();
    initYUV();
 
}
void  HGLWidget::resizeGL(int w, int h)
{
    //设置视口
    glViewport(0,0,w,h);
}
void  HGLWidget::paintGL()
{
    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
    glClear(GL_COLOR_BUFFER_BIT);
}
void HGLWidget::loadYUVShader() {
 
    //创建顶点着色器
    QOpenGLShader VSHader(QOpenGLShader::Vertex);//, this
 
    //顶点着色器源码
    const char *vsrc = "attribute vec4 vertexIn; \
    attribute vec2 textureIn; \
    varying vec2 textureOut;  \
    void main(void)           \
    {                         \
        gl_Position = vertexIn; \
        textureOut = textureIn; \
    }";
    //源码送入顶点着色器程序
    bool bCompile = VSHader.compileSourceCode(vsrc);
    if(!bCompile)
    {
    }
 
    //创建片元着色器
    QOpenGLShader FSHader(QOpenGLShader::Fragment);//,this
 
    //片段着色器源码(windows下opengl es 需要加上float这句话)
        const char *fsrc =
    #if defined(WIN32)
        "#ifdef GL_ES\n"
        "precision mediump float;\n"
        "#endif\n"
    #else
    #endif
    "varying vec2 textureOut; \
    uniform sampler2D tex_y; \
    uniform sampler2D tex_u; \
    uniform sampler2D tex_v; \
    void main(void) \
    { \
        vec3 yuv; \
        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); \
    }";
 
    //源码送入片元着色器程序
    bCompile = FSHader.compileSourceCode(fsrc);
    if(!bCompile)
    {
    }
 
    //创建一个新的程序
    //创建着色器程序容器
 
    //m_ShaderProgram = new QOpenGLShaderProgram;
    //关联着色器
     //将片段着色器添加到程序容器
     m_ShaderProgram.addShader(&FSHader);
     //将顶点着色器添加到程序容器
    m_ShaderProgram.addShader(&VSHader);
    //绑定attribute变量
     //绑定属性vertexIn到指定位置ATTRIB_VERTEX,该属性在顶点着色源码其中有声明
    m_ShaderProgram.bindAttributeLocation("vertexIn", VER_ATTR_VER);
     //绑定属性textureIn到指定位置ATTRIB_TEXTURE,该属性在顶点着色源码其中有声明
     m_ShaderProgram.bindAttributeLocation("textureIn", VER_ATTR_TEX);
 
    //链接程序
     //链接所有所有添入到的着色器程序
     m_ShaderProgram.link();
     //激活所有链接
    m_ShaderProgram.bind();
    //获取Uniform变量
 
     //读取着色器中的数据变量tex_y, tex_u, tex_v的位置,这些变量的声明可以在
     //片段着色器源码中可以看到
     texUniformY =m_ShaderProgram.uniformLocation("tex_y");
     texUniformU =m_ShaderProgram.uniformLocation("tex_u");
     texUniformV =m_ShaderProgram.uniformLocation("tex_v");
 
     qDebug("loadYUVShader ok");
 
}
 
 
//VAO(Vertex Array Object)顶点数组对象
void  HGLWidget::initVAO()
{
    setVertices(1.0);
 
    GLfloat tmp[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f,
    };
 
    // reverse
    /*
    GLfloat tmp[] = {
        0.0f, 0.0f,
        1.0f, 0.0f,
        0.0f, 1.0f,
        1.0f, 1.0f,
    };
    */
    memcpy(textures, tmp, sizeof(GLfloat)*8);
 
    glVertexAttribPointer(VER_ATTR_VER, 2, GL_FLOAT, GL_FALSE, 0, vertices);
    glEnableVertexAttribArray(VER_ATTR_VER);
 
    glVertexAttribPointer(VER_ATTR_TEX, 2, GL_FLOAT, GL_FALSE, 0, textures);
    glEnableVertexAttribArray(VER_ATTR_TEX);
}
void  HGLWidget::initYUV()
{
    glGenTextures(3, tex_yuv);
    for (int i = 0; i < 3; ++i) {
        glBindTexture(GL_TEXTURE_2D, tex_yuv[i]);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    }
}
 
 
//画yuv数据
void  HGLWidget::drawYUV(HFrame* pFrame)
{
    assert(pFrame->type == GL_I420 || pFrame->type == GL_YV12);
 
    int w = pFrame->w;
    int h = pFrame->h;
    int y_size = w*h;
    GLubyte* y = (GLubyte*)pFrame->buf.base;
    GLubyte* u = y + y_size;
    GLubyte* v = u + (y_size>>2);
    if (pFrame->type == GL_YV12) {
        GLubyte* tmp = u;
        u = v;
        v = tmp;
    }
 
    //使用Program
    glUseProgram(prog_yuv);
    m_ShaderProgram.bind();
    glActiveTexture(GL_TEXTURE0);
    glBindTexture(GL_TEXTURE_2D, tex_yuv[0]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w, h, 0, GL_RED, GL_UNSIGNED_BYTE, y);
    glUniform1i(texUniformY, 0);
 
    glActiveTexture(GL_TEXTURE1);
    glBindTexture(GL_TEXTURE_2D, tex_yuv[1]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w/2, h/2, 0, GL_RED, GL_UNSIGNED_BYTE, u);
    glUniform1i(texUniformU, 1);
 
    glActiveTexture(GL_TEXTURE2);
    glBindTexture(GL_TEXTURE_2D, tex_yuv[2]);
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, w/2, h/2, 0, GL_RED, GL_UNSIGNED_BYTE, v);
    glUniform1i(texUniformV, 2);
 
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
 
    //使用Program
    glUseProgram(0);
}

 

  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值