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

时间:2009-08-06 23:05:09

标签: c++ qt image-processing

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

我喜欢这个结果,看起来相对较快,但我不禁想到必须有一个更有效的方法。我也听说QImage到QPixmap的转换很昂贵。我已经实现了一个在小部件上使用SDL覆盖的解决方案,但我想继续使用Qt,因为我能够使用QGraphicsView轻松捕获点击和其他用户与视频显示的交互。

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

感谢。

3 个答案:

答案 0 :(得分:32)

感谢您的回答,但我终于重新审视了这个问题,并提出了一个相当简单的解决方案,可以提供良好的性能。它涉及从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中实现了非常快速的转换。主要收益来自两件事:

  • 无需软件扩展。缩放在视频卡上完成(如果可用)
  • QImage转换为QPixmapQPainter::drawImage()功能正在以原始图像分辨率执行,而不是升级后的全屏分辨率。

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

答案 1 :(得分:3)

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

我的2美分

答案 2 :(得分:3)

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