qml使用opengl渲染yuv

时间:2022-03-19 19:00:36

前言

使用qml制作视频播放器。在视频帧解码出来后,最后免不了还要交给qml还显示。

使用qml制作播放器,两种架构的比较

qml提供图片传入接口,解码完成后,将QImage 交给qml显示。
这种直接抛弃了。
原因是:
1,图片传输太慢
2,整个动作是交个cpu来执行的,在性能弱的机子上,刷新图片很慢
自己实现继承自QQuickItem的类,数据在Item内部流转,然后像使用普通qml控件的方式来调用相应的功能。
这是推荐使用的方式。QtAV就是这么弄的。但是它封装的太好了,很多时候,我们可能只需要其中一点点的功能模块,拆解起来很难。因此不得不又造了一遍*。

前辈们的肩膀

qt5_qml_Opengl_shader,这篇博客已经完成的差不多了。但还是写了一遍,有些细节可能与自己理解的不一样。有的细节要特别注意处理一下。

加载Shader语言

Sharder简要说明

Sharder的说明,网上汗牛充栋。
我们用到的功能:简短点说说就是,告诉opengl你要传入的三维物体形状和渲染所需的颜色。

Sharder的加载

我没有选择将sharder内容直接包含在代码中做法,而是单独放到了一个资源文件中。

没有选择将sharder内容直接包含在代码中做法,而是单独放到了一个资源文件中。
m_program = new QOpenGLShaderProgram();
bool ok0 = m_program->addShaderFromSourceFile(QOpenGLShader::Vertex,":/os/openglResource/qmlopenglvideovertexshader.vsh");
bool ok1 = false;
if(QOpenGLContext::currentContext()->isOpenGLES())
{//这里opengl与opengles加载不同的Shader,不用一样的是因为,两者还是有那么一点点的区别的(关键词precision在opengl中没有)
ok1 = m_program->addShaderFromSourceFile(QOpenGLShader::Fragment,":/os/openglResource/qmlopenglvideofragmentshader.fsh");
}
else
{
ok1 = m_program->addShaderFromSourceFile(QOpenGLShader::Fragment,":/os/openglResource/qmlopenglvideofragmentshader.frag");
}

其中opengl es 加载了不同的fragmentshader,因为opengl和opengl es还是有那么一点不同的,这个要取决于设备
主要是这里的区别:

//这里的宏判定必须加,不然绝大部分的android tv不会显示
#ifdef GL_FRAGMENT_PRECISION_HIGH
precision highp float;
#else
precision mediump float;
#endif

qml渲染流程

opengl作为组件被qml使用,存在着一个谁先渲染的问题。同一区域,后渲染的会覆盖之前渲染的。

这个渲染的次序决定了层叠的次序

//先渲染qml,再渲染opengl
connect(window(), &QQuickWindow::afterRendering, m_renderer, &ALOpenglQmlVideoShowRenderer::paint, Qt::DirectConnection);
//先渲染opengl,再渲染qml
connect(window(), &QQuickWindow::afterRendering, m_renderer, &ALOpenglQmlVideoShowRenderer::paint, Qt::DirectConnection);

opengl的渲染

其实这里没有什么好说的,感觉整个流程是死的,没有太大的发挥空间。
在整个demo中,我尽量使用Qt的opengl类,实在没有办法了才用opengl原生的。
但我总觉得Qt这里做的不是很好。

m_program->bind();
int vertsLocation = m_program->attributeLocation("vertexIn");
int textureLocation = m_program->attributeLocation("textureIn");
m_program->enableAttributeArray(vertsLocation);
m_program->enableAttributeArray(textureLocation);
m_program->setAttributeArray(vertsLocation, GL_FLOAT, vertexVertices,2);
m_program->setAttributeArray(textureLocation, GL_FLOAT, textureVertices,2);

textureUniformY = m_program->uniformLocation("tex_y");
textureUniformU = m_program->uniformLocation("tex_u");
textureUniformV = m_program->uniformLocation("tex_v");
glViewport(m_nX, m_viewportSize.height() - m_nY - m_nHeight , m_nWidth, m_nHeight);
QByteArray t_byteArray;//
{
QMutexLocker locker(&m_imageDataMutex);
t_byteArray= m_imageData;
}
m_pBufYuv420p = (unsigned char*)t_byteArray.data();
glActiveTexture(GL_TEXTURE0);
//使用来自y数据生成纹理
glBindTexture(GL_TEXTURE_2D, id_y);
//使用内存中m_pBufYuv420p数据创建真正的y数据纹理
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW, m_nVideoH, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, m_pBufYuv420p);
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);
//加载u数据纹理
glActiveTexture(GL_TEXTURE1);//激活纹理单元GL_TEXTURE1
glBindTexture(GL_TEXTURE_2D, id_u);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE, (char*)m_pBufYuv420p + m_nVideoH*m_nVideoW);
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);
//加载v数据纹理
glActiveTexture(GL_TEXTURE2);//激活纹理单元GL_TEXTURE2
glBindTexture(GL_TEXTURE_2D, id_v);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,(char*)m_pBufYuv420p + m_nVideoH*m_nVideoW * 5 / 4);
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);
glUniform1i(textureUniformY, 0);
//指定u纹理要使用新值
glUniform1i(textureUniformU, 1);
//指定v纹理要使用新值
glUniform1i(textureUniformV, 2);
// glClearColor(0.0, 0.5, 0.0, 1.0);
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
// Draw the triangle !
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4); // Starting from vertex 0; 3 vertices total -> 1 triangle
m_program->disableAttributeArray(textureLocation);
m_program->disableAttributeArray(vertsLocation);
m_program->release();
// Not strictly needed for this example, but generally useful for when
// mixing with raw OpenGL.
m_window->resetOpenGLState();

其中需要注意的是

// glClearColor(0.0, 0.5, 0.0, 1.0); 
// glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);

作用是清空背景,如果打开的话。先前绘制的内容都会被清空。

opengl 对yuv数据的要求

除了格式要求,还有就是传入的数据中没有没用的数据。
也就是内存对齐为1。

glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, m_nVideoW/2, m_nVideoH/2, 0, GL_LUMINANCE, GL_UNSIGNED_BYTE,(char*)m_pBufYuv420p + m_nVideoH*m_nVideoW * 5 / 4);

这里源代码
learn_openglunderqml_5.zip