You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在Qt+OpenGL中渲染高度大于宽度的YUV视频?

问题分析与解决方案

你的核心问题是未处理视频宽高比与窗口尺寸的适配,导致竖屏视频(高>宽)渲染时被拉伸或显示异常。以下是具体问题点和修复步骤:

问题根源

  1. 视口设置错误:当前paintGL中直接将视口设为视频分辨率glViewport(0, 0, VideoWidth, VideoHeight),但QOpenGLWidget的窗口尺寸通常与视频分辨率不同,竖屏视频的视口会超出窗口范围或比例不匹配。
  2. 无比例适配:顶点坐标使用全屏的(-1~1),未考虑视频宽高比,导致竖屏画面被强行拉伸到窗口的正方形NDC空间中。

修复步骤

1. 修改顶点着色器,引入MVP矩阵

在顶点着色器中添加MVP矩阵,用于调整顶点位置以保持视频比例:

#version 330 core
layout (location = 0) in vec2 position;
out vec2 texCoord;

uniform mat4 mvp; // 新增MVP矩阵,用于比例适配

void main()
{
    gl_Position = mvp * vec4(position, 0.0, 1.0);
    texCoord = position * 0.5 + 0.5;
}

2. 代码中添加宽高比适配逻辑

步骤2.1:添加窗口尺寸成员变量

在GLShowWidget类的私有部分新增变量:

private:
    int windowWidth_ = 0;
    int windowHeight_ = 0; // 存储当前窗口尺寸

步骤2.2:记录窗口尺寸

修改resizeGL函数,保存窗口的实时尺寸:

void GLShowWidget::resizeGL(int w, int h) {
    qDebug() << "resizeGL "<<w<<":"<<h;
    windowWidth_ = w;
    windowHeight_ = h;
}

步骤2.3:在paintGL中计算适配矩阵并修正视口

重写paintGL中的视口设置和MVP矩阵计算逻辑:

void GLShowWidget::paintGL() {
    qDebug() << "GLShowWidget::paintGL";
    glClear(GL_COLOR_BUFFER_BIT);

    // 计算视频与窗口的宽高比
    float videoRatio = static_cast<float>(VideoWidth) / VideoHeight;
    float windowRatio = static_cast<float>(windowWidth_) / windowHeight_;

    float scaleX = 1.0f;
    float scaleY = 1.0f;

    // 根据比例调整缩放,保持视频原始比例
    if (windowRatio > videoRatio) {
        // 窗口更宽,按高度缩放,左右留黑边
        scaleX = videoRatio / windowRatio;
    } else {
        // 窗口更高,按宽度缩放,上下留黑边
        scaleY = windowRatio / videoRatio;
    }

    // 构建正交投影的MVP矩阵
    QMatrix4x4 mvp;
    mvp.scale(scaleX, scaleY, 1.0f);

    // 设置视口为整个窗口,让OpenGL在窗口范围内渲染
    glViewport(0, 0, windowWidth_, windowHeight_);

    vao_.bind();
    vbo_.bind();
    m_sharder_program_->bind();
    // 将MVP矩阵传入着色器
    m_sharder_program_->setUniformValue("mvp", mvp);

    // --- 以下读取YUV数据、绑定纹理的代码保持不变 ---
    if (m_file_.atEnd())
    {
        m_file_.seek(0);
    }

    QByteArray buf1 = m_file_.read(VideoWidth * VideoHeight);
    m_textureYUV_[0]->setData(QOpenGLTexture::Red,QOpenGLTexture::UInt8,buf1.data());
    m_textureYUV_[0]->bind(0);

    QByteArray buf2 = m_file_.read(VideoWidth * VideoHeight / 4);
    m_textureYUV_[1]->setData(QOpenGLTexture::Red,QOpenGLTexture::UInt8,buf2.data());
    m_textureYUV_[1]->bind(1);

    QByteArray buf3 = m_file_.read(VideoWidth * VideoHeight / 4);
    m_textureYUV_[2]->setData(QOpenGLTexture::Red,QOpenGLTexture::UInt8,buf3.data());
    m_textureYUV_[2]->bind(2);

    glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);

    m_textureYUV_[0]->release();
    m_textureYUV_[1]->release();
    m_textureYUV_[2]->release();

    vbo_.release();
    vao_.release();
}

3. 可选优化:修复纹理过滤设置

当前纹理使用了LinearMipMapNearest过滤,但未生成Mipmap,可能引发GPU警告。建议改为无需Mipmap的过滤方式:

// 在initializeGL的纹理创建部分修改
m_textureYUV_[i]->setMinMagFilters(QOpenGLTexture::Linear, QOpenGLTexture::Linear);

效果说明

修改后,无论视频是横屏还是竖屏,都会保持原始比例在窗口中居中显示,避免拉伸或显示不全的问题。

内容的提问来源于stack exchange,提问作者ChengChao

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.08 13:37:02