项目目录:
一般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#include #include 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#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类的构造函数



