1背景
在上一篇里从媒体文件里提取视频流,之前11里面提到了默认的是使用gl渲染的,而在工厂目录也有个gl,里面使用了glew。这一篇就从gl渲染开始。
2步骤
2.1 GLWnd
头文件(src/ui/GLWnd.h)如下
class GLWnd : public HVideoWnd, HGLWidget {
public:
GLWnd(QWidget* parent = nullptr);
virtual void setGeometry(const QRect& rc) {
HGLWidget::setGeometry(rc);
}
virtual void update() {
HGLWidget::update();
}
protected:
virtual void paintGL();
void drawTime();
void drawFPS();
void drawResolution();
};
这里直接去关注HGLWidget
2.2HGLWidget
在(src/qt/HGLWidget.h)头文件中可以看到如下
class HGLWidget : public QOpenGLWidget
{
public:
HGLWidget(QWidget* parent = NULL);
// ratio = 0 means spread
void setAspectRatio(double ratio);
void drawFrame(HFrame *pFrame);
void drawTexture(HRect rc, GLTexture *tex);
void drawRect(HRect rc, HColor clr, int line_width = 1, bool bFill = false);
void drawText(QPoint lb, const char* text, int fontsize, QColor clr);
protected:
virtual void initializeGL();
virtual void resizeGL(int w, int h);
virtual void paintGL();
void setVertices(double ratio);
void setVertices(QRect rc);
static void loadYUVShader();
void initVAO();
void initYUV();
void drawYUV(HFrame* pFrame);
protected:
static std::atomic_flag s_glew_init;
static GLuint prog_yuv;
static GLuint texUniformY;
static GLuint texUniformU;
static GLuint texUniformV;
GLuint tex_yuv[3];
double aspect_ratio;
GLfloat vertices[8];
GLfloat textures[8];
// NOTE: QPainter used 3 VertexAttribArray
enum VER_ATTR {
VER_ATTR_VER = 3,
VER_ATTR_TEX,
};
};
从上面可以看到这里是用了qt的OpenGL组件
2.4 update
2.1中HGLWidget::update()这个没在QOpenGLWidget的头文件找到,查文档好像是基类QWidget中有申明。
void QWidget::update()
Updates the widget unless updates are disabled or the widget is hidden.
This function does not cause an immediate repaint; instead it schedules a paint event for processing when Qt returns to the main event loop. This permits Qt to optimize for more speed and less flicker than a call to repaint() does.
Calling update() several times normally results in just one paintEvent() call.
Qt normally erases the widget's area before the paintEvent() call. If the Qt::WA_OpaquePaintEvent widget attribute is set, the widget is responsible for painting all its pixels with an opaque color.
See also repaint(), paintEvent(), setUpdatesEnabled(), and Analog Clock.
**** paintGL() 的调用时机
- 首次显示窗口时 流程: 当 QOpenGLWidget 第一次显示在屏幕上时,Qt 会依次调用: initializeGL() :初始化 OpenGL 资源(如着色器、纹理)。 resizeGL(w, h) :根据初始尺寸设置视口和投影。 paintGL() :执行首次渲染。
- 调用 update() 请求重绘 用途:通过调用 update() 触发异步重绘(推荐)。 触发流程: update() 将重绘事件加入 Qt 事件队列。 在下一个事件循环中,Qt 自动调用 paintGL() 。
- 窗口尺寸变化时 流程: resizeGL(w, h) :调整视口和顶点坐标。 paintGL() :立即重绘以适应新尺寸。
总结下来,就是update调用时,paintGL会被触发
2.5 paintGL
在2.1中看到子类重写了paintGL,这里直接去cpp中看对应的实现
void GLWnd::paintGL() {
calcFPS();
HGLWidget::paintGL();
if (last_frame.isNull()) {
/*
QPoint pt = rect().center() - QPoint(80, -10);
drawText(pt, "NO VIDEO", 16, Qt::white);
QPainter painter(this);
QPixmap pixmap(":/image/media_bk.png");
int w = pixmap.width();
int h = pixmap.height();
QRect rc((width()-w)/2, (height()-h)/2, w, h);
painter.drawPixmap(rc, pixmap);
*/
}
else {
drawFrame(&last_frame);
if (draw_time) {
drawTime();
}
if (draw_fps) {
drawFPS();
}
if (draw_resolution) {
drawResolution();
}
}
计算fps,这个是在另一个基类HVideoWnd中实现的
void HVideoWnd::calcFPS() {
if (gettick() - tick > 1000) {
fps = framecnt;
framecnt = 0;
tick = gettick();
}
else {
++framecnt;
}
}
之后调用了父类的paintGL
void HGLWidget::paintGL() {
glClearColor(0.0f, 0.0f, 0.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT);
}
清空画布
之后又调用了父类的drawFrame
2.6 drawFrame
void HGLWidget::drawFrame(HFrame *pFrame) {
if (pFrame->type == PIX_FMT_IYUV || pFrame->type == PIX_FMT_YV12) {
drawYUV(pFrame);
}
else {
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
glRasterPos3f(-1.0f,1.0f,0);
glPixelZoom(width()/(float)pFrame->w, -height()/(float)pFrame->h);
glDrawPixels(pFrame->w, pFrame->h, glPixFmt(pFrame->type), GL_UNSIGNED_BYTE, pFrame->buf.base);
}
}