From 4cbaf2dca742bfc9a1b50288e96a574e23230cb0 Mon Sep 17 00:00:00 2001 From: Barry <870709864@qq.com> Date: Fri, 2 Nov 2018 03:12:39 +0800 Subject: [PATCH] =?UTF-8?q?=E5=AE=8C=E7=BE=8E=E6=94=AF=E6=8C=81opengl,open?= =?UTF-8?q?gles,=E8=BD=AF=E8=A7=A3?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/QtScrcpy.pro | 6 +- src/dialog.cpp | 21 +++- src/dialog.h | 2 + src/dialog.ui | 96 +++++++--------- src/main.cpp | 3 +- src/qyuvopenglwidget.cpp | 239 +++++++++++++++++++++++++++++++++++++++ src/qyuvopenglwidget.h | 53 +++++++++ 7 files changed, 358 insertions(+), 62 deletions(-) create mode 100644 src/qyuvopenglwidget.cpp create mode 100644 src/qyuvopenglwidget.h diff --git a/src/QtScrcpy.pro b/src/QtScrcpy.pro index 83b4096..938adb1 100644 --- a/src/QtScrcpy.pro +++ b/src/QtScrcpy.pro @@ -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 diff --git a/src/dialog.cpp b/src/dialog.cpp index fcb583e..5478521 100644 --- a/src/dialog.cpp +++ b/src/dialog.cpp @@ -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); } diff --git a/src/dialog.h b/src/dialog.h index 6d50357..777bdc4 100644 --- a/src/dialog.h +++ b/src/dialog.h @@ -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; }; diff --git a/src/dialog.ui b/src/dialog.ui index ec17742..1c02c34 100644 --- a/src/dialog.ui +++ b/src/dialog.ui @@ -13,61 +13,47 @@ Dialog - - - - 30 - 20 - 111 - 28 - - - - adbProcess - - - - - - 160 - 20 - 111 - 28 - - - - startServer - - - - - - 290 - 20 - 111 - 28 - - - - stopServer - - - - - - 20 - 60 - 1051 - 621 - - - - background-color: rgb(0, 0, 0); - - - - - + + + + + + 0 + 50 + + + + + 16777215 + 50 + + + + + + + adbProcess + + + + + + + startServer + + + + + + + stopServer + + + + + + + diff --git a/src/main.cpp b/src/main.cpp index 5dee987..23767c6 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -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); diff --git a/src/qyuvopenglwidget.cpp b/src/qyuvopenglwidget.cpp new file mode 100644 index 0000000..6ad983d --- /dev/null +++ b/src/qyuvopenglwidget.cpp @@ -0,0 +1,239 @@ +#include +#include + +#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(); +} diff --git a/src/qyuvopenglwidget.h b/src/qyuvopenglwidget.h new file mode 100644 index 0000000..bfc3fef --- /dev/null +++ b/src/qyuvopenglwidget.h @@ -0,0 +1,53 @@ +#ifndef QYUVOPENGLWIDGET_H +#define QYUVOPENGLWIDGET_H +#include +#include +#include +#include + +//QT_FORWARD_DECLARE_CLASS(QOpenGLTexture) //TODO先不用QOpenGLTexture,Texture先使用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