以下是使用QOpenGLWidget渲染YUV视频帧并设置饱和度、亮度和对比度后转换为RGB显示的完整示例代码:
#include <QtWidgets/QApplication>
#include <QtWidgets/QOpenGLWidget>
#include <QtGui/QOpenGLFunctions>
#include <QtGui/QOpenGLShaderProgram>
#include <QtGui/QOpenGLBuffer>
#include <QtGui/QMatrix4x4>
#include <QtCore/QFile>
class VideoWidget : public QOpenGLWidget, protected QOpenGLFunctions
{
public:
VideoWidget(QWidget *parent = 0) : QOpenGLWidget(parent)
{
m_yuvBuffer.resize(m_width * m_height + m_width / 2 * m_height / 2 * 2);
// 初始化shader程序
initializeOpenGLFunctions();
m_shaderProgram = new QOpenGLShaderProgram(this);
m_shaderProgram->addShaderFromSourceFile(QOpenGLShader::Vertex, ":/vertex.glsl");
m_shaderProgram->addShaderFromSourceFile(QOpenGLShader::Fragment, ":/fragment.glsl");
m_shaderProgram->link();
m_shaderProgram->bind();
// 初始化顶点缓冲区和纹理坐标缓冲区
QVector<float> vertices = {
-1.0f, 1.0f,
-1.0f, -1.0f,
1.0f, 1.0f,
1.0f, -1.0f,
};
m_vertexBuffer.create();
m_vertexBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_vertexBuffer.bind();
m_vertexBuffer.allocate(vertices.constData(), vertices.size() * sizeof(float));
QVector<float> texCoords = {
0.0f, 1.0f,
0.0f, 0.0f,
1.0f, 1.0f,
1.0f, 0.0f,
};
m_texCoordBuffer.create();
m_texCoordBuffer.setUsagePattern(QOpenGLBuffer::StaticDraw);
m_texCoordBuffer.bind();
m_texCoordBuffer.allocate(texCoords.constData(), texCoords.size() * sizeof(float));
// 创建纹理
glGenTextures(3, m_textures);
for (int i = 0; i < 3; ++i) {
glBindTexture(GL_TEXTURE_2D, m_textures[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
}
}
~VideoWidget()
{
makeCurrent();
m_vertexBuffer.destroy();
m_texCoordBuffer.destroy();
glDeleteTextures(3, m_textures);
doneCurrent();
}
void setYUVData(const QByteArray &data, int width = 1920, int height = 1080)
{
if (data.size() != width * height + width / 2 * height / 2 * 2) {
return;
}
m_width = width;
m_height = height;
m_yuvBuffer = data;
update();
}
void setBrightness(qreal brightness)
{
m_brightness = brightness;
update();
}
void setContrast(qreal contrast)
{
m_contrast = contrast;
update();
}
void setSaturation(qreal saturation)
{
m_saturation = saturation;
update();
}
protected:
void initializeGL()
{
glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
}
void resizeGL(int w, int h)
{
glViewport(0, 0, w, h);
}
void paintGL()
{
// 矩阵变换
QMatrix4x4 modelViewProjection;
modelViewProjection.setToIdentity();
modelViewProjection.ortho(-1.0f, 1.0f, -1.0f, 1.0f, -1.0f, 1.0f);
// 设置shader uniform
m_shaderProgram->bind();
m_shaderProgram->setUniformValue("u_modelViewProjection", modelViewProjection);
m_shaderProgram->setUniformValue("u_textureY", 0);
m_shaderProgram->setUniformValue("u_textureU", 1);
m_shaderProgram->setUniformValue("u_textureV", 2);
m_shaderProgram->setUniformValue("u_brightness", m_brightness);
m_shaderProgram->setUniformValue("u_contrast", m_contrast);
m_shaderProgram->setUniformValue("u_saturation", m_saturation);
// 更新纹理
glActiveTexture(GL_TEXTURE0);
glBindTexture(GL_TEXTURE_2D, m_textures[0]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_width, m_height, 0, GL_RED, GL_UNSIGNED_BYTE, m_yuvBuffer.constData());
glActiveTexture(GL_TEXTURE1);
glBindTexture(GL_TEXTURE_2D, m_textures[1]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_width / 2, m_height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, m_yuvBuffer.constData() + m_width * m_height);
glActiveTexture(GL_TEXTURE2);
glBindTexture(GL_TEXTURE_2D, m_textures[2]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_RED, m_width / 2, m_height / 2, 0, GL_RED, GL_UNSIGNED_BYTE, m_yuvBuffer.constData() + m_width * m_height * 5 / 4);
// 开始绘制
glEnableVertexAttribArray(0);
m_vertexBuffer.bind();
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glEnableVertexAttribArray(1);
m_texCoordBuffer.bind();
glVertexAttribPointer(1, 2, GL_FLOAT, GL_FALSE, 0, nullptr);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
glDisableVertexAttribArray(0);
glDisableVertexAttribArray(1);
m_shaderProgram->release();
}
private:
QOpenGLShaderProgram *m_shaderProgram;
QOpenGLBuffer m_vertexBuffer;
QOpenGLBuffer m_texCoordBuffer;
GLuint m_textures[3];
QByteArray m_yuvBuffer;
int m_width = 1920;
int m_height = 1080;
qreal m_brightness = 0.0;
qreal m_contrast = 1.0;
qreal m_saturation = 1.0;
};
int main(int argc, char *argv[])
{
QApplication app(argc, argv);
VideoWidget widget;
widget.show();
QFile file(":/test.yuv");
file.open(QIODevice::ReadOnly);
widget.setYUVData(file.readAll(), 1920, 1080);
widget.setBrightness(0.5);
widget.setContrast(1.5);
widget.setSaturation(2.0);
return app.exec();
}
其中,vertex.glsl代码如下:
attribute vec4 a_position;
attribute vec2 a_texCoord;
varying vec2 v_texCoord;
uniform mat4 u_modelViewProjection;
void main()
{
gl_Position = u_modelViewProjection * a_position;
v_texCoord = a_texCoord;
}
fragment.glsl代码如下:
precision highp float;
varying vec2 v_texCoord;
uniform sampler2D u_textureY;
uniform sampler2D u_textureU;
uniform sampler2D u_textureV;
uniform float u_brightness;
uniform float u_contrast;
uniform float u_saturation;
vec3 RGB2YUV(vec3 rgb)
{
return vec3(
0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b,
-0.14713 * rgb.r - 0.28886 * rgb.g + 0.436 * rgb.b,
0.615 * rgb.r - 0.51498 * rgb.g - 0.10001 * rgb.b
);
}
vec3 YUV2RGB(vec3 yuv)
{
return vec3(
yuv.r + 1.13983 * yuv.b,
yuv.r - 0.39465 * yuv.g - 0.58060 * yuv.b,
yuv.r + 2.03211 * yuv.g
);
}
void main()
{
vec3 yuv;
yuv.x = texture2D(u_textureY, v_texCoord).r;
yuv.y = texture2D(u_textureU, v_texCoord / 2.0).r - 0.5;
yuv.z = texture2D(u_textureV, v_texCoord / 2.0).r - 0.5;
vec3 rgb = YUV2RGB(yuv);
rgb = rgb * u_contrast + vec3((u_brightness - 0.5) * 2.0, (u_brightness - 0.5) * 2.0, (u_brightness - 0.5) * 2.0);
float gray = 0.299 * rgb.r + 0.587 * rgb.g + 0.114 * rgb.b;
rgb = mix(vec3(gray), rgb, u_saturation);
gl_FragColor = vec4(rgb, 1.0);
}
其中,RGB2YUV和YUV2RGB函数用于颜色空间的转换。在主函数中,首先从文件中读取了一个YUV视频帧,并设置了亮度、对比度、饱和度三个参数。在paintGL函数中,首先进行矩阵变换和uniform赋值,然后更新纹理,最后开启顶点属性和纹理坐标属性,执行绘制操作。