390 lines
13 KiB
C++
390 lines
13 KiB
C++
|
|
|||
|
#include <QOpenGLTexture>
|
|||
|
#include <QOpenGLBuffer>
|
|||
|
#include <QMouseEvent>
|
|||
|
#include "CPlayWidget.h"
|
|||
|
|
|||
|
|
|||
|
// 顶点着色器源码
|
|||
|
const char *vsrcyuv = "attribute vec4 vertexIn; \
|
|||
|
attribute vec2 textureIn; \
|
|||
|
varying vec2 textureOut; \
|
|||
|
void main(void) \
|
|||
|
{ \
|
|||
|
gl_Position = vertexIn; \
|
|||
|
textureOut = textureIn; \
|
|||
|
}";
|
|||
|
// 片段着色器源码
|
|||
|
const char *fsrcyuv = "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); \
|
|||
|
}";
|
|||
|
// rgb片段着色器源码
|
|||
|
// 注意MEDIASUBTYPE_RGB32 是bgr的,所以需要再进行一次转换
|
|||
|
const char *fsrcrgb = "varying vec2 textureOut; \
|
|||
|
uniform sampler2D rgbdata; \
|
|||
|
void main() \
|
|||
|
{ \
|
|||
|
gl_FragColor = texture(rgbdata, textureOut); \
|
|||
|
}";
|
|||
|
|
|||
|
void CPlayWidget::OnUpdateFrame() {
|
|||
|
this->PlayOneFrame();
|
|||
|
}
|
|||
|
|
|||
|
void CPlayWidget::OnPaintData(const uint8_t *data, uint32_t len)
|
|||
|
{
|
|||
|
if(nullptr == m_pBufYuv420p)
|
|||
|
{
|
|||
|
m_pBufYuv420p = new unsigned char[len];
|
|||
|
qDebug("CPlayWidget::PlayOneFrame new data memory. Len=%d width=%d height=%d\n",
|
|||
|
len, m_nVideoW, m_nVideoW);
|
|||
|
memcpy(m_pBufYuv420p, data,len);
|
|||
|
//刷新界面,触发paintGL接口
|
|||
|
update();
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
CPlayWidget::CPlayWidget(QWidget *parent):QOpenGLWidget(parent) {
|
|||
|
textureUniformY = 0;
|
|||
|
textureUniformU = 0;
|
|||
|
textureUniformV = 0;
|
|||
|
id_y = 0;
|
|||
|
id_u = 0;
|
|||
|
id_v = 0;
|
|||
|
m_pTextureRGB = nullptr;
|
|||
|
m_pBufYuv420p = NULL;
|
|||
|
m_pVSHader = NULL;
|
|||
|
m_pFSHader = NULL;
|
|||
|
m_pShaderProgram = NULL;
|
|||
|
m_pTextureY = NULL;
|
|||
|
m_pTextureU = NULL;
|
|||
|
m_pTextureV = NULL;
|
|||
|
m_pYuvFile = NULL;
|
|||
|
m_nVideoH = 0;
|
|||
|
m_nVideoW = 0;
|
|||
|
mType = TYPE_YUV420P;
|
|||
|
connect(&this->tm,SIGNAL(timeout()),this,SLOT(OnUpdateFrame()));
|
|||
|
//tm.start(1000);
|
|||
|
}
|
|||
|
|
|||
|
CPlayWidget::~CPlayWidget() {
|
|||
|
}
|
|||
|
|
|||
|
void CPlayWidget::PlayOneFrame() {//函数功能读取一张yuv图像数据进行显示,每单击一次,就显示一张图片
|
|||
|
if(NULL == m_pYuvFile)
|
|||
|
{
|
|||
|
//打开yuv视频文件 注意修改文件路径
|
|||
|
// m_pYuvFile = fopen("F://OpenglYuvDemo//1920_1080.yuv", "rb");
|
|||
|
m_pYuvFile = fopen("F://md_sample_sp420_1080p.yuv", "rb");
|
|||
|
//根据yuv视频数据的分辨率设置宽高,demo当中是1080p,这个地方要注意跟实际数据分辨率对应上
|
|||
|
// m_nVideoW = 1920;
|
|||
|
// m_nVideoH = 1080;
|
|||
|
}
|
|||
|
//申请内存存一帧yuv图像数据,其大小为分辨率的1.5倍
|
|||
|
int nLen = m_nVideoW*m_nVideoH*3/2;
|
|||
|
if(nullptr == m_pBufYuv420p)
|
|||
|
{
|
|||
|
m_pBufYuv420p = new unsigned char[nLen];
|
|||
|
qDebug("CPlayWidget::PlayOneFrame new data memory. Len=%d width=%d height=%d\n",
|
|||
|
nLen, m_nVideoW, m_nVideoW);
|
|||
|
}
|
|||
|
//将一帧yuv图像读到内存中
|
|||
|
if(NULL == m_pYuvFile)
|
|||
|
{
|
|||
|
qFatal("read yuv file err.may be path is wrong!\n");
|
|||
|
return;
|
|||
|
}
|
|||
|
fread(m_pBufYuv420p, 1, nLen, m_pYuvFile);
|
|||
|
//刷新界面,触发paintGL接口
|
|||
|
update();
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
int CPlayWidget::SetDataType(CPlayWidget::IMG_TYPE type){
|
|||
|
this->mType = type;
|
|||
|
}
|
|||
|
|
|||
|
int CPlayWidget::OnCameraData(uint8_t *dat, uint32_t size)
|
|||
|
{
|
|||
|
memcpy(this->m_pBufRgb32,dat,size);
|
|||
|
update();
|
|||
|
}
|
|||
|
|
|||
|
int CPlayWidget::SetImgSize(uint32_t width, uint32_t height)
|
|||
|
{
|
|||
|
m_nVideoH = height;
|
|||
|
m_nVideoW = width;
|
|||
|
if(mType == TYPE_RGB32){
|
|||
|
m_pBufRgb32 = new uint8_t[width * height *4];
|
|||
|
}
|
|||
|
if(mType == TYPE_YUV420P){
|
|||
|
m_pBufYuv420p = new uint8_t[width * height *3/2];
|
|||
|
}
|
|||
|
}
|
|||
|
|
|||
|
|
|||
|
/*
|
|||
|
|
|||
|
* Y = 0.299 R + 0.587 G + 0.114 B
|
|||
|
|
|||
|
U = - 0.1687 R - 0.3313 G + 0.5 B + 128
|
|||
|
|
|||
|
V = 0.5 R - 0.4187 G - 0.0813 B + 128
|
|||
|
|
|||
|
反过来,RGB 也可以直接从YUV (256级别) 计算:
|
|||
|
|
|||
|
R = Y + 1.402 (Cr-128)
|
|||
|
|
|||
|
G = Y - 0.34414 (Cb-128) - 0.71414 (Cr-128)
|
|||
|
|
|||
|
B = Y + 1.772 (Cb-128)
|
|||
|
*/
|
|||
|
void CPlayWidget::initializeGL()
|
|||
|
{
|
|||
|
initializeOpenGLFunctions();
|
|||
|
glEnable(GL_DEPTH_TEST);
|
|||
|
//现代opengl渲染管线依赖着色器来处理传入的数据
|
|||
|
//着色器:就是使用openGL着色语言(OpenGL Shading Language, GLSL)编写的一个小函数,
|
|||
|
// GLSL是构成所有OpenGL着色器的语言,具体的GLSL语言的语法需要读者查找相关资料
|
|||
|
//初始化顶点着色器 对象
|
|||
|
m_pVSHader = new QOpenGLShader(QOpenGLShader::Vertex, this);
|
|||
|
|
|||
|
//编译顶点着色器程序
|
|||
|
bool bCompile = m_pVSHader->compileSourceCode(vsrcyuv);
|
|||
|
if(!bCompile)
|
|||
|
{
|
|||
|
// todo 设置错误状态
|
|||
|
}
|
|||
|
//初始化片段着色器 功能gpu中yuv转换成rgb
|
|||
|
m_pFSHader = new QOpenGLShader(QOpenGLShader::Fragment, this);
|
|||
|
if(mType == TYPE_RGB32){
|
|||
|
bCompile = m_pFSHader->compileSourceCode(fsrcrgb);
|
|||
|
}
|
|||
|
if(mType == TYPE_YUV420P){
|
|||
|
bCompile = m_pFSHader->compileSourceCode(fsrcyuv);
|
|||
|
}
|
|||
|
if(!bCompile)
|
|||
|
{
|
|||
|
// todo 设置错误状态
|
|||
|
}
|
|||
|
#define PROGRAM_VERTEX_ATTRIBUTE 0
|
|||
|
#define PROGRAM_TEXCOORD_ATTRIBUTE 1
|
|||
|
//创建着色器程序容器
|
|||
|
m_pShaderProgram = new QOpenGLShaderProgram;
|
|||
|
//将片段着色器添加到程序容器
|
|||
|
m_pShaderProgram->addShader(m_pFSHader);
|
|||
|
//将顶点着色器添加到程序容器
|
|||
|
m_pShaderProgram->addShader(m_pVSHader);
|
|||
|
//绑定属性vertexIn到指定位置ATTRIB_VERTEX,该属性在顶点着色源码其中有声明
|
|||
|
m_pShaderProgram->bindAttributeLocation("vertexIn", ATTRIB_VERTEX);
|
|||
|
//绑定属性textureIn到指定位置ATTRIB_TEXTURE,该属性在顶点着色源码其中有声明
|
|||
|
m_pShaderProgram->bindAttributeLocation("textureIn", ATTRIB_TEXTURE);
|
|||
|
//链接所有所有添入到的着色器程序
|
|||
|
m_pShaderProgram->link();
|
|||
|
//激活所有链接
|
|||
|
m_pShaderProgram->bind();
|
|||
|
|
|||
|
if(this->mType == TYPE_YUV420P){
|
|||
|
initShaderYuv();
|
|||
|
}
|
|||
|
if(this->mType == TYPE_RGB32){
|
|||
|
initShaderRgb();
|
|||
|
}
|
|||
|
glClearColor(0.0,0.0,0.0,0.0);//设置背景色
|
|||
|
}
|
|||
|
void CPlayWidget::resizeGL(int w, int h)
|
|||
|
{
|
|||
|
if(h == 0)// 防止被零除
|
|||
|
{
|
|||
|
h = 1;// 将高设为1
|
|||
|
}
|
|||
|
//设置视口
|
|||
|
glViewport(0,0, w,h);
|
|||
|
}
|
|||
|
void CPlayWidget::paintGL()
|
|||
|
{
|
|||
|
if(mType == TYPE_YUV420P)
|
|||
|
loadYuvTexture();
|
|||
|
if(mType == TYPE_RGB32){
|
|||
|
loadRgbTexture();
|
|||
|
}
|
|||
|
//使用顶点数组方式绘制图形
|
|||
|
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
|
|||
|
return;
|
|||
|
}
|
|||
|
|
|||
|
void CPlayWidget::initShaderYuv()
|
|||
|
{
|
|||
|
//读取着色器中的数据变量tex_y, tex_u, tex_v的位置,这些变量的声明可以在
|
|||
|
//片段着色器源码中可以看到
|
|||
|
textureUniformY = m_pShaderProgram->uniformLocation("tex_y");
|
|||
|
textureUniformU = m_pShaderProgram->uniformLocation("tex_u");
|
|||
|
textureUniformV = m_pShaderProgram->uniformLocation("tex_v");
|
|||
|
// 顶点矩阵
|
|||
|
static const GLfloat vertexVertices[] = {
|
|||
|
-1.0f, -1.0f,
|
|||
|
1.0f, -1.0f,
|
|||
|
-1.0f, 1.0f,
|
|||
|
1.0f, 1.0f,
|
|||
|
};
|
|||
|
//纹理矩阵
|
|||
|
static const GLfloat textureVertices[] = {
|
|||
|
0.0f, 1.0f,
|
|||
|
1.0f, 1.0f,
|
|||
|
0.0f, 0.0f,
|
|||
|
1.0f, 0.0f,
|
|||
|
};
|
|||
|
//设置属性ATTRIB_VERTEX的顶点矩阵值以及格式
|
|||
|
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices);
|
|||
|
//设置属性ATTRIB_TEXTURE的纹理矩阵值以及格式
|
|||
|
glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices);
|
|||
|
//启用ATTRIB_VERTEX属性的数据,默认是关闭的
|
|||
|
glEnableVertexAttribArray(ATTRIB_VERTEX);
|
|||
|
//启用ATTRIB_TEXTURE属性的数据,默认是关闭的
|
|||
|
glEnableVertexAttribArray(ATTRIB_TEXTURE);
|
|||
|
//分别创建y,u,v纹理对象
|
|||
|
m_pTextureY = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
|||
|
m_pTextureU = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
|||
|
m_pTextureV = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
|||
|
m_pTextureY->create();
|
|||
|
m_pTextureU->create();
|
|||
|
m_pTextureV->create();
|
|||
|
//获取返回y分量的纹理索引值
|
|||
|
id_y = m_pTextureY->textureId();
|
|||
|
//获取返回u分量的纹理索引值
|
|||
|
id_u = m_pTextureU->textureId();
|
|||
|
//获取返回v分量的纹理索引值
|
|||
|
id_v = m_pTextureV->textureId();
|
|||
|
}
|
|||
|
|
|||
|
void CPlayWidget::initShaderRgb()
|
|||
|
{
|
|||
|
//读取着色器中的数据变量tex_y, tex_u, tex_v的位置,这些变量的声明可以在
|
|||
|
//片段着色器源码中可以看到
|
|||
|
textureUniformRGB = m_pShaderProgram->uniformLocation("rgbdata");
|
|||
|
// 顶点矩阵
|
|||
|
static const GLfloat vertexVertices[] = {
|
|||
|
-1.0f, -1.0f,
|
|||
|
1.0f, -1.0f,
|
|||
|
-1.0f, 1.0f,
|
|||
|
1.0f, 1.0f,
|
|||
|
};
|
|||
|
//纹理矩阵
|
|||
|
static const GLfloat textureVertices[] = {
|
|||
|
0.0f, 0.0f,
|
|||
|
1.0f, 0.0f,
|
|||
|
0.0f, 1.0f,
|
|||
|
1.0f, 1.0f,
|
|||
|
};
|
|||
|
//设置属性ATTRIB_VERTEX的顶点矩阵值以及格式
|
|||
|
glVertexAttribPointer(ATTRIB_VERTEX, 2, GL_FLOAT, 0, 0, vertexVertices);
|
|||
|
//设置属性ATTRIB_TEXTURE的纹理矩阵值以及格式
|
|||
|
glVertexAttribPointer(ATTRIB_TEXTURE, 2, GL_FLOAT, 0, 0, textureVertices);
|
|||
|
//启用ATTRIB_VERTEX属性的数据,默认是关闭的
|
|||
|
glEnableVertexAttribArray(ATTRIB_VERTEX);
|
|||
|
//启用ATTRIB_TEXTURE属性的数据,默认是关闭的
|
|||
|
glEnableVertexAttribArray(ATTRIB_TEXTURE);
|
|||
|
//分别创建y,u,v纹理对象
|
|||
|
m_pTextureRGB = new QOpenGLTexture(QOpenGLTexture::Target2D);
|
|||
|
m_pTextureRGB->create();
|
|||
|
//获取返回y分量的纹理索引值
|
|||
|
id_rgb = m_pTextureRGB->textureId();
|
|||
|
}
|
|||
|
|
|||
|
int CPlayWidget::loadYuvTexture()
|
|||
|
{
|
|||
|
//加载y数据纹理
|
|||
|
//激活纹理单元GL_TEXTURE0
|
|||
|
glActiveTexture(GL_TEXTURE0);
|
|||
|
//使用来自y数据生成纹理
|
|||
|
glBindTexture(GL_TEXTURE_2D, id_y);
|
|||
|
//使用内存中m_pBufYuv420p数据创建真正的y数据纹理
|
|||
|
glTexImage2D(GL_TEXTURE_2D,
|
|||
|
0,
|
|||
|
GL_RED,
|
|||
|
m_nVideoW,
|
|||
|
m_nVideoH,
|
|||
|
0,
|
|||
|
GL_RED,
|
|||
|
GL_UNSIGNED_BYTE,
|
|||
|
m_pBufYuv420p);
|
|||
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
|||
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
|||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|||
|
//加载u数据纹理
|
|||
|
glActiveTexture(GL_TEXTURE1);//激活纹理单元GL_TEXTURE1
|
|||
|
glBindTexture(GL_TEXTURE_2D, id_u);
|
|||
|
glTexImage2D(GL_TEXTURE_2D,
|
|||
|
0, GL_RED,
|
|||
|
m_nVideoW/2,
|
|||
|
m_nVideoH/2,
|
|||
|
0,
|
|||
|
GL_RED,
|
|||
|
GL_UNSIGNED_BYTE,
|
|||
|
(char*)m_pBufYuv420p+m_nVideoW*m_nVideoH);
|
|||
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
|||
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
|||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|||
|
//加载v数据纹理
|
|||
|
glActiveTexture(GL_TEXTURE2);//激活纹理单元GL_TEXTURE2
|
|||
|
glBindTexture(GL_TEXTURE_2D, id_v);
|
|||
|
glTexImage2D(GL_TEXTURE_2D,
|
|||
|
0, GL_RED,
|
|||
|
m_nVideoW/2,
|
|||
|
m_nVideoH/2,
|
|||
|
0, GL_RED,
|
|||
|
GL_UNSIGNED_BYTE,
|
|||
|
(char*)m_pBufYuv420p+m_nVideoW*m_nVideoH*5/4);
|
|||
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
|||
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
|||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|||
|
//指定y纹理要使用新值 只能用0,1,2等表示纹理单元的索引,这是opengl不人性化的地方
|
|||
|
//0对应纹理单元GL_TEXTURE0 1对应纹理单元GL_TEXTURE1 2对应纹理的单元
|
|||
|
glUniform1i(textureUniformY, 0);
|
|||
|
//指定u纹理要使用新值
|
|||
|
glUniform1i(textureUniformU, 1);
|
|||
|
//指定v纹理要使用新值
|
|||
|
glUniform1i(textureUniformV, 2);
|
|||
|
return 0;
|
|||
|
}
|
|||
|
|
|||
|
int CPlayWidget::loadRgbTexture()
|
|||
|
{
|
|||
|
//加载rgb数据纹理
|
|||
|
//激活纹理单元GL_TEXTURE0
|
|||
|
glActiveTexture(GL_TEXTURE0);
|
|||
|
//使用来自y数据生成纹理
|
|||
|
glBindTexture(GL_TEXTURE_2D, id_rgb);
|
|||
|
//使用内存中m_pBufYuv420p数据创建真正的y数据纹理
|
|||
|
glTexImage2D(GL_TEXTURE_2D,
|
|||
|
0,
|
|||
|
GL_RGBA,
|
|||
|
m_nVideoW,
|
|||
|
m_nVideoH,
|
|||
|
0,
|
|||
|
GL_BGRA,
|
|||
|
GL_UNSIGNED_BYTE,
|
|||
|
m_pBufRgb32);
|
|||
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
|
|||
|
glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
|
|||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
|||
|
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
|||
|
glUniform1i(textureUniformRGB, 0);
|
|||
|
}
|