Browse Source

V0.7.12
1、完成了YUV420格式的显示

Apple 1 week ago
parent
commit
2d371c711c

+ 1 - 1
External

@@ -1 +1 @@
-Subproject commit de168bfdac56a7cf68c7478629f3925991b9fac2
+Subproject commit fe6dd8a6f8ca766c91e76b962e51deaf4e515c69

+ 2 - 0
demo/VideoPlayerGL/GLShader/ShaderBase.h

@@ -20,6 +20,8 @@ public:
 
     /* 初始化形状 */
     virtual GLuint initShape() = 0;
+    /* 初始化纹理对象 */
+    virtual bool initTextures() = 0;
     /* 绘制图形 */
     virtual void drawShape() = 0;
 

+ 14 - 0
demo/VideoPlayerGL/GLShader/ShaderRect.cpp

@@ -107,6 +107,20 @@ void ShaderRect::drawShape()
     gl->glBindVertexArray(0); // 解绑VAO
 }
 
+/* 初始化纹理对象 */
+bool ShaderRect::initTextures()
+{
+    /* 创建一个纹理对象 */
+    auto unit = createTexture(m_textureRGBAName);
+    if(unit < 0)
+    {
+        SPDLOG_ERROR("RGBA着色器创建纹理失败!");
+        return false;
+    }
+
+    return true;
+}
+
 /* 刷新一帧 */
 bool ShaderRect::refreshFrameRGBA(const QImage& image, int textureUnit)
 {

+ 4 - 0
demo/VideoPlayerGL/GLShader/ShaderRect.h

@@ -17,6 +17,8 @@ public:
 
     /* 初始化形状 */
     GLuint initShape() override;
+    /* 初始化纹理对象 */
+    bool initTextures() override;
     /* 绘制图形 */
     void drawShape() override;
 
@@ -28,6 +30,8 @@ private:
     GLuint m_VBO = 0;              // 顶点缓冲对象的ID
     GLuint m_EBO = 0;              // 索引缓冲对象的ID
     QSize m_lastSize;               /* 上一张图片的大小 */
+
+    const QString m_textureRGBAName = "textureRGBA"; // RGBA纹理名称
 };
 
 

+ 282 - 0
demo/VideoPlayerGL/GLShader/ShaderYUV420.cpp

@@ -0,0 +1,282 @@
+#include "ShaderYUV420.h"
+
+
+#include <QOpenGLWidget>
+#include <QOpenGLFunctions_3_3_Core>
+
+#include "spdlog/spdlog.h"
+
+Image_YUV420::Image_YUV420(Image_YUV420&& other): width(other.width), height(other.height),
+          yData(std::move(other.yData)), uData(std::move(other.uData)), vData(std::move(other.vData))
+{}
+
+Image_YUV420::Image_YUV420(const Image_YUV420& other)
+        : width(other.width), height(other.height),
+          yData(other.yData), uData(other.uData), vData(other.vData)
+{}
+
+Image_YUV420& Image_YUV420::operator=(Image_YUV420&& other) 
+{
+    if (this != &other) {
+        width = other.width;
+        height = other.height;
+        yData = std::move(other.yData);
+        uData = std::move(other.uData);
+        vData = std::move(other.vData);
+    }
+    return *this;
+}
+Image_YUV420& Image_YUV420::operator=(const Image_YUV420& other) 
+{
+    if (this != &other) {
+        width = other.width;
+        height = other.height;
+        yData = other.yData;
+        uData = other.uData;
+        vData = other.vData;
+    }
+    return *this;
+}
+
+bool Image_YUV420::isValid() const 
+{
+    return width > 0 && height > 0 && !yData.isEmpty() && !uData.isEmpty() && !vData.isEmpty();
+}
+
+void Image_YUV420::clear()
+{
+    width = 0;
+    height = 0;
+    yData.clear();
+    uData.clear();
+    vData.clear();
+}
+
+
+ShaderYUV420::ShaderYUV420()
+{
+
+}
+ShaderYUV420::~ShaderYUV420()
+{
+    QOpenGLFunctions_3_3_Core *gl = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
+    if (m_VAO != 0) {
+        if (gl) {
+            gl->glDeleteVertexArrays(1, &m_VAO);
+            gl->glDeleteBuffers(1, &m_VBO);
+            gl->glDeleteBuffers(1, &m_EBO);
+        }
+    }
+}
+
+/* 初始化形状 */
+GLuint ShaderYUV420::initShape()
+{
+    const int vertexCount = 4;    // 顶点数量
+    const int vertexSize = 8;     // 顶点大小
+    /* 矩形位置,铺满屏幕 */
+    float vertices [vertexCount * vertexSize] {
+        /* ----- 位置 -----|-------- 颜色 ----------|----- 纹理坐标 ------ */
+        -1.0f, -1.0f, 0.0f,     1.0f, 1.0f, 0.0f,   0.0f, 0.0f,     // 左下
+        1.0f, -1.0f, 0.0f,      0.0f, 0.0f, 1.0f,   1.0f, 0.0f,     // 右下
+        1.0f,  1.0f, 0.0f,      1.0f, 0.0f, 0.0f,   1.0f, 1.0f,     // 右上
+        -1.0f, 1.0f, 0.0f,      0.0f, 1.0f, 0.0f,   0.0f, 1.0f      // 右上
+    };
+    const int indexCount = 6; // 索引数量
+    /* 顶点顺序 */
+    uint32_t indices [indexCount] {
+        0, 1, 2, // 第一个三角形
+        2, 3, 0  // 第二个三角形
+    };
+
+    QOpenGLFunctions_3_3_Core *gl = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
+    if (!gl) {
+        SPDLOG_ERROR("Failed to get OpenGL functions");
+        return 0;
+    }
+    /* -------------------------------------------------------------------------------------
+     * 顶点数组对象
+     * ------------------------------------------------------------------------------------ */
+    gl->glGenVertexArrays(1, &m_VAO); // 生成一个VAO,返回的ID存储在VAO1中
+    gl->glBindVertexArray(m_VAO); // 绑定VAO
+
+    /* -------------------------------------------------------------------------------------
+     * 创建顶点数组缓冲区
+     * ------------------------------------------------------------------------------------ */
+    gl->glGenBuffers(1, &m_VBO);
+    gl->glBindBuffer(GL_ARRAY_BUFFER, m_VBO); // 绑定VBO
+    gl->glBufferData(GL_ARRAY_BUFFER, vertexCount * vertexSize * sizeof(float), vertices, GL_STATIC_DRAW);
+
+    /* -------------------------------------------------------------------------------------
+     * 创建EBO
+     * ------------------------------------------------------------------------------------ */
+    gl->glGenBuffers(1, &m_EBO);
+    gl->glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, m_EBO);
+    gl->glBufferData(GL_ELEMENT_ARRAY_BUFFER, indexCount * sizeof(uint32_t), indices, GL_STATIC_DRAW); // 绑定EBO
+
+
+    /* -------------------------------------------------------------------------------------
+     * 链接顶点属性
+     * ------------------------------------------------------------------------------------ */
+    // 设置顶点属性指针,告诉OpenGL如何解析顶点数据
+    gl->glVertexAttribPointer(
+                    0,                  /* 顶点属性位置,0表示第一个属性 */
+                    3,                  /* 每个顶点属性的大小,这里是3,表示x、y、z坐标 */
+                    GL_FLOAT, GL_FALSE, /* 数据类型和是否标准化 */
+                    vertexSize * sizeof(float), /* 步长,表示每个顶点属性之间的间隔 */
+                    (void*)0            /* 偏移量,表示该属性(顶点坐标)从第0个字节开始 */
+                ); 
+    /* 启用顶点属性 */
+    gl->glEnableVertexAttribArray(0);
+ 
+    // 设置颜色位置,最后一个参数表示从第3个字节开始,倒数第二个参数是两个数据之间起始位置间隔8个字节
+    gl->glVertexAttribPointer(1, 3, GL_FLOAT, GL_FALSE, vertexSize * sizeof(float), (void*)(3*sizeof(float))); 
+    gl->glEnableVertexAttribArray(1);
+ 
+    /* 设置纹理位置 */
+    gl->glVertexAttribPointer(2, 2, GL_FLOAT, GL_FALSE, vertexSize * sizeof(float), (void*)(6*sizeof(float))); // 纹理坐标位置
+    gl->glEnableVertexAttribArray(2); // 启用纹理坐标属性
+
+    return m_VAO;
+}
+
+/* 初始化纹理对象 */
+bool ShaderYUV420::initTextures()
+{
+    int unit = 0;
+    unit = createTexture(m_textureYName, 0);
+    if(unit < 0) {
+        SPDLOG_ERROR("Failed to create Y texture");
+        return false;
+    }
+    unit = createTexture(m_textureUName, 1);
+    if(unit < 0) {
+        SPDLOG_ERROR("Failed to create U texture");
+        return false;
+    }
+    unit = createTexture(m_textureVName, 2);
+    if(unit < 0) {
+        SPDLOG_ERROR("Failed to create V texture");
+        return false;
+    }
+
+    return true;
+}
+
+/* 绘制图形 */
+void ShaderYUV420::drawShape()
+{
+    QOpenGLFunctions_3_3_Core *gl = QOpenGLContext::currentContext()->versionFunctions<QOpenGLFunctions_3_3_Core>();
+    /* 需要在使用时手动绑定VAO,绘制图形 */
+    gl->glBindVertexArray(m_VAO); // 绑定VAO
+
+    gl->glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); // 绘制四边形
+
+    gl->glBindVertexArray(0); // 解绑VAO
+}
+
+/* 刷新一帧 */
+bool ShaderYUV420::refreshFrameYUV420(const Image_YUV420& yuvData)
+{
+    if (!yuvData.isValid()) {
+        SPDLOG_ERROR("Invalid YUV420 data");
+        return false;
+    }
+    QOpenGLTexture *textureY = m_mapTexture.find(0)->second;
+    QOpenGLTexture *textureU = m_mapTexture.find(1)->second;
+    QOpenGLTexture *textureV = m_mapTexture.find(2)->second;
+
+    if(textureY == nullptr || textureU == nullptr || textureV == nullptr) {
+        SPDLOG_ERROR("Failed to get YUV textures");
+        return false;
+    }
+
+    if(yuvData.width != m_lastYUVSize.width() || yuvData.height != m_lastYUVSize.height()) 
+    {
+        /* 如果YUV数据的大小发生变化,则需要重新创建纹理 */
+        textureY->destroy();
+        textureU->destroy();
+        textureV->destroy();
+        textureY->create();
+        textureU->create();
+        textureV->create();
+
+        textureY->setSize(yuvData.width, yuvData.height, 1); // 设置Y分量纹理大小
+        textureY->setFormat(QOpenGLTexture::R8_UNorm); // 设置Y分量纹理格式
+        textureY->allocateStorage(); // 分配Y分量纹理存储空间
+        
+        textureU->setSize(yuvData.width / 2, yuvData.height / 2, 1); // 设置U分量纹理大小
+        textureU->setFormat(QOpenGLTexture::R8_UNorm); // 设置U分量纹理格式
+        textureU->allocateStorage(); // 分配U分量纹理存储空间
+
+        textureV->setSize(yuvData.width / 2, yuvData.height / 2, 1); // 设置V分量纹理大小
+        textureV->setFormat(QOpenGLTexture::R8_UNorm); // 设置V分量纹理格式
+        textureV->allocateStorage(); // 分配V分量纹理存储空间
+
+        m_lastYUVSize = QSize(yuvData.width, yuvData.height);
+    }
+
+    /* 设置Y分量纹理 */
+    textureY->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvData.yData.constData());
+    textureY->generateMipMaps(); // 生成多级渐远纹理
+
+    /* 设置U分量纹理 */
+    textureU->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvData.uData.constData());
+    textureU->generateMipMaps(); // 生成多级渐远纹理
+
+    /* 设置V分量纹理 */
+    textureV->setData(QOpenGLTexture::Red, QOpenGLTexture::UInt8, yuvData.vData.constData());
+    textureV->generateMipMaps(); // 生成多级渐远纹理
+
+    printGLerror("ShaderYUV420::refreshFrameYUV420");
+
+    return true;
+}
+
+/* 刷新一帧 */
+bool ShaderYUV420::refreshFrameRGBA(const QImage& image, int textureUnit)
+{
+    m_yuvData.clear(); // 清空YUV数据
+    convertRGBAToYUV420(image, m_yuvData);
+    
+    refreshFrameYUV420(m_yuvData);
+    return true;
+}
+
+/* RGBA转换成YUV420 */
+bool ShaderYUV420::convertRGBAToYUV420(const QImage& image, Image_YUV420& yuvData)
+{
+    int width = image.width();
+    int height = image.height();
+    const int ySize = yuvData.width * yuvData.height;
+    const int uvSize = ySize / 4;
+
+    QImage image_rgb(width, height, QImage::Format_RGB888);
+
+    yuvData.width = width;
+    yuvData.height = height;
+
+    yuvData.yData.resize(ySize);
+    yuvData.uData.resize(uvSize);
+    yuvData.vData.resize(uvSize);
+
+    for (int y = 0; y < height; ++y) {
+        for (int x = 0; x < width; ++x) {
+            QColor color = image.pixelColor(x, y);
+            int r = color.red();
+            int g = color.green();
+            int b = color.blue();
+
+            int yIndex = y * width + x;
+            yuvData.yData[yIndex] = static_cast<unsigned char>((0.257 * r) + (0.504 * g) + (0.098 * b) + 16);
+
+            if (y % 2 == 0 && x % 2 == 0) {
+                int uvIndex = (y / 2) * (width / 2) + (x / 2);
+                yuvData.uData[uvIndex] = static_cast<unsigned char>((-0.148 * r) - (0.291 * g) + (0.439 * b) + 128);
+                yuvData.vData[uvIndex] = static_cast<unsigned char>((0.439 * r) - (0.368 * g) - (0.071 * b) + 128);
+            }
+        }
+    }
+
+    return true;
+}

+ 62 - 0
demo/VideoPlayerGL/GLShader/ShaderYUV420.h

@@ -0,0 +1,62 @@
+#ifndef _SHADERYUV4210_H_
+#define _SHADERYUV4210_H_
+
+#include "ShaderBase.h"
+
+struct Image_YUV420
+{
+    int width;          /* 宽度 */
+    int height;         /* 高度 */
+    QByteArray yData;   /* Y分量数据 */
+    QByteArray uData;   /* U分量数据 */
+    QByteArray vData;   /* V分量数据 */
+
+    Image_YUV420() : width(0), height(0) {}
+    Image_YUV420(Image_YUV420&& other);
+    Image_YUV420(const Image_YUV420& other);
+    Image_YUV420& operator=(Image_YUV420&& other);
+    Image_YUV420& operator=(const Image_YUV420& other);
+    bool isValid() const;
+    void clear();
+};
+
+
+class ShaderYUV420 : public ShaderBase
+{
+
+public:
+    ShaderYUV420();
+    ~ShaderYUV420() override;
+
+    /* 初始化形状 */
+    GLuint initShape() override;
+    /* 初始化纹理对象 */
+    bool initTextures() override;
+    /* 绘制图形 */
+    void drawShape() override;
+
+    /* 刷新一帧 */
+    bool refreshFrameYUV420(const Image_YUV420& yuvData);
+    /* 刷新一帧 */
+    bool refreshFrameRGBA(const QImage& image, int textureUnit = 0);
+
+private:
+    /* RGBA转换成YUV420 */
+    bool convertRGBAToYUV420(const QImage& image, Image_YUV420& yuvData);
+
+private:
+    GLuint m_VAO = 0;              // 顶点数组对象的ID
+    GLuint m_VBO = 0;              // 顶点缓冲对象的ID
+    GLuint m_EBO = 0;              // 索引缓冲对象的ID
+    QSize m_lastSize;               /* 上一张图片的大小 */
+
+    Image_YUV420 m_yuvData;         /* YUV420数据 */
+    QSize m_lastYUVSize;          /* 上一张YUV图片的大小 */
+
+    const QString m_textureYName = "textureY"; // Y分量纹理名称
+    const QString m_textureUName = "textureU"; // U分量纹理名称
+    const QString m_textureVName = "textureV"; // V分量纹理名称
+};
+
+#endif // _SHADERYUV4210_H_
+

+ 61 - 10
demo/VideoPlayerGL/GLWidget/PlayerGLWidget.cpp

@@ -36,18 +36,36 @@ void PlayerGLWidget::showOneRGBAImage(const QImage& image)
     update(); // 更新界面,触发paintGL函数
 }
 
+/* 显示一张YUV420的图片 */
+void PlayerGLWidget::showOneYUV420Image(const Image_YUV420& yuvData)
+{
+    if(!yuvData.isValid()) {
+        SPDLOG_WARN("YUV420数据无效");
+        return;
+    }
 
-void PlayerGLWidget::initializeGL()
+    m_shaderYUV420->refreshFrameYUV420(yuvData);
+    m_shaderCurr = m_shaderYUV420; // 设置当前着色器为显示YUV420图片的着色器
+    update(); // 更新界面,触发paintGL函数
+}
+
+void PlayerGLWidget::testShowYUV420Image(const QImage& image)
 {
-    SPDLOG_DEBUG("编译着色器...");
-    /* 初始化OpenGL函数,OpenGL的函数是在运行时才确定函数指针的 */
-    initializeOpenGLFunctions();
+    m_shaderYUV420->refreshFrameRGBA(image);
+    m_shaderCurr = m_shaderYUV420; // 设置当前着色器为显示YUV420图片的着色器
+    update(); // 更新界面,触发paintGL函数
+}
 
-    /* 初始化显示RGBA图片的着色器 */
+/* 初始化RGBA的Shader */
+void PlayerGLWidget::initShaderRGBA()
+{
+    if(m_shaderRGBA != nullptr) {
+        delete m_shaderRGBA; // 删除旧的着色器对象
+        m_shaderRGBA = nullptr;
+    }
     m_shaderRGBA = new ShaderRect();
 
     m_shaderRGBA->initShape(); // 初始化形状
-
     /* 初始化RGBA着色器 */
     QString vertexShaderFile = ":/shaderCode/vertexShaderRGBA.glsl";
     QString fragmentShaderFile = ":/shaderCode/fragmentShaderRGBA.glsl";
@@ -58,12 +76,45 @@ void PlayerGLWidget::initializeGL()
     }
     /* 编译着色器 */
     m_shaderRGBA->compileShader();
-    /* 创建一个纹理对象 */
-    m_textureRGBAUnit = m_shaderRGBA->createTexture("textureRGBA");
-    if(m_textureRGBAUnit < 0)
+    /* 初始化纹理 */
+    m_shaderRGBA->initTextures();
+}
+
+/* 初始化YUV420的Shader */
+void PlayerGLWidget::initShaderYUV420()
+{
+    if(m_shaderYUV420 != nullptr) {
+        delete m_shaderYUV420; // 删除旧的着色器对象
+        m_shaderYUV420 = nullptr;
+    }
+    m_shaderYUV420 = new ShaderYUV420();
+
+    m_shaderYUV420->initShape(); // 初始化形状
+    /* 初始化RGBA着色器 */
+    QString vertexShaderFile = ":/shaderCode/vertexShaderYUV420.glsl";
+    QString fragmentShaderFile = ":/shaderCode/fragmentShaderYUV420.glsl";
+    if (!m_shaderYUV420->loadShaderCode(vertexShaderFile, fragmentShaderFile)) 
     {
-        SPDLOG_ERROR("RGBA着色器创建纹理失败!");
+        SPDLOG_ERROR("加载着色器代码失败");
+        return;
     }
+    /* 编译着色器 */
+    m_shaderYUV420->compileShader();
+    /* 初始化纹理 */
+    m_shaderYUV420->initTextures();
+}
+
+
+void PlayerGLWidget::initializeGL()
+{
+    SPDLOG_DEBUG("编译着色器...");
+    /* 初始化OpenGL函数,OpenGL的函数是在运行时才确定函数指针的 */
+    initializeOpenGLFunctions();
+
+    /* 初始化显示RGBA图片的着色器 */
+    initShaderRGBA();
+    /* 初始化显示YUV420图片的着色器 */
+    initShaderYUV420();
 
 
     SPDLOG_DEBUG("着色器编译完成");

+ 9 - 2
demo/VideoPlayerGL/GLWidget/PlayerGLWidget.h

@@ -7,7 +7,7 @@
 #include <GL/gl.h>
 
 #include "ShaderRect.h"
-
+#include "ShaderYUV420.h"
 
 /**
  * @brief 
@@ -25,9 +25,16 @@ public:
 
     /* 显示RGBA图片 */
     void showOneRGBAImage(const QImage& image);
+    /* 显示一张YUV420的图片 */
+    void showOneYUV420Image(const Image_YUV420& yuvData);
     
+    void testShowYUV420Image(const QImage& image);
 
 private:
+    /* 初始化RGBA的Shader */
+    void initShaderRGBA();
+    /* 初始化YUV420的Shader */
+    void initShaderYUV420();
  
 protected:
     void initializeGL() override;
@@ -40,7 +47,7 @@ private:
     
     ShaderBase* m_shaderCurr = nullptr;     /* 当前使用的着色器对象 */
     ShaderRect* m_shaderRGBA = nullptr;     /* 显示RGBA图片的着色器对象 */
-    int m_textureRGBAUnit = -1;
+    ShaderYUV420* m_shaderYUV420 = nullptr; /* 显示YUV420图片的着色器对象 */
 
 };
 

+ 0 - 31
demo/VideoPlayerGL/PlayerGlobalInfo.cpp

@@ -2,37 +2,6 @@
 
 
 
-/* 移动构造函数 */
-Image_YUV420::Image_YUV420(Image_YUV420&& other)
-{
-    yData = std::move(other.yData);
-    uData = std::move(other.uData);
-    vData = std::move(other.vData);
-    width = other.width;
-    height = other.height;
-}
-/* 拷贝构造函数 */
-Image_YUV420::Image_YUV420(const Image_YUV420& other)
-{
-    yData = other.yData;
-    uData = other.uData;
-    vData = other.vData;
-    width = other.width;
-    height = other.height;
-}
-
-
-/* 重载= */
-Image_YUV420& Image_YUV420::operator=(const Image_YUV420& other)
-{
-    yData = other.yData;
-    uData = other.uData;
-    vData = other.vData;
-    width = other.width;
-    height = other.height;
-    return *this;
-}
-
 Image_QImage::Image_QImage()
 {
     width = 0;

+ 0 - 22
demo/VideoPlayerGL/PlayerGlobalInfo.h

@@ -5,28 +5,6 @@
 #include <QByteArray>
 #include <QImage>
 
-/**
- * @brief 一帧YUV420的图片
- * 
- */
-struct Image_YUV420
-{
-    QByteArray yData;
-    QByteArray uData;
-    QByteArray vData;
-
-    int width;          /* 图片的宽 */
-    int height;         /* 图片的高 */
-
-    Image_YUV420() : width(0), height(0) {}
-    ~Image_YUV420() {}
-    /* 移动构造函数 */
-    Image_YUV420(Image_YUV420&& other);
-    /* 拷贝构造函数 */
-    Image_YUV420(const Image_YUV420& other);
-    /* 重载= */
-    Image_YUV420& operator=(const Image_YUV420& other);
-};
 
 struct Image_QImage
 {

+ 2 - 0
demo/VideoPlayerGL/resShaderCode.qrc

@@ -2,5 +2,7 @@
     <qresource prefix="/">
         <file>shaderCode/vertexShaderRGBA.glsl</file>
         <file>shaderCode/fragmentShaderRGBA.glsl</file>
+        <file>shaderCode/vertexShaderYUV420.glsl</file>
+        <file>shaderCode/fragmentShaderYUV420.glsl</file>
     </qresource>
 </RCC>

+ 21 - 0
demo/VideoPlayerGL/shaderCode/fragmentShaderYUV420.glsl

@@ -0,0 +1,21 @@
+#version 330 core
+
+in vec3 vertexColor;    //输入颜色数据
+in vec2 TexCoord;       //输入纹理坐标数据
+
+out vec4 FragColor;     //输出颜色数据
+
+uniform sampler2D textureY; //Y分量纹理采样器
+uniform sampler2D textureU; //U分量纹理采样器
+uniform sampler2D textureV;
+
+void main()
+{
+    float y = texture(textureY, TexCoord).r;
+    float u = texture(textureU, TexCoord).r - 0.5;
+    float v = texture(textureV, TexCoord).r - 0.5;
+    float r = y + 1.402 * v;
+    float g = y - 0.344 * u - 0.714 * v;
+    float b = y + 1.772 * u;
+    FragColor = vec4(r, g, b, 1.0);
+}

+ 15 - 0
demo/VideoPlayerGL/shaderCode/vertexShaderYUV420.glsl

@@ -0,0 +1,15 @@
+#version 330 core
+layout(location = 0) in vec3 aPos;
+layout(location = 1) in vec3 aColor;
+layout(location = 2) in vec2 aTexCoord; // 纹理坐标
+
+out vec3 vertexColor;       //向片段着色器传递颜色数据
+out vec2 TexCoord;          //向片段着色器传递纹理坐标数据
+
+void main()
+{
+    gl_Position = vec4(aPos, 1.0);
+    vertexColor = aColor;
+    /* 传递纹理数据,并将垂直坐标翻转 */
+    TexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y);
+}

+ 1 - 1
demo/VideoPlayerGL/widget.cpp

@@ -24,7 +24,7 @@ Widget::Widget(QWidget *parent)
 
     connect(&m_timer, &QTimer::timeout, this, [=]() {
         // SPDLOG_DEBUG("刷新一帧");
-        m_playerGLWidget->showOneRGBAImage(QImage(":/image/1.jpg")); // 显示一张测试图片
+        m_playerGLWidget->testShowYUV420Image(QImage(":/image/1.jpg")); // 显示一张测试图片
     });
     m_timer.setSingleShot(false);
     m_timer.start(10); // 60 FPS