使用QAbstractVideoSurface

lio*_*ekz 3 c++ qt videochat

我想用QT制作视频会议应用.我是新手.如果有人有一个例子,我会很感激.

现在,我正在尝试使用qabstractvideosurface的子类在屏幕上显示相机图片.任何人都知道如何做到这一点?

UmN*_*obe 19

QAbstractVideoSurface是视频帧的制作者和消费者之间的接口.您只有两个要实现的功能:

  1. supportedPixelFormats,以便生产者可以选择适当的格式QVideoFrame
  2. 现在这是show\display这个框架的更通用的措辞

让我们说你想使用经典QWidget的显示器.在这种情况下,您可以选择使用a QImage来绘制窗口小部件.

第一个Qt保证QImage在大多数平台上绘制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>();
    }
}
Run Code Online (Sandbox Code Playgroud)

现在,为了呈现QVideoFrame,您将其数据映射到QImage,并将QImage绘制到窗口小部件.为简单起见,我将使用a 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

这个例子显然不是最佳的.

  1. QVideoFrame由于我们正在使用像素图绘制,因此可能存储在视频内存中这一事实并不需要花钱.
  2. 从图像到像素图的转换是不必要的.

您可以编写自己的小部件,并实现paintEvent以获得更好的性能.此外,您对present()行为方式有几个设计自由.例如 :

  • 是否是非阻挡表面,即当存在完成时框架已经显示.在它上面将意味着使用mylabel->repaint()而不是mylabel->update()
  • 当您无法完成演示时会发生什么.您可能想要绘制一个空白帧而不是返回可能会停止音乐的错误.