zoukankan      html  css  js  c++  java
  • 12.QT-通过QOpenGLWidget显示YUV画面,通过QOpenGLTexture纹理渲染YUV

    在上章11.QT-ffmpeg+QAudioOutput实现音频播放器,我们学习了如何播放音频,接下来我们便来学习如何通过opengl来显示YUV画面
     
    1.为什么使用QOpenGLWidget显示YUV
    如果软件中通过公式来实现软解码的话,会耗掉很多CPU,所以使用opengl,我们只需要将YUV数据传给opengl,然后opengl通过GPU硬件加速图形绘制来实现硬解码.
    需要学习:
     
    项目流程如下所示:
     
    项目界面最终如下所示:

     

     
    2.shader源码分析
    首先通过ffmpeg命令提取出yuv数据:
    ffmpeg -i v1080.mp4 -t 10 -s 640x340 -pix_fmt yuv420p out640x340.yuv
    然后将文件放置到G盘目录下
    2.1顶点shader源码如下所示:
    #version 330 core
    layout (location = 0) in vec3 aPos;
    layout (location = 1) in vec2 aTexCoord;
     
    out vec2 TexCoord;
     
    void main()
    {
    gl_Position = vec4(aPos, 1.0);
    TexCoord = aTexCoord;
    }
    • #version 330 core : 定义版本号,需要注意的是,使用版本3.0以上后、则不能用attribute、varying变量修饰变量了,只能用in和out来代替
    • layout (location = 0) in vec3 aPos : 使用in关键字来声明顶点属性输入,这里创建一个输入变量aPos(3分量),通过layout (location = 0)设定了输入变量的顶点属性的位置值(Location)为0,后面将会通过     setAttributeBuffer()函数来设置它.
    • gl_Position : 设置顶点着色器的输出,这里gl_Position之所以为vec4类型,是因为3d图形演算要用到 4x4的矩阵(4行4列),而矩阵乘法要求n行m列 和 m行p列才能相乘,所以是vec4而不是vec3,由于position 是位置所以应该是 (x,y,z,1.0f),如果是方向向量,则就是 (x,y,z,0.0f).
    2.2片元shader源码如下所示:
    #version 330 core
    const char *fsrc =GET_GLSTR(
     
    out vec4 FragColor;
    in vec2 TexCoord;
    uniform sampler2D texY;
    uniform sampler2D texU;
    uniform sampler2D texV;
     
    void main()
    {
    vec3 yuv;
    vec3 rgb;
     
    yuv.x = texture2D(texY, TexCoord).r;
    yuv.y = texture2D(texU, TexCoord).r-0.5;
    yuv.z = texture2D(texV, TexCoord).r-0.5;
     
    rgb = mat3(1.0, 1.0, 1.0,
    0.0, -0.3455, 1.779,
    1.4075, -0.7169, 0.0) * yuv;
     
    FragColor = vec4(rgb, 1.0);
    }
    );
    • sampler2D: 纹理采样器,存的是一个画面的颜色值,对应的还有sampler3D等
    • texture2D(texY, TexCoord): 其实等价于texture()函数,第一个参数为纹理采样器,第二个参数是对应的纹理坐标,该函数就会根据当前所在纹理坐标去获取对应的颜色,然后输出到FragColor来显示颜色.
    • FragColor : 控制输出的颜色(rgba),(在3.3版本后需要通过out的方式来声明)
    • texture2D(texU, TexCoord).r-0.5: 由于opengl接受的颜色值为(0.0~1.0)浮点数,而不是0~255方式,所以这里减去0.5其实是减去128
    • mat3()函数 : mat3表示的是3x3全矩阵,由于yuv是个1x3矩阵,所以计算出来的rgb也是1x3矩阵.
    以R为例:
    由于R=yuv的第1行(y,u,v)和mat3()内的第1列(1.0,0.0,1.4075)的相乘和、
    所以R=1.0Y+0*(U-128)+1.4075(V-128)
     

    3.myglwidget源文件

    #include "myglwidget.h"
    #include <QtDebug>
    #include <QTimer>
    
    ////GLSL3.0版本后,废弃了attribute关键字(以及varying关键字),属性变量统一用in/out作为前置关键字
    #define GL_VERSION  "#version 330 core
    "
    #define GET_GLSTR(x) GL_VERSION#x
    
    static int VideoWidth=640;
    static int VideoHeight=340;
    
    
    const char *vsrc = GET_GLSTR(
    
        layout (location = 0) in vec3 aPos;
        layout (location = 1) in vec2 aTexCoord;
    
        out vec2 TexCoord;
    
        void main()
        {
            gl_Position = vec4(aPos, 1.0);
            TexCoord = aTexCoord;
        }
    
    );
    
    const char *fsrc =GET_GLSTR(
    
        out vec4 FragColor;
        in vec2 TexCoord;
        uniform sampler2D texY;
        uniform sampler2D texU;
        uniform sampler2D texV;
    
        void main()
        {
            vec3 yuv;
            vec3 rgb;
    
            yuv.x = texture(texY, TexCoord).r;
            yuv.y = texture(texU, TexCoord).r-0.5;
            yuv.z = texture(texV, TexCoord).r-0.5;
    
            rgb = mat3(1.0, 1.0, 1.0,
                0.0, -0.3455, 1.779,
                1.4075, -0.7169, 0.0) * yuv;
    
            FragColor = vec4(rgb, 1.0);
        }
    );
    
    myGlWidget::myGlWidget(QWidget *parent):QOpenGLWidget(parent)
    {
    
    }
    
    
    void myGlWidget::paintGL()
    {
        glClear(GL_COLOR_BUFFER_BIT);
    
    
        // 渲染Shader
        vao.bind();
         if (file->atEnd())
         {
            qDebug()<<"repaly!!!!!!!!";
            file->seek(0);
         }
          program->setUniformValue("texY", 0);
          program->setUniformValue("texU", 1);
          program->setUniformValue("texV", 2);
    
          for(int i=0;i<3;i++)
          {
              if(i==0)
              {
    
                      yuvArr[i] = file->read(VideoWidth*VideoHeight);
              }
              else
              {
                      yuvArr[i] = file->read(VideoWidth*VideoHeight/4);
              }
    
    
    
              m_textureYUV[i]->setData(QOpenGLTexture::Red,QOpenGLTexture::UInt8,yuvArr[i]);
    
              m_textureYUV[i]->bind(i);
          }
    
         glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    
         m_textureYUV[0]->release();
         m_textureYUV[1]->release();
         m_textureYUV[2]->release();
    
        vao.release();       //解绑
    }
    
    void myGlWidget::initializeGL()
    {
        //为当前环境初始化OpenGL函数
        initializeOpenGLFunctions();
    
        glClearColor(1.0f, 1.0f, 1.0f, 1.0f);    //设置背景色为白色
    
    
        file =new QFile("G:\out640x340.yuv");
        if (!file->open(QIODevice::ReadOnly ))
        {
            qDebug()<<"out640x340.yuv file open failed!";
        }
    
    
         //初始化纹理对象
         for(int i=0;i<3;i++)
         {
    
             m_textureYUV[i]  = new QOpenGLTexture(QOpenGLTexture::Target2D);
    
             if(i==0)
             {
                     m_textureYUV[i]->setSize(VideoWidth,VideoHeight);
             }
             else
             {
                     m_textureYUV[i]->setSize(VideoWidth/2,VideoHeight/2);
             }
    
             m_textureYUV[i]->setMinMagFilters(QOpenGLTexture::LinearMipMapLinear,QOpenGLTexture::Linear);
             m_textureYUV[i]->create();
             m_textureYUV[i]->setFormat(QOpenGLTexture::R8_UNorm);
             m_textureYUV[i]->allocateStorage();        //存储配置(放大缩小过滤、格式、size)
             m_textureYUV[i]->setData(QOpenGLTexture::Red,QOpenGLTexture::UInt8,yuvArr[i]);
    
         }
    
    
        program = new  QOpenGLShaderProgram(this);
    
        program->addShaderFromSourceCode(QOpenGLShader::Fragment,fsrc);
    
        program->addShaderFromSourceCode(QOpenGLShader::Vertex,vsrc);
    
        program->link();
        program->bind();
    
    
        //初始化VBO,将顶点数据存储到buffer中,等待VAO激活后才能释放
    
        float vertices[] = {
             //顶点坐标               //纹理坐标的Y方向需要是反的,因为opengl中的坐标系是Y原点位于下方
            -1.0f, -1.0f, 0.0f,  0.0f, 1.0f,        //左下
            1.0f , -1.0f, 0.0f,  1.0f, 1.0f,        //右下
            -1.0f, 1.0f,  0.0f,  0.0f, 0.0f,        //左上
            1.0f,  1.0f,  0.0f,  1.0f, 0.0f         //右上
    
        };
    
    
        vbo.create();
        vbo.bind();
        vbo.bind();              //绑定到当前的OpenGL上下文,
        vbo.allocate(vertices, sizeof(vertices));
        vbo.setUsagePattern(QOpenGLBuffer::StaticDraw);  //设置为一次修改,多次使用(坐标不变,变得只是像素点)
    
        //初始化VAO,设置顶点数据状态(顶点,法线,纹理坐标等)
        vao.create();
        vao.bind();
    
        // void setAttributeBuffer(int location, GLenum type, int offset, int tupleSize, int stride = 0);
        program->setAttributeBuffer(0, GL_FLOAT, 0,                  3, 5 * sizeof(float));   //设置aPos顶点属性
        program->setAttributeBuffer(1, GL_FLOAT, 3 * sizeof(float),  2, 5 * sizeof(float));   //设置aColor顶点颜色
    
        program->enableAttributeArray(0); //使能
        program->enableAttributeArray(1);
    
    
    
        //解绑所有对象
        vao.release();
        vbo.release();
    
        //启动定时器
        QTimer *ti = new QTimer(this);
        connect(ti, SIGNAL(timeout()), this, SLOT(update()));
        ti->start(40);
    
    
    }
    
    
    
    // 窗口尺寸变化
    void myGlWidget::resizeGL(int width, int height)
    {
        qDebug() << "resizeGL "<<width<<":"<<height;
    }
     

  • 相关阅读:
    shiro角色与权限
    shiro Realm体系
    shiro AuthenticationToken体系
    shiro身份认证流程
    git相关
    Logback 快速入门 / 使用详解
    SLF4J 快速入门 / 绑定原理
    Java 日志框架概述(slf4j / log4j / JUL / Common-logging(JCL) / logback)
    Java 浮点数精确性探讨(IEEE754 / double / float)与 BigDecimal 解决方案
    Maven 快速入门
  • 原文地址:https://www.cnblogs.com/lifexy/p/13915330.html
Copyright © 2011-2022 走看看