Opengl 渲染YUYV(YUV422)图像

7 篇文章 1 订阅
2 篇文章 0 订阅

Opengl 渲染YUYV(YUV422)图像

//YUYV数据格式: 1个Y代表一个像素,YUYV代表2个像素,4字节,[所以2字节/每像素]
//w=width //图片宽度
//h=high //图片高度
//p=wh //图片像素总数
//c=p
2 //数组总长度
//d=(w*h)/2 //一半像素
————————————————
版权声明:本文为CSDN博主「科技ing」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/WMX843230304WMX/article/details/119898913

h文件

#ifndef GLYUVWIDGET_H
#define GLYUVWIDGET_H


#include <QOpenGLWidget>
#include <QOpenGLFunctions>
#include <QOpenGLBuffer>
#include <QTimer>

QT_FORWARD_DECLARE_CLASS(QOpenGLShaderProgram)
QT_FORWARD_DECLARE_CLASS(QOpenGLTexture)

class GLYuvWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
    Q_OBJECT
public:
    GLYuvWidget(QWidget *parent =0);
    ~GLYuvWidget();

public slots:
    void slotShowYuv(uchar *ptr,uint width,uint height); //显示一帧Yuv图像
    void onShowYUV(uchar *Yptr,uchar *Uptr,uchar *Vptr,uint width,uint height);

protected:
    void initializeGL() Q_DECL_OVERRIDE;
    void paintGL() Q_DECL_OVERRIDE;

private:
    QOpenGLShaderProgram *program;
    QOpenGLBuffer vbo;

    //opengl中y、u、v分量位置
    GLuint posUniformY,posUniformU,posUniformV;

    //纹理
    QOpenGLTexture *textureY = nullptr,*textureU = nullptr,*textureV = nullptr;

    //纹理ID,创建错误返回0
    GLuint m_idY,m_idU,m_idV;

    //像素分辨率
    uint videoW,videoH;

    uchar *m_yuvPtr = nullptr;
    //Y U V 分量
    uchar *m_yPtr = nullptr;
    uchar *m_uPtr = nullptr;
    uchar *m_vPtr = nullptr;
};


#endif // GLYUVWIDGET_H


cpp


#include "glyuvwidget.h"
#include <QOpenGLShaderProgram>
#include <QOpenGLTexture>
#include <QDebug>

#define VERTEXIN 0
#define TEXTUREIN 1


const char *vshadersrc ="attribute vec4 vertexIn; \
attribute vec2 textureIn; \
varying vec2 textureOut;  \
void main(void)           \
{                         \
    gl_Position = vertexIn; \
    textureOut = textureIn; \
}";


const char *fshadersrc = "varying vec2 textureOut; \
uniform sampler2D tex_y; \
uniform sampler2D tex_u; \
uniform sampler2D tex_v; \
void main(void) \
{ \
    vec3 yuv; \
    vec3 rgb; \
    yuv.x = texture2D(tex_y, textureOut).r; \
    yuv.y = texture2D(tex_u, textureOut).r - 0.5; \
    yuv.z = texture2D(tex_v, textureOut).r - 0.5; \
    rgb = mat3( 1,       1,         1, \
                0,       -0.39465,  2.03211, \
                1.13983, -0.58060,  0) * yuv; \
    gl_FragColor = vec4(rgb, 1); \
}";






GLYuvWidget::GLYuvWidget(QWidget *parent):
    QOpenGLWidget(parent)
{
}

GLYuvWidget::~GLYuvWidget()
{
    makeCurrent();
    vbo.destroy();
    textureY->destroy();
    textureU->destroy();
    textureV->destroy();
    doneCurrent();
}

void GLYuvWidget::slotShowYuv(uchar *ptr, uint width, uint height)
{
    m_yuvPtr = ptr;

    videoW = width;
    videoH = height;
    update();
}

void GLYuvWidget::onShowYUV(uchar *Yptr, uchar *Uptr, uchar *Vptr, uint width, uint height)
{
    m_yPtr = Yptr;
    m_uPtr = Uptr;
    m_vPtr = Vptr;

    videoW = width;
    videoH = height;
    update();
}

void GLYuvWidget::initializeGL()
{
    initializeOpenGLFunctions();
    glEnable(GL_DEPTH_TEST);

    static const GLfloat vertices[]{
        //顶点坐标
        -1.0f,-1.0f,
        -1.0f,+1.0f,
        +1.0f,+1.0f,
        +1.0f,-1.0f,
        //纹理坐标
        0.0f,1.0f,
        0.0f,0.0f,
        1.0f,0.0f,
        1.0f,1.0f,
    };


    vbo.create();
    vbo.bind();
    vbo.allocate(vertices,sizeof(vertices));


    program = new QOpenGLShaderProgram(this);
     //Compile vertex shader
//    if (!program->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vs.glsl"))
//        close();

//    // Compile fragment shader
//    if (!program->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fs.glsl"))
//        close();

    program->addShaderFromSourceCode(QOpenGLShader::Vertex,vshadersrc);
    program->addShaderFromSourceCode(QOpenGLShader::Fragment,fshadersrc);

    //绑定输入的定点坐标和纹理坐标属性
    program->bindAttributeLocation("vertexIn",VERTEXIN);
    program->bindAttributeLocation("textureIn",TEXTUREIN);

    // Link shader pipeline
    if (!program->link())
    {
        qDebug()<<"program->link error";
        close();
    }


    // Bind shader pipeline for use
    if (!program->bind())
    {
        qDebug()<<"program->bind error";
        close();
    }

    //启用并且设置定点位置和纹理坐标
    program->enableAttributeArray(VERTEXIN);
    program->enableAttributeArray(TEXTUREIN);
    program->setAttributeBuffer(VERTEXIN,GL_FLOAT, 0, 2, 2*sizeof(GLfloat));
    program->setAttributeBuffer(TEXTUREIN,GL_FLOAT,8*sizeof(GLfloat), 2 , 2*sizeof(GLfloat));

    //定位shader中 uniform变量
    posUniformY = program->uniformLocation("tex_y");
    posUniformU = program->uniformLocation("tex_u");
    posUniformV = program->uniformLocation("tex_v");

    //创建纹理并且获取id
    textureY = new QOpenGLTexture(QOpenGLTexture::Target2D);
    textureU = new QOpenGLTexture(QOpenGLTexture::Target2D);
    textureV = new QOpenGLTexture(QOpenGLTexture::Target2D);
    textureY->create();
    textureU->create();
    textureV->create();
    m_idY = textureY->textureId();
    m_idU = textureU->textureId();
    m_idV = textureV->textureId();

    glClearColor(0.0,0.0,0.0,0.0);

}

void GLYuvWidget::paintGL()
{
    //激活纹理单元GL_TEXTURE0,系统里面的
    glActiveTexture(GL_TEXTURE0);

    //绑定y分量纹理对象id到激活的纹理单元
    glBindTexture(GL_TEXTURE_2D,m_idY);


    ///------------------------
    /// glTexImage2D(
    /// GLenum target,
    /// GLint level,
    /// GLint internalformat,
    /// GLsizei width,
    /// GLsizei height,
    /// GLint border,
    /// GLenum format,
    /// GLenum type,
    /// const GLvoid* pixels)
    /* 函数很长,参数也不少,所以我们一个一个地讲解:
    第一个参数指定了纹理目标(Target)。设置为GL_TEXTURE_2D意味着会生成与当前绑定的纹理对象在同一个目标上的纹理
            (任何绑定到GL_TEXTURE_1D和GL_TEXTURE_3D的纹理不会受到影响)。
    第二个参数为纹理指定多级渐远纹理的级别,如果你希望单独手动设置每个多级渐远纹理的级别的话。这里我们填0,也就是基本级别。
    第三个参数告诉OpenGL我们希望把纹理储存为何种格式。
    第四个和第五个参数设置最终的纹理的宽度和高度。我们之前加载图像的时候储存了它们,所以我们使用对应的变量。
    第六个参数应该总是被设为0(历史遗留的问题)。
    第七个参数定义了源图的格式。
    第八个参数定义了源图的数据类型。
    最后一个参数是真正的图像数据。

    当调用glTexImage2D时,当前绑定的纹理对象就会被附加上纹理图像。然而,目前只有基本级别(Base-level)的纹理图像被加载了,
    如果要使用多级渐远纹理,我们必须手动设置所有不同的图像(不断递增第二个参数)。
    或者,直接在生成纹理之后调用glGenerateMipmap。这会为当前绑定的纹理自动生成所有需要的多级渐远纹理。
    */

    //使用内存中的数据创建真正的y分量纹理数据
    glTexImage2D(GL_TEXTURE_2D,0,GL_RED,videoW,videoH,0,GL_RED,GL_UNSIGNED_BYTE,m_yPtr);//m_yPtr 大小是(videoW*videoH)
    //https://blog.csdn.net/xipiaoyouzi/article/details/53584798 纹理参数解析
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    //激活纹理单元GL_TEXTURE1
    glActiveTexture(GL_TEXTURE1);
    //绑定u分量纹理对象id到激活的纹理单元
    glBindTexture(GL_TEXTURE_2D,m_idU);
    //使用内存中的数据创建真正的u分量纹理数据
    glTexImage2D(GL_TEXTURE_2D,0,GL_RED,videoW>>1 , videoH,0,GL_RED,GL_UNSIGNED_BYTE,m_uPtr);//m_uPtr 大小是(videoW*videoH / 2)
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);

    //激活纹理单元GL_TEXTURE2
    glActiveTexture(GL_TEXTURE2);
    //绑定v分量纹理对象id到激活的纹理单元
    glBindTexture(GL_TEXTURE_2D,m_idV);
    //使用内存中的数据创建真正的v分量纹理数据
    glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, videoW>>1 , videoH, 0, GL_RED, GL_UNSIGNED_BYTE, m_vPtr);//m_vPtr 大小是(videoW*videoH / 2)
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
    glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
    //指定y纹理要使用新值
    glUniform1i(posUniformY, 0);
    //指定u纹理要使用新值
    glUniform1i(posUniformU, 1);
    //指定v纹理要使用新值
    glUniform1i(posUniformV, 2);
    //使用顶点数组方式绘制图形
    glDrawArrays(GL_TRIANGLE_FAN, 0, 4);
}

main.cpp

int main(int argc, char *argv[])
{
    QCoreApplication::setAttribute(Qt::AA_EnableHighDpiScaling);

    QApplication app(argc, argv);
    //相机
    QSharedPointer<QCamera> cameraPtr(new QCamera(cameras.at(0)));
    //取景器
    QSharedPointer<MyVideoSurface> surface(new MyVideoSurface);
    cameraPtr.get()->setViewfinder(qobject_cast<MyVideoSurface*>(surface.get()));

    //opengl widget
    GLYuvWidget openGLRenderWidget;
    //QObject::connect(surface.get(),&MyVideoSurface::sendOpenGLYuv,&openGLRenderWidget,&GLYuvWidget::slotShowYuv);
    QObject::connect(surface.get(),&MyVideoSurface::sendYUVChanged,&openGLRenderWidget,&GLYuvWidget::onShowYUV);
    openGLRenderWidget.show();

    cameraPtr->start();
    return app.exec();
}

其中 MyVideoSurface 参考 《自定义 QAbstractVideoSurface 添加视频水印
发出YUYV信号在

bool MyVideoSurface::present(const QVideoFrame &frame)
{

    QVideoFrame frametodraw(frame);

   if(!frametodraw.map(QAbstractVideoBuffer::ReadOnly))
   {
      setError(ResourceError);
      return false;
   }
    unsigned char*bits = (unsigned char *)frametodraw.bits();
	 SpliterYUYV(frametodraw.width(),frametodraw.height(),
                (unsigned char *)bits,
                (unsigned char *)YBuf,
                (unsigned char *)UBuf,
                (unsigned char *)VBuf);
    sendYUVChanged((uchar*)YBuf,(uchar*)UBuf,(uchar*)VBuf,frametodraw.width(),frametodraw.height());
}
评论 9
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值