diff --git a/src/QtScrcpy.pro b/src/QtScrcpy.pro index fe7b42c..c53bad3 100644 --- a/src/QtScrcpy.pro +++ b/src/QtScrcpy.pro @@ -29,46 +29,35 @@ DEFINES += QT_DEPRECATED_WARNINGS SOURCES += \ main.cpp \ dialog.cpp \ - adbprocess.cpp \ - decoder.cpp \ - server.cpp \ - convert.cpp \ - frames.cpp \ - fpscounter.cpp \ - qyuvopenglwidget.cpp \ - videoform.cpp \ - devicesocket.cpp \ - tcpserver.cpp \ - controlevent.cpp \ - controller.cpp + videoform.cpp HEADERS += \ dialog.h \ - adbprocess.h \ - decoder.h \ - server.h \ - convert.h \ - frames.h \ - fpscounter.h \ - qyuvopenglwidget.h \ - videoform.h \ - devicesocket.h \ - tcpserver.h \ - qscrcpyevent.h \ - controlevent.h \ - controller.h + videoform.h FORMS += \ dialog.ui \ videoform.ui #子工程 +include ($$PWD/common/common.pri) +include ($$PWD/server/server.pri) +include ($$PWD/adb/adb.pri) +include ($$PWD/decoder/decoder.pri) +include ($$PWD/render/render.pri) include ($$PWD/android/android.pri) +include ($$PWD/inputcontrol/inputcontrol.pri) # 附加包含路径 INCLUDEPATH += \ $$PWD/ffmpeg/include \ - $$PWD/android + $$PWD/common \ + $$PWD/server \ + $$PWD/adb \ + $$PWD/decoder \ + $$PWD/render \ + $$PWD/android \ + $$PWD/inputcontrol # 依赖库 LIBS += \ diff --git a/src/QtScrcpy.pro.user.37b22fe b/src/QtScrcpy.pro.user.37b22fe deleted file mode 100644 index 24ede2b..0000000 --- a/src/QtScrcpy.pro.user.37b22fe +++ /dev/null @@ -1,318 +0,0 @@ - - - - - - EnvironmentId - {37b22fe7-3e02-43d3-85e3-251a3ebe4093} - - - ProjectExplorer.Project.ActiveTarget - 0 - - - ProjectExplorer.Project.EditorSettings - - true - false - true - - Cpp - - CppGlobal - - - - QmlJS - - QmlJSGlobal - - - 2 - UTF-8 - false - 4 - false - 80 - true - true - 1 - true - false - 0 - true - true - 0 - 8 - true - 1 - true - true - true - false - - - - ProjectExplorer.Project.PluginSettings - - - - ProjectExplorer.Project.Target.0 - - Desktop Qt 5.9.6 MSVC2015 32bit - Desktop Qt 5.9.6 MSVC2015 32bit - qt.596.win32_msvc2015_kit - 0 - 0 - 0 - - G:/mygitcode/QtScrcpy/build-QtScrcpy-Desktop_Qt_5_9_6_MSVC2015_32bit-Debug - - - true - qmake - - QtProjectManager.QMakeBuildStep - true - - false - false - false - - - true - Make - - Qt4ProjectManager.MakeStep - - false - - - - 2 - Build - - ProjectExplorer.BuildSteps.Build - - - - true - Make - - Qt4ProjectManager.MakeStep - - true - clean - - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - Debug - Debug - Qt4ProjectManager.Qt4BuildConfiguration - 2 - true - - - G:/mygitcode/QtScrcpy/build-QtScrcpy-Desktop_Qt_5_9_6_MSVC2015_32bit-Release - - - true - qmake - - QtProjectManager.QMakeBuildStep - false - - false - false - false - - - true - Make - - Qt4ProjectManager.MakeStep - - false - - - - 2 - Build - - ProjectExplorer.BuildSteps.Build - - - - true - Make - - Qt4ProjectManager.MakeStep - - true - clean - - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - Release - Release - Qt4ProjectManager.Qt4BuildConfiguration - 0 - true - - - G:/mygitcode/QtScrcpy/build-QtScrcpy-Desktop_Qt_5_9_6_MSVC2015_32bit-Profile - - - true - qmake - - QtProjectManager.QMakeBuildStep - true - - false - true - false - - - true - Make - - Qt4ProjectManager.MakeStep - - false - - - - 2 - Build - - ProjectExplorer.BuildSteps.Build - - - - true - Make - - Qt4ProjectManager.MakeStep - - true - clean - - - 1 - Clean - - ProjectExplorer.BuildSteps.Clean - - 2 - false - - Profile - Profile - Qt4ProjectManager.Qt4BuildConfiguration - 0 - true - - 3 - - - 0 - 部署 - - ProjectExplorer.BuildSteps.Deploy - - 1 - 部署设置 - - ProjectExplorer.DefaultDeployConfiguration - - 1 - - - false - false - 1000 - - true - - false - false - false - false - true - 0.01 - 10 - true - 1 - 25 - - 1 - true - false - true - valgrind - - 0 - 1 - 2 - 3 - 4 - 5 - 6 - 7 - 8 - 9 - 10 - 11 - 12 - 13 - 14 - - 2 - - QtScrcpy - - Qt4ProjectManager.Qt4RunConfiguration:G:/mygitcode/QtScrcpy/src/QtScrcpy.pro - true - - QtScrcpy.pro - false - - G:/mygitcode/QtScrcpy/build-QtScrcpy-Desktop_Qt_5_9_6_MSVC2015_32bit-Debug - 3768 - false - true - false - false - true - - 1 - - - - ProjectExplorer.Project.TargetCount - 1 - - - ProjectExplorer.Project.Updater.FileVersion - 18 - - - Version - 18 - - diff --git a/src/adb/adb.pri b/src/adb/adb.pri new file mode 100644 index 0000000..762cbac --- /dev/null +++ b/src/adb/adb.pri @@ -0,0 +1,5 @@ +HEADERS += \ + $$PWD/adbprocess.h + +SOURCES += \ + $$PWD/adbprocess.cpp diff --git a/src/adbprocess.cpp b/src/adb/adbprocess.cpp similarity index 100% rename from src/adbprocess.cpp rename to src/adb/adbprocess.cpp diff --git a/src/adbprocess.h b/src/adb/adbprocess.h similarity index 100% rename from src/adbprocess.h rename to src/adb/adbprocess.h diff --git a/src/android/android.pri b/src/android/android.pri new file mode 100644 index 0000000..0b6db18 --- /dev/null +++ b/src/android/android.pri @@ -0,0 +1,3 @@ +HEADERS += \ + $$PWD/input.h \ + $$PWD/keycodes.h diff --git a/src/common/common.pri b/src/common/common.pri new file mode 100644 index 0000000..6bdf623 --- /dev/null +++ b/src/common/common.pri @@ -0,0 +1,2 @@ +HEADERS += \ + $$PWD/qscrcpyevent.h diff --git a/src/qscrcpyevent.h b/src/common/qscrcpyevent.h similarity index 100% rename from src/qscrcpyevent.h rename to src/common/qscrcpyevent.h diff --git a/src/convert.cpp b/src/decoder/convert.cpp similarity index 100% rename from src/convert.cpp rename to src/decoder/convert.cpp diff --git a/src/convert.h b/src/decoder/convert.h similarity index 100% rename from src/convert.h rename to src/decoder/convert.h diff --git a/src/decoder.cpp b/src/decoder/decoder.cpp similarity index 100% rename from src/decoder.cpp rename to src/decoder/decoder.cpp diff --git a/src/decoder.h b/src/decoder/decoder.h similarity index 100% rename from src/decoder.h rename to src/decoder/decoder.h diff --git a/src/decoder/decoder.pri b/src/decoder/decoder.pri new file mode 100644 index 0000000..f563b9e --- /dev/null +++ b/src/decoder/decoder.pri @@ -0,0 +1,12 @@ +HEADERS += \ + $$PWD/convert.h \ + $$PWD/decoder.h \ + $$PWD/frames.h \ + $$PWD/fpscounter.h + +SOURCES += \ + $$PWD/convert.cpp \ + $$PWD/decoder.cpp \ + $$PWD/frames.cpp \ + $$PWD/fpscounter.cpp + diff --git a/src/fpscounter.cpp b/src/decoder/fpscounter.cpp similarity index 93% rename from src/fpscounter.cpp rename to src/decoder/fpscounter.cpp index 2cf0781..492e51e 100644 --- a/src/fpscounter.cpp +++ b/src/decoder/fpscounter.cpp @@ -1,71 +1,71 @@ -#include -#include - -#include "fpscounter.h" - -FpsCounter::FpsCounter(QObject* parent) : QObject(parent) -{ - -} - -FpsCounter::~FpsCounter() -{ - -} - -void FpsCounter::start() -{ - resetCounter(); - startCounterTimer(); -} - -void FpsCounter::stop() -{ - stopCounterTimer(); - resetCounter(); -} - -bool FpsCounter::isStarted() -{ - return m_counterTimer; -} - -void FpsCounter::addRenderedFrame() -{ - m_rendered++; -} - -void FpsCounter::addSkippedFrame() -{ - m_skipped++; -} - -void FpsCounter::timerEvent(QTimerEvent *event) -{ - if (event && m_counterTimer == event->timerId()) { - m_curRendered = m_rendered; - m_curSkipped = m_skipped; - resetCounter(); - //qInfo("FPS:%d Discard:%d", m_curRendered, m_skipped); - } -} - -void FpsCounter::startCounterTimer() -{ - stopCounterTimer(); - m_counterTimer = startTimer(1000); -} - -void FpsCounter::stopCounterTimer() -{ - if (m_counterTimer) { - killTimer(m_counterTimer); - m_counterTimer = 0; - } -} - -void FpsCounter::resetCounter() -{ - m_rendered = 0; - m_skipped = 0; -} +#include +#include + +#include "fpscounter.h" + +FpsCounter::FpsCounter(QObject* parent) : QObject(parent) +{ + +} + +FpsCounter::~FpsCounter() +{ + +} + +void FpsCounter::start() +{ + resetCounter(); + startCounterTimer(); +} + +void FpsCounter::stop() +{ + stopCounterTimer(); + resetCounter(); +} + +bool FpsCounter::isStarted() +{ + return m_counterTimer; +} + +void FpsCounter::addRenderedFrame() +{ + m_rendered++; +} + +void FpsCounter::addSkippedFrame() +{ + m_skipped++; +} + +void FpsCounter::timerEvent(QTimerEvent *event) +{ + if (event && m_counterTimer == event->timerId()) { + m_curRendered = m_rendered; + m_curSkipped = m_skipped; + resetCounter(); + //qInfo("FPS:%d Discard:%d", m_curRendered, m_skipped); + } +} + +void FpsCounter::startCounterTimer() +{ + stopCounterTimer(); + m_counterTimer = startTimer(1000); +} + +void FpsCounter::stopCounterTimer() +{ + if (m_counterTimer) { + killTimer(m_counterTimer); + m_counterTimer = 0; + } +} + +void FpsCounter::resetCounter() +{ + m_rendered = 0; + m_skipped = 0; +} diff --git a/src/fpscounter.h b/src/decoder/fpscounter.h similarity index 94% rename from src/fpscounter.h rename to src/decoder/fpscounter.h index bfad3d2..82daa79 100644 --- a/src/fpscounter.h +++ b/src/decoder/fpscounter.h @@ -1,35 +1,35 @@ -#ifndef FPSCOUNTER_H -#define FPSCOUNTER_H -#include - -class FpsCounter : public QObject -{ - Q_OBJECT -public: - FpsCounter(QObject* parent = Q_NULLPTR); - virtual ~FpsCounter(); - - void start(); - void stop(); - bool isStarted(); - void addRenderedFrame(); - void addSkippedFrame(); - -protected: - virtual void timerEvent(QTimerEvent *event); - -private: - void startCounterTimer(); - void stopCounterTimer(); - void resetCounter(); - -private: - quint32 m_counterTimer = 0; - quint32 m_curRendered = 0; - quint32 m_curSkipped = 0; - - quint32 m_rendered = 0; - quint32 m_skipped = 0; -}; - -#endif // FPSCOUNTER_H +#ifndef FPSCOUNTER_H +#define FPSCOUNTER_H +#include + +class FpsCounter : public QObject +{ + Q_OBJECT +public: + FpsCounter(QObject* parent = Q_NULLPTR); + virtual ~FpsCounter(); + + void start(); + void stop(); + bool isStarted(); + void addRenderedFrame(); + void addSkippedFrame(); + +protected: + virtual void timerEvent(QTimerEvent *event); + +private: + void startCounterTimer(); + void stopCounterTimer(); + void resetCounter(); + +private: + quint32 m_counterTimer = 0; + quint32 m_curRendered = 0; + quint32 m_curSkipped = 0; + + quint32 m_rendered = 0; + quint32 m_skipped = 0; +}; + +#endif // FPSCOUNTER_H diff --git a/src/frames.cpp b/src/decoder/frames.cpp similarity index 100% rename from src/frames.cpp rename to src/decoder/frames.cpp diff --git a/src/frames.h b/src/decoder/frames.h similarity index 100% rename from src/frames.h rename to src/decoder/frames.h diff --git a/src/controlevent.cpp b/src/inputcontrol/controlevent.cpp similarity index 100% rename from src/controlevent.cpp rename to src/inputcontrol/controlevent.cpp diff --git a/src/controlevent.h b/src/inputcontrol/controlevent.h similarity index 100% rename from src/controlevent.h rename to src/inputcontrol/controlevent.h diff --git a/src/controller.cpp b/src/inputcontrol/controller.cpp similarity index 100% rename from src/controller.cpp rename to src/inputcontrol/controller.cpp diff --git a/src/controller.h b/src/inputcontrol/controller.h similarity index 100% rename from src/controller.h rename to src/inputcontrol/controller.h diff --git a/src/inputcontrol/inputcontrol.pri b/src/inputcontrol/inputcontrol.pri new file mode 100644 index 0000000..bcbbdaf --- /dev/null +++ b/src/inputcontrol/inputcontrol.pri @@ -0,0 +1,8 @@ +HEADERS += \ + $$PWD/controlevent.h \ + $$PWD/controller.h + +SOURCES += \ + $$PWD/controlevent.cpp \ + $$PWD/controller.cpp + diff --git a/src/qyuvopenglwidget.cpp b/src/render/qyuvopenglwidget.cpp similarity index 97% rename from src/qyuvopenglwidget.cpp rename to src/render/qyuvopenglwidget.cpp index 9fd0aba..02c1c2e 100644 --- a/src/qyuvopenglwidget.cpp +++ b/src/render/qyuvopenglwidget.cpp @@ -1,241 +1,241 @@ -#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_needUpdate = true; - // inittexture immediately - repaint(); - } -} - -const QSize& QYUVOpenGLWidget::frameSize() -{ - return m_frameSize; -} - -void QYUVOpenGLWidget::updateTextures(quint8 *dataY, quint8 *dataU, quint8 *dataV, quint32 linesizeY, quint32 linesizeU, quint32 linesizeV) -{ - if (m_textureInited) { - 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_needUpdate) { - deInitTextures(); - initTextures(); - m_needUpdate = false; - } - - if (m_textureInited) { - 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); - } -} - -void QYUVOpenGLWidget::resizeGL(int width, int height) -{ - glViewport(0, 0, width, height); - repaint(); -} - -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() -{ - // 创建纹理 - 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); - - m_textureInited = true; -} - -void QYUVOpenGLWidget::deInitTextures() -{ - glDeleteTextures(3, m_texture); - memset(m_texture, 0, 3); - m_textureInited = false; -} - -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(); -} +#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_needUpdate = true; + // inittexture immediately + repaint(); + } +} + +const QSize& QYUVOpenGLWidget::frameSize() +{ + return m_frameSize; +} + +void QYUVOpenGLWidget::updateTextures(quint8 *dataY, quint8 *dataU, quint8 *dataV, quint32 linesizeY, quint32 linesizeU, quint32 linesizeV) +{ + if (m_textureInited) { + 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_needUpdate) { + deInitTextures(); + initTextures(); + m_needUpdate = false; + } + + if (m_textureInited) { + 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); + } +} + +void QYUVOpenGLWidget::resizeGL(int width, int height) +{ + glViewport(0, 0, width, height); + repaint(); +} + +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() +{ + // 创建纹理 + 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); + + m_textureInited = true; +} + +void QYUVOpenGLWidget::deInitTextures() +{ + glDeleteTextures(3, m_texture); + memset(m_texture, 0, 3); + m_textureInited = false; +} + +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/render/qyuvopenglwidget.h similarity index 96% rename from src/qyuvopenglwidget.h rename to src/render/qyuvopenglwidget.h index ed7fff0..d9d4f1b 100644 --- a/src/qyuvopenglwidget.h +++ b/src/render/qyuvopenglwidget.h @@ -1,49 +1,49 @@ -#ifndef QYUVOPENGLWIDGET_H -#define QYUVOPENGLWIDGET_H -#include -#include -#include -#include - -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); - 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_needUpdate = false; - bool m_textureInited = false; - - // 顶点缓冲对象(Vertex Buffer Objects, VBO):默认即为VertexBuffer(GL_ARRAY_BUFFER)类型 - QOpenGLBuffer m_vbo; - - // 着色器程序:编译链接着色器 - QOpenGLShaderProgram m_shaderProgram; - - // YUV纹理,用于生成纹理贴图 - GLuint m_texture[3] = {0}; -}; - -#endif // QYUVOPENGLWIDGET_H +#ifndef QYUVOPENGLWIDGET_H +#define QYUVOPENGLWIDGET_H +#include +#include +#include +#include + +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); + 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_needUpdate = false; + bool m_textureInited = false; + + // 顶点缓冲对象(Vertex Buffer Objects, VBO):默认即为VertexBuffer(GL_ARRAY_BUFFER)类型 + QOpenGLBuffer m_vbo; + + // 着色器程序:编译链接着色器 + QOpenGLShaderProgram m_shaderProgram; + + // YUV纹理,用于生成纹理贴图 + GLuint m_texture[3] = {0}; +}; + +#endif // QYUVOPENGLWIDGET_H diff --git a/src/render/render.pri b/src/render/render.pri new file mode 100644 index 0000000..357d548 --- /dev/null +++ b/src/render/render.pri @@ -0,0 +1,5 @@ +HEADERS += \ + $$PWD/qyuvopenglwidget.h + +SOURCES += \ + $$PWD/qyuvopenglwidget.cpp diff --git a/src/devicesocket.cpp b/src/server/devicesocket.cpp similarity index 100% rename from src/devicesocket.cpp rename to src/server/devicesocket.cpp diff --git a/src/devicesocket.h b/src/server/devicesocket.h similarity index 100% rename from src/devicesocket.h rename to src/server/devicesocket.h diff --git a/src/server.cpp b/src/server/server.cpp similarity index 100% rename from src/server.cpp rename to src/server/server.cpp diff --git a/src/server.h b/src/server/server.h similarity index 100% rename from src/server.h rename to src/server/server.h diff --git a/src/server/server.pri b/src/server/server.pri new file mode 100644 index 0000000..428d8e4 --- /dev/null +++ b/src/server/server.pri @@ -0,0 +1,9 @@ +HEADERS += \ + $$PWD/devicesocket.h \ + $$PWD/server.h \ + $$PWD/tcpserver.h + +SOURCES += \ + $$PWD/devicesocket.cpp \ + $$PWD/server.cpp \ + $$PWD/tcpserver.cpp diff --git a/src/tcpserver.cpp b/src/server/tcpserver.cpp similarity index 94% rename from src/tcpserver.cpp rename to src/server/tcpserver.cpp index fdc9a64..bddb56d 100644 --- a/src/tcpserver.cpp +++ b/src/server/tcpserver.cpp @@ -1,19 +1,19 @@ -#include "tcpserver.h" -#include "devicesocket.h" - -TcpServer::TcpServer(QObject *parent) : QTcpServer(parent) -{ - -} - -TcpServer::~TcpServer() -{ - -} - -void TcpServer::incomingConnection(qintptr handle) -{ - DeviceSocket *socket = new DeviceSocket(); - socket->setSocketDescriptor(handle); - addPendingConnection(socket); -} +#include "tcpserver.h" +#include "devicesocket.h" + +TcpServer::TcpServer(QObject *parent) : QTcpServer(parent) +{ + +} + +TcpServer::~TcpServer() +{ + +} + +void TcpServer::incomingConnection(qintptr handle) +{ + DeviceSocket *socket = new DeviceSocket(); + socket->setSocketDescriptor(handle); + addPendingConnection(socket); +} diff --git a/src/tcpserver.h b/src/server/tcpserver.h similarity index 94% rename from src/tcpserver.h rename to src/server/tcpserver.h index 7156814..d8e52be 100644 --- a/src/tcpserver.h +++ b/src/server/tcpserver.h @@ -1,17 +1,17 @@ -#ifndef TCPSERVER_H -#define TCPSERVER_H - -#include - -class TcpServer : public QTcpServer -{ - Q_OBJECT -public: - explicit TcpServer(QObject *parent = nullptr); - virtual ~TcpServer(); - -protected: - virtual void incomingConnection(qintptr handle); -}; - -#endif // TCPSERVER_H +#ifndef TCPSERVER_H +#define TCPSERVER_H + +#include + +class TcpServer : public QTcpServer +{ + Q_OBJECT +public: + explicit TcpServer(QObject *parent = nullptr); + virtual ~TcpServer(); + +protected: + virtual void incomingConnection(qintptr handle); +}; + +#endif // TCPSERVER_H