#include "PlayerGLWidget.h" #include // 添加此行 #include #include #include "LHQLogAPI.h" PlayerGLWidget::PlayerGLWidget(QWidget *parent) : QOpenGLWidget(parent) { textureY_ = nullptr; textureU_ = nullptr; textureV_ = nullptr; textureIdY_ = 0; textureIdU_ = 0; textureIdV_ = 0; Ortho2DSize_ = QSize(0, 0); shaderProgram = 0; VAO = 0; VBO = 0; EBO = 0; // QString imagePath = QApplication::applicationDirPath() + "/0.jpg"; // QImage image(imagePath); // Image_YUV420 yuv420; // convertQImageToYUV420(image, yuv420); // /* 显示图片 */ // updateFrame(yuv420); } PlayerGLWidget::~PlayerGLWidget() { if (textureIdY_ != 0) { glDeleteTextures(1, &textureIdY_); textureIdY_ = 0; } if (textureIdU_ != 0) { glDeleteTextures(1, &textureIdU_); textureIdU_ = 0; } if (textureIdV_ != 0) { glDeleteTextures(1, &textureIdV_); textureIdV_ = 0; } if (VAO != 0) { glDeleteVertexArrays(1, &VAO); VAO = 0; } if (VBO != 0) { glDeleteBuffers(1, &VBO); VBO = 0; } if (EBO != 0) { glDeleteBuffers(1, &EBO); EBO = 0; } if (shaderProgram != 0) { glDeleteProgram(shaderProgram); shaderProgram = 0; } if(textureY_ != nullptr) { delete textureY_; textureY_ = nullptr; } if(textureU_ != nullptr) { delete textureU_; textureU_ = nullptr; } if(textureV_ != nullptr) { delete textureV_; textureV_ = nullptr; } } /* 设置一张图片,用于显示默认的图片 */ void PlayerGLWidget::setImage(const QImage& image) { Image_YUV420 yuv420; convertQImageToYUV420(image, yuv420); /* 显示图片 */ updateFrame(yuv420); } /* 刷新一帧 */ void PlayerGLWidget::updateFrame(Image_YUV420& image) { m_yData = std::move(image.yData); m_uData = std::move(image.uData); m_vData = std::move(image.vData); m_imageSize = QSize(image.width, image.height); update(); } /* 刷新一帧QImage */ void PlayerGLWidget::updateFrame(Image_QImage& image) { LH_WRITE_LOG("图片宽度: " + QString::number(image.image.width()) + "图片高度: " + QString::number(image.image.height())); convertQImageToYUV420(image.image, m_YUV420); m_yData = m_YUV420.yData; m_uData = m_YUV420.uData; m_vData = m_YUV420.vData; m_imageSize = QSize(m_YUV420.width, m_YUV420.height); update(); } // void PlayerGLWidget::convertQImageToYUV420(const QImage& image, Image_YUV420& yuv420) // { // int width = image.width(); // int height = image.height(); // int frameSize = width * height; // int chromaSize = frameSize / 4; // uint8_t* yuv = new uint8_t[frameSize + chromaSize * 2]; // uint8_t* yPlane = yuv; // uint8_t* uPlane = yuv + frameSize; // uint8_t* vPlane = yuv + frameSize + chromaSize; // auto rgb = image.bits(); // for (int j = 0; j < height; j++) { // for (int i = 0; i < width; i++) { // int r = rgb[(j * width + i) * 3]; // int g = rgb[(j * width + i) * 3 + 1]; // int b = rgb[(j * width + i) * 3 + 2]; // int y = (0.299 * r) + (0.587 * g) + (0.114 * b); // int u = (-0.169 * r) - (0.331 * g) + (0.5 * b) + 128; // int v = (0.5 * r) - (0.419 * g) - (0.081 * b) + 128; // yPlane[j * width + i] = std::clamp(y, 0, 255); // if (j % 2 == 0 && i % 2 == 0) { // uPlane[(j / 2) * (width / 2) + (i / 2)] = std::clamp(u, 0, 255); // vPlane[(j / 2) * (width / 2) + (i / 2)] = std::clamp(v, 0, 255); // } // } // } // yuv420.width = width; // yuv420.height = height; // yuv420.yData.resize(frameSize); // yuv420.uData.resize(chromaSize); // yuv420.vData.resize(chromaSize); // yuv420.yData.append(reinterpret_cast(yPlane), frameSize); // yuv420.uData.append(reinterpret_cast(uPlane), chromaSize); // yuv420.vData.append(reinterpret_cast(vPlane), chromaSize); // } void PlayerGLWidget::convertQImageToYUV420(const QImage& image, Image_YUV420& yuv420) { int width = image.width(); int height = image.height(); int ySize = width * height; int chromaWidth = (width + 1) / 2; int chromaHeight = (height + 1) / 2; int uvSize = chromaWidth * chromaHeight; yuv420.width = width; yuv420.height = height; yuv420.yData.resize(ySize); yuv420.uData.resize(uvSize); yuv420.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; if (yIndex >= 0 && yIndex < ySize) { yuv420.yData[yIndex] = static_cast((0.257 * r) + (0.504 * g) + (0.098 * b) + 16); } if (y % 2 == 0 && x % 2 == 0) { int uvIndex = (y / 2) * chromaWidth + (x / 2); if (uvIndex >= 0 && uvIndex < uvSize) { yuv420.uData[uvIndex] = static_cast((-0.148 * r) - (0.291 * g) + (0.439 * b) + 128); yuv420.vData[uvIndex] = static_cast((0.439 * r) - (0.368 * g) - (0.071 * b) + 128); } } } } } void PlayerGLWidget::initializeGL() { LH_WRITE_LOG("开始初始化OpenGL"); // 打印当前实际创建到的 OpenGL 上下文信息(UOS 上用于确认是 OpenGL 还是 OpenGL ES、版本多少) if (QOpenGLContext* ctx = context()) { const QSurfaceFormat fmt = ctx->format(); QString profileStr = "NoProfile"; switch (fmt.profile()) { case QSurfaceFormat::CoreProfile: profileStr = "Core"; break; case QSurfaceFormat::CompatibilityProfile: profileStr = "Compatibility"; break; case QSurfaceFormat::NoProfile: default: profileStr = "NoProfile"; break; } QString renderableStr = "Default"; switch (fmt.renderableType()) { case QSurfaceFormat::OpenGL: renderableStr = "OpenGL"; break; case QSurfaceFormat::OpenGLES: renderableStr = "OpenGLES"; break; case QSurfaceFormat::DefaultRenderableType: default: renderableStr = "Default"; break; } LH_WRITE_LOG(QString("Qt Context: renderable=%1 isOpenGLES=%2 version=%3.%4 profile=%5") .arg(renderableStr) .arg(ctx->isOpenGLES() ? "true" : "false") .arg(fmt.majorVersion()) .arg(fmt.minorVersion()) .arg(profileStr)); } else { LH_WRITE_LOG("Qt Context: context() == nullptr (initializeGL)"); } // glGetString 必须在“已 current 的上下文”里调用;某些平台直接调用全局 glGetString 可能会段错误 QOpenGLContext* currentCtx = QOpenGLContext::currentContext(); if (!currentCtx) { LH_WRITE_LOG("QOpenGLContext::currentContext() == nullptr (initializeGL),跳过 glGetString"); return; } QOpenGLFunctions* f = currentCtx->functions(); if (!f) { LH_WRITE_LOG("currentContext()->functions() == nullptr (initializeGL),跳过 glGetString"); return; } const char* glVendor = reinterpret_cast(f->glGetString(GL_VENDOR)); const char* glRenderer = reinterpret_cast(f->glGetString(GL_RENDERER)); const char* glVersion = reinterpret_cast(f->glGetString(GL_VERSION)); const char* glslVersion = reinterpret_cast(f->glGetString(GL_SHADING_LANGUAGE_VERSION)); LH_WRITE_LOG(QString("GL_VENDOR: %1").arg(glVendor ? glVendor : "(null)")); LH_WRITE_LOG(QString("GL_RENDERER: %1").arg(glRenderer ? glRenderer : "(null)")); LH_WRITE_LOG(QString("GL_VERSION: %1").arg(glVersion ? glVersion : "(null)")); LH_WRITE_LOG(QString("GLSL_VERSION: %1").arg(glslVersion ? glslVersion : "(null)")); // 确保初始化 OpenGL 函数(在 OpenGL 版本不匹配时,返回 false,避免后续直接段错误) if (!initializeOpenGLFunctions()) { LH_WRITE_LOG("initializeOpenGLFunctions() 失败:当前上下文可能不支持 QOpenGLFunctions_3_3_Core 所需版本"); return; } // 创建并编译顶点着色器 const char* vertexShaderSource = R"( #version 330 core layout(location = 0) in vec3 aPos; layout(location = 1) in vec2 aTexCoord; out vec2 TexCoord; void main() { gl_Position = vec4(aPos, 1.0); TexCoord = vec2(aTexCoord.x, 1.0 - aTexCoord.y); // 翻转纹理坐标 } )"; GLuint vertexShader = glCreateShader(GL_VERTEX_SHADER); glShaderSource(vertexShader, 1, &vertexShaderSource, nullptr); glCompileShader(vertexShader); // 创建并编译片段着色器 const char* fragmentShaderSource = R"( #version 330 core out vec4 FragColor; in vec2 TexCoord; uniform sampler2D textureY; uniform sampler2D textureU; 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); } )"; GLuint fragmentShader = glCreateShader(GL_FRAGMENT_SHADER); glShaderSource(fragmentShader, 1, &fragmentShaderSource, nullptr); glCompileShader(fragmentShader); // 链接着色器程序 shaderProgram = glCreateProgram(); glAttachShader(shaderProgram, vertexShader); // 确保传递的是程序对象和着色器对象 glAttachShader(shaderProgram, fragmentShader); // 确保传递的是程序对象和着色器对象 glLinkProgram(shaderProgram); // 删除着色器对象 glDeleteShader(vertexShader); glDeleteShader(fragmentShader); // 创建纹理 glGenTextures(1, &textureIdY_); glGenTextures(1, &textureIdU_); glGenTextures(1, &textureIdV_); glBindTexture(GL_TEXTURE_2D, textureIdY_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, textureIdU_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glBindTexture(GL_TEXTURE_2D, textureIdV_); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); // 设置顶点数据和缓冲 float vertices[] = { // positions // texture coords -1.0f, -1.0f, 0.0f, 0.0f, 0.0f, 1.0f, -1.0f, 0.0f, 1.0f, 0.0f, 1.0f, 1.0f, 0.0f, 1.0f, 1.0f, -1.0f, 1.0f, 0.0f, 0.0f, 1.0f }; unsigned int indices[] = { 0, 1, 2, 2, 3, 0 }; glGenVertexArrays(1, &VAO); glGenBuffers(1, &VBO); glGenBuffers(1, &EBO); glBindVertexArray(VAO); glPixelStorei(GL_UNPACK_ALIGNMENT, 1); // 设置纹理对齐方式 glBindBuffer(GL_ARRAY_BUFFER, VBO); glBufferData(GL_ARRAY_BUFFER, sizeof(vertices), vertices, GL_STATIC_DRAW); glBindBuffer(GL_ELEMENT_ARRAY_BUFFER, EBO); glBufferData(GL_ELEMENT_ARRAY_BUFFER, sizeof(indices), indices, GL_STATIC_DRAW); // 位置属性 glVertexAttribPointer(0, 3, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)0); glEnableVertexAttribArray(0); // 纹理坐标属性 glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 5 * sizeof(float), (void*)(3 * sizeof(float))); glEnableVertexAttribArray(1); glBindBuffer(GL_ARRAY_BUFFER, 0); glBindVertexArray(0); LH_WRITE_LOG("OpenGL初始化完成"); } void PlayerGLWidget::resizeGL(int w, int h) { Ortho2DSize_.setWidth(w); Ortho2DSize_.setHeight(h); glViewport(0, 0, w, h); } void PlayerGLWidget::paintGL() { glClear(GL_COLOR_BUFFER_BIT); if (shaderProgram == 0 || VAO == 0 || textureIdY_ == 0 || textureIdU_ == 0 || textureIdV_ == 0) { return; } if (m_yData.isEmpty() || m_uData.isEmpty() || m_vData.isEmpty()) { return; } int uvWidth = m_imageSize.width() / 2; int uvHeight = m_imageSize.height() / 2; // 仅对“缩小显示”启用 mipmap + 三线性过滤,减少锯齿/闪烁;放大显示则保持线性过滤 const bool downscaling = (width() > 0 && height() > 0) && (width() < m_imageSize.width() || height() < m_imageSize.height()); // 使用着色器程序 glUseProgram(shaderProgram); // 设置纹理单元 glUniform1i(glGetUniformLocation(shaderProgram, "textureY"), 0); glUniform1i(glGetUniformLocation(shaderProgram, "textureU"), 1); glUniform1i(glGetUniformLocation(shaderProgram, "textureV"), 2); // 更新纹理数据 glActiveTexture(GL_TEXTURE0); glBindTexture(GL_TEXTURE_2D, textureIdY_); // OpenGL 3.3 Core 下 GL_LUMINANCE 已移除,使用 GL_RED/GL_R8 glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, m_imageSize.width(), m_imageSize.height(), 0, GL_RED, GL_UNSIGNED_BYTE, m_yData.data()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscaling ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); if (downscaling) { glGenerateMipmap(GL_TEXTURE_2D); } glActiveTexture(GL_TEXTURE1); glBindTexture(GL_TEXTURE_2D, textureIdU_); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, uvWidth, uvHeight, 0, GL_RED, GL_UNSIGNED_BYTE, m_uData.data()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscaling ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); if (downscaling) { glGenerateMipmap(GL_TEXTURE_2D); } glActiveTexture(GL_TEXTURE2); glBindTexture(GL_TEXTURE_2D, textureIdV_); glTexImage2D(GL_TEXTURE_2D, 0, GL_R8, uvWidth, uvHeight, 0, GL_RED, GL_UNSIGNED_BYTE, m_vData.data()); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, downscaling ? GL_LINEAR_MIPMAP_LINEAR : GL_LINEAR); if (downscaling) { glGenerateMipmap(GL_TEXTURE_2D); } // 绘制四边形 glBindVertexArray(VAO); glDrawElements(GL_TRIANGLES, 6, GL_UNSIGNED_INT, 0); glBindVertexArray(0); }