Qt5.4之后,OpenGL在Qt中可以通过QOpenGLWidget和QOpenGLFunctions来实现,以下Demo(只展示OpenGL相关部分)解码出AVFrame后对其进行渲染。
//顶点Shader
static const char vertexShaderSource[] =
"attribute vec4 position; \n"
"attribute vec2 texcoord; \n"
"varying vec2 v_texcoord; \n"
"void main() \n"
"{ \n"
" gl_Position = position; \n"
" v_texcoord = texcoord.xy; \n"
"} \n";
//片元Shader
static const char yuvFragmentShaderSource[] =
"precision mediump float; \n"
"varying vec2 v_texcoord; \n"
"uniform sampler2D s_texture_y; \n"
"uniform sampler2D s_texture_u; \n"
"uniform sampler2D s_texture_v; \n"
"void main() \n"
"{ \n"
" vec3 yuv; \n"
" vec3 rgb; \n"
" yuv.r = texture2D(s_texture_y, v_texcoord).r; \n"
" yuv.g = texture2D(s_texture_u, v_texcoord).r - 0.5;\n"
" yuv.b = texture2D(s_texture_v, v_texcoord).r - 0.5;\n"
" rgb = mat3(1.0, 1.0, 1.0, \n"
" 0.0, -0.39465, 2.03211, \n"
" 1.13983, -0.58060, 0.0)*yuv; \n"
" gl_FragColor = vec4(rgb,1.0); \n"
"} \n";
//Vertex coordinates
static const GLfloat verCoords[] = {
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f
};
//Material coordinates
static const GLfloat texCoords[] = {
0.0f, 1.0f,
1.0f, 1.0f,
0.0f, 0.0f,
1.0f, 0.0f
};
头文件部分
class VideoWidget : public QOpenGLWidget, protected QOpenGLFunctions {
Q_OBJECT
public:
explicit VideoWidget(QWidget *parent = 0);
~VideoWidget() override;
GLuint VideoWidget::InitShader(GLenum type, const GLchar *code);
protected:
void initializeGL() override;
void paintGL() override;
void resizeGL(int w, int h) override;
private:
int viewPortWidth_ = 0;
int viewPortHeight_ = 0;
GLuint program;
std::array<GLuint, 3> textures;
};
在initializeGL函数中需要添加initializeOpenGLFunctions()完成初始化,然后启动定时器QTimer
void VideoWidget::initializeGL() {
initializeOpenGLFunctions();
//初始化等相关加载工作
// ......
// ......
QTimer *time = new QTimer(this);
connect(time, SIGNAL(timeout()), this, SLOT(update()));
time->start(15);
}
void VideoWidget::resizeGL(int w, int h) {
LOG(INFO) << "resizeGL: " << w << "," << h;
viewPortWidth_ = w;
viewPortHeight_ = h;
}
Shader的创建、编译
GLuint VideoWidget::InitShader(GLenum type, const GLchar *code) {
GLuint shader = glCreateShader(type);
if (shader == 0 || shader == GL_INVALID_ENUM) {
LOG(ERROR) << "Failed to create shader";
return 0;
}
glShaderSource(shader, 1, &code, NULL);
glCompileShader(shader);
#ifdef DEBUG
GLint logLength;
glGetShaderiv(shader, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetShaderInfoLog(shader, logLength, &logLength, log);
LOG(INFO) << "Shader compile log : " << log;
free(log);
}
#endif
GLint status;
glGetShaderiv(shader, GL_COMPILE_STATUS, &status);
if (status == GL_FALSE) {
glDeleteShader(shader);
LOG(ERROR) << "Failed to compile shader";
return 0;
}
return shader;
}
void VideoWidget::paintGL() {
//需要获取frame
//......
//......
auto frame = mediaSamples[0]->GetFrame();
glViewport(0, 0, viewPortWidth_, viewPortHeight_);
glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
glGenTextures(3, textures.data());
int width = frame->width;
int height = frame->height;
int widths[3] = { width, width >> 1, width >> 1 };
int heights[3] = { height, height >> 1, height >> 1 };
//支持的是yuv格式数据
for (int i = 0; i < 3; ++i) {
glPixelStorei(GL_UNPACK_ROW_LENGTH, frame->linesize[i]);
glBindTexture(GL_TEXTURE_2D, textures[i]);
glTexImage2D(GL_TEXTURE_2D, 0, GL_LUMINANCE, widths[i], heights[i],
0, GL_LUMINANCE, GL_UNSIGNED_BYTE, frame->data[i]);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
}
glBindTexture(GL_TEXTURE_2D, 0);
program = glCreateProgram();
GLuint vShader = InitShader(GL_VERTEX_SHADER, vertexShaderSource);
if (!vShader) {
glDeleteShader(vShader);
CHECK(false) << "CreateShader GL_VERTEX_SHADER failed";
}
GLuint fShader = InitShader(GL_FRAGMENT_SHADER, yuvFragmentShaderSource);
if (!fShader) {
glDeleteShader(fShader);
CHECK(false) << "CreateShader GL_VERTEX_SHADER failed";
}
glAttachShader(program, vShader);
glAttachShader(program, fShader);
glBindAttribLocation(program, 0, "position");
glBindAttribLocation(program, 1, "texcoord");
glLinkProgram(program);
GLint status;
glGetProgramiv(program, GL_LINK_STATUS, &status);
if (status == GL_FALSE) {
glDeleteProgram(program);
CHECK(false) << "failed to link program : " << program;
}
glValidateProgram(program);
#ifdef DEBUG
GLint logLength;
glGetProgramiv(program, GL_INFO_LOG_LENGTH, &logLength);
if (logLength > 0) {
GLchar *log = (GLchar *)malloc(logLength);
glGetProgramInfoLog(program, logLength, &logLength, log);
LOG(INFO) << "program validate log : " << log;
free(log);
}
#endif
glGetProgramiv(program, GL_VALIDATE_STATUS, &status);
if (status == GL_FALSE) {
LOG(INFO) << "ValidateProgram failed";
exit(1);
}
glUseProgram(program);
glUniform1i(glGetUniformLocation(program, "s_texture_y"), 0);
glUniform1i(glGetUniformLocation(program, "s_texture_u"), 1);
glUniform1i(glGetUniformLocation(program, "s_texture_v"), 2);
for (int i = 0; i < 3; ++i) {
glActiveTexture(GL_TEXTURE0 + i);
glBindTexture(GL_TEXTURE_2D, textures[i]);
}
glVertexAttribPointer((GLuint)ATTRIBUTE_COORD::ATTRIBUTE_VERTEX, 2, GL_FLOAT, GL_FALSE, 0, verCoords);
glEnableVertexAttribArray((GLuint)ATTRIBUTE_COORD::ATTRIBUTE_VERTEX);
glVertexAttribPointer((GLuint)ATTRIBUTE_COORD::ATTRIBUTE_TEXCOORD, 2, GL_FLOAT, GL_FALSE, 0, texCoords);
glEnableVertexAttribArray((GLuint)ATTRIBUTE_COORD::ATTRIBUTE_TEXCOORD);
glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);
}