【问题标题】:What is the most efficient way to display decoded video frames in Qt?在 Qt 中显示解码视频帧的最有效方法是什么?
【发布时间】:2010-11-17 12:57:13
【问题描述】:

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

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

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

谢谢。

【问题讨论】:

    标签: c++ qt image-processing


    【解决方案1】:

    感谢您的回答,但我终于重新审视了这个问题并提出了一个相当简单的解决方案,它可以提供良好的性能。它涉及从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);
    }
    

    有了这个,我仍然需要将 YUV 420P 转换为 RGB32,但是 ffmpeg 在 libswscale 中可以非常快速地实现该转换。主要收益来自两件事:

    • 无需软件缩放。在视频卡上进行缩放(如果有)
    • QPainter::drawImage() 函数中发生的从QImageQPixmap 的转换是以原始图像分辨率而不是放大的全屏分辨率执行的。

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

    【讨论】:

    • 我在解码器线程中使用 swscale 将 yuv 数据包转换为 rgb 并使用了您的方法。完美运行。
    • p.SetRenderHint(QPainter::SmoothPixmapTransform, 1); - setRenderHint 应该是小写的。
    • Qt Designer 说这里的QGLWidget 是什么,没有这样的文件?
    • 很好的例子,为我节省了很多时间!!
    • @BahramdunAdil, QGLWidget 已被弃用,取而代之的是 QOpenGLWidget。它略有不同,但对于这个简单的示例应该类似。
    【解决方案2】:

    我对 gtkmm(gtk+ C++ 包装)有同样的问题。除了使用 SDL 覆盖之外,最好的解决方案是直接更新小部件的图像缓冲区,然后请求重绘。但是不知道用Qt是否可行...

    我的 2 美分

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2011-08-05
        • 2013-11-23
        • 2013-12-10
        • 2019-03-23
        • 1970-01-01
        • 1970-01-01
        • 2015-10-28
        相关资源
        最近更新 更多