本文已参与「新人创作礼」活动,一起开启掘金创作之路
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); // 开始绘制
}