随笔 - 632  文章 - 17  评论 - 54  阅读 - 93万

Qt6.3+OpenGL播放yuv420p视频

一、概述

  案例:使用OpenGL播放yuv420p的视频文件(文件是自动准备好的),基于上一篇

  实现步骤:

    1.初始化QOpenGLFunctions initializeOpenGLFunctions()

    2.创建QOpenGLShaderProgram

    3.添加顶点和片元的shader

    4.设置顶点和材质坐标变量

    5.编译并绑定shader

    6.设置及激活顶点及材质坐标

    7.创建材质并绑定材质

    8.创建材质显卡空间

    9.激活材质、绑定材质、并修改显存内容

    10.shader与材质数据做关联

    11.使用glDrawArrays绘制内容

  注意事项:

    1.需要导入的头文件

#include <QtOpenGLWidgets/QtOpenGLWidgets>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>

 

二、代码示例

  1.XVideoWidget.h

复制代码
#ifndef XVIDEOWIDGET_H
#define XVIDEOWIDGET_H

#include <QObject>
#include <QtOpenGLWidgets/QtOpenGLWidgets>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>

class XVideoWidget:public QOpenGLWidget,protected QOpenGLFunctions
{
    Q_OBJECT

public:
    XVideoWidget(QWidget *parent = nullptr);
    ~XVideoWidget();

protected:
    //刷新显示
    void paintGL() override;
    //初始化GL
    void initializeGL() override;
    //窗口尺寸变化
    void resizeGL(int width,int height) override;

private:
    //shader程序
    QOpenGLShaderProgram mProgram;
    //shader中yuv变量地址
    GLuint unis[3] = {0};
    //opengl的texture地址
    GLuint texs[3] = {0};

    //材质内存空间
    unsigned char *datas[3] = {0};


    int width = 240;
    int height = 128;


};

#endif // XVIDEOWIDGET_H
复制代码

 

  2.XVideoWidget.cpp

复制代码
#include "XVideoWidget.h"
#include <QDebug>
#include <QTimer>

//自动加双引号
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4

FILE *fp  =NULL;

//顶点shader
const char *vString = GET_STR(
            attribute vec4 vertexIn;
            attribute vec2 textureIn;
            varying vec2 textureOut;
            void main(void){
                gl_Position = vertexIn;
                textureOut = textureIn;
            }
);
//片元shader
const char *tString = GET_STR(
            varying vec2 textureOut;
            uniform sampler2D text_y;
            uniform sampler2D text_u;
            uniform sampler2D text_v;
            void main(void){
                vec3 yuv;
                vec3 rgb;
                yuv.x = texture2D(text_y,textureOut).r;
                yuv.y = texture2D(text_u,textureOut).r-0.5;
                yuv.z = texture2D(text_v,textureOut).r-0.5;
                rgb = mat3(1.0, 1.0, 1.0,
                    0.0, -0.39465, 2.03211,
                    1.13983, -0.58060, 0.0)*yuv;
                gl_FragColor = vec4(rgb,1.0);

            }
);



//准备yuv数据
//ffmpeg -i v1080.mp4 -t 10 -s 240x128 -pix_fmt yuv420p  out240x128.yuv
XVideoWidget::XVideoWidget(QWidget *parent):QOpenGLWidget(parent)
{

}

//初始化GL
void XVideoWidget::initializeGL(){
    qDebug()<<"初始化GL";
    //初始化OpenGL QOpenGLFunctions继承来的函数
    initializeOpenGLFunctions();

    //创建Program

    //program加载shader(顶点和片元脚本)
    //片元
    bool ret = mProgram.addShaderFromSourceCode(QOpenGLShader::Fragment,tString);
    if(!ret){
        qDebug()<<"片元着色器加载失败";
    }
    //顶点
    ret  = mProgram.addShaderFromSourceCode(QOpenGLShader::Vertex,vString);
    if(!ret){
        qDebug()<<"顶点着色器加载失败";
    }
    //设置顶点坐标的变量
    mProgram.bindAttributeLocation("vertexIn",A_VER);
    //设置材质坐标变量
    mProgram.bindAttributeLocation("textureIn",T_VER);

    //编译shader
    ret = mProgram.link();
    if(!ret){
        qDebug()<<"mProgram.link failed!!";
    }
    ret = mProgram.bind();
    if(!ret){
        qDebug()<<"mProgram.bind() Failed!!";
    }

    //传递顶点和材质坐标
    //顶点
    static const GLfloat ver[] = {
        -1.0f,-1.0f,
        1.0f,-1.0f,
        -1.0f, 1.0f,
        1.0f,1.0f
    };
    //材质
    static const GLfloat tex[] = {
        0.0f, 1.0f,
        1.0f, 1.0f,
        0.0f, 0.0f,
        1.0f, 0.0f
    };

    //顶点
    glVertexAttribPointer(A_VER,2,GL_FLOAT,0,0,ver);
    glEnableVertexAttribArray(A_VER);
    //材质
    glVertexAttribPointer(T_VER,2,GL_FLOAT,0,0,tex);
    glEnableVertexAttribArray(T_VER);


    //从shader获取材质
    unis[0] = mProgram.uniformLocation("text_y");
    unis[1] = mProgram.uniformLocation("text_u");
    unis[2] = mProgram.uniformLocation("text_v");

    //创建材质(yuv3个)
    glGenTextures(3,texs);

    //绑定材质yuv
    //y
    glBindTexture(GL_TEXTURE_2D, texs[0]);
    //放大过滤,线性插值   GL_NEAREST(效率高,但马赛克严重)
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    //创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width, height, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
    //u
    glBindTexture(GL_TEXTURE_2D, texs[1]);
    //放大过滤,线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    //创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width/2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);
    //v
    glBindTexture(GL_TEXTURE_2D, texs[2]);
    //放大过滤,线性插值
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
    
    
    //创建材质显卡空间
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, width / 2, height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, 0);

    ///分配材质内存空间
    datas[0] = new unsigned char[width*height];        //Y
    datas[1] = new unsigned char[width*height/4];    //U
    datas[2] = new unsigned char[width*height/4];    //V

    fp = fopen("D:/wei.yang/tony/tool/qtworkspace/XPlayer/out240x128.yuv","rb");
    if(!fp){
        qDebug()<<"out240x128.yuv open failed!!";
    }


    //启动定时器
    QTimer *timer = new QTimer(this);
    connect(timer,&QTimer::timeout,this,[=](){update();});
    timer->start(40);
 
}


//刷新显示
void XVideoWidget::paintGL(){
    qDebug()<<"刷新显示GL";
    if(feof(fp)){
        fseek(fp,0,SEEK_SET);
    }
    fread(datas[0],1,width*height,fp);
    fread(datas[1],1,width*height/4,fp);
    fread(datas[2],1,width*height/4,fp);

    //激活材质
    glActiveTexture(GL_TEXTURE0);
    //绑定材质到Y
    glBindTexture(GL_TEXTURE_2D,texs[0]);
    //修改材质内容,显存
    glTexSubImage2D(GL_TEXTURE_2D,0,0,0,width,height,GL_RED, GL_UNSIGNED_BYTE, datas[0]);
    //与shader uni变量关联
    glUniform1i(unis[0], 0);

    glActiveTexture(GL_TEXTURE0+1);
    glBindTexture(GL_TEXTURE_2D, texs[1]); //1层绑定到U材质
    //修改材质内容(复制内存内容)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
    //与shader uni变量关联
    glUniform1i(unis[1],1);


    glActiveTexture(GL_TEXTURE0+2);
    glBindTexture(GL_TEXTURE_2D, texs[2]); //2层绑定到V材质
    //修改材质内容(复制内存内容)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
    //与shader uni变量关联
    glUniform1i(unis[2], 2);

    //绘制
    glDrawArrays(GL_TRIANGLE_STRIP,0,4);

    qDebug()<<"paint end";



}


//窗口尺寸变化
void XVideoWidget::resizeGL(int width,int height){
    qDebug()<<"窗口尺寸变化GL";
}

XVideoWidget::~XVideoWidget(){


}
复制代码

 

posted on   飘杨......  阅读(610)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· 物流快递公司核心技术能力-地址解析分单基础技术分享
· .NET 10首个预览版发布:重大改进与新特性概览!
· 单线程的Redis速度为什么快?
· 展开说说关于C#中ORM框架的用法!
· Pantheons:用 TypeScript 打造主流大模型对话的一站式集成库
< 2025年3月 >
23 24 25 26 27 28 1
2 3 4 5 6 7 8
9 10 11 12 13 14 15
16 17 18 19 20 21 22
23 24 25 26 27 28 29
30 31 1 2 3 4 5

点击右上角即可分享
微信分享提示