qt实现opengl播放yuv视频 原创

qt使用opengl播放yuv视频

更多精彩内容
👉个人内容分类汇总 👈

1、实现效果

在这里插入图片描述

2、pro文件

  • 添加Qt += widgets opengl

3、xvideowidget.h

#ifndef XVIDEOWIDGET_H
#define XVIDEOWIDGET_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QGLShaderProgram>
#include <QFile>
#include <QTimer>

class XVideoWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    explicit XVideoWidget(QWidget* parent = nullptr);

signals:

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

private:
    QGLShaderProgram program;    // shader程序

    GLuint unis[3] = {0};   // shader中的yuv变量地址
    GLuint texs[3] = {0};   // opengl的texture地址

    int width = 1920;
    int height = 1080;

    QFile m_file;
    QByteArray m_buf;  // 视频数据

    QTimer m_timer;
};

#endif // XVIDEOWIDGET_H

4、xvideowidget.cpp

#include "xvideowidget.h"

#include <QDebug>

#pragma execution_character_set("utf-8")
// 准备yuv数据
// ffmpeg -i ffmpeg.mp4 -t 10 -s 240x128 -pix_fmt yuv420p out240X128.yuv

// 自动加双引号
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4
// 顶点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 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.0, 1.0, 1.0,
                   0.0, -0.39465, 2.03211,
                   1.13983, -0.58060, 0.0) * yuv;
        gl_FragColor = vec4(rgb, 1.0);
    }
);

XVideoWidget::XVideoWidget(QWidget *parent) : QOpenGLWidget(parent)
{
    connect(&m_timer, &QTimer::timeout, this,
            [&]()
    {
        this->update();
    });

    m_timer.start(1);
}

void XVideoWidget::initializeGL()
{
    qDebug() << "初始化";
    initializeOpenGLFunctions();    // 初始化opengl

    // program加载shader脚本(顶点和片元)
    qDebug() << "加载片元脚本:" <<program.addShaderFromSourceCode(QGLShader::Fragment, tString);  // 片元
    qDebug() << "加载顶点脚本:" <<program.addShaderFromSourceCode(QGLShader::Vertex, vString);    // 顶点

    // 设置顶点坐标的变量
    program.bindAttributeLocation("vertexIn", A_VER);
    // 设置材质坐标
    program.bindAttributeLocation("textureIn", T_VER);
    qDebug() << "编译shader:" << program.link();
    qDebug() << "绑定shader:" << program.bind();

    // 传递顶点和材质坐标
    // 顶点
    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] = program.uniformLocation("tex_y");
    unis[1] = program.uniformLocation("tex_u");
    unis[2] = program.uniformLocation("tex_v");

    // 创建材质
    glGenTextures(3, texs);
    // Y
    glBindTexture(GL_TEXTURE_2D, texs[0]);   // 绑定材质
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  // 设置纹理参数,放大过滤,线性插值 GL_NEAREST(效率高,效果差)
    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);   // 创建材质显卡空间

    m_file.setFileName("./out.yuv");
    if(!m_file.open(QIODevice::ReadOnly))
    {
        qDebug() << "打开失败!";
        return;
    }
}

void XVideoWidget::resizeGL(int w, int h)
{
    qDebug() << w <<" " << h;
}

void XVideoWidget::paintGL()
{
    qDebug() << "绘制";
    if(m_file.atEnd())
    {
        m_file.seek(0);
    }
    QByteArray buf = m_file.read(width * height);

    glActiveTexture(GL_TEXTURE0);   // 激活第0层
    glBindTexture(GL_TEXTURE_2D, texs[0]);  // 0层绑定到y材质中
    // 修改材质内容(复制内存内容)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, buf.data());
    glUniform1i(unis[0], 0);   // 与shader的uni变量关联

    buf = m_file.read(width * height / 4);
    glActiveTexture(GL_TEXTURE0 + 1);   // 激活第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, buf.data());
    glUniform1i(unis[1], 1);   // 与shader的uni变量关联

    buf = m_file.read(width * height / 4);
    glActiveTexture(GL_TEXTURE0 + 2);   // 激活第2层  V
    glBindTexture(GL_TEXTURE_2D, texs[2]);  // 2层绑定到v材质中
    // 修改材质内容(复制内存内容)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, buf.data());
    glUniform1i(unis[2], 2);   // 与shader的uni变量关联

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  // 开始绘制
}
posted @ 2022-03-05 15:27  mahuifa  阅读(0)  评论(0编辑  收藏  举报  来源