FFMpeg-3、基于QT实现音视频播放显示_qt实现视频从数据库读出并显示(1)

img
img

既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!

由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新

需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)

如果你需要这些资料,可以戳这里获取

//自动加双引号 定义常量串都可以使用
#define GET\_STR(x) #x
//顶点着色器的shader代码
const char \*vString = GET\_STR(
	attribute vec4 vertexIn;
	attribute vec2 textureIn;
	varying vec2 textureOut;
	void main(void)
	{
		gl_Position = vertexIn;//传入的顶点坐标记录
		textureOut = textureIn;//传入的材质坐标保存到textureOut
	}
);


//自动加双引号 定义常量串都可以使用
#define GET\_STR(x) #x
//片元着色器的shader代码
const char \*tString = GET\_STR(
	varying vec2 textureOut;//刚刚顶点着色器算出来的坐标
	uniform sampler2D tex_y;//uniform是外部传入的变量
	uniform sampler2D tex_u;
	uniform sampler2D tex_v;
	void main(void)
	{
	//420P是平面存储的,最后都是要转换为每个像素都是有yuv再方便转换
	//用的是灰度图的形式存储的
	//如安装那边硬解码出来的都是yuv420sp,uv是打包存在一起的,则不能使用这套shader代码了,要增加透明度存储方式。
		vec3 yuv;
		vec3 rgb;
		//根据那个坐标把材质坐标计算出来
		//传入材质,和坐标 返回材质当中的颜色rgb,但是用灰度图存储的,都是一样的。
		//三个材质就可以拼出一个yuv的数据
		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, -0.39465, 2.03211,
			1.13983, -0.58060, 0.0) \* yuv;
		gl_FragColor = vec4(rgb, 1.0);//转换成显示的颜色,再设置一下
	}

);

创建材质
shader其实就是绘制某一个材质,那么就要看创建材质了。qt也有创建材质的函数,但是openGL这边要简单一点。

glGenTextures(1,t);创建几个材质,传入二维数组存放创建的材质地址

glBindTexture(GL_TEXTURE_2D \*t)进行绑定 绑定为2d的图像

glTexPatameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR)设置属性  放大的属性  
glTexPatameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR)设置属性  缩小的属性
GL_TEXTURE_2D操作的是2D纹理
GL_TEXTURE_MAG_FILTER  放大过滤
GL_TEXTURE_MIN_FILTER  缩小过滤
GL_LINEAR用线性差值的方式进行缩放 使用距离当前渲染像素中心最近的四个纹素加权平均值,则最后的图像变化就比较荣和

写入和绘制材质

glActiveTexture(GL_TEXTURE0);激活材质 序号0
glBindTexture(GL_TEXTURE_2D,id_y);绑定之前创建的材质
在显存当中创建材质 
//将openGL里面的创建的材质设置传入到内存当中,就是显存到内存的操作
//GL\_LUMINANCE,pixel\_w,pixel\_h 这个就表示已灰度图的形式存放的,传入宽高
glTexImage2D(GL_TEXTURE_2D,//创建为了
 0, //细节显示 0默认 是拉远拉近摄像机相关的显示
 GL_LUMINANCE,//gpu显卡内部的格式,就是创建的材质是存放到显卡当中,这是灰度图
 pixel_w,pixel_h,
 0,
 GL_LUMINANCE, 数据格式,内存到显存的格式,但是无法转换因此要一致
 GL_UNSIGNED_BYTE,//单个像素的存放格式
 plane[0])
 //因为创建纹理有很大的开销,因此提供了一个修改纹理的函数
 glTexSubImage2D()  修改纹理  存在偏移值的变量就是表示从当前的纹理当中只取一部分

glUniform1i(textureUniformY, 0);材质设完之后与shaeder相关联,通过uniform变量出入到材质0层
glDrawArrays(GL_TRINGLE_STRIP,0,4)//绘制矩形,四个顶点

显示播放整体流程分析
重载QOpenGLWidget的三个函数
//重载那三个函数
//刷新初始化
void paintGL();//具体绘制在这里面实现
//初始化gl
void initializeGL();//初始化
//窗口大小变化
void resizeGL(int width,int height);//当窗口发生变化的时候调用,这个函数

initializeGL 在OPenGL创建的时候就会调用
初始化OpenGL函数
	initializeOpenGLFunctions()
调用着色器program添加shader代码
QGLShaderProgram.addShaderFromSourceCode(QGLShader::Fragment,tString);
QGLShaderProgram.addShaderFromSourceCode(QGLShader::Vertex,vString);
	//QGLShader::Fragment 纹理材质类型 tString,vString常量串是Shader具体源代码
	//这个addShader还可以从文件获取 Fragment是fsh文件,Vertex是vsh文件
	
//给shader绑定属性 给顶点shader的attribute赋值,A\_VER、T\_VER这个只是标记作用
	program.bindAttributeLocation("vertexIn", A_VER);
	program.bindAttributeLocation("textureIn",T_VER);
	
//编译,绑定shader
	program.link();
	program.bind();
	
//定义顶点和材质纹理的坐标
	static const GLfloat ver[] = {}
	static const GLfloat tex[] = {}

//将顶点和材质纹理的坐标设置到shader当中去,A\_VER、T\_VER就是之前赋值属性的时候绑定的flag
	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当中获取uniform sampler2D的位置,之后好传入
	unis[0] = program.uniformLocation("tex\_y");
    unis[1] = program.uniformLocation("tex\_u");
    unis[2] = program.uniformLocation("tex\_v");
	
//创建材质,并绑定类型,属性及创建空间,对yuv都进行操作,注意uv的大小变化
	glGenTextures(3,texs);
	//Y
    glBindTexture(GL_TEXTURE_2D, texs[0]);//绑定2D
    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);//创建材质显卡的空间 ,与内存的空间是不一样的
	//width/2 height/2根据yuv420的特性来的 uv是y的四分之一
    //U
    glBindTexture(GL_TEXTURE_2D, texs[1]);//绑定2D
    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]);//绑定2D
    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("output240X128.yuv", "rb");
    if(!fp)
    {
        qDebug() << "fopen error";
    }

//启动定时器,绑定到OPenGL的update()函数调用绘画函数
    QTimer \*ti = new QTimer(this);
    connect(ti, SIGNAL(timeout()), this, SLOT(update()));//定时器刷新到update()里面取
    ti->start(40);
	
paintGL  绘画时调用
//读取帧数据,存放到分配材质的内存空间
    fread(datas[0],1,width\*height, fp);
    fread(datas[1], 1, width\*height/4, fp);
    fread(datas[2], 1, width\*height/4, fp);
	
//在显卡中创建材质 并绑定到了0层渲染材质
    glActiveTexture(GL_TEXTURE0);//激活第0层
    glBindTexture(GL_TEXTURE_2D, texs[0]);//把0层绑定Y材质
//修改材质(复制内存内存)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
//与shader uni变量关联起来
    glUniform1i(unis[0], 0);
	
	重复三次
	glActiveTexture(GL_TEXTURE0+1);//激活第1层
    glBindTexture(GL_TEXTURE_2D, texs[1]);//把1层绑定Y材质
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_RED, GL_UNSIGNED_BYTE, datas[1]);
    glUniform1i(unis[1], 1);
	
	glActiveTexture(GL_TEXTURE0+2);//激活第0层
    glBindTexture(GL_TEXTURE_2D, texs[2]);//把0层绑定Y材质
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width/2, height/2, GL_RED, GL_UNSIGNED_BYTE, datas[2]);
    glUniform1i(unis[2], 2);
	
最后绘画 //画三角形 从0开始 四个顶点,
	 glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//画三角形 从0开始 四个顶点,


视频播放实践
拿到output240X128.yuv也可以使用命令制作ffmpeg -i 1.mp4 -t 10 -s 240x128 -pix_fmt yuv420p out240x128.yuv

注意几点
控件这里
在这里插入图片描述
qt工程文件.pro文件
需添加
QT += opengl
QT += openglextensions

代码

XVideoWidget.h文件

#ifndef XVIDEOWIDGET\_H
#define XVIDEOWIDGET\_H

#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QGLShaderProgram>
/\*
 \* //引入这个需要在pro文件添加模块
 \* QT += opengl
 \* QT += openglextensions
\*/

class XVideoWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    //注意Q\_OBJECT这个变量如果没有添加进来则槽函数无法调用
    Q_OBJECT

public:
    XVideoWidget(QWidget \*parent);
    ~XVideoWidget();
    //从shader的yuv变量地址
    GLuint unis[3] = {0};
    //opengl的texture地址
    GLuint texs[3] = {0};

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

    //宽高度
    int width = 240;
    int height = 128;
protected:
     //重载那三个函数
    //刷新初始化
    void paintGL();//具体绘制在这里面实现

    //初始化gl
    void initializeGL();//初始化

    //窗口大小变化
    void resizeGL(int width,int height);//当窗口发生变化的时候调用,这个函数


private:
    QGLShaderProgram program;

};

#endif // XVIDEOWIDGET\_H


XVideoWidget.cpp文件

#include "XVideoWidget.h"
#include <QDebug>
#include <QTimer>

FILE \*fp = NULL;

//定义一个宏表示数组
#define A\_VER 3
#define T\_VER 4
//自动加双引号 定义常量串都可以使用
#define GET\_STR(x) #x
//顶点着色器的shader代码
const char \*vString = GET\_STR(
    attribute vec4 vertexIn;
    attribute vec2 textureIn;
    varying vec2 textureOut;//varying顶点和片元shader共享变量
    void main(void)
    {
        gl_Position = vertexIn;//传入的顶点坐标记录
        textureOut = textureIn;//传入的材质坐标保存到textureOut,从而传出去了
    }
);
//片元着色器的shader代码
const char \*tString = GET\_STR(
    varying vec2 textureOut;//刚刚顶点着色器算出来的坐标 共享的
    uniform sampler2D tex_y;//uniform是外部传入的变量
    uniform sampler2D tex_u;//sampler2D是一个2d图像
    uniform sampler2D tex_v;
    void main(void)
    {
    //420P是平面存储的,最后都是要转换为每个像素都是有yuv再方便转换
    //用的是灰度图的形式存储的
    //如安装那边硬解码出来的都是yuv420sp,uv是打包存在一起的,则不能使用这套shader代码了,要增加透明度存储方式。
        vec3 yuv;
        vec3 rgb;
        //根据那个坐标把材质坐标计算出来
        //传入材质,和坐标 返回材质当中的颜色rgb,但是用灰度图存储的,都是一样的。
        //三个材质就可以拼出一个yuv的数据
        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, -0.39465, 2.03211,
            1.13983, -0.58060, 0.0) \* yuv;
        gl_FragColor = vec4(rgb, 1.0);//转换成显示的颜色,再设置一下
    }

);
XVideoWidget::XVideoWidget(QWidget \*parent)
    :QOpenGLWidget(parent)//初始化列表调用父类构造方法调用paint画出OpenGLWidget
{

    int a = 1;
}
XVideoWidget::~XVideoWidget()
{

}
//刷新初始化,每次移动都会调用一次,进行刷新
void XVideoWidget::paintGL()
{
    if(feof(fp))
    {
     fseek(fp, 0, SEEK\_SET);
    }
    fread(datas[0],1,width\*height, fp);
    fread(datas[1], 1, width\*height/4, fp);
    fread(datas[2], 1, width\*height/4, fp);

    //在显卡中创建材质 并绑定到了0层渲染材质
    glActiveTexture(GL_TEXTURE0);//激活第0层
    glBindTexture(GL_TEXTURE_2D, texs[0]);//把0层绑定Y材质
    //修改材质(复制内存内存)
    glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, width, height, GL_RED, GL_UNSIGNED_BYTE, datas[0]);
    //与shader uni变量关联起来
    glUniform1i(unis[0], 0);

    glActiveTexture(GL_TEXTURE0+1);//激活第1层
    glBindTexture(GL_TEXTURE_2D, texs[1]);//把1层绑定Y材质
    //修改材质(复制内存内存)
    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);//激活第0层
    glBindTexture(GL_TEXTURE_2D, texs[2]);//把0层绑定Y材质
    //修改材质(复制内存内存)
    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开始 四个顶点,

    qDebug() << "paintGL";
}

//初始化gl
void XVideoWidget::initializeGL()
{
    qDebug() << "initializeGL";

    //初始化 opengl(QOpenGLFunctions继承过来的函数)
    initializeOpenGLFunctions();

    //program(opengl和qt都提供了)加载shader脚本文件 顶点shader 片元shader
    // qt 提供的 QGLShaderProgram
    qDebug() << "addShaderFromSourceCode :" <<program.addShaderFromSourceCode(QGLShader::Fragment,tString);//在源代码中添加好一些,从文件怕有泄露
    qDebug() << "addShaderFromSourceCode :" << program.addShaderFromSourceCode(QGLShader::Vertex,vString);

    //设置顶点坐标的变量
    program.bindAttributeLocation("vertexIn", A_VER);//这个vertexIn变量对应的本地位置 vertexIn是顶点shader定义的 3的位置

    //材质坐标
    program.bindAttributeLocation("textureIn",T_VER);//下标四,等下往4的位置存

    //编译shader
    qDebug() << "program.link():" << program.link();
    //绑定shader
    qDebug() << "program.link():" << program.bind();

    //传递顶点和材质坐标,
    //这个坐标是在这个函数中时候,之后draw的时候还要使用的,只传入二维
    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
    };

    //将坐标写入GL当中
    glVertexAttribPointer(A_VER,//索引地址
                          2,//表示一点顶点几个元素,
                          GL_FLOAT,//存放的类型,浮点数
                          0,
                          0,
                          ver //顶点地址
                          );
    //顶点坐标生效
    glEnableVertexAttribArray(A_VER);

    //材质 就是shader准备数据
    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]);//绑定2D
    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);//创建材质显卡的空间 ,与内存的空间是不一样的

    //width/2 height/2根据yuv420的特性来的 uv是y的四分之一
    //U
    glBindTexture(GL_TEXTURE_2D, texs[1]);//绑定2D
    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]);//绑定2D
    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];


![img](https://img-blog.csdnimg.cn/img_convert/1b6d695b2464fd6280868964bef8dc91.png)
![img](https://img-blog.csdnimg.cn/img_convert/5911e245a27c02beacc68d2b84d745e9.png)

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

;//创建材质显卡的空间 ,与内存的空间是不一样的

    //分配材质的内存空间
    datas[0] = new unsigned char[width\*height];
    datas[1] = new unsigned char[width\*height/4];


[外链图片转存中...(img-miSUiS2O-1715872326565)]
[外链图片转存中...(img-vJypu8te-1715872326565)]

**既有适合小白学习的零基础资料,也有适合3年以上经验的小伙伴深入学习提升的进阶课程,涵盖了95%以上物联网嵌入式知识点,真正体系化!**

**由于文件比较多,这里只是将部分目录截图出来,全套包含大厂面经、学习笔记、源码讲义、实战项目、大纲路线、电子书籍、讲解视频,并且后续会持续更新**

**需要这些体系化资料的朋友,可以加我V获取:vip1024c (备注嵌入式)**

**[如果你需要这些资料,可以戳这里获取](https://bbs.csdn.net/topics/618679757)**

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值