Browse Source

V0.4
1.完成了使用CPU绘制的窗口

Apple 2 weeks ago
parent
commit
41cc7d7257

+ 2 - 5
CMakeLists.txt

@@ -6,7 +6,7 @@ set(CMAKE_AUTOUIC ON)
 set(CMAKE_AUTOMOC ON)
 set(CMAKE_AUTORCC ON)
 
-set(CMAKE_CXX_STANDARD 11)
+set(CMAKE_CXX_STANDARD 17)
 set(CMAKE_CXX_STANDARD_REQUIRED ON)
 
 #=========================================================
@@ -145,10 +145,7 @@ find_package(Qt5 COMPONENTS
     Widgets
     Core
     Network
-    Sql
-    Xml
-    Multimedia
-    MultimediaWidgets
+    # OpenGL
     REQUIRED
 )
 

+ 0 - 25
CPlayer/CVideoPlayer.cpp

@@ -1,25 +0,0 @@
-#include "CVideoPlayer.h"
-#include <QMouseEvent>
-
-
-
-CVideoPlayer::CVideoPlayer(QWidget *parent) : VideoPlayer(parent)
-{
-
-}
-CVideoPlayer::~CVideoPlayer()
-{
-
-}
-
-
-
-/* 双击事件函数 */
-void CVideoPlayer::mouseDoubleClickEvent(QMouseEvent *event)
-{
-    if(event->button() == Qt::LeftButton)
-    {
-        
-    }
-}
-

+ 0 - 25
CPlayer/CVideoPlayer.h

@@ -1,25 +0,0 @@
-#ifndef CVideoPlayer_H
-#define CVideoPlayer_H
-
-#include "VideoPlayer.h"
-
-class CVideoPlayer : public VideoPlayer
-{
-    Q_OBJECT
-public:
-    CVideoPlayer(QWidget *parent = nullptr);
-    ~CVideoPlayer();
-
-
-
-protected:
-    /* 双击事件函数 */
-    void mouseDoubleClickEvent(QMouseEvent *event) override;
-
-private:
-
-};
-
-
-
-#endif // CVideoPlayer_H

+ 149 - 0
CPlayer/Player/PlayerGLWidget(副本).cpp_

@@ -0,0 +1,149 @@
+#include "PlayerGLWidget.h"
+
+
+PlayerGLWidget::PlayerGLWidget(QWidget *parent) : QOpenGLWidget(parent)
+{
+
+}
+
+PlayerGLWidget::~PlayerGLWidget()
+{
+
+}
+
+/* 刷新一帧 */
+void PlayerGLWidget::updateFrame(Image_YUV420& image)
+{
+    yData = image.yData;
+    uData = image.uData;
+    vData = image.vData;
+    update();
+}
+
+
+void PlayerGLWidget::initShaders()
+{
+    // 顶点着色器
+    const char *vshader = R"(
+        #version 330 core
+        layout(location = 0) in vec4 vertexIn;
+        layout(location = 1) in vec2 textureIn;
+        out vec2 textureOut;
+        void main(void)
+        {
+            gl_Position = vertexIn;
+            textureOut = textureIn;
+        })";
+    // 片段着色器(YUV420P 转 RGB)
+    const char *fshader = R"(
+        #version 330 core
+        in vec2 textureOut;
+        out vec4 fragColor;
+        uniform sampler2D tex_y;
+        uniform sampler2D tex_u;
+        uniform sampler2D tex_v;
+        void main(void)
+        {
+            float y = texture(tex_y, textureOut).r;
+            float u = texture(tex_u, textureOut).r - 0.5;
+            float v = texture(tex_v, textureOut).r - 0.5;
+            float r = y + 1.403 * v;
+            float g = y - 0.344 * u - 0.714 * v;
+            float b = y + 1.770 * u;
+            fragColor = vec4(r, g, b, 1.0);
+        })";
+    // 创建着色器程序
+    program.addShaderFromSourceCode(QOpenGLShader::Vertex, vshader);
+    program.addShaderFromSourceCode(QOpenGLShader::Fragment, fshader);
+    program.link();
+}
+
+void PlayerGLWidget::initTextures()
+{
+    // 创建 YUV 纹理
+        textureY = new QOpenGLTexture(QOpenGLTexture::Target2D);
+        textureU = new QOpenGLTexture(QOpenGLTexture::Target2D);
+        textureV = new QOpenGLTexture(QOpenGLTexture::Target2D);
+
+        textureY->create();
+        textureU->create();
+        textureV->create();
+}
+
+// void PlayerGLWidget::initVertex()
+// {
+
+// }
+
+// void PlayerGLWidget::initFrameBuffer()
+// {
+
+// }
+
+void PlayerGLWidget::renderYUV420P()
+{
+    if (yData.isEmpty() || uData.isEmpty() || vData.isEmpty())
+            return;
+
+        // 绑定纹理
+        textureY->bind(0);
+        textureU->bind(1);
+        textureV->bind(2);
+
+        // 上传 YUV 数据到纹理
+        textureY->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yData.constData());
+        textureU->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, uData.constData());
+        textureV->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, vData.constData());
+
+        // 绑定着色器程序
+        program.bind();
+        program.setUniformValue("tex_y", 0);
+        program.setUniformValue("tex_u", 1);
+        program.setUniformValue("tex_v", 2);
+
+        // 绘制矩形
+        static const GLfloat vertices[] = {
+            -1.0f, -1.0f, 0.0f,
+             1.0f, -1.0f, 0.0f,
+            -1.0f,  1.0f, 0.0f,
+             1.0f,  1.0f, 0.0f
+        };
+
+        static const GLfloat texCoords[] = {
+            0.0f, 1.0f,
+            1.0f, 1.0f,
+            0.0f, 0.0f,
+            1.0f, 0.0f
+        };
+
+        glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
+        glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
+        glEnableVertexAttribArray(0);
+        glEnableVertexAttribArray(1);
+
+        glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
+
+        program.release();
+}
+
+void PlayerGLWidget::initializeGL()
+{
+    initializeOpenGLFunctions();
+    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+    // 初始化着色器
+    initShaders();
+    initTextures();
+}
+
+void PlayerGLWidget::resizeGL(int w, int h)
+{
+    glViewport(0, 0, w, h);
+}
+
+void PlayerGLWidget::paintGL()
+{
+    glClear(GL_COLOR_BUFFER_BIT);
+
+    // 渲染 YUV 数据
+    renderYUV420P();
+}

+ 54 - 0
CPlayer/Player/PlayerGLWidget(副本).h_

@@ -0,0 +1,54 @@
+#ifndef PLAYEROPENGLWIDGET_H
+#define PLAYEROPENGLWIDGET_H
+
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLTexture>
+#include <QTimer>
+
+#include "PlayerGlobalInfo.h"
+
+
+class PlayerGLWidget : public QOpenGLWidget , public QOpenGLFunctions
+{
+    Q_OBJECT
+public:
+    explicit PlayerGLWidget(QWidget *parent = nullptr);
+    ~PlayerGLWidget();
+
+    /* 刷新一帧 */
+    void updateFrame(Image_YUV420& image);
+
+
+private:
+    void initShaders();
+    void initTextures();
+    // void initVertex();
+    // void initFrameBuffer();
+
+    void renderYUV420P();
+
+protected:
+    void initializeGL() override;
+    void resizeGL(int w, int h) override;
+    void paintGL() override;
+
+private:
+
+    QOpenGLShaderProgram program;
+    QOpenGLTexture *textureY = nullptr;
+    QOpenGLTexture *textureU = nullptr;
+    QOpenGLTexture *textureV = nullptr;
+
+    QByteArray yData;
+    QByteArray uData;
+    QByteArray vData;
+
+    int width = 0;
+    int height = 0;
+
+};
+
+#endif /* PLAYEROPENGLWIDGET_H */

+ 67 - 104
CPlayer/Player/PlayerGLWidget.cpp

@@ -14,128 +14,91 @@ PlayerGLWidget::~PlayerGLWidget()
 /* 刷新一帧 */
 void PlayerGLWidget::updateFrame(Image_YUV420& image)
 {
-    yData = image.yData;
-    uData = image.uData;
-    vData = image.vData;
+    // yData = image.yData;
+    // uData = image.uData;
+    // vData = image.vData;
     update();
 }
 
-
-void PlayerGLWidget::initShaders()
-{
-    // 顶点着色器
-    const char *vshader =
-        "#version 330 core\n"
-        "layout(location = 0) in vec4 vertexIn;\n"
-        "layout(location = 1) in vec2 textureIn;\n"
-        "out vec2 textureOut;\n"
-        "void main(void)\n"
-        "{\n"
-        "    gl_Position = vertexIn;\n"
-        "    textureOut = textureIn;\n"
-        "}\n";
-    // 片段着色器(YUV420P 转 RGB)
-    const char *fshader =
-        "#version 330 core\n"
-        "in vec2 textureOut;\n"
-        "out vec4 fragColor;\n"
-        "uniform sampler2D tex_y;\n"
-        "uniform sampler2D tex_u;\n"
-        "uniform sampler2D tex_v;\n"
-        "void main(void)\n"
-        "{\n"
-        "    float y = texture(tex_y, textureOut).r;\n"
-        "    float u = texture(tex_u, textureOut).r - 0.5;\n"
-        "    float v = texture(tex_v, textureOut).r - 0.5;\n"
-        "    float r = y + 1.403 * v;\n"
-        "    float g = y - 0.344 * u - 0.714 * v;\n"
-        "    float b = y + 1.770 * u;\n"
-        "    fragColor = vec4(r, g, b, 1.0);\n"
-        "}\n";
-    // 创建着色器程序
-    program.addShaderFromSourceCode(QOpenGLShader::Vertex, vshader);
-    program.addShaderFromSourceCode(QOpenGLShader::Fragment, fshader);
-    program.link();
-}
-
-void PlayerGLWidget::initTextures()
-{
-    // 创建 YUV 纹理
-    textureY = new QOpenGLTexture(QOpenGLTexture::Target2D);
-    textureU = new QOpenGLTexture(QOpenGLTexture::Target2D);
-    textureV = new QOpenGLTexture(QOpenGLTexture::Target2D);
-    textureY->create();
-    textureU->create();
-    textureV->create();
-}
-
-void PlayerGLWidget::initVertex()
-{
-
-}
-
-void PlayerGLWidget::initFrameBuffer()
+/* 刷新一帧QImage */
+void PlayerGLWidget::updateFrame(Image_QImage& image)
 {
+    m_image = image;
 
-}
-
-void PlayerGLWidget::renderYUV420P()
-{
-    if (yData.isEmpty() || uData.isEmpty() || vData.isEmpty())
-        return;
-    // 绑定纹理
-    textureY->bind(0);
-    textureU->bind(1);
-    textureV->bind(2);
-    // 上传 YUV 数据到纹理
-    textureY->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yData.constData());
-    textureU->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, uData.constData());
-    textureV->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, vData.constData());
-    // 绑定着色器程序
-    program.bind();
-    program.setUniformValue("tex_y", 0);
-    program.setUniformValue("tex_u", 1);
-    program.setUniformValue("tex_v", 2);
-    // 绘制矩形
-    static const GLfloat vertices[] = {
-        -1.0f, -1.0f, 0.0f,
-         1.0f, -1.0f, 0.0f,
-        -1.0f,  1.0f, 0.0f,
-         1.0f,  1.0f, 0.0f
-    };
-    static const GLfloat texCoords[] = {
-        0.0f, 1.0f,
-        1.0f, 1.0f,
-        0.0f, 0.0f,
-        1.0f, 0.0f
-    };
-    glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 0, vertices);
-    glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
-    glEnableVertexAttribArray(0);
-    glEnableVertexAttribArray(1);
-    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
-    program.release();
+    imageData_ = m_image.image.bits();
+    imageSize_.setWidth(m_image.width);
+    imageSize_.setHeight(m_image.height);
+    update();
 }
 
 void PlayerGLWidget::initializeGL()
 {
     initializeOpenGLFunctions();
-    glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
+    texture_ = new QOpenGLTexture(QOpenGLTexture::Target2D);
+
+    texture_->create();
+    textureId_ = texture_->textureId();
+    glBindTexture(GL_TEXTURE_2D, textureId_);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
+    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
 
-    // 初始化着色器
-    initShaders();
-    initTextures();
 }
 
 void PlayerGLWidget::resizeGL(int w, int h)
 {
-    glViewport(0, 0, w, h);
+    Ortho2DSize_.setWidth(w);
+    Ortho2DSize_.setHeight(h);
+    glViewport(0,0, w, h);
+    glMatrixMode(GL_PROJECTION);
+    glLoadIdentity();
+    glOrtho(0, Ortho2DSize_.width(), Ortho2DSize_.height(), 0, -1, 1);
+    glMatrixMode(GL_MODELVIEW);
+
 }
 
 void PlayerGLWidget::paintGL()
 {
-    glClear(GL_COLOR_BUFFER_BIT);
+    static bool initTextureFlag = false;
+    // initializeOpenGLFunctions(); // 初始化 OpenGL 函数
+
+    glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
+
+    if(imageData_ == nullptr){
+        return;
+    }
+
+    //QByteArray ba((const char*)imageData_,64);
+    //qDebug() <<imageData_ << ba;
+    glBindTexture(GL_TEXTURE_2D, textureId_);
+
+    if(!initTextureFlag){
+        // 首次显示纹理
+        glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageSize_.width(), imageSize_.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData_);
+        initTextureFlag = true;
+    }
+    else{
+        // 动态修改纹理数据
+        glTexSubImage2D(GL_TEXTURE_2D, 0, 0, 0, imageSize_.width(), imageSize_.height(), GL_RGBA, GL_UNSIGNED_BYTE, imageData_);
+        //glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, imageSize_.width(), imageSize_.height(), 0, GL_RGBA, GL_UNSIGNED_BYTE, imageData_);
+    }
+
+    glEnable(GL_TEXTURE_2D);
+    glBegin(GL_POLYGON);
+
+    //顶点坐标和纹理坐标必须一一对应
+    glTexCoord2d(0.0f, 0.0f);
+    glVertex2d(0, 0);
+    glTexCoord2d(0.0f, 1.0f);
+    glVertex2d(0, Ortho2DSize_.height());
+    glTexCoord2d(1.0f, 1.0f);
+    glVertex2d(Ortho2DSize_.width(), Ortho2DSize_.height());
+    glTexCoord2d(1.0f, 0.0f);
+    glVertex2d(Ortho2DSize_.width(), 0);
+
+    glEnd();
+    glDisable(GL_TEXTURE_2D);
+
+    
 
-    // 渲染 YUV 数据
-    renderYUV420P();
 }

+ 33 - 22
CPlayer/Player/PlayerGLWidget.h

@@ -6,53 +6,64 @@
 #include <QOpenGLFunctions>
 #include <QOpenGLShaderProgram>
 #include <QOpenGLTexture>
-#include <QTimer>
 
 #include "PlayerGlobalInfo.h"
 
+/**
+ * @brief 
+ * 
+ */
+
 
 class PlayerGLWidget : public QOpenGLWidget , public QOpenGLFunctions
 {
     Q_OBJECT
+
+    enum
+    {
+        Left_Bottom_X,
+        Left_Bottom_Y,
+        Right_Bottom_X,
+        Right_Bottom_Y,
+        Right_Top_X,
+        Right_Top_Y,
+        Left_Top_X,
+        Left_Top_Y,
+        Pos_Max
+    };
+
+
 public:
     explicit PlayerGLWidget(QWidget *parent = nullptr);
     ~PlayerGLWidget();
 
     /* 刷新一帧 */
     void updateFrame(Image_YUV420& image);
+    /* 刷新一帧QImage */
+    void updateFrame(Image_QImage& image);
 
-public slots:
-    void updateYUVData(const QByteArray &y, const QByteArray &u, const QByteArray &v) {
-        yData = y;
-        uData = u;
-        vData = v;
-        update();
-    }
 
 private:
-    void initShaders();
-    void initTextures();
-    void initVertex();
-    void initFrameBuffer();
-
-    void renderYUV420P();
 
 protected:
     void initializeGL() override;
     void resizeGL(int w, int h) override;
     void paintGL() override;
 
-private:
+    // void setImage(const QImage &image);
 
-    QOpenGLShaderProgram program;
-    QOpenGLTexture *textureY = nullptr;
-    QOpenGLTexture *textureU = nullptr;
-    QOpenGLTexture *textureV = nullptr;
 
-    QByteArray yData;
-    QByteArray uData;
-    QByteArray vData;
+private:
+
+    uchar* imageData_;           //纹理显示的数据源
+    QSize imageSize_;            //图片尺寸
+    QSize Ortho2DSize_;          //窗口尺寸
+    QOpenGLTexture* texture_;
+    GLuint textureId_;           //纹理对象ID
+    int vertexPos_[Pos_Max];     //窗口坐标
+    float texturePos_[Pos_Max];  //纹理坐标
 
+    Image_QImage m_image;
 };
 
 #endif /* PLAYEROPENGLWIDGET_H */

+ 137 - 0
CPlayer/Player/PlayerGLWidget2.cpp_

@@ -0,0 +1,137 @@
+#include "PlayerGLWidget.h"
+
+
+PlayerGLWidget::PlayerGLWidget(QWidget *parent) : QOpenGLWidget(parent)
+{
+
+}
+
+PlayerGLWidget::~PlayerGLWidget()
+{
+
+}
+
+/* 刷新一帧 */
+void PlayerGLWidget::updateFrame(Image_YUV420& image)
+{
+    // yData = image.yData;
+    // uData = image.uData;
+    // vData = image.vData;
+    update();
+}
+
+/* 刷新一帧QImage */
+void PlayerGLWidget::updateFrame(Image_QImage& image)
+{
+    m_image = image;
+
+    texture->setData(m_image.image);
+    /* 设置纹理细节 */
+    texture->setLevelofDetailBias(-1);
+    update();
+}
+
+
+void PlayerGLWidget::initShaders()
+{
+    //纹理坐标
+    texCoords.append(QVector2D(0, 1)); //左上
+    texCoords.append(QVector2D(1, 1)); //右上
+    texCoords.append(QVector2D(0, 0)); //左下
+    texCoords.append(QVector2D(1, 0)); //右下
+    //顶点坐标
+    vertices.append(QVector3D(-1, -1, 1));//左下
+    vertices.append(QVector3D(1, -1, 1)); //右下
+    vertices.append(QVector3D(-1, 1, 1)); //左上
+    vertices.append(QVector3D(1, 1, 1));  //右上
+    QOpenGLShader *vshader = new QOpenGLShader(QOpenGLShader::Vertex, this);
+    const char *vsrc =
+            "attribute vec4 vertex;\n"
+            "attribute vec2 texCoord;\n"
+            "varying vec2 texc;\n"
+            "void main(void)\n"
+            "{\n"
+            "    gl_Position = vertex;\n"
+            "    texc = texCoord;\n"
+            "}\n";
+    vshader->compileSourceCode(vsrc);//编译顶点着色器代码
+ 
+    QOpenGLShader *fshader = new QOpenGLShader(QOpenGLShader::Fragment, this);
+    const char *fsrc =
+            "uniform sampler2D texture;\n"
+            "varying vec2 texc;\n"
+            "void main(void)\n"
+            "{\n"
+            "    gl_FragColor = texture2D(texture,texc);\n"
+            "}\n";
+    fshader->compileSourceCode(fsrc); //编译纹理着色器代码
+ 
+    program.addShader(vshader);//添加顶点着色器
+    program.addShader(fshader);//添加纹理碎片着色器
+    program.bindAttributeLocation("vertex", 0);//绑定顶点属性位置
+    program.bindAttributeLocation("texCoord", 1);//绑定纹理属性位置
+    // 链接着色器管道
+    if (!program.link())
+        close();
+    // 绑定着色器管道
+    if (!program.bind())
+        close();
+}
+
+void PlayerGLWidget::initTextures()
+{
+    // 加载 Avengers.jpg 图片
+    texture = new QOpenGLTexture(QOpenGLTexture::Target2D);
+    texture->setMinificationFilter(QOpenGLTexture::LinearMipMapLinear);
+    texture->setMagnificationFilter(QOpenGLTexture::Linear);
+    //重复使用纹理坐标
+    //纹理坐标(1.1, 1.2)与(0.1, 0.2)相同
+    texture->setWrapMode(QOpenGLTexture::Repeat);
+    //设置纹理大小
+    texture->setSize(this->width(), this->height());
+    //分配储存空间
+    texture->allocateStorage();
+
+}
+
+
+void PlayerGLWidget::initializeGL()
+{
+    initializeOpenGLFunctions(); //初始化OPenGL功能函数
+    glClearColor(0, 0, 0, 0);    //设置背景为黑色
+    glEnable(GL_TEXTURE_2D);     //设置纹理2D功能可用
+    initTextures();              //初始化纹理设置
+    initShaders();               //初始化shaders
+
+}
+
+void PlayerGLWidget::resizeGL(int w, int h)
+{
+    // 计算窗口横纵比
+    qreal aspect = qreal(w) / qreal(h ? h : 1);
+    // 设置近平面值 3.0, 远平面值 7.0, 视场45度
+    const qreal zNear = 3.0, zFar = 7.0, fov = 45.0;
+    // 重设投影
+    projection.setToIdentity();
+    // 设置透视投影
+    projection.perspective(fov, static_cast<float>(aspect), zNear, zFar);
+
+}
+
+void PlayerGLWidget::paintGL()
+{
+    glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); //清除屏幕缓存和深度缓冲
+    QMatrix4x4 matrix;
+    matrix.translate(0.0, 0.0, -5.0);                   //矩阵变换
+    program.enableAttributeArray(0);
+    program.enableAttributeArray(1);
+    program.setAttributeArray(0, vertices.constData());
+    program.setAttributeArray(1, texCoords.constData());
+    program.setUniformValue("texture", 0); //将当前上下文中位置的统一变量设置为value
+    texture->bind();  //绑定纹理
+    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);//绘制纹理
+    texture->release(); //释放绑定的纹理
+    texture->destroy(); //消耗底层的纹理对象
+    texture->create();
+
+}

+ 55 - 0
CPlayer/Player/PlayerGLWidget2.h_

@@ -0,0 +1,55 @@
+#ifndef PLAYEROPENGLWIDGET_H
+#define PLAYEROPENGLWIDGET_H
+
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions>
+#include <QOpenGLShaderProgram>
+#include <QOpenGLTexture>
+#include <QTimer>
+
+#include "PlayerGlobalInfo.h"
+
+/**
+ * @brief 可以播放,但是CPU占用率比纯CPU绘制还高
+ * 
+ */
+
+
+class PlayerGLWidget : public QOpenGLWidget , public QOpenGLFunctions
+{
+    Q_OBJECT
+public:
+    explicit PlayerGLWidget(QWidget *parent = nullptr);
+    ~PlayerGLWidget();
+
+    /* 刷新一帧 */
+    void updateFrame(Image_YUV420& image);
+    /* 刷新一帧QImage */
+    void updateFrame(Image_QImage& image);
+
+
+private:
+    void initShaders();
+    void initTextures();
+
+protected:
+    void initializeGL() override;
+    void resizeGL(int w, int h) override;
+    void paintGL() override;
+
+    // void setImage(const QImage &image);
+
+
+private:
+
+    QVector<QVector3D> vertices;
+    QVector<QVector2D> texCoords;
+    QOpenGLShaderProgram program;
+    QOpenGLTexture *texture;
+    QMatrix4x4 projection;
+
+    Image_QImage m_image;
+};
+
+#endif /* PLAYEROPENGLWIDGET_H */

+ 58 - 6
CPlayer/Player/PlayerWidget.cpp

@@ -1,6 +1,8 @@
 #include "PlayerWidget.h"
 
+#include <algorithm>
 #include <QResizeEvent>
+#include <QPainter>
 
 
 PlayerWidget::PlayerWidget(QWidget *parent)
@@ -14,17 +16,28 @@ PlayerWidget::~PlayerWidget()
 }
 
 /* 刷新一帧 */
-void PlayerWidget::updateFrame(Image_YUV420& image)
+void PlayerWidget::updateFrame(Image_YUV420& yuvData)
 {
-    m_image = image;
+    YUV420ToQImage(yuvData, m_image);
     if(m_windowSize.width() == 0 || m_windowSize.height() == 0)
     {
-        m_windowSize.setWidth(image.width);
-        m_windowSize.setHeight(image.height);
+        m_imageWidth = yuvData.width;
+        m_imageHeight = yuvData.height;
     }
     update();
 }
 
+void PlayerWidget::updateFrame(Image_QImage& imageData)
+{
+    if(m_windowSize.width() == 0 || m_windowSize.height() == 0)
+    {
+        m_imageWidth = imageData.width;
+        m_imageHeight = imageData.height;
+    }
+    m_image = std::move(imageData.image);
+    update();
+}
+
 
 /* 绘画事件 */
 void PlayerWidget::paintEvent(QPaintEvent *event)
@@ -32,12 +45,12 @@ void PlayerWidget::paintEvent(QPaintEvent *event)
 
     // SPDLOG_TRACE("开始绘制画面...");
     /* 对图像进行缩放 */
-    if(m_windowSize.width() != m_image.width || m_windowSize.height() != m_image.height)
+    if(m_windowSize.width() != m_imageWidth || m_windowSize.height() != m_imageHeight)
     {
         m_image = m_image.scaled(m_windowSize.width(), m_windowSize.height(), Qt::KeepAspectRatio, Qt::SmoothTransformation);
     }
     QPainter painter(this);
-    painter.drawImage(0, 0, *m_image);
+    painter.drawImage(0, 0, m_image);
 }
 
 /* 重新设置大小事件 */
@@ -49,3 +62,42 @@ void PlayerWidget::resizeEvent(QResizeEvent *event)
     QWidget::resizeEvent(event);
 }
 
+/* YUV420转QImage */
+void PlayerWidget::YUV420ToQImage(Image_YUV420& yuvData, QImage& image)
+{
+    int width = yuvData.width;
+    int height = yuvData.height;
+    int frameSize = yuvData.width * yuvData.height;
+    int chromaSize = frameSize / 4;
+
+    QImage image_rgb(width, height, QImage::Format_RGB888);
+
+    for (int y = 0; y < height; ++y) {
+        for (int x = 0; x < width; ++x) {
+            int yIndex = y * width + x;
+            int uIndex = (y / 2) * (width / 2) + (x / 2);
+            int vIndex = uIndex;
+
+            uint8_t Y = yuvData.yData.at(yIndex);
+            uint8_t U = yuvData.uData[uIndex];
+            uint8_t V = yuvData.vData[vIndex];
+
+            int C = Y - 16;
+            int D = U - 128;
+            int E = V - 128;
+
+            int R = (298 * C + 409 * E + 128) >> 8;
+            int G = (298 * C - 100 * D - 208 * E + 128) >> 8;
+            int B = (298 * C + 516 * D + 128) >> 8;
+
+            R = std::clamp(R, 0, 255);
+            G = std::clamp(G, 0, 255);
+            B = std::clamp(B, 0, 255);
+
+            image_rgb.setPixel(x, y, qRgb(R, G, B));
+        }
+    }
+
+    image = image_rgb;
+}
+

+ 11 - 2
CPlayer/Player/PlayerWidget.h

@@ -12,7 +12,8 @@ public:
     ~PlayerWidget();
 
     /* 刷新一帧 */
-    void updateFrame(Image_YUV420& image);
+    void updateFrame(Image_YUV420& yuvData);
+    void updateFrame(Image_QImage& imageData);
 
 protected:
     /* 绘画事件 */
@@ -21,7 +22,15 @@ protected:
     void resizeEvent(QResizeEvent *event) override;
 
 private:
-    Image_YUV420 m_image;
+    /* YUV420转QImage */
+    void YUV420ToQImage(Image_YUV420& yuvData, QImage& image);
+
+private:
+
+    int m_imageWidth = 0;
+    int m_imageHeight = 0;
+    QImage m_image;
+    // Image_QImage m_image;
 
     QSize m_windowSize;         /* 窗口大小 */
 };

+ 42 - 0
CPlayer/PlayerGlobalInfo.cpp

@@ -32,3 +32,45 @@ Image_YUV420& Image_YUV420::operator=(const Image_YUV420& other)
     height = other.height;
     return *this;
 }
+
+Image_QImage::Image_QImage()
+{
+    width = 0;
+    height = 0;
+
+    /* 设置为RGB888 */
+    image =  QImage(1, 1, QImage::Format_RGB888);
+}
+
+/* 移动构造函数 */
+Image_QImage::Image_QImage(Image_QImage&& other)
+{
+    width = other.width;
+    height = other.height;
+    image = std::move(other.image);
+}
+
+/* 拷贝构造函数 */
+Image_QImage::Image_QImage(const Image_QImage& other)
+{
+    width = other.width;
+    height = other.height;
+    image = other.image;
+}
+
+/* 重载= */
+Image_QImage& Image_QImage::operator=(const Image_QImage& other)
+{
+    width = other.width;
+    height = other.height;
+    image = other.image;
+    return *this;
+}
+
+/* 移动幅值函数 */
+void Image_QImage::moveFrom(Image_QImage& other)
+{
+    width = other.width;
+    height = other.height;
+    image = std::move(other.image);
+}

+ 19 - 0
CPlayer/PlayerGlobalInfo.h

@@ -3,6 +3,7 @@
 
 // #include <vector>
 #include <QByteArray>
+#include <QImage>
 
 /**
  * @brief 一帧YUV420的图片
@@ -27,6 +28,24 @@ struct Image_YUV420
     Image_YUV420& operator=(const Image_YUV420& other);
 };
 
+struct Image_QImage
+{
+    QImage image;
+    int width;
+    int height;
+
+    Image_QImage();
+    ~Image_QImage() {}
+    /* 移动构造函数 */
+    Image_QImage(Image_QImage&& other);
+    /* 拷贝构造函数 */
+    Image_QImage(const Image_QImage& other);
+    /* 重载= */
+    Image_QImage& operator=(const Image_QImage& other);
+    /* 移动幅值函数 */
+    void moveFrom(Image_QImage& other);
+};
+
 
 
 #endif /* PLAYERGLOBALINFO_H */

+ 46 - 120
CPlayer/cameraplayer.cpp

@@ -4,16 +4,19 @@
 #include <QDebug>
 
 #include "LHQLogAPI.h"
+#include "Transcode.h"
 
 
 CameraPlayer::CameraPlayer(QObject *parent) : QObject(parent)
 {
     // qDebug() << "主线程ID:" << QThread::currentThreadId();
 
-    m_yuvQueue = new RingQueue<Image_YUV420*>(10);
-    m_yuvQueue->setDefaultValue(nullptr);
+    // m_imageQueue = new RingQueue<Image_YUV420*>(10);
+    m_imageQueue.setQueueCapacity(10);
+    m_imageQueue.setDefaultValue(nullptr);
 
-    m_player = new PlayerGLWidget();
+    // m_player = new PlayerGLWidget();
+    m_player = new PlayerWidget();
     /* 连接信号和槽 */
     connect(&m_frameTimer, &QTimer::timeout, this, &CameraPlayer::do_updateFrame);
 }
@@ -117,6 +120,12 @@ bool CameraPlayer::realPlay(int channel)
     // NET_DVR_SetStandardDataCallBack(m_realPlayHandle, realDataCallBackStandard, 0);
     // NET_DVR_SetRealDataCallBackEx(m_realPlayHandle, realDataCallBack, nullptr);
 
+    /* 创建转码实例 */
+    if(m_transCode == nullptr)
+    {
+        m_transCode = new TransCode();
+    }
+
     return true;
 }
 
@@ -158,13 +167,13 @@ void CameraPlayer::setPlayWndSize(int width, int height)
 void CameraPlayer::do_updateFrame()
 {
     /* 获取一帧 */
-    if(m_yuvQueue->isEmpty())
+    if(m_imageQueue.isEmpty())
     {
-        LH_WRITE_LOG("环形队列为空,无法更新一帧数据");
+        // LH_WRITE_LOG("图像环形队列为空,无法更新一帧数据");
         return;
     }
     /* 以非阻塞的方式先获取一帧数据,获取成功后再出队 */
-    auto one = m_yuvQueue->front_pop_NoBlock();
+    auto one = m_imageQueue.front_pop_NoBlock();
     if(one != nullptr)
     {
         m_player->updateFrame(*one);
@@ -246,7 +255,7 @@ void CameraPlayer::realDataCallBack(LONG realHandle, DWORD dataType, BYTE *pBuff
             {
                 cameraPlayer->m_playPort = playPort;
                 /* 最后一个参数是 设置播放器中存放数据流的缓冲区大小,不能太大也不能太小 */
-                if(!PlayM4_OpenStream(playPort, pBuffer, bufSize, 1024 * 1024))
+                if(!PlayM4_OpenStream(playPort, pBuffer, bufSize, 1024 * 1024 * 10))
                 {
                     auto ret  = PlayM4_GetLastError(playPort);
                     LH_WRITE_ERROR(QString("打开流失败,错误代码: %1").arg(ret));
@@ -285,10 +294,18 @@ void CameraPlayer::realDataCallBack(LONG realHandle, DWORD dataType, BYTE *pBuff
             if(bufSize > 0 && playPort != -1)
             {
                 auto ret = PlayM4_InputData(playPort, pBuffer, bufSize);
-                if(!ret)
+                while(!ret)
                 {
-                    LH_WRITE_ERROR("解码数据失败");
-                    break;
+                    auto errNum = PlayM4_GetLastError(playPort);
+                    if(errNum == PLAYM4_BUF_OVER)
+                    {
+                        LH_WRITE_ERROR("数据解码失败, 缓冲区满, 需要重新输入");
+                        /* 重新发送给解码器 */
+                        ret = PlayM4_InputData(playPort, pBuffer, bufSize);
+                    }else {
+                        LH_WRITE_ERROR(QString("数据解码失败, 其他错误: %1").arg(errNum));
+                    }
+                    // std::this_thread::sleep_for(std::chrono::milliseconds(1));
                 }
             }
             break; 
@@ -344,6 +361,9 @@ void CameraPlayer::realDataCallBackStandard(LONG realHandle, DWORD dataType, BYT
  */
 void CameraPlayer::DecCallBack(int nPort,char * pBuf,int nSize,FRAME_INFO * pFrameInfo, void* nUser,int nReserved2)
 {
+    /* 获取当前时间 */
+    // auto now = std::chrono::system_clock::now();
+   
     auto player = static_cast<CameraPlayer*>(nUser);
     // LH_WRITE_LOG(QString("解码回调函数,解码通道号: %1 %2").arg(nPort).arg(player->m_playPort));
     // qDebug() << "DecCallBack所在的线程:" << QThread::currentThreadId();
@@ -361,21 +381,26 @@ void CameraPlayer::DecCallBack(int nPort,char * pBuf,int nSize,FRAME_INFO * pFra
         }
         int width = pFrameInfo->nWidth;
         int height = pFrameInfo->nHeight;
-        LH_WRITE_LOG_DEBUG(QString("视频宽高: %1 x %2, 视频帧率: %3").arg(width).arg(height).arg(frameRate));
+
+        LH_WRITE_LOG_DEBUG(QString("视频宽高: %1 x %2, 视频帧率: %3, 当前队列数目: %4").arg(width).arg(height).arg(frameRate).arg(player->m_imageQueue.QueueSize()));
 
         /* 截图标志位,保存图片 */
 
         /* 转换成yuv,保存到环形队列中 */
-        Image_YUV420* image = new Image_YUV420();
-        player->YV12ToYUV420((unsigned char*)pBuf, *image, width, height);
+        // Image_YUV420* image = new Image_YUV420();
+        // player->m_transCode->YV12ToYUV420((unsigned char*)pBuf, *image, width, height);
+        
+        Image_QImage* image = new Image_QImage();
+        player->m_transCode->YV12ToQImage((unsigned char*)pBuf, *image, width, height);
+
         /* 判断环形队列是否满了,满了就出队一个 */
-        if(player->m_yuvQueue->isFull())
+        if(player->m_imageQueue.isFull())
         {
-            LH_WRITE_LOG_DEBUG("环形队列满了,出队一个");
-            auto one = player->m_yuvQueue->front_pop();
+            // LH_WRITE_LOG_DEBUG("图像环形队列满了,出队一个");
+            auto one = player->m_imageQueue.front_pop();
             delete one;
         }
-        player->m_yuvQueue->push_NoBlock(image);
+        player->m_imageQueue.push_NoBlock(image);
     }
     else if(pFrameInfo->nType == T_AUDIO16)
     {
@@ -384,109 +409,10 @@ void CameraPlayer::DecCallBack(int nPort,char * pBuf,int nSize,FRAME_INFO * pFra
     {
         LH_WRITE_LOG("其他数据");
     }
-}
-
 
-/* YV12转RGB888 */
-void CameraPlayer::YV12ToRGB888(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height)
-{
-    int ySize = width * height;
-    int uvSize = ySize / 4;
-
-    const uint8_t* yData = pYV12Data;
-    const uint8_t* vData = pYV12Data + ySize;
-    const uint8_t* uData = pYV12Data + ySize + uvSize;
-
-    for (int y = 0; y < height; y++) {
-        for (int x = 0; x < width; x++) {
-            int yIndex = y * width + x;
-            int uvIndex = (y / 2) * (width / 2) + (x / 2);
-
-            uint8_t Y = yData[yIndex];
-            uint8_t U = uData[uvIndex];
-            uint8_t V = vData[uvIndex];
-
-            // YUV 转 RGB
-            int R = Y + 1.403 * (V - 128);
-            int G = Y - 0.344 * (U - 128) - 0.714 * (V - 128);
-            int B = Y + 1.770 * (U - 128);
-
-            // 限制范围 [0, 255]
-            R = std::min(std::max(R, 0), 255);
-            G = std::min(std::max(G, 0), 255);
-            B = std::min(std::max(B, 0), 255);
-
-            // 存储 RGB 数据
-            int rgbIndex = yIndex * 3;
-            pRGB24Data[rgbIndex]     = static_cast<uint8_t>(R);
-            pRGB24Data[rgbIndex + 1] = static_cast<uint8_t>(G);
-            pRGB24Data[rgbIndex + 2] = static_cast<uint8_t>(B);
-        }
-    }
-}
-
-
-/* YV12转YUV420 */
-// void CameraPlayer::YV12ToYUV420(unsigned char *pYV12Data, Image_YUV420& yuvData, int width, int height)
-// {
-    // int y_size = width * height;
-    // int uv_size = y_size / 4;
-
-    // // 分配 YUV 数据的空间
-    // yuvData.yData.resize(y_size);
-    // yuvData.uData.resize(uv_size);
-    // yuvData.vData.resize(uv_size);
-
-    // // 提取 Y、V、U 分量
-    // const uint8_t* y = vecYUV.data();
-    // const uint8_t* v = vecYUV.data() + y_size;
-    // const uint8_t* u = vecYUV.data() + y_size + uv_size;
-
-    // // 复制 Y 分量
-    // std::memcpy(vecYUV.data(), y, y_size);
-
-    // // 复制 U 分量
-    // uint8_t* yuv_u = vecYUV.data() + y_size;
-    // for (int i = 0; i < uv_size; ++i) {
-    //     yuv_u[i] = u[i];
-    // }
-
-    // // 复制 V 分量
-    // uint8_t* yuv_v = vecYUV.data() + y_size + uv_size;
-    // for (int i = 0; i < uv_size; ++i) {
-    //     yuv_v[i] = v[i];
-    // }
-// }
-
-void CameraPlayer::YV12ToYUV420(unsigned char *pYV12Data, Image_YUV420& yuvData, int width, int height)
-{
-    int y_size = width * height;
-    int uv_size = y_size / 4;
-
-    // 分配 YUV 数据的空间
-    yuvData.yData.resize(y_size);
-    yuvData.uData.resize(uv_size);
-    yuvData.vData.resize(uv_size);
-
-    // 提取 Y、V、U 分量
-    const uint8_t* y = pYV12Data;
-    const uint8_t* v = pYV12Data + y_size;
-    const uint8_t* u = pYV12Data + y_size + uv_size;
-
-    // 复制 Y 分量
-    // std::memcpy(yuvData.yData.data(), y, y_size);
-    yuvData.yData = QByteArray((const char*)y, y_size);
-
-    // 复制 U 分量
-    // std::memcpy(yuvData.uData.data(), u, uv_size);
-    yuvData.uData.append((const char*)u, uv_size);
-
-    // 复制 V 分量
-    // std::memcpy(yuvData.vData.data(), v, uv_size);
-    yuvData.vData.append((const char*)v, uv_size);
-
-    /* 设置宽和高 */
-    yuvData.width = width;
-    yuvData.height = height;
+    /* 计算解码时间 */
+    // auto lastTime = std::chrono::system_clock::now();
+    // auto duration = std::chrono::duration_cast<std::chrono::milliseconds>(lastTime - now);
+    // LH_WRITE_LOG_DEBUG(QString("解码时间: %1 ms").arg(duration.count()));
 }
 

+ 12 - 7
CPlayer/cameraplayer.h

@@ -9,6 +9,9 @@
 #include "RingQueue.hpp"
 #include "PlayerGlobalInfo.h"
 #include "PlayerGLWidget.h"
+#include "PlayerWidget.h"
+
+class TransCode;
 
 /**
  * @brief 登录设备返回的设备信息
@@ -42,6 +45,7 @@ public:
     void setPlayerParent(QWidget* playWnd);
     /* 设置播放窗口大小 */
     void setPlayWndSize(int width, int height);
+    void setPlayWndSize(QSize size) { setPlayWndSize(size.width(), size.height()); }
     /* 获取播放窗口指针 */
     QWidget* getPlayWnd() { return m_player; }
 
@@ -59,10 +63,6 @@ private:
     /* 解码回调函数 */
     static void DecCallBack(int nPort,char * pBuf,int nSize,FRAME_INFO * pFrameInfo, void* nUser,int nReserved2);
 
-    /* YV12转RGB888 */
-    static void YV12ToRGB888(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height);
-    /* YV12转YUV */
-    static void YV12ToYUV420(unsigned char *pYV12Data, Image_YUV420& yuvData, int width, int height);
 
 private:
 
@@ -74,10 +74,15 @@ private:
 
     LONG m_playPort = -1;                               /* 全局的播放库port号 */
     int m_frameRate = 0;                                /* 帧率 */
-    RingQueue<Image_YUV420*>* m_yuvQueue = nullptr;     /* 环形队列,存放YUV数据 */
-
     QTimer m_frameTimer;                                /* 定时器,用于更新一帧数据 */
-    PlayerGLWidget* m_player = nullptr;                 /* 播放窗口 */
+
+    // RingQueue<Image_YUV420*> m_imageQueue;     /* 环形队列,存放YUV数据 */
+    // PlayerGLWidget* m_player = nullptr;                 /* 播放窗口 */
+
+    RingQueue<Image_QImage*> m_imageQueue;              /* 环形队列,存放QImage数据 */
+    PlayerWidget* m_player = nullptr;                   /* 播放窗口 */
+
+    TransCode* m_transCode = nullptr;                   /* 转码类 */
 };
 
 

+ 227 - 0
CPlayer/transcode/Transcode.cpp

@@ -0,0 +1,227 @@
+#include "Transcode.h"
+#include "LHQLogAPI.h"
+
+extern "C"
+{
+#include "libavcodec/avcodec.h"
+#include "libswscale/swscale.h"
+#include "libavutil/pixfmt.h"
+#include "libavutil/imgutils.h"
+}
+
+TransCode::TransCode()
+{
+
+}
+
+TransCode::~TransCode()
+{
+    if(m_swsCtx != nullptr)
+    {
+        sws_freeContext(m_swsCtx);
+        m_swsCtx = nullptr;
+    }
+}
+
+/* 使用ffmpeg将yv12转换成RGB24 */
+// bool YV12ToRGB24_FFmpeg(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height)
+// {
+
+//     if (width < 1 || height < 1 || pYV12Data == NULL || pRGB24Data == NULL)
+//         return false;
+//     //int srcNumBytes,dstNumBytes;
+//     //uint8_t *pSrc,*pDst;
+//     AVPicture pFrameYUV, pFrameBGR;
+    
+//     //pFrameYUV = avpicture_alloc();
+//     //srcNumBytes = avpicture_get_size(PIX_FMT_YUV420P,width,height);
+//     //pSrc = (uint8_t *)malloc(sizeof(uint8_t) * srcNumBytes);
+//     avpicture_fill(&pFrameYUV, pYV12Data, AV_PIX_FMT_YUV420P, width, height);
+
+//     //U,V互换
+//     uint8_t * ptmp=pFrameYUV.data[1];
+//     pFrameYUV.data[1]=pFrameYUV.data[2];
+//     pFrameYUV.data [2]=ptmp;
+
+//     //pFrameBGR = avcodec_alloc_frame();
+//     //dstNumBytes = avpicture_get_size(PIX_FMT_BGR24,width,height);
+//     //pDst = (uint8_t *)malloc(sizeof(uint8_t) * dstNumBytes);
+//     avpicture_fill(&pFrameBGR, pRGB24Data, AV_PIX_FMT_BGR24, width,height);
+
+//     struct SwsContext* imgCtx = NULL;
+//     imgCtx = sws_getContext(width,height, AV_PIX_FMT_YUV420P, width, height, AV_PIX_FMT_BGR24,SWS_BILINEAR, 0, 0, 0);
+
+//     if (imgCtx != NULL){
+//         sws_scale(imgCtx,pFrameYUV.data,pFrameYUV.linesize,0,height,pFrameBGR.data,pFrameBGR.linesize);
+//         if(imgCtx){
+//             sws_freeContext(imgCtx);
+//             imgCtx = NULL;
+//         }
+//         return true;
+//     }
+//     else{
+//         sws_freeContext(imgCtx);
+//         imgCtx = NULL;
+//         return false;
+//     }
+// }
+
+bool TransCode::YV12ToRGB24_FFmpeg(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height)
+{
+    int ret = 0;
+    /* 创建AVFrame */
+    AVFrame* pFrameYV12 = av_frame_alloc();
+    AVFrame* pFrameBGR = av_frame_alloc();
+
+    /* 将pFrameYV12与pYV12Data相关联 */
+    ret = av_image_fill_arrays(pFrameYV12->data, pFrameYV12->linesize, pYV12Data, AV_PIX_FMT_YUV420P, width, height, 1);
+    if(ret < 0)
+    {
+        av_frame_free(&pFrameYV12);
+        av_frame_free(&pFrameBGR);
+        return false;
+    }
+    /* UV互换,海康的UV是反的 */
+    uint8_t* pTmp = pFrameYV12->data[1];
+    pFrameYV12->data[1] = pFrameYV12->data[2];
+    pFrameYV12->data[2] = pTmp;
+
+    /* 将pFrameBGR和数组pRGB24Data关联起来 */
+    // ret = av_image_fill_arrays(pFrameBGR->data, pFrameBGR->linesize, pRGB24Data, AV_PIX_FMT_BGR24, width, height, 1);
+    ret = av_image_fill_arrays(pFrameBGR->data, pFrameBGR->linesize, pRGB24Data, AV_PIX_FMT_RGB24, width, height, 1);
+    if(ret < 0)
+    {
+        av_frame_free(&pFrameYV12);
+        av_frame_free(&pFrameBGR);
+        return false;
+    }
+
+    /* 获取转码上下文 */
+    if(m_swsCtx == nullptr)
+    {
+        m_swsCtx = sws_getCachedContext(m_swsCtx,
+                                width, height,                              /* 原图像大小和格式 */
+                                AV_PIX_FMT_YUV420P,                         /* 输入图像的像素格式 */
+                                width, height,                              /* 目标图像的大小 */
+                                AV_PIX_FMT_RGB24,                           /* 目标图像的格式 */
+                                SWS_BILINEAR,                               /* 图像缩放算法,双线性 */
+                                nullptr,                                    /* 输入图像的滤波器信息,不需要传NULL */
+                                nullptr,                                    /* 输出图像的滤波器信息,不需要传NULL */
+                                nullptr);                                   /* 特定缩放算法需要的参数,不需要传NULL */
+    }
+    /* 转换图像 */
+    bool result = false;
+    if(m_swsCtx != nullptr)
+    {
+        sws_scale(m_swsCtx, pFrameYV12->data, pFrameYV12->linesize, 0, height, pFrameBGR->data, pFrameBGR->linesize);
+        result = true;
+    }else {
+        LH_WRITE_ERROR(QString("获取转码上下文失败"));
+        result = false;
+    }
+    /* 释放资源 */
+    av_frame_free(&pFrameYV12);
+    av_frame_free(&pFrameBGR);
+    return result;
+}
+
+
+
+/* YV12转RGB888 */
+void TransCode::YV12ToRGB888(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height)
+{
+    int ySize = width * height;
+    int uvSize = ySize / 4;
+
+    const uint8_t* yData = pYV12Data;
+    const uint8_t* vData = pYV12Data + ySize;
+    const uint8_t* uData = pYV12Data + ySize + uvSize;
+
+    for (int y = 0; y < height; y++) {
+        for (int x = 0; x < width; x++) {
+            int yIndex = y * width + x;
+            int uvIndex = (y / 2) * (width / 2) + (x / 2);
+
+            uint8_t Y = yData[yIndex];
+            uint8_t U = uData[uvIndex];
+            uint8_t V = vData[uvIndex];
+
+            // YUV 转 RGB
+            int R = Y + 1.403 * (V - 128);
+            int G = Y - 0.344 * (U - 128) - 0.714 * (V - 128);
+            int B = Y + 1.770 * (U - 128);
+
+            // 限制范围 [0, 255]
+            R = std::min(std::max(R, 0), 255);
+            G = std::min(std::max(G, 0), 255);
+            B = std::min(std::max(B, 0), 255);
+
+            // 存储 RGB 数据
+            int rgbIndex = yIndex * 3;
+            pRGB24Data[rgbIndex]     = static_cast<uint8_t>(R);
+            pRGB24Data[rgbIndex + 1] = static_cast<uint8_t>(G);
+            pRGB24Data[rgbIndex + 2] = static_cast<uint8_t>(B);
+        }
+    }
+}
+
+
+/* RGB888转QImage */
+void TransCode::YV12ToQImage(unsigned char *pYV12Data, Image_QImage& image, int width, int height)
+{
+    unsigned char* rgbData = new unsigned char [width * height * 3];
+    // YV12ToRGB888(pYV12Data, rgbData, width, height);
+    if(YV12ToRGB24_FFmpeg(pYV12Data, rgbData, width, height))
+    {
+        image.image = QImage(rgbData, width, height, QImage::Format_RGB888);
+        image.width = width;
+        image.height = height;
+    }else {
+        LH_WRITE_ERROR("YV12ToRGB24_FFmpeg failed!");
+    }
+
+    
+
+    /* 释放资源 */
+    delete [] rgbData;
+}
+
+
+/* YV12转YUV420 */
+void TransCode::YV12ToYUV420(unsigned char *pYV12Data, Image_YUV420& yuvData, int width, int height)
+{
+    int y_size = width * height;
+    int uv_size = y_size / 4;
+
+    // 分配 YUV 数据的空间
+    yuvData.yData.resize(y_size);
+    yuvData.uData.resize(uv_size);
+    yuvData.vData.resize(uv_size);
+
+    // 提取 Y、V、U 分量,交换uv分量,海康的uv是反的
+    const uint8_t* y = pYV12Data;
+    const uint8_t* v = pYV12Data + y_size;
+    const uint8_t* u = pYV12Data + y_size + uv_size;
+
+    // 复制 Y 分量
+    // std::memcpy(yuvData.yData.data(), y, y_size);
+    yuvData.yData.append((char*)y, y_size);
+
+    // 复制 U 分量
+    yuvData.uData.append((char*)v, uv_size);
+    // uint8_t* yuv_u = (uint8_t*)yuvData.uData.data() + y_size;
+    // for (int i = 0; i < uv_size; ++i) {
+    //     yuv_u[i] = u[i];
+    // }
+
+    // 复制 V 分量
+    yuvData.vData.append((char*)u, uv_size);
+    // uint8_t* yuv_v = (uint8_t*)yuvData.vData.data() + y_size + uv_size;
+    // for (int i = 0; i < uv_size; ++i) {
+    //     yuv_v[i] = v[i];
+    // }
+}
+
+
+
+

+ 38 - 0
CPlayer/transcode/Transcode.h

@@ -0,0 +1,38 @@
+#ifndef TRANSCODE_H
+#define TRANSCODE_H
+
+#include "PlayerGlobalInfo.h"
+
+
+extern "C"
+{
+#include "libswscale/swscale.h"
+}
+
+
+class TransCode 
+{
+
+public:
+    TransCode();
+    ~TransCode();
+
+    /* 使用ffmpeg将yv12转换成RGB24 */
+    bool YV12ToRGB24_FFmpeg(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height);
+    // bool YV12ToRGB24_FFmpeg(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height, int stride);
+
+    /* YV12转RGB888 */
+    void YV12ToRGB888(unsigned char *pYV12Data, unsigned char *pRGB24Data, int width, int height);
+    /* RGB888转QImage */
+    void YV12ToQImage(unsigned char *pYV12Data, Image_QImage& image, int width, int height);
+    /* YV12转YUV */
+    void YV12ToYUV420(unsigned char *pYV12Data, Image_YUV420& yuvData, int width, int height);
+
+private:
+    /* 转码上下文 */
+    SwsContext* m_swsCtx = nullptr;
+
+
+};
+
+#endif /* TRANSCODE_H */

+ 10 - 1
show1/CMakeLists.txt

@@ -12,6 +12,7 @@ file(GLOB LOCAL_SRC
     ${CMAKE_SOURCE_DIR}/CPlayer/*.cpp
     ${CMAKE_SOURCE_DIR}/CPlayer/VideoPlayer/*.cpp
     ${CMAKE_SOURCE_DIR}/CPlayer/Player/*.cpp
+    ${CMAKE_SOURCE_DIR}/CPlayer/transcode/*.cpp
 
     ${CMAKE_SOURCE_DIR}/External/module/Logs/*.cpp
     
@@ -32,9 +33,10 @@ target_include_directories(${execName} PRIVATE
     ${CMAKE_SOURCE_DIR}/CPlayer/VideoPlayer
     ${CMAKE_SOURCE_DIR}/CPlayer/common/RingQueue
     ${CMAKE_SOURCE_DIR}/CPlayer/Player
+    ${CMAKE_SOURCE_DIR}/CPlayer/transcode
+
     ${CMAKE_SOURCE_DIR}/libs/hk_sdk/include
     ${CMAKE_SOURCE_DIR}/libs/hk_sdk
-    
 
     ${CMAKE_SOURCE_DIR}/External/common
     ${CMAKE_SOURCE_DIR}/External/common/LHQLogLight
@@ -49,6 +51,7 @@ target_link_libraries(${execName} PRIVATE
     Qt5::Widgets
     Qt5::Core
     Qt5::Network
+    # Qt5::OpenGL
 
     ${spdlog_LIBRARY}
     ${FFMPEG_LIBRARY}
@@ -67,6 +70,12 @@ target_link_libraries(${execName} PRIVATE
     SuperRender
 )
 
+#链接OpenGL库
+target_link_libraries(${execName} PRIVATE
+    GL
+    GLU
+)
+
 
 
 #连接动态库

+ 15 - 0
show1/widget.cpp

@@ -1,8 +1,12 @@
 #include "widget.h"
 #include "ui_widget.h"
 
+#include <QResizeEvent>
+
 #include "cameraplayer.h"
 
+
+
 widget::widget(QWidget *parent) :
     QWidget(parent),
     ui(new Ui::widget)
@@ -32,3 +36,14 @@ void widget::on_pBtn_startRealPlay_clicked()
 {
     m_cameraPlayer->startPlay();
 }
+
+/* 窗口缩放事件 */
+void widget::resizeEvent(QResizeEvent *event)
+{
+    QWidget::resizeEvent(event);
+
+    /* 获取窗口大小 */
+    QSize size = event->size();
+    ui->widget_display->resize(size);
+    m_cameraPlayer->setPlayWndSize(event->size());
+}

+ 4 - 0
show1/widget.h

@@ -20,6 +20,10 @@ public:
 private slots:
     /* 开启预览按钮 */
     void on_pBtn_startRealPlay_clicked();
+
+protected:
+    /* 窗口缩放事件 */
+    void resizeEvent(QResizeEvent *event) override;
     
 private:
     Ui::widget *ui;