项目目录:
一般ffmpeg解码后的数据类型都是I420,即YUV420P,OpenGL没有提供直接渲染yuv的接口,我们可以通过可编程渲染管线,利用多重纹理将Y、U、V纹理分别传入,在片元着色器GL_FRAGMENT_SHADER中将yuv进行矩阵转化成RGB,然后进行渲染。
1.打开testQTOpenGL.ui
可以看到已经有一个框了,这个框的类为自己输入的xvideo,再加一个框,默认类为QOpenGLwidget,随后将其提升为xvideo类。
在自动生成的xvideo.h中也可以看到xvideo为QOpenGLwidget的公有继承
2.xvideo.h
#pragma once
#include <QOpenGLWidget>
#include<qopenglfunctions.h>
#include<qglshaderprogram.h>
class xvideo : public QOpenGLWidget,protected QOpenGLFunctions
{
Q_OBJECT
public:
xvideo(QWidget *parent);
~xvideo();
protected:
void paintGL(); //绘制GL
void initialGL();//初始化GL
void resizeGL(int width,int height);//改尺寸
private:
//通过该成员运行shader程序
QGLShaderProgram program;
};
QOpenGLFunctions 类提供了跨平台的OpenGl ES2.0 API版本。
OpenGL 2.0 提供了OpenGL中的子类集合,可以提供跨多个平台的桌面系统以及嵌入式OpenGL的实现。
然而,却很难使用子类因为子类需要解决许多平台系统的操作问题。
因此 QOpenGLFunctions提供了这样的API,可以保证在所有的OpenGL系统中使用, 并且也关注不同系统中的OpenGL的版本API的使用。
Qt推荐直接继承的方式来使用 QOpenGLFunctions类,就不用把库引用进来了
在后面代码中将通过program成员加载shader
3.xvideo.cpp
#include "xvideo.h"
#include<qdebug.h>
#define GET_STR(x) #x //自动加双引号
//
const char* Vstring = GET_STR(
attribute vec4 vertexIn;//顶点输入
attribute vec2 textureIn;//材质输入
varying vec2 textureOut;//顶点与片元shader共享变量
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.39465, 2.03211,
1.13983, -0.58060, 0) * yuv;//YUV转换成rgb
gl_FragColor = vec4(rgb, 1.0);
}
);
xvideo::xvideo(QWidget *parent)
: QOpenGLWidget(parent)
{
qDebug() << "gouzao" << endl;
}
xvideo::~xvideo()
{
qDebug() << "xigou" << endl;
}
//初始化opengl
void xvideo::paintGL() //绘制GL
{
qDebug() << "paintGL" << endl;
}
void xvideo::initialGL() //初始化GL
{
qDebug() << "initialGL" << endl;
//初始化opengl (QOpenGLFunctions继承)函数
initializeOpenGLFunctions();
qDebug() << program.addShaderFromSourceCode(QGLShader::Fragment, Tstring);
//顶点shader
qDebug() << program.addShaderFromSourceCode(QGLShader::Vertex, Vstring);
}
void xvideo::resizeGL(int width, int height)
{
qDebug() << "resizeGL" << endl;
}
顶点着色器:
const char* Vstring = GET_STR(
attribute vec4 vertexIn;//顶点输入
attribute vec2 textureIn;//材质输入
varying vec2 textureOut;//顶点与片元shader共享变量
void main(void)
{
gl_Position = vertexIn;
textureOut = textureIn;
}
);//为了设置顶点着色器的输出,我们必须把位置数据赋值给预定义的gl_Position变量,
//它在幕后是vec4类型的。
传进来的坐标值(vertexIn)只能在顶点shader(Vstring)中获取,获取后转化为位置(GL_POSITION)顶点主要是转发,实际转化(YUV转rgb)还是在片元shader(Tstring)中
片元着色器:
//片元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.39465, 2.03211,
1.13983, -0.58060, 0) * yuv;//YUV转换成rgb
gl_FragColor = vec4(rgb, 1.0);
}
);
yuv.x = texture2D(tex_y, textureOut).r;
通过uniform读出来转成yuv
根据坐标textureOut可以取出材质中对应的数值,yuv分开存放,若放在一起则需要遍历取出数值,开销巨大
.r为取第一个数据(rgb中r为第一个)
通过三个材质(tex_y,tex_u,tex_v)拼出yuv数据
可以看到,两个框调用了两次xvideo类的构造函数