播放器实战14 显示YUV

1.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 initialGL();//初始化GL
	void paintGL();  //绘制GL
	void resizeGL(int width,int height);//改尺寸
private:
	//通过该成员运行shader程序
	QGLShaderProgram program;
	//shader中yuv变量地址 unis_y,unis_u,unis_v
	GLuint unis[3] = { 0 };
	//opengl的 texture地址
	GLuint texs[3] = { 0 };

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

	int width = 240;
	int height = 128;

};

2.void xvideo::initialGL()

void xvideo::initialGL()  //初始化GL
{
	qDebug() << "initialGL" << endl;
	initializeOpenGLFunctions();
	
	qDebug() << program.addShaderFromSourceCode(QGLShader::Fragment, Tstring);
	//顶点shader
	qDebug() << program.addShaderFromSourceCode(QGLShader::Vertex, Vstring);

用QT封装的program加载shader脚本(顶点shader和片元shader)
Tsting和Vstring为指针,指向的空间中放有我们定义的着色器的功能(顶点着色器用于转发,片元着色器用于将YUV转换成RGB)
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类型的。

	program.bindAttributeLocation("vertexIn", A_VER);
	program.bindAttributeLocation("textureIn", T_VER);

设置顶点坐标与材质坐标的变量 使用bindAttributeLocation来设置变量在着色器程序中参数列表的索引。(A_VER与T_VER都是经过宏定义的整型)
vertexIn为shader中的,OpenGL并不认识,因此这里将vertexIn与索引A_VER绑定,后面往OpenGL写顶点坐标或者材质坐标时往A_VER或者T_VER里传就行了,比如:glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);

	qDebug() << "program.link() = " << program.link();
	qDebug() << "program.bind() = " << program.bind();

编译shader
绑定shader与OpenGL


	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
	};

传递顶点和材质坐标
顶点 z坐标默认都为0
顶点坐标在这个线程使用完后面draw还要用,因此设为static
	glVertexAttribPointer(A_VER, 2, GL_FLOAT, 0, 0, ver);
	glEnableVertexAttribArray(A_VER);
	glVertexAttribPointer(T_VER, 2, GL_FLOAT, 0, 0, tex);
	glEnableVertexAttribArray(T_VER);

将顶点与材质坐标写入OpenGL中
glVertexAttribPointer()指定了渲染时着色器索引值为 index 的顶点属性数组,(颜色、纹理、法线坐标)的数据格式和位置,用于从内存向显存上传数据
然后使坐标生效

	
	unis[0] = program.uniformLocation("tex_y");
	unis[1] = program.uniformLocation("tex_u");
	unis[2] = program.uniformLocation("tex_v");

	
	glGenTextures(3, texs);

从shader获取材质,等会渲染的时候再传过去
根据储存的纹理索(texs)引texs创建三层材质
texs:存储纹理索引的第一个元素指针
(glGenTextures就是用来产生你要操作的纹理对象的索引的,比如你告诉OpenGL,我需要3个纹理对象,它会从没有用到的整数里返回3个给你,然后将索引放到texs中

	//Y 将上面创建的三个材质中的第一个绑定成2D的图像
	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);

	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];		
	datas[1] = new unsigned char[width * height / 4];
	datas[2] = new unsigned char[width * height / 4];	
	fp = fopen("out240x128.yuv", "rb");
	if (!fp)
	{
		qDebug() << "out240x128.yuv file open failed!";
	}
	QTimer* ti = new QTimer(this);
	connect(ti, SIGNAL(timeout()), this, SLOT(update()));
	ti->start(40);
}

在这里插入图片描述
在初始化中只打开了YUV文件
初始化中主要完成了初始化,分配显存或内存空间,定义缩放算法等任务
其中因为我们所使用的YUV为YUV420P,即4个Y对应一组(共2个)UV,所以Y:U:V=4:1:1,所以Y占的内存空间为U的4倍,即U的宽高都为Y的一半
3.void xvideo::paintGL() //绘制GL

void xvideo::paintGL()  //绘制GL
{
	qDebug() << "paintGL" << endl;
	if (feof(fp))fseek(fp, 0, SEEK_SET);
	//将fp指向的视频数据中的数据读到datas中
	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);
	//显卡中创建的材质绑定到第0层渲染材质
	glBindTexture(GL_TEXTURE_2D, texs[0]); //显卡中的0层材质绑定到之前创建好索引的材质texs[0]
	//修改材质内容(复制内存内容)将显卡中创建好的材质传到内存当中
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);

	//现在第一个材质创建完毕,接下来将其与shader关联
	// uni为shader中的变量,第一个unis与第0层材质相绑定
	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);//从0开始画4个顶点开始绘制三角形
	qDebug() << "paintGL";
}

glActiveTexture(GL_TEXTURE0 + 1);
由于OpenGL是状态机,当使用glBindTexture绑定一张纹理后,如果不再绑定新的纹理,则OpenGL之后的操作都会对应此纹理,
当一个纹理与目标绑定时,该目标之前的绑定关系(GL_TEXTURE0)将自动被打破,此时的绑定关系为(GL_TEXTURE1)
将以下四个相关联:

glBindTexture(GL_TEXTURE_2D, texs[1]);
将之前创建好索引的材质texs[1]绑定为2D图像,并且绑定到现在所使用的第1层材质上

glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);

显存中创建好的材质(texs[1]/第1层材质)传到内存当中

现在第一个材质创建完毕,接下来将其与shader关联
uni为shader中的变量,第一个unis与第0层材质相绑定
glUniform1i(unis[1], 1);

在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值