完美支持opengl,opengles,软解

This commit is contained in:
Barry 2018-11-02 03:12:39 +08:00
parent 3d10b20b46
commit 4cbaf2dca7
7 changed files with 358 additions and 62 deletions

View file

@ -34,7 +34,8 @@ SOURCES += \
convert.cpp \
frames.cpp \
yuvglwidget.cpp \
fpscounter.cpp
fpscounter.cpp \
qyuvopenglwidget.cpp
HEADERS += \
dialog.h \
@ -44,7 +45,8 @@ HEADERS += \
convert.h \
frames.h \
yuvglwidget.h \
fpscounter.h
fpscounter.h \
qyuvopenglwidget.h
FORMS += \
dialog.ui

View file

@ -5,6 +5,7 @@
#include "ui_dialog.h"
#include "adbprocess.h"
#include "yuvglwidget.h"
#include "qyuvopenglwidget.h"
void saveAVFrame_YUV_ToTempFile(const AVFrame *pFrame)
{
@ -38,16 +39,23 @@ void saveAVFrame_YUV_ToTempFile(const AVFrame *pFrame)
t_file.flush();
}
#define OPENGL_EX
Dialog::Dialog(QWidget *parent) :
QDialog(parent),
ui(new Ui::Dialog)
{
ui->setupUi(this);
setWindowFlags(windowFlags()
| Qt::WindowMaximizeButtonHint
| Qt::WindowMinimizeButtonHint);
#ifdef OPENGL_EX
w = new QYUVOpenGLWidget(this);
ui->verticalLayout->addWidget(w);
#else
w2 = new YUVGLWidget(this);
ui->verticalLayout->addWidget(w2);
#endif
w2 = new YUVGLWidget(this);
w2->resize(ui->imgLabel->size());
w2->move(ui->imgLabel->pos());
Decoder::init();
@ -73,8 +81,13 @@ Dialog::Dialog(QWidget *parent) :
frames.lock();
const AVFrame *frame = frames.consumeRenderedFrame();
//saveAVFrame_YUV_ToTempFile(frame);
#ifdef OPENGL_EX
w->setFrameSize(QSize(frame->width, frame->height));
w->updateTextures(frame->data[0], frame->data[1], frame->data[2], frame->linesize[0], frame->linesize[1], frame->linesize[2]);
#else
w2->setFrameSize(QSize(frame->width, frame->height));
w2->updateTextures(frame->data[0], frame->data[1], frame->data[2], frame->linesize[0], frame->linesize[1], frame->linesize[2]);
#endif
frames.unLock();
},Qt::QueuedConnection);
}

View file

@ -12,6 +12,7 @@ class Dialog;
}
class YUVGLWidget;
class QYUVOpenGLWidget;
class Dialog : public QDialog
{
Q_OBJECT
@ -32,6 +33,7 @@ private:
Server* server;
Decoder decoder;
Frames frames;
QYUVOpenGLWidget* w;
YUVGLWidget* w2;
};

View file

@ -13,61 +13,47 @@
<property name="windowTitle">
<string>Dialog</string>
</property>
<widget class="QPushButton" name="adbProcess">
<property name="geometry">
<rect>
<x>30</x>
<y>20</y>
<width>111</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>adbProcess</string>
</property>
</widget>
<widget class="QPushButton" name="startServerBtn">
<property name="geometry">
<rect>
<x>160</x>
<y>20</y>
<width>111</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>startServer</string>
</property>
</widget>
<widget class="QPushButton" name="stopServerBtn">
<property name="geometry">
<rect>
<x>290</x>
<y>20</y>
<width>111</width>
<height>28</height>
</rect>
</property>
<property name="text">
<string>stopServer</string>
</property>
</widget>
<widget class="QLabel" name="imgLabel">
<property name="geometry">
<rect>
<x>20</x>
<y>60</y>
<width>1051</width>
<height>621</height>
</rect>
</property>
<property name="styleSheet">
<string notr="true">background-color: rgb(0, 0, 0);</string>
</property>
<property name="text">
<string/>
</property>
</widget>
<layout class="QVBoxLayout" name="verticalLayout">
<item>
<widget class="QWidget" name="topWidget" native="true">
<property name="minimumSize">
<size>
<width>0</width>
<height>50</height>
</size>
</property>
<property name="maximumSize">
<size>
<width>16777215</width>
<height>50</height>
</size>
</property>
<layout class="QHBoxLayout" name="horizontalLayout">
<item>
<widget class="QPushButton" name="adbProcess">
<property name="text">
<string>adbProcess</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="startServerBtn">
<property name="text">
<string>startServer</string>
</property>
</widget>
</item>
<item>
<widget class="QPushButton" name="stopServerBtn">
<property name="text">
<string>stopServer</string>
</property>
</widget>
</item>
</layout>
</widget>
</item>
</layout>
</widget>
<layoutdefault spacing="6" margin="11"/>
<resources/>

View file

@ -7,8 +7,9 @@
int main(int argc, char *argv[])
{
// only support AA_UseDesktopOpenGL
QApplication::setAttribute(Qt::AA_UseDesktopOpenGL);
//QApplication::setAttribute(Qt::AA_UseOpenGLES);
//QApplication::setAttribute(Qt::AA_UseSoftwareOpenGL);
QApplication a(argc, argv);

239
src/qyuvopenglwidget.cpp Normal file
View file

@ -0,0 +1,239 @@
#include <QCoreApplication>
#include <QOpenGLTexture>
#include "qyuvopenglwidget.h"
// 存储顶点坐标和纹理坐标
// 存在一起缓存在vbo
// 使用glVertexAttribPointer指定访问方式即可
static const GLfloat coordinate[] = {
// 顶点坐标存储4个xyz坐标
// 坐标范围为[-1,1],中心点为 0,0
// 二维图像z始终为0
// GL_TRIANGLE_STRIP的绘制方式
// 使用前3个坐标绘制一个三角形使用后三个坐标绘制一个三角形正好为一个矩形
// x y z
-1.0f, -1.0f, 0.0f,
1.0f, -1.0f, 0.0f,
-1.0f, 1.0f, 0.0f,
1.0f, 1.0f, 0.0f,
// 纹理坐标存储4个xy坐标
// 坐标范围为[0,1],左下角为 0,0
// TODO 为什么这个顺序指定四个顶点?顶点坐标和纹理坐标如何映射的?
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
// 顶点着色器
static const QString s_vertShader = R"(
attribute vec3 vertexIn; // xyz顶点坐标
attribute vec2 textureIn; // xy纹理坐标
varying vec2 textureOut; // 传递给片段着色器的纹理坐标
void main(void)
{
gl_Position = vec4(vertexIn, 1.0); // 1.0表示vertexIn是一个顶点位置
textureOut = textureIn; // 纹理坐标直接传递给片段着色器
}
)";
// 片段着色器
static QString s_fragShader = R"(
varying vec2 textureOut; // 由顶点着色器传递过来的纹理坐标
uniform sampler2D textureY; // uniform 纹理单元,利用纹理单元可以使用多个纹理
uniform sampler2D textureU; // sampler2D是2D采样器
uniform sampler2D textureV; // 声明yuv三个纹理单元
void main(void)
{
vec3 yuv;
vec3 rgb;
// 根据指定的纹理textureY和坐标textureOut来采样
yuv.x = texture2D(textureY, textureOut).r;
yuv.y = texture2D(textureU, textureOut).r - 0.5;
yuv.z = texture2D(textureV, textureOut).r - 0.5;
// 采样完转为rgb
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);
}
)";
QYUVOpenGLWidget::QYUVOpenGLWidget(QWidget *parent) : QOpenGLWidget(parent)
{
}
QYUVOpenGLWidget::~QYUVOpenGLWidget()
{
makeCurrent();
m_vbo.destroy();
deInitTextures();
doneCurrent();
}
QSize QYUVOpenGLWidget::minimumSizeHint() const
{
return QSize(50, 50);
}
QSize QYUVOpenGLWidget::sizeHint() const
{
return size();
}
void QYUVOpenGLWidget::setFrameSize(const QSize &frameSize)
{
if (m_frameSize != frameSize) {
m_frameSize = frameSize;
m_needInit = true;
}
}
void QYUVOpenGLWidget::updateTextures(quint8 *dataY, quint8 *dataU, quint8 *dataV, quint32 linesizeY, quint32 linesizeU, quint32 linesizeV)
{
updateTexture(m_texture[0], 0, dataY, linesizeY);
updateTexture(m_texture[1], 1, dataU, linesizeU);
updateTexture(m_texture[2], 2, dataV, linesizeV);
update();
}
void QYUVOpenGLWidget::initializeGL()
{
initializeOpenGLFunctions();
glDisable(GL_DEPTH_TEST);
// 顶点缓冲对象初始化
m_vbo.create();
m_vbo.bind();
m_vbo.allocate(coordinate, sizeof(coordinate));
initShader();
// 设置背景清理色为黑色
glClearColor(0.0,0.0,0.0,0.0);
// 清理颜色背景
glClear(GL_COLOR_BUFFER_BIT);
}
void QYUVOpenGLWidget::paintGL()
{
if (m_needInit) {
//TODO 需要deInitTextures吗
initTextures();
m_needInit = false;
}
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_texture[0]);
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_texture[1]);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, m_texture[2]);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
// 没有画面则显示黑屏
//glClear(GL_COLOR_BUFFER_BIT);
}
void QYUVOpenGLWidget::resizeGL(int width, int height)
{
glViewport(0, 0, width, height);
}
void QYUVOpenGLWidget::initShader()
{
// opengles的float、int等要手动指定精度
if (QCoreApplication::testAttribute(Qt::AA_UseOpenGLES)) {
s_fragShader.prepend(R"(
precision mediump int;
precision mediump float;
)");
}
m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Vertex, s_vertShader);
m_shaderProgram.addShaderFromSourceCode(QOpenGLShader::Fragment, s_fragShader);
m_shaderProgram.link();
m_shaderProgram.bind();
// 指定顶点坐标在vbo中的访问方式
// 参数解释顶点坐标在shader中的参数名称顶点坐标为float起始偏移为0顶点坐标类型为vec3步幅为3个float
m_shaderProgram.setAttributeBuffer("vertexIn", GL_FLOAT, 0, 3, 3 * sizeof(float));
// 启用顶点属性
m_shaderProgram.enableAttributeArray("vertexIn");
// 指定纹理坐标在vbo中的访问方式
// 参数解释纹理坐标在shader中的参数名称纹理坐标为float起始偏移为12个float跳过前面存储的12个顶点坐标纹理坐标类型为vec2步幅为2个float
m_shaderProgram.setAttributeBuffer("textureIn", GL_FLOAT, 12 * sizeof(float), 2, 2 * sizeof(float));
m_shaderProgram.enableAttributeArray("textureIn");
// 关联片段着色器中的纹理单元和opengl中的纹理单元opengl一般提供16个纹理单元
m_shaderProgram.setUniformValue("textureY", 0);
m_shaderProgram.setUniformValue("textureU", 1);
m_shaderProgram.setUniformValue("textureV", 2);
}
void QYUVOpenGLWidget::initTextures()
{
/*
// 创建纹理
m_textrueY = new QOpenGLTexture(QOpenGLTexture::Target2D);
// 设置纹理缩放时的策略
m_textrueY->setMinificationFilter(QOpenGLTexture::Linear);
m_textrueY->setMagnificationFilter(QOpenGLTexture::Linear);
// 设置所有方向上纹理超出坐标时的显示策略(也可单个方向单独设置)
m_textrueY->setWrapMode(QOpenGLTexture::ClampToEdge);
*/
// 创建纹理
glGenTextures(1, &m_texture[0]);
glBindTexture(GL_TEXTURE_2D, m_texture[0]);
// 设置纹理缩放时的策略
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 设置st方向上纹理超出坐标时的显示策略
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_frameSize.width(), m_frameSize.height(), 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
glGenTextures(1, &m_texture[1]);
glBindTexture(GL_TEXTURE_2D, m_texture[1]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_frameSize.width()/2, m_frameSize.height()/2, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
glGenTextures(1, &m_texture[2]);
glBindTexture(GL_TEXTURE_2D, m_texture[2]);
// 设置纹理缩放时的策略
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
// 设置st方向上纹理超出坐标时的显示策略
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_frameSize.width()/2, m_frameSize.height()/2, 0, GL_RED, GL_UNSIGNED_BYTE, NULL);
}
void QYUVOpenGLWidget::deInitTextures()
{
glDeleteTextures(3, m_texture);
memset(m_texture, 0, 3);
}
void QYUVOpenGLWidget::updateTexture(GLuint texture, quint32 textureType, quint8 *pixels, quint32 stride)
{
if (!pixels)
return;
QSize size = 0 == textureType ? m_frameSize : m_frameSize/2;
makeCurrent();
glBindTexture(GL_TEXTURE_2D, texture);
glPixelStorei(GL_UNPACK_ROW_LENGTH, stride);
glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, size.width(), size.height(), GL_RED, GL_UNSIGNED_BYTE, pixels);
doneCurrent();
}

53
src/qyuvopenglwidget.h Normal file
View file

@ -0,0 +1,53 @@
#ifndef QYUVOPENGLWIDGET_H
#define QYUVOPENGLWIDGET_H
#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLShaderProgram>
#include <QOpenGLBuffer>
//QT_FORWARD_DECLARE_CLASS(QOpenGLTexture) //TODO先不用QOpenGLTextureTexture先使用opengl原生方式
class QYUVOpenGLWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
explicit QYUVOpenGLWidget(QWidget *parent = nullptr);
virtual ~QYUVOpenGLWidget();
QSize minimumSizeHint() const override;
QSize sizeHint() const override;
void setFrameSize(const QSize& frameSize);
void updateTextures(quint8* dataY, quint8* dataU, quint8* dataV, quint32 linesizeY, quint32 linesizeU, quint32 linesizeV);
protected:
void initializeGL();
void paintGL();
void resizeGL(int width, int height);
private:
void initShader();
void initTextures();
void deInitTextures();
void updateTexture(GLuint texture, quint32 textureType, quint8* pixels, quint32 stride);
private:
// 视频帧尺寸
QSize m_frameSize = {-1, -1};
bool m_needInit = false;
// 顶点缓冲对象(Vertex Buffer Objects, VBO)默认即为VertexBuffer(GL_ARRAY_BUFFER)类型
QOpenGLBuffer m_vbo;
// 着色器程序:编译链接着色器
QOpenGLShaderProgram m_shaderProgram;
// YUV纹理用于生成纹理贴图
//QOpenGLTexture* m_textrueY = Q_NULLPTR;
//QOpenGLTexture* m_textrueU = Q_NULLPTR;
//QOpenGLTexture* m_textrueV = Q_NULLPTR;
// YUV纹理用于生成纹理贴图
GLuint m_texture[3] = {0};
};
#endif // QYUVOPENGLWIDGET_H