ffmpeg播放器13

74 阅读3分钟

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中有申明。

doc.qt.io/qt-6.7/qwid…

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()  的调用时机

  1. 首次显示窗口时 流程: 当  QOpenGLWidget  第一次显示在屏幕上时,Qt 会依次调用:  initializeGL() :初始化 OpenGL 资源(如着色器、纹理)。  resizeGL(w, h) :根据初始尺寸设置视口和投影。  paintGL() :执行首次渲染。
  2. 调用  update()  请求重绘 用途:通过调用  update()  触发异步重绘(推荐)。 触发流程:  update()  将重绘事件加入 Qt 事件队列。 在下一个事件循环中,Qt 自动调用  paintGL() 。
  3. 窗口尺寸变化时 流程:  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);
    }
}

未完待续