高度大于宽度时如何渲染YUV视频?

问题描述 投票:0回答:1

我正在尝试使用 Qt 和 OpenGL 渲染 YUV 视频文件。如果它的高度小于它的宽度,它就可以工作;否则,就不会。

这是使用以下代码和分辨率为“886x1920”的 YUV 文件的示例:

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;
}
c++ qt opengl yuv qopenglfunctions
1个回答
0
投票

默认情况下,OpenGL 假定传递给

glTexImage2D
等函数的图像的每条扫描线都从 4 字节边界开始。如果您的代码失败,则图像的宽度为 886 像素,并且是(或看起来是)平面填充而不是像素填充 - 因此扫描线“不”从 4 字节边界开始。在这种情况下,您需要通过使用 glPixelStorei
 显式告诉它扫描行如何对齐来让 OpenGL 了解;例如...
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);

或者,由于您使用的是
QOpenGLTexture

,您可以在设置数据时指定像素传输属性...

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

© www.soinside.com 2019 - 2024. All rights reserved.