代码之家  ›  专栏  ›  技术社区  ›  Jason B

在Qt中显示解码视频帧最有效的方法是什么?

  •  33
  • Jason B  · 技术社区  · 17 年前

    向Qt小部件显示图像的最快方式是什么?我已经用libavformat和libavcodec解码了视频,所以我已经有了原始RGB或YCbCr 4:2:0帧。我目前正在使用QGraphicsView和包含QGraphicsPixmapItem的QGraphicscene对象。我目前正在使用QImage构造函数从内存缓冲区获取帧数据到QPixmap,并使用QPixmap::fromImage()将其转换为QPixmap。

    我喜欢这样做的结果,而且速度似乎相对较快,但我忍不住认为必须有一种更有效的方法。我还听说QImage到QPixmap的转换非常昂贵。我已经实现了一个在小部件上使用SDL覆盖的解决方案,但我只想使用Qt,因为我能够使用QGraphicsView轻松捕获点击和其他用户与视频显示的交互。

    我正在使用libswscale进行任何所需的视频缩放或颜色空间转换,因此我只想知道在执行所有处理之后,是否有人有更有效的方法来显示图像数据。

    谢谢

    3 回复  |  直到 17 年前
        1
  •  35
  •   Jason B    9 年前

    感谢您的回答,但我最终重新审视了这个问题,并提出了一个相当简单的解决方案,它可以提供良好的性能。它涉及从 QGLWidget paintEvent() 作用内部 paintEvent() 函数,您可以调用 QPainter::drawImage(...) 如果可用,它将使用硬件为您执行到指定矩形的缩放。看起来是这样的:

    class QGLCanvas : public QGLWidget
    {
    public:
        QGLCanvas(QWidget* parent = NULL);
        void setImage(const QImage& image);
    protected:
        void paintEvent(QPaintEvent*);
    private:
        QImage img;
    };
    
    QGLCanvas::QGLCanvas(QWidget* parent)
        : QGLWidget(parent)
    {
    }
    
    void QGLCanvas::setImage(const QImage& image)
    {
        img = image;
    }
    
    void QGLCanvas::paintEvent(QPaintEvent*)
    {
        QPainter p(this);
    
        //Set the painter to use a smooth scaling algorithm.
        p.setRenderHint(QPainter::SmoothPixmapTransform, 1);
    
        p.drawImage(this->rect(), img);
    }
    

    有了这个,我仍然需要将YUV420P转换为RGB32,但是ffmpeg在libswscale中有一个非常快速的转换实现。主要的收益来自两方面:

    • 不需要软件扩展。在视频卡上进行缩放(如果可用)
    • QImage QPixmap ,这是发生在 QPainter::drawImage() 该功能以原始图像分辨率执行,而不是以放大的全屏分辨率执行。

    我用以前的方法将处理器固定在显示器上(解码在另一个线程中完成)。现在,我的显示线程只使用大约8-9%的内核进行全屏1920x1200 30fps播放。我相信,如果我能直接将YUV数据发送到视频卡,它可能会变得更好,但现在这已经足够好了。

        2
  •  3
  •   neuro    17 年前

    我的2美分

        3
  •  3
  •   count0    16 年前

    根据您的OpenGL/着色技能,您可以尝试将视频帧复制到纹理,将纹理映射到矩形(或任何其他..有趣!)并在OpenGL场景中显示。这不是最直接的方法,但速度很快,因为您直接将数据写入图形内存(如SDL)。我还建议只使用YCbCR,因为这种格式是压缩的(颜色,Y=完全Cb,Cr是帧的1/4),所以显示帧需要更少的内存+更少的复制。我不是直接使用Qts GL,而是在Qt(vis OSG)中间接使用GL,可以实时显示大约7-11个全高清(1440 x 1080)视频。