如何在Qt+OpenGL中渲染高度大于宽度的YUV视频?
问题分析与解决方案
你的核心问题是未处理视频宽高比与窗口尺寸的适配,导致竖屏视频(高>宽)渲染时被拉伸或显示异常。以下是具体问题点和修复步骤:
问题根源
- 视口设置错误:当前
paintGL中直接将视口设为视频分辨率glViewport(0, 0, VideoWidth, VideoHeight),但QOpenGLWidget的窗口尺寸通常与视频分辨率不同,竖屏视频的视口会超出窗口范围或比例不匹配。 - 无比例适配:顶点坐标使用全屏的
(-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
相关产品推荐
相关产品推荐

