我正在尝试使用Qt和OpenGL渲染一个YUV视频文件。如果它的高度小于它的宽度,它就工作;否则,它不会。
下面是一个使用下面的代码和YUV文件的例子,分辨率为“886 x1920”:
one frame of 886x1920.yuv
头文件:
#ifndef GL_SHOW_WIDGET_H
#define GL_SHOW_WIDGET_H
#include <QtOpenGLWidgets/QOpenGLWidget>
#include <QOpenGLShaderProgram>
#include <QOpenGLFunctions>
#include <QOpenGLTexture>
#include <QFile>
#include <QOpenGLVertexArrayObject>
#include <QOpenGLBuffer>
#include <vector>
class GLShowWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
Q_OBJECT
public:
GLShowWidget(QWidget* parent = nullptr);
~GLShowWidget();
protected:
void initializeGL() Q_DECL_OVERRIDE;
void resizeGL(int w, int h) Q_DECL_OVERRIDE;
void paintGL() Q_DECL_OVERRIDE;
private:
QOpenGLVertexArrayObject vao_;
QOpenGLBuffer vbo_;
QOpenGLShaderProgram* m_sharder_program_;
std::vector<QOpenGLTexture*> m_textureYUV_;
// for test
QFile m_file_;
};
#endif // GL_SHOW_WIDGET_H
源文件:
#include "gl_show_widget.h"
#include <QOpenGLTexture>
#include <QOpenGLBuffer>
#include <QMouseEvent>
#include <QTimer>
static const uint32_t VideoWidth = 886;
static const uint32_t VideoHeight = 1920;
#define GL_VERSION_this "#version 330 core\n"
#define GET_GLSTR(x) GL_VERSION_this#x
const char *vsrc = GET_GLSTR(
layout (location = 0) in vec2 position;
out vec2 texCoord;
void main()
{
gl_Position = vec4(position, 0.0, 1.0);
texCoord = position * 0.5 + 0.5;
}
);
const char *fsrc =GET_GLSTR(
in vec2 texCoord;
uniform sampler2D yTexture;
uniform sampler2D uTexture;
uniform sampler2D vTexture;
out vec4 fragColor;
void main()
{
float y = texture(yTexture, texCoord).r;
float u = texture(uTexture, texCoord).r - 0.5;
float v = texture(vTexture, texCoord).r - 0.5;
float r = y + 1.13983 * v;
float g = y - 0.39465 * u - 0.5806 * v;
float b = y + 2.03211 * u;
fragColor = vec4(r, g, b, 1.0);
}
);
GLShowWidget::GLShowWidget(QWidget *parent) : QOpenGLWidget(parent)
{
qDebug() << "GLShowWidget::GLShowWidget";
vbo_ = QOpenGLBuffer(QOpenGLBuffer::VertexBuffer);
m_sharder_program_ = new QOpenGLShaderProgram();
m_textureYUV_ = std::vector<QOpenGLTexture*>(3, nullptr);
QSurfaceFormat format;
format.setDepthBufferSize(24);
format.setStencilBufferSize(8);
format.setVersion(3, 3);
format.setProfile(QSurfaceFormat::CoreProfile);
QSurfaceFormat::setDefaultFormat(format);
setFormat(format);
}
GLShowWidget::~GLShowWidget()
{
qDebug("GLShowWidget::~GLShowWidget");
if (!m_textureYUV_.empty()) {
for (auto texture : m_textureYUV_) {
texture->destroy();
delete texture;
}
m_textureYUV_.clear();
}
if (m_sharder_program_) {
m_sharder_program_->release();
delete m_sharder_program_;
m_sharder_program_ = nullptr;
}
vbo_.destroy();
vao_.destroy();
}
void GLShowWidget::initializeGL() {
qDebug() << "GLShowWidget::initializeGL";
m_file_.setFileName("path_to_file/886x1920.yuv");
if(!m_file_.open(QIODevice::ReadOnly))
{
qDebug() << "打开失败!";
return;
}
initializeOpenGLFunctions();
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
for(int i = 0; i < 3; i++)
{
m_textureYUV_[i] = new QOpenGLTexture(QOpenGLTexture::Target2D);
m_textureYUV_[i]->create();
if(i == 0)
{
m_textureYUV_[i]->setSize(VideoWidth, VideoHeight);
}
else
{
m_textureYUV_[i]->setSize(VideoWidth / 2, VideoHeight / 2);
}
m_textureYUV_[i]->setMinMagFilters(QOpenGLTexture::LinearMipMapNearest, QOpenGLTexture::Linear);
m_textureYUV_[i]->setFormat(QOpenGLTexture::R8_UNorm);
m_textureYUV_[i]->setWrapMode(QOpenGLTexture::ClampToEdge);
m_textureYUV_[i]->allocateStorage();
qDebug() << "textureId: " << m_textureYUV_[i]->textureId();
}
vao_.create();
vao_.bind();
vbo_.create();
vbo_.bind();
m_sharder_program_->addShaderFromSourceCode(QOpenGLShader::Vertex, vsrc);
m_sharder_program_->addShaderFromSourceCode(QOpenGLShader::Fragment, fsrc);
m_sharder_program_->link();
m_sharder_program_->bind();
float vertices[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
};
vbo_.allocate(vertices, sizeof(vertices));
vbo_.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_sharder_program_->setAttributeBuffer(0, GL_FLOAT, 0, 2, 2 * sizeof(float));
m_sharder_program_->enableAttributeArray(0);
m_sharder_program_->setUniformValue("yTexture", 0);
m_sharder_program_->setUniformValue("uTexture", 1);
m_sharder_program_->setUniformValue("vTexture", 2);
vbo_.release();
vao_.release();
QTimer *ti = new QTimer(this);
connect(ti, SIGNAL(timeout()), this, SLOT(update()));
ti->start(30);
}
void GLShowWidget::paintGL() {
qDebug() << "GLShowWidget::paintGL";
glClear(GL_COLOR_BUFFER_BIT);
glViewport(0, 0, VideoWidth, VideoHeight);
vao_.bind();
vbo_.bind();
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();
}
void GLShowWidget::resizeGL(int w, int h) {
qDebug() << "resizeGL "<<w<<":"<<h;
}
1条答案
按热度按时间hvvq6cgz1#
默认情况下,OpenGL假定传递给
glTexImage2D
等函数的图像的每一扫描线都从4字节边界开始。在代码失败的情况下,图像的宽度为886像素,并且是(或看起来是)平面 Package 而不是像素 Package --因此扫描行 * 不会 * 开始于4字节边界。在这种情况下,你需要让OpenGL知道,通过显式地告诉它扫描行是如何使用glPixelStorei
对齐的;例如或者,由于您使用的是
QOpenGLTexture
,您可以在设置数据时指定像素传输属性...