【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv

这篇具有很好参考价值的文章主要介绍了【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

通过Qt opengl不是为了3D绘制,而是为了将视频绘制起来
使用opengl 可以极大降低yuv转rgb的转换开销

使用Opengl需要考虑三大问题:

1、QOpenGLWidget(与界面如何交互)

1、为什么用QT的opengl
简单,界面可以自动叠加
void paintGL(); // 具体的绘制写在该函数里
void initializeGL(); // 材质初始化
void resizeGL(int width, int height); // 当窗口发生变化(缩放)
QOpenGLFunctions // 不需要手动添加库,直接继承该函数

2、Program GLSL 顶点和片元(如何与显卡交互)

GLSL是新的语言,通过GLSL与显卡进行交互,GLSL 跑在显卡上

QGLShaderProgram

Program用来编译和运行Shader代码,包括与shader的交互

编译和运行shader // shader两部分:顶点和片元
addShaderFromSourceCode // 加入shader代码
bindAttributeLocation // 设置传入的变量, 顶点和坐标
uniformLocation // 获取变量

GLSL着色器语言,专门针对opengl所设计,用于显卡运行

顶点着色器是针对每个顶点执行一次,用于确定顶点的位置;——三维
片元着色器是针对每个片元(可以理解为每个像素)执行一次,用于确定每个片元(像素)的颜色 ——平面
GLSL基本语法与C基本相同
它完美地支持向量和矩阵操作
GLSL提供了大量的内置函数来提供丰富的拓展功能
它是通过限定符操作来管理输入输出类型

顶点着色器(画两个三角形,形成一个矩形)

显卡运算能力:值以三角形为单位,所画的数量

顶点着色器被使用在传统的基于顶点的操作, 例如位移矩阵、计算光照方程、产生贴图坐标。
顶点着色器被应用指定, 应用于客户的顶点转化。
【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv

片元着色器

在片元着色器阶 段只有唯一的 varying 输出变量- 即内建变量: gl_FragColor(像素点颜色)
【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv

顶点信息

【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv
【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv

材质坐标信息(全部在第一象限)

【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv
【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv

传入顶点和材质坐标

glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices); 
	ATTRIB_VERTEX:顶点坐标 	2 :坐标数量 		GL_FLOAT:单位数
	0:法线		0:步宽	
glEnableVertexAttribArray(ATTRIB_VERTEX); 
	使生效
glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices);
	材质坐标
glEnableVertexAttribArray(ATTRIB_TEXTURE);
	

三种GLSL变量类型

varying 顶点与片元共享 		// 算出顶点坐标
attribute 顶点使用,由bindAttributeLocation传入
uniform 程序传入 uniformLocation获取地址
glUniform1i(textureUniformY, 0); 设置

顶点shader

attribute vec4 vertexIn;   // 顶点输入
attribute vec2 textureIn;  // 材质输入

void main(void) 
 { gl_Position = vertexIn;
  textureOut = textureIn;  }

片元shader

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, 1, 1, 0, -0.39465, 2.03211, 1.13983, -0.58060, 0) * yuv; 
  gl_FragColor = vec4(rgb, 1);
 }

3、材质Texture(如何写入ffmpeg数据)

前面通过OpenGLWidget管理整个窗口,最终显示涉及在某个材质上,最终要把ffmpeg数据写入,要考虑如何在材质中写入ffmpeg数据

创建材质

glGenTextures(1, t); 		// 创建材质个数,指针地址
glBindTexture(GL_TEXTURE_2D, *t);  // 绑定材质类型成2D图像
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);  // 放大、缩小(通过线性插值)
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri

glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER, GL_LINEAR); 
GL_TEXTURE_2D: 操作2D纹理.
GL_TEXTURE_MIN_FILTE: 缩小过滤
GL_TEXTURE_MAG_FILTER: 放大过滤
GL_LINEAR: 线性过滤, 使用距离当前渲染像素中心最近的4个纹素加权平均值.

ps:如果是一个点直接复制四倍的话,会产生马赛克的现象
加权计算的话就比较柔和文章来源地址https://www.toymoban.com/news/detail-406708.html

写入和绘制材质

glActiveTexture(GL_TEXTURE0); 		// 激活材质,通过编号
glBindTexture(GL_TEXTURE_2D, id_y);  // 绑定
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, pixel_w, pixel_h, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, plane[0]); glUniform1i(textureUniformY, 0);    // 0层材质,材质可以多层
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  // 从0开始绘制,4个
glViewport(0, 0, width, height);

glTexImage2D 材质创建函数

glTexImage2D(GL_TEXTURE_2D, 	// 在显存中创建纹理 
0, 				//细节 0默认 镜头拉远拉近
GL_RED, 		//gpu内部格式 
videoWidth, 	
videoHeight 
, GL_RED, 			//数据格式   数据格式和gpu内部格式 要一致
 GL_UNSIGNED_BYTE 	//像素的数据类型
 , data); 
 glTexSubImage2D 	// 修改纹理

解决方案:VS2017中QT的ui文件打开闪退问题

https://blog.csdn.net/jiaolu295/article/details/115898600

项目代码 cpp

#include "XVideoWidget.h"
#include <QDebug>
#include <QTimer>
// 自动加双引号
#define GET_STR(x) #x
#define A_VER 3
#define T_VER 4

FILE *fp = NULL;  // 文件接口

// 顶点shader
const char *vString = GET_STR(
	attribute vec4 vertexIn;   // 顶点坐标
	attribute vec2 textureIn;  // 材质坐标
	varying vec2 textureOut;	// 顶点shader和片元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;

		// 用矩阵转换yuv
		rgb = mat3(1.0, 1.0, 1.0,
			0, -0.39465, 2.03211,
			1.13983, -0.58060, 0.0)*yuv;
		// 获取输出颜色
		gl_FragColor = vec4(rgb, 1.0);
	}
);



// 准备yuv数据
// ffmpeg -i v1080.mp4 -t 10  -s 240x128 -pix_fmt yuv420p  out240x128.yuv
//						-t 10: 时长10秒钟, 指定输出yuv420p
XVideoWidget::XVideoWidget(QWidget *parent)
	:QOpenGLWidget(parent)
{

}


XVideoWidget::~XVideoWidget()
{

}


// 初始化opengl
void XVideoWidget::initializeGL()
{
	qDebug() << "initializeGL";
	// 初始化opengl函数(QOpenGLFunctions继承)函数
	initializeOpenGLFunctions();

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

	// #############################################以上shader已创建好,接下来要与shader进行交互

	// 设置顶点坐标的变量
	program.bindAttributeLocation("vertexIn", A_VER); // 将变量名称关联到一个索引中,索引可以用一个宏来实现

	// 设置材质坐标
	program.bindAttributeLocation("textureIn", T_VER);

	// 编译shader,打印
	qDebug() << "program.link() = " << program.link();

	// 绑定shader,打印
	qDebug() << "program.bind() = " << program.bind();   // 将opengl 和shader关联起来

	// 传递顶点和材质坐标
	// 顶点			顶点坐标是三维,但最后一位不传默认为0
	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
	};

	// 将坐标写入opengl中

	//顶点				位置索引,一个顶点的元素个数(2),存放类型GL_FLOAT,是否有法线向量 0没有  0默认,ver顶点地址
	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]);
	// 放大过滤,线性插值(要对周边的点进行加权处理,有渐变的效果)      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);

	// 绑定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);


	// 分配材质内存空间 
	datas[0] = new unsigned char[width * height];		// Y
	datas[1] = new unsigned char[width * height / 4];   // U
	datas[2] = new unsigned char[width * height / 4];   // V 

	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()));   // 信号槽  timeout信号  this:当前窗体  更新
	ti->start(40);   // 25帧,40ms刷新一次
	 

}


// 刷新显示,实现按钮的叠加
void XVideoWidget::paintGL()
{
	if (feof(fp))  // 假如到了结尾,移到开头的位置
	{
		fseek(fp, 0, SEEK_SET);// 循环播放
	}

	// 读取数据,存放在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层 绑定到材质Y的位置				将显卡中创建的材质绑定到0层材质
	//修改材质内容(复制内存内容)
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);    // 再与内存空间datas进行关联
	// 与shader uni 变量关联起来
	glUniform1i(unis[0],0);

	glActiveTexture(GL_TEXTURE0 + 1 ); // 激活第1层
	glBindTexture(GL_TEXTURE_2D, texs[1]); // 把1层 绑定到材质U的位置				将显卡中创建的材质绑定到0层材质
	//修改材质内容(复制内存内容)
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);    // 再与内存空间datas进行关联
	// 与shader uni 变量关联起来
	glUniform1i(unis[1], 1);


	glActiveTexture(GL_TEXTURE0 +  2); // 激活第2层
	glBindTexture(GL_TEXTURE_2D, texs[2]); // 把2层 绑定到材质V的位置				将显卡中创建的材质绑定到0层材质
	//修改材质内容(复制内存内容)
	glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width / 2, height / 2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);    // 再与内存空间datas进行关联
	// 与shader uni 变量关联起来
	glUniform1i(unis[2], 2);


	// 开始画
	glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // 从0 开始 画4个点

	qDebug() << "paintGL";


}



// 窗口尺寸变化
void XVideoWidget::resizeGL(int width, int height)
{
	qDebug() << "resizeGL"<< width<< height;
}

项目代码 头文件



#pragma once

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QGLShaderProgram>

class XVideoWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
	Q_OBJECT


public:
	XVideoWidget(QWidget *parent);

	~XVideoWidget();
protected:
	//重载三个函数
	// 刷新显示,实现按钮的叠加
	void paintGL();

	// 初始化gl
	void initializeGL();

	// 窗口尺寸变化
	void resizeGL(int width, int height);

private:
	// shader程序,通过program运行
	QGLShaderProgram program;

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

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

	int width = 240;
	int height = 128;

};

到了这里,关于【QT项目:视频播放器——Qt opengl编程】通过shader完成显示yuv的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • Qt 实现简易的视频播放器,功能选择视频,播放,暂停,前进,后退,进度条拖拉,视频时长显示

    1.效果图 2.代码实现 2.1 .pro文件 2.2 .h文件 2.3 .cpp文件

    2024年04月12日
    浏览(41)
  • 【OpenCV+Qt】实现简易视频播放器——支持进度条拖动

    OpenCV实现视频播放器,其思路大致就是在线程中使用OpenCV中的VideoCapture循环读取本地视频的每一帧Mat,然后发送到界面转换成QImage进行显示,而进度条拖动则用到了VideoCapture中的set函数,进度条则是使用Qslider;并且通过自定义新的进度条类实现点击跳转功能; 效果: 1.进行

    2023年04月08日
    浏览(52)
  • linux系统中QT里面的视频播放器的实现方法

          大家好,今天主要和大家聊一聊,如何使用QT中视频播放器的方法。   目录 第一:视频播放器基本简介 第二:视频播放器头文件说明  第三:源文件的具体实现方法 第四:运行效果显示        与音乐播放器一样使用 QMediaPlayer 类,不同的是需要使用 setVideoOutput(QVi

    2024年02月04日
    浏览(51)
  • Qt项目实战:MP3音乐播放器

      首先通过打开文件,获取文件夹下mp3文件的名字,将其存入QTableWidget控件下,通过双击QTableWidget空间中的内容,播放相应的mp3音乐文件,同时通过使用QMediaPlaylist来实现上一首,下一首的功能。   widget.h widget.cpp main.cpp 本文福利, 莬 费领取Qt开发学习资料包、技术视频,

    2024年02月06日
    浏览(51)
  • Qt音视频开发41-文件推流(支持网页和播放器播放并切换进度)

    本功能最初也是有一些人提过类似的需求,就是能不能将本地的音视频文件,通过纯Qt程序推流出去,然后用户可以直接在网页上播放,也可以用各种播放器播放,然后还可以任意切换播放进度,其实说白了就是个文件服务器,用户通过网络地址访问以后,告诉对方当前是媒

    2024年02月01日
    浏览(68)
  • Qt之基于QMediaPlayer的音视频播放器(支持常见音视频格式)

    Qt自带了一个Media Player的例子,如下图所示: 但是运行这个例子机会发现,连最基本的MP4格式视频都播放不了。因为QMediaPlayer是个壳(也可以叫框架),依赖本地解码器,视频这块默认基本上就播放个MP4,甚至连MP4都不能播放,如果要支持其他格式需要下载k-lite或者LAVFilter

    2024年02月02日
    浏览(67)
  • QT软件开发-基于FFMPEG设计视频播放器-软解图像(一)

    QT软件开发-基于FFMPEG设计视频播放器-CPU软解视频(一) https://xiaolong.blog.csdn.net/article/details/126832537 QT软件开发-基于FFMPEG设计视频播放器-GPU硬解视频(二) https://xiaolong.blog.csdn.net/article/details/126833434 QT软件开发-基于FFMPEG设计视频播放器-解码音频(三) https://xiaolong.blog.csdn.

    2023年04月08日
    浏览(47)
  • 用Qt开发的ffmpeg流媒体播放器,支持截图、录像,支持音视频播放,支持本地文件播放、网络流播放

    本工程qt用的版本是5.8-32位,ffmpeg用的版本是较新的5.1版本。它支持TCP或UDP方式拉取实时流,实时流我采用的是监控摄像头的RTSP流。音频播放采用的是QAudioOutput,视频经ffmpeg解码并由YUV转RGB后是在QOpenGLWidget下进行渲染显示。本工程的代码有注释,可以通过本博客查看代码或者

    2024年02月03日
    浏览(98)
  • Qt/C++编写手机版本视频播放器和Onvif工具(可云台和录像)

    用Qt+ffmpeg写播放器很多人有疑问,为何不用Qt自己的多媒体框架来写,最重要的原因是Qt自带的目前都依赖具体的本地解码器,如果解码器不支持,那就是歇菜的,最多支持个MP4格式,而且在手机上也都是支持本地少部分格式的文件,对于各种视频流完全无能无力。而ffmpeg就不

    2024年02月10日
    浏览(42)
  • 用VLC开发视频播放器/组件(两种方式:libVLC / VLC-Qt)

    MSVC-2015 Qt 5.14.2 QCreator 参考:心流剑 libVLC 各版本 下载链接 我的下载版本为:3.0.11 sdk/lib文件夹目录 qmake vlc 部分的配置(路径根据自己的修改) 只需要 : libvlc.lib、libvlccore.lib 把 plugins 文件夹、libvlc.dll、libvlccore.dll 复制到 bin_Debug / bin_Release VLC-Qt 下载地址 参考链接1:链接

    2024年02月12日
    浏览(89)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包