在屏幕上使用QAbstractVideoSurface显示

5
我正在尝试使用QAbstractVideoSurface子类在屏幕上显示相机图片,但我对此没有经验。如果有人能够解释如何做到这一点,我将不胜感激。

一个好的问题应该表明一定程度的研究...例如,你是否尝试搜索任何相关文档?从陈述方式来看,这个问题似乎非常开放。 - Vikas Gupta
是的,我已经苦苦挣扎了一个月。我使用了相机视图查看器,它只显示相机所见的内容。为了获取视频帧,我应该使用上面提到的方法。但是,互联网上缺乏我可以尝试的示例。谢谢您的评论,这是我的第一个问题,请不要太严厉。 - lionlinekz
@lionlinekz,你是否在子类实现或如何使用VideoSurface对象方面遇到了困难? - UmNyobe
据我所知,需要使用子类来获取帧。对我不清楚的是如何在屏幕上显示视频。 - lionlinekz
1个回答

24

QAbstractVideoSurface是视频帧生产者和消费者之间的接口。您需要实现以下两个函数:

  1. supportedPixelFormats,以便生产者可以为QVideoFrame选择适当的格式。
  2. present是更通用的措辞,表示显示此帧。

假设您想要在经典的QWidget上显示。在这种情况下,您可以选择使用QImage来绘制小部件。

首先,在大多数平台上,Qt保证会对RGB24(或BGR24)进行绘制。

QList<QVideoFrame::PixelFormat> LabelBasedVideoSurface::supportedPixelFormats(
        QAbstractVideoBuffer::HandleType handleType) const
{
    if (handleType == QAbstractVideoBuffer::NoHandle) {
        return QList<QVideoFrame::PixelFormat>()
                << QVideoFrame::Format_RGB24;
    } else {
        return QList<QVideoFrame::PixelFormat>();
    }
}

现在要呈现QVideoFrame,您需要将其数据映射到QImage,并将QImage绘制到小部件上。出于简单起见,我将使用一个 QLabel,直接访问它(没有信号没有槽)。

bool LabelBasedVideoSurface::present(const QVideoFrame &frame)
{
    if (notMyFormat(frame.pixelFormat())) {
        setError(IncorrectFormatError);
        return false;
    } else {

        QVideoFrame frametodraw(frame);

        if(!frametodraw.map(QAbstractVideoBuffer::ReadOnly))
        {
           setError(ResourceError);
           return false;
        } 

         //this is a shallow operation. it just refer the frame buffer
         QImage image(
                frametodraw.bits(),
                frametodraw.width(),
                frametodraw.height(),
                frametodraw.bytesPerLine(),
                QImage::Format_RGB444);

        mylabel->resize(image.size());

        //QPixmap::fromImage create a new buffer for the pixmap
        mylabel->setPixmap(QPixmap::fromImage(image));

        //we can release the data
        frametodraw.unmap();

        mylabel->update();

        return true;
    }
}

这个示例显然不是最优的。

  1. 它没有考虑到QVideoFrame可能存储在视频内存中,因为我们使用了 pixmap 进行绘制。
  2. 从图像到 pixmap 的转换是不必要的开销。

你可以编写自己的小部件,并实现一个 paintEvent 以获得更好的性能。此外,在 present() 函数的行为上,你有几种设计自由度。例如:

  • 是否是非阻塞表面,即 present 完成时帧已经显示。在此情况下,需要使用 mylabel->repaint() 而不是 mylabel->update()
  • 无法完成演示时会发生什么。你可能想要绘制一个空白帧,而不是返回一个可能会停止音乐的错误。

非常感谢,这对我帮助很大!然而,显示器上显示的图像是绿色和品红色,有什么建议如何修复它? - lionlinekz
QVideoFrame::Format_RGB24 可能是 QVideoFrame::Format_BGR24。尝试使用不同的 RGB 格式来匹配您的源。 - UmNyobe
present() 方法将由什么调用?我将这个类添加到了我的 QtMediaPlayer 中,但是 QtMediaPlayer 永远不会调用 present()。 - Guerlando OCs

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