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

36
什么是在Qt小部件中显示图像的最快方法?我已经使用libavformat和libavcodec解码视频,因此我已经拥有原始RGB或YCbCr 4:2:0帧。我目前正在使用一个包含QGraphicsPixmapItem的QGraphicsView和QGraphicsScene对象。我目前通过使用来自内存缓冲区的QImage构造函数并使用QPixmap :: fromImage()将其转换为QPixmap,将帧数据传递到QPixmap中。
我喜欢这个结果,它似乎相对较快,但我不禁想,一定有更高效的方法。我还听说QImage到QPixmap的转换很昂贵。我实现了一个在小部件上使用SDL叠加层的解决方案,但我想继续使用Qt,因为我能够轻松捕获单击和其他用户与视频显示的交互,使用QGraphicsView。
我正在使用libswscale进行所有必需的视频缩放或颜色空间转换,所以我只想知道是否有更有效的方式在执行所有处理后显示图像数据。
谢谢。
3个回答

36

谢谢回答,但我最终重新审视了这个问题,并想出了一个相当简单的解决方案,它具有良好的性能。它涉及从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转换为QPixmap,这发生在QPainter::drawImage()函数中,在原始图像分辨率下执行,而不是在升频全屏分辨率下执行。

我的以前的方法只是显示就让处理器忙碌不已(解码在另一个线程中进行)。现在,我的显示线程只使用约8-9%的核心来进行全屏1920x1200 30fps播放。我相信,如果我能直接将YUV数据发送到视频卡,效果可能会更好,但现在这已经足够好了。


这里Qt Designer说的QGLWidget是什么?为什么会提示没有这个文件? - Bahramdun Adil
不错的例子,帮我省了很多时间!! - Bahramdun Adil
@BahramdunAdil,QGLWidget已被弃用,现已被QOpenGLWidget所取代。虽然有些不同,但对于这个简单的例子应该可以类似地工作。 - Jason B
请问您能否分享完整的工作源代码,谢谢。 - Lê Vũ Huy
@JasonB 为什么要使用GL小部件?你的代码与从QWidget派生的代码相同。我认为最好通过OpenGL上下文显示框架,并从原始数据上传纹理缓冲区。 - iperov
显示剩余3条评论

3

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


3

我在使用gtkmm (gtk+ C++ wrapping)时遇到了同样的问题。除了使用SDL叠加之外,最好的解决方案是直接更新窗口小部件的图像缓冲区,然后请求重新绘制。但我不知道在Qt中是否可行...

我的建议


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接