通过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提供了大量的内置函数来提供丰富的拓展功能
它是通过限定符操作来管理输入输出类型

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

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

顶点着色器被使用在传统的基于顶点的操作, 例如位移矩阵、计算光照方程、产生贴图坐标。
顶点着色器被应用指定, 应用于客户的顶点转化。

片元着色器

在片元着色器阶 段只有唯一的 varying 输出变量- 即内建变量: gl_FragColor(像素点颜色)

顶点信息


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


传入顶点和材质坐标

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:如果是一个点直接复制四倍的话,会产生马赛克的现象
加权计算的话就比较柔和

写入和绘制材质

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 4FILE *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;// 用矩阵转换yuvrgb = 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(顶点和片元)脚本// 片元(像素)shaderqDebug() << program.addShaderFromSourceCode(QGLShader::Fragment, tString);// 顶点shaderqDebug() << 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关联起来// 传递顶点和材质坐标// 顶点         顶点坐标是三维,但最后一位不传默认为0static 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);// 绑定YglBindTexture(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);// 绑定UglBindTexture(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);// 绑定VglBindTexture(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];      // Ydatas[1] = new unsigned char[width * height / 4];   // Udatas[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);// 循环播放}// 读取数据,存放在datasfread(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_OBJECTpublic:XVideoWidget(QWidget *parent);~XVideoWidget();
protected://重载三个函数// 刷新显示,实现按钮的叠加void paintGL();// 初始化glvoid 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相关推荐

  1. 【QT项目——视频播放器——开发】

    播放器项目开发 从设计的角度来说,要减少继承,继承带来的复杂度.调试度.难度较大.扩展性较差 继承多代的话会导致代码更新比较困难 1面向对象的五大原则(降低代码的维护成本) 1.1单一职责 让类尽量单 ...

  2. 【QT项目——视频播放器——音频录制】6.1QAudioFormat音频播放

    基于QT的音频录制(三个类) 6.1QAudioFormat 记录格式 setSampleRate: // 样本率 setSampleSize; // 样本大小 setChannelCount set ...

  3. 【QT项目——视频播放器——解码】5.1decoder-5.10音频重采样

    5.1 avcodec_find_decoder.AVCodecContext.avcodec_parameters_to_context 1.确定解码器,通过avcodec_find_decoder ...

  4. linux下qt实现vlc视频播放器,Qt封装本地视频播放器(VLC二次开发)

    Qt本地视频播放器 1.使用vlc官方sdk封装,并在QLabel上面播放 2.首先到vlc官网下载vlc的sdk环境,下载地址:http://download.videolan.org/pub/vi ...

  5. vs2019中如何创建qt项目_在VS2015中创建Qt项目【VS+Qt项目开发系列】(二)

    在VS2015中创建Qt项目[VS+Qt项目开发系列](二) 发布时间:2018-04-20 22:44, 浏览次数:1269 , 标签: VS Qt 在上一篇[VS+Qt项目开发](一)在VS201 ...

  6. 全景视频播放器中OpenGL的相关记录

    全景视频播放器中OpenGL的相关记录 一.OpenGL顶点数组 二.坐标系与投影 三.坐标系相关函数 四.纹理坐标 五.纹理过滤 六.深度缓冲区 七.OpenGL的glut库 OpenGL函数功能g ...

  7. Qt FFmpeg视频播放器开发(八):播放器UI改造、高仿QQ影音

      最近把播放器项目进行了更新,决定参照QQ影音的界面进行实现,我现在的实现如下:   下图是真实的QQ影音   相比QQ影音界面,我的实现有一定的差距,主要是控件的配色,以及中间那个动态图,由于没有 ...

  8. QML+Qt音视频播放器

    代码地址 xyygudu/Player: Qt和QML实现了视频播放器和音乐播放器 (github.com) 部分效果展示 实现的功能 视频相关:播放暂停.播放进度调节.音量调节.列表显示指定目录下的 ...

  9. QT视频播放器(windows qt、linux qt 音视频播放器)

    想要更多项目私wo!!! 一.项目简介        这是基于Qt的QMediaPlayer实现的音视频播放器,实现了播放器的常用功能,例如根据播放列表选择播放的音视频,拖动滑块控制音视频播放. 二. ...

  10. Qt FFmpeg视频播放器开发(二):FFmepg基本使用与视频播放

    本篇博客目标:读帧解码显示视频 开始进入ffmepg的开发之旅.音视频的细节知识不统一讲解,我在教程中逐点渗透,容我以雷神的话开篇. 视频播放器播放一个互联网上的视频文件,需要经过以下几个步骤:解协议 ...

最新文章

  1. android 自定义view文字不齐,Android 解决TextView排版参差不齐的问题
  2. 指示灯组与3个复位按钮的介绍Arduino Yun快速入门教程
  3. oralce 角色 权限
  4. Jupyter Notebook——设置远程服务器登陆
  5. Jetty在win10上的配置,IDEA中配置Jetty,Maven中配置Jetty插件,Eclipse中配置Jetty插件及其使用,通过java代码内嵌Jetty Server
  6. BZOJ4066:简单题(K-D Tree)
  7. oracle 练习 50_萨克斯练习中的常用技巧
  8. python的类程序的结构_Python程序员学习路径之数据结构篇
  9. Log4j的扩展-支持设置最大日志数量的DailyRollingFileAppender
  10. Handler的一个图片轮播程序
  11. html语言中%3c%%%3e中语言,[工学]C语言程序设计习题解答.doc
  12. spring-boot(2)--环境搭建
  13. 如何让caffe读取多通道图片(=4)
  14. PLSQL 的安装与简单使用
  15. JavaScript代码实现冒泡排序
  16. 电脑定时执行的软件 - 定时执行专家
  17. 邮件合并的逆向应用,从多个Word文档中取值到Excel中
  18. 如何关闭135端口、139端口、445端口
  19. 全然用linux工作,放弃windows
  20. PHPYUN人才招聘系统 V5.0商业授权vip版和授权版及旗舰版有什么区别

热门文章

  1. 无线电能传输 wpt 磁耦合谐振 过零检测 matlab simulink仿真 pwm MOSFET,过零检测模块 基于二极管整流的无线电能传输设计
  2. ZIGBEE学习之---ZSTACK1.4.3修改密钥(CC2430)
  3. 使用百度云主机的GPU主机教程_第一部分
  4. html+word+clou,AE脚本:Word Cloud 1.0.3_文字云排版动画脚本+教程
  5. Android开发——Android中的二维码生成与扫描
  6. ios共享账号公众号_forest 专注森林 ios下载账号分享 公众号 iphone ipad
  7. 最新云开秒赞系统公益版网站源码
  8. 基于HTML5的捕鱼达人游戏网页版
  9. 测试用例-微信消息撤回
  10. Tita OKR:不可或缺的工作法看板