qt采用opengl显示yuv视频数据

2023-10-27

作者:  使徒保罗

邮箱:   297329588szh@163.com

声明:  欢迎交流学习,如有任何疑问,请通过邮箱联系本人

应用场景:qt opengl渲染yuv420p数据
开发环境:win7 64位 qt-opensource-windows-x86-msvc2010-5.5.1.exe(即qt5.5.1 32位版本)
功能:      下面的核心代码都有详细注释,学术名词不清楚请自行百度。本例通过PlayOneFrame()接口从yuv文件中读取一帧yuv图像数据显示,每                   调用一次,就显示下一帧图像。


#ifndef GLPLAYWIDGET_H
#define GLPLAYWIDGET_H
 
#include <QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QFile>
 
#define ATTRIB_VERTEX 3
#define ATTRIB_TEXTURE 4
 
class CPlayWidget:public QOpenGLWidget,protected QOpenGLFunctions
{
    Q_OBJECT
public:
    CPlayWidget(QWidget* parent);
    ~CPlayWidget();
 
    void PlayOneFrame();
 
protected:
    void initializeGL() Q_DECL_OVERRIDE;
    void resizeGL(int w, int h) Q_DECL_OVERRIDE;
    void paintGL() Q_DECL_OVERRIDE;
 
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
    QOpenGLTexture* m_pTextureY;  //y纹理对象
    QOpenGLTexture* m_pTextureU;  //u纹理对象
    QOpenGLTexture* m_pTextureV;  //v纹理对象
    QOpenGLShader *m_pVSHader;  //顶点着色器程序对象
    QOpenGLShader *m_pFSHader;  //片段着色器对象
    QOpenGLShaderProgram *m_pShaderProgram; //着色器程序容器
 
    int m_nVideoW; //视频分辨率宽
    int m_nVideoH; //视频分辨率高
 
    unsigned char* m_pBufYuv420p;   
    FILE* m_pYuvFile;
};
#endif
 
 

#include "PlayWidget.h"
#include <QOpenGLTexture>
#include <QOpenGLBuffer>
#include <QMouseEvent>
 
CPlayWidget::CPlayWidget(QWidget *parent):QOpenGLWidget(parent)
{
    textureUniformY = 0;
    textureUniformU = 0;
    textureUniformV = 0;
    id_y = 0;
    id_u = 0;
    id_v = 0;
    m_pBufYuv420p = NULL;
    m_pVSHader = NULL;
    m_pFSHader = NULL;
    m_pShaderProgram = NULL;
    m_pTextureY = NULL;
    m_pTextureU = NULL;
    m_pTextureV = NULL;
    m_pYuvFile = NULL;
    m_nVideoH = 0;
    m_nVideoW = 0;
}
 
CPlayWidget::~CPlayWidget()
{
 
}
 
void CPlayWidget::PlayOneFrame()
{//函数功能读取一张yuv图像数据进行显示,每单击一次,就显示一张图片
 
    if(NULL == m_pYuvFile)
    {
        //打开yuv视频文件 注意修改文件路径
       // m_pYuvFile = fopen("F://OpenglYuvDemo//1920_1080.yuv", "rb");
         m_pYuvFile = fopen("F://OpenglYuvDemo//test_yuv420p_320x180.yuv", "rb");
 
         m_nVideoW = 320;
         m_nVideoH = 180;
        //根据yuv视频数据的分辨率设置宽高,demo当中是1080p,这个地方要注意跟实际数据分辨率对应上
//        m_nVideoW = 1920;
//        m_nVideoH = 1080;
    }
 
 
 
    //申请内存存一帧yuv图像数据,其大小为分辨率的1.5倍
    int nLen = m_nVideoW*m_nVideoH*3/2;
    if(NULL == m_pBufYuv420p)
    {
        m_pBufYuv420p = new unsigned char[nLen];
        qDebug("CPlayWidget::PlayOneFrame new data memory. Len=%d width=%d height=%d\n",
               nLen, m_nVideoW, m_nVideoW);
    }
 
    //将一帧yuv图像读到内存中
    if(NULL == m_pYuvFile)
    {
        qFatal("read yuv file err.may be path is wrong!\n");
        return;
    }
    fread(m_pBufYuv420p, 1, nLen, m_pYuvFile);
 
 
    //刷新界面,触发paintGL接口
    update();
    return;
}
 
void CPlayWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);
 
    //现代opengl渲染管线依赖着色器来处理传入的数据
    //着色器:就是使用openGL着色语言(OpenGL Shading Language, GLSL)编写的一个小函数,
    //       GLSL是构成所有OpenGL着色器的语言,具体的GLSL语言的语法需要读者查找相关资料
 
    //初始化顶点着色器 对象
    m_pVSHader = new QOpenGLShader(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 = m_pVSHader->compileSourceCode(vsrc);
    if(!bCompile)
    {
 
    }
 
    //初始化片段着色器 功能gpu中yuv转换成rgb
    m_pFSHader = new QOpenGLShader(QOpenGLShader::Fragment, this);
 
    //片段着色器源码
    const char *fsrc = "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); \
    }";
 
    //将glsl源码送入编译器编译着色器程序
    bCompile = m_pFSHader->compileSourceCode(fsrc);
    if(!bCompile)
    {
 
    }
 
#define PROGRAM_VERTEX_ATTRIBUTE 0
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
    //创建着色器程序容器
    m_pShaderProgram = new QOpenGLShaderProgram;
    //将片段着色器添加到程序容器
    m_pShaderProgram->addShader(m_pFSHader);
    //将顶点着色器添加到程序容器
    m_pShaderProgram->addShader(m_pVSHader);
 
    //绑定属性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");
 
    // 顶点矩阵
    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,
    };
 
 
    //设置属性ATTRIB_VERTEX的顶点矩阵值以及格式
    glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices);
    //设置属性ATTRIB_TEXTURE的纹理矩阵值以及格式
    glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices);
 
    //启用ATTRIB_VERTEX属性的数据,默认是关闭的
    glEnableVertexAttribArray(ATTRIB_VERTEX);
 
    //启用ATTRIB_TEXTURE属性的数据,默认是关闭的
    glEnableVertexAttribArray(ATTRIB_TEXTURE);
 
    //分别创建y,u,v纹理对象
    m_pTextureY = new QOpenGLTexture(QOpenGLTexture::Target2D);
    m_pTextureU = new QOpenGLTexture(QOpenGLTexture::Target2D);
    m_pTextureV = new QOpenGLTexture(QOpenGLTexture::Target2D);
    m_pTextureY->create();
    m_pTextureU->create();
    m_pTextureV->create();
 
    //获取返回y分量的纹理索引值
    id_y = m_pTextureY->textureId();
    //获取返回u分量的纹理索引值
    id_u = m_pTextureU->textureId();
    //获取返回v分量的纹理索引值
    id_v = m_pTextureV->textureId();
 
    glClearColor(0.3,0.3,0.3,0.0);//设置背景色
 
    //qDebug("addr=%x id_y = %d id_u=%d id_v=%d\n", this, id_y, id_u, id_v);
}
 
void CPlayWidget::resizeGL(int w, int h)
{
    if(h == 0)// 防止被零除
    {
        h = 1;// 将高设为1
    }
 
    //设置视口
    glViewport(0,0, w,h);
}
 
 void CPlayWidget::paintGL()
 {
    //加载y数据纹理
 
     //激活纹理单元GL_TEXTURE0
    glActiveTexture(GL_TEXTURE0);
    //使用来自y数据生成纹理
    glBindTexture(GL_TEXTURE_2D, id_y);
    //使用内存中m_pBufYuv420p数据创建真正的y数据纹理
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_nVideoW, m_nVideoH, 0, GL_RED, GL_UNSIGNED_BYTE, m_pBufYuv420p);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,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);
 
    //加载u数据纹理
    glActiveTexture(GL_TEXTURE1);//激活纹理单元GL_TEXTURE1
    glBindTexture(GL_TEXTURE_2D, id_u);
    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);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,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);
 
    //加载v数据纹理
    glActiveTexture(GL_TEXTURE2);//激活纹理单元GL_TEXTURE2
    glBindTexture(GL_TEXTURE_2D, id_v);
    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);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,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);
 
    //指定y纹理要使用新值 只能用0,1,2等表示纹理单元的索引,这是opengl不人性化的地方
    //0对应纹理单元GL_TEXTURE0 1对应纹理单元GL_TEXTURE1 2对应纹理的单元
    glUniform1i(textureUniformY, 0);
    //指定u纹理要使用新值
    glUniform1i(textureUniformU, 1);
    //指定v纹理要使用新值
    glUniform1i(textureUniformV, 2);
 
    //使用顶点数组方式绘制图形
    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
    return;
 }

效果图:



本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

qt采用opengl显示yuv视频数据 的相关文章

随机推荐

  • Hibernate 配置文件(hibernate.cfg.xml、hbm.xml)

    目录 Hibernate xml 1 数据库的基本信息 2 集成 C3P0 设置数据库连接池信息 3 Hibernate 基本信息 4 注册实体关系映射文件 实体关系映射文件 实体类文件名 hbm xml 1 hibernate mappi
  • Hive 删除表报错: FAILED: Execution Error, return code 1 from org.apache.hadoop.hive.ql.exec.DDLTask.

    操作Hive 的时候报错 提示如下 hive gt drop table toss1 FAILED Execution Error return code 1 from org apache hadoop hive ql exec DDLT
  • 点开软件显示从服务器返回参照,win10打开软件从服务器返回了一个参照怎么解决...

    很多网友刚升级了windows10系统的 就出现了从服务器返回了一个参照的问题 更升级了系统对出现这样的问题 很多网友不知所措 不知道该怎么办 不要急 就让win10官网小编告诉大家win10打开软件从服务器返回了一个参照解决方法吧 win
  • 学生宿舍管理系统ASP .NET CORE MVC

    学生宿舍管理系统 管理系统应设计的主要功能和数据要求 管理系统应设计的主要功能和数据要求 用户管理 将系统用户分为学生用户 类 管理员类 学生用户只能查询和修改自己的基本信息 管理员可以 使用本 系统的所有功能 学生宿舍管理 学生用户可以查
  • 简单实现x的n次方(10 分)

    简单实现x的n次方 10 分 本题要求实现一个计算x n n 0 的函数 函数接口定义 double mypow double x int n 函数mypow应返回x的n次幂的值 题目保证结果在双精度范围内 裁判测试程序样例 include
  • YOLOv3-药草识别实现

    一 标注工具 labelimg 使用工具lableimg 1 操作步骤 使用cmd进入下载目录之后 使用python lableimg进入标注工具 open选择文件之后 选择Create nRectBox进行选择区域 并且标注药草名 如下
  • 爬虫逆向学习进阶路线

    大数据时代下 爬虫技术逐渐成为一套完整的系统性工程技术 涉及的知识面广 平台多 技术越来越多样化 对抗性也日益显著 大家可以参考一下学习路线 看看自己需要对哪些知识进行补充 爬虫逆向学习路线 学习路线总结 系统提高 加密算法特征和实现 传输
  • [创业之路-51] :动态股权机制 -6- 创始团队股权比例如何分配比较合理

    创始团队股权比例如何分配 可以参考以下几个标准 提出创意并获得执行可以先分10 不能只是空点子 要执行并落地 最初召集大家一起创业的创始人可以多分5 创始人是公司主要运营负责人的额外多分5 让公司发展从0到1 增加5 20 例如建立良好的市
  • 八目云播服务器维护,LiveQing 云平台直播点播使用说明

    LiveQing 云平台直播点播使用说明 下载安装包 zip 是windows环境中使用的安装包 tar gz 是linux环境中使用的安装包 Windows下安装 说明 Windows 64位 系统 下载的安装包放置在任意一个目录下 解压
  • CSS笔记

    2 3 4 5
  • 在Spark上运行apache beam

    目录 apache beam 个人使用经验总结目录和入门指导 Java 为了方便 以下面这个名字替换的程序做简单例子 The ReplaceMyName 把Create数组里的myName替换成xxx since 2019 12 17 pu
  • [C语言]define 定义常量/宏

    1 运用define定义常量 2 运用define定义宏 1 运用define定义常量 格式 define 符号 数值 define A 100 定义后的常量 作用域在整个项目 可以跨源文件调用 2 运用define定义宏 宏与函数其实十分
  • 学习PWM——控制的关键

    目录 一 PWM的简介 1 PWM是什么 2 PWM的参数 1 PWM的频率 2 PWM的周期 3 占空比 3 PWM的原理 1 高级输出比较通道 2 通用输出比较通道 3 输出比较模式 二 PWM的结构 1 PWM的基本结构 2 参数计算
  • Blip2 文章阅读

    论文地址https arxiv org pdf 2301 12597 pdf BLIP 2 Bootstrapping Language Image Pre training with Frozen Image Encoders and L
  • 【PyTorch教程】05-如何使用PyTorch训练神经网络模型 (2022年最新)

    本期目录 使用PyTorch训练神经网络 torch autograd 1 神经网络背景 2 加载预训练模型 有重大更新 2 1 新老版本写法对比 2 2 新写法的好处 2 3 图像数据的预处理 2 4 训练模式和验证模式之间的转换 3 正
  • 机器学习技术(六)——有监督学习算法之线性回归算法实操

    机器学习技术 五 有监督学习之线性回归算法实操 引言 机器学习监督算法是一种基于已有标记数据的学习方法 通过对已知输入和输出数据的学习 建立一个模型来预测新的输入数据的输出 这种算法模仿人类的学习过程 从已有的经验中总结规律 并将其应用于新
  • python在excel表里如何新增一列或者一行?新增之后,公式和合并单元格会怎样变化?

    首先导入openpyxl库 即 import openpyxl as op 然后打开你已有的excel 即 wb op open 新增一列 xlsx 打开新增一列 xlsx这个工作簿 ws wb Sheet1 打开Sheet1这个工作表 我
  • 2020-09-06

    Nexus仓库管理服务 1 Nexus端口为8081 Nexus是Maven仓库管理器 如果你使用Maven 你可以从Maven中央仓库下载所需要的构件 artifac 但这通常不是一个好的做法 你应该在本地假设一个Maven仓库服务器 在
  • 静态代码分析工具列表分析---代码分析工具列表(30款工具)

    本文是一个静态代码分析工具的清单 共有30个工具 包括4个 NET工具 2个Ada工具 7个C 工具 4个Java工具 2个JavaScript工具 1个Opa工具 2个Packaging工具 3个Perl工具 1个Python工具 1个嵌
  • qt采用opengl显示yuv视频数据

    作者 使徒保罗 邮箱 297329588szh 163 com 声明 欢迎交流学习 如有任何疑问 请通过邮箱联系本人 应用场景 qt opengl渲染yuv420p数据 开发环境 win7 64位 qt opensource windows