简体   繁体   English

使用 QT6 在 QVideoWidget 上绘制 QVideoFrame

[英]Painting a QVideoFrame on a QVideoWidget with QT6

I'd like to process the stream of my webcam frame by frame with QT6.我想用 QT6 逐帧处理我的网络摄像头的 stream。 I've checked the internet but since QTMultimedia was heavily reworked with QT6, and since QT6 is pretty new, all the documentation/questions available are outdated.我已经检查过互联网,但由于 QTMultimedia 使用 QT6 进行了大量重新设计,并且由于 QT6 相当新,所有可用的文档/问题都已过时。

So, In order to achieve my goal, I'm using a QMediaCaptureSession with a camera set on QMediaDevices::defaultVideoInput() .所以,为了实现我的目标,我使用QMediaCaptureSessionQMediaDevices::defaultVideoInput()上的摄像头。 I checked that this was working by setting the video output of the QMediaCaptureSession to a QVideoWidget with m_session.setVideoOutput(ui->videowidget);我通过使用m_session.setVideoOutput(ui->videowidget);QVideoWidget的视频 output 设置为QMediaCaptureSession来检查这是否有效。 , and it's working fine, except that I can't process the frames (basically, it's rendering my webcam on the QVideoWidget ). ,它工作正常,除了我无法处理帧(基本上,它在QVideoWidget上渲染我的网络摄像头)。

Now, to process the frames, I have to use a QVideoSink as far as I understand the documentation here and there .现在,要处理帧,据我了解这里那里的文档,我必须使用QVideoSink So I replaced m_session.setVideoOutput(ui->videowidget);所以我替换m_session.setVideoOutput(ui->videowidget); with m_session.setVideoSink(&mysink);m_session.setVideoSink(&mysink); , where mysink is a QVideoSink . ,其中mysinkQVideoSink

Then, since I want to process the frames, I'm connecting the videoFrameChanged signal of mysink to a function processVideoFrame where I want to do 2 things:然后,因为我想处理帧,所以我将 mysink 的videoFrameChanged信号连接到processVideoFrame mysink我想做两件事:

  1. process the current frame处理当前帧
  2. render the result on the UI, ideally on ui->videowidget在 UI 上渲染结果,最好是在ui->videowidget

This is the point where I'm struggling.这就是我苦苦挣扎的地方。 I do not understand how to use the paint function of the class QVideoFrame to render the processed frame on the QVideoWidget .我不明白如何使用 class QVideoFrame 的paint functionQVideoFrame上渲染处理后的QVideoWidget More precisely:更确切地说:

  • I do not understand how I'm supposed to instantiate the QPainter .我不明白我应该如何实例化QPainter I tried a straightforward new QPainter(ui->videowidget) but it ends up in a QWidget::paintEngine: Should no longer be called exception and nothing is rendered我尝试了一个简单的new QPainter(ui->videowidget)但它最终出现在QWidget::paintEngine: Should no longer be called异常并且没有渲染任何内容
  • I do not understand what is actually representing the second parameter rect of QVideoFrame::paint ?我不明白QVideoFrame::paint的第二个参数rect实际代表什么?

I made a MWE, code is below.我做了一个MWE,代码如下。

mwe_videosinkpainting.h mwe_videosinkpainting.h

#ifndef MWE_VIDEOSINKPAINTING_H
#define MWE_VIDEOSINKPAINTING_H

#include <QMainWindow>
#include <QMediaCaptureSession>
#include <QMediaDevices>
#include <QCamera>
#include <QVideoSink>
#include <QPainter>

QT_BEGIN_NAMESPACE
namespace Ui { class MWE_VideoSinkPainting; }
QT_END_NAMESPACE

class MWE_VideoSinkPainting : public QMainWindow
{
    Q_OBJECT

public:
    MWE_VideoSinkPainting(QWidget *parent = nullptr);
    ~MWE_VideoSinkPainting();

private slots:
    void processVideoFrame();


private:
    Ui::MWE_VideoSinkPainting *ui;

    QVideoSink mysink;
    QMediaCaptureSession m_session;
    QScopedPointer<QCamera> m_camera;
};
#endif // MWE_VIDEOSINKPAINTING_H

mwe_videosinking.cpp mwe_videosinking.cpp

#include "mwe_videosinkpainting.h"
#include "ui_mwe_videosinkpainting.h"

MWE_VideoSinkPainting::MWE_VideoSinkPainting(QWidget *parent)
    : QMainWindow(parent)
    , ui(new Ui::MWE_VideoSinkPainting)
{
    ui->setupUi(this);

    m_camera.reset(new QCamera(QMediaDevices::defaultVideoInput()));
    m_session.setCamera(m_camera.data());
    //m_session.setVideoOutput(ui->videowidget);

    connect(&mysink, &QVideoSink::videoFrameChanged, this, &MWE_VideoSinkPainting::processVideoFrame);

    m_session.setVideoSink(&mysink);
    m_camera->start();
}

MWE_VideoSinkPainting::~MWE_VideoSinkPainting()
{
    delete ui;
}

void MWE_VideoSinkPainting::processVideoFrame()
{
    QVideoFrame videoframe = mysink.videoFrame();
    if(videoframe.map(QVideoFrame::ReadOnly))
    {
        //This is the part I'm struggling to understand and achieve
        videoframe.paint(new QPainter(ui->videowidget), QRectF(0.0f,0.0f,100.0f,100.0f), QVideoFrame::PaintOptions());
        videoframe.unmap();
    }
}

main.cpp主文件

#include "mwe_videosinkpainting.h"

#include <QApplication>

int main(int argc, char *argv[])
{
    QApplication a(argc, argv);
    MWE_VideoSinkPainting w;
    w.show();
    return a.exec();
}

ui_mwe_videosinkpainting.h (just so that you have the whole code, it has no value for the question) ui_mwe_videosinkpainting.h(只是为了让你拥有整个代码,它对这个问题没有价值)

#ifndef UI_MWE_VIDEOSINKPAINTING_H
#define UI_MWE_VIDEOSINKPAINTING_H

#include <QtCore/QVariant>
#include <QtMultimediaWidgets/QVideoWidget>
#include <QtWidgets/QApplication>
#include <QtWidgets/QGridLayout>
#include <QtWidgets/QHBoxLayout>
#include <QtWidgets/QMainWindow>
#include <QtWidgets/QMenuBar>
#include <QtWidgets/QStatusBar>
#include <QtWidgets/QWidget>

QT_BEGIN_NAMESPACE

class Ui_MWE_VideoSinkPainting
{
public:
    QWidget *centralwidget;
    QGridLayout *gridLayout;
    QVideoWidget *videowidget;
    QHBoxLayout *horizontalLayout;
    QMenuBar *menubar;
    QStatusBar *statusbar;

    void setupUi(QMainWindow *MWE_VideoSinkPainting)
    {
        if (MWE_VideoSinkPainting->objectName().isEmpty())
            MWE_VideoSinkPainting->setObjectName(QString::fromUtf8("MWE_VideoSinkPainting"));
        MWE_VideoSinkPainting->resize(800, 600);
        centralwidget = new QWidget(MWE_VideoSinkPainting);
        centralwidget->setObjectName(QString::fromUtf8("centralwidget"));
        gridLayout = new QGridLayout(centralwidget);
        gridLayout->setObjectName(QString::fromUtf8("gridLayout"));
        videowidget = new QVideoWidget(centralwidget);
        videowidget->setObjectName(QString::fromUtf8("videowidget"));
        horizontalLayout = new QHBoxLayout(videowidget);
        horizontalLayout->setObjectName(QString::fromUtf8("horizontalLayout"));

        gridLayout->addWidget(videowidget, 0, 0, 1, 1);

        MWE_VideoSinkPainting->setCentralWidget(centralwidget);
        menubar = new QMenuBar(MWE_VideoSinkPainting);
        menubar->setObjectName(QString::fromUtf8("menubar"));
        menubar->setGeometry(QRect(0, 0, 800, 21));
        MWE_VideoSinkPainting->setMenuBar(menubar);
        statusbar = new QStatusBar(MWE_VideoSinkPainting);
        statusbar->setObjectName(QString::fromUtf8("statusbar"));
        MWE_VideoSinkPainting->setStatusBar(statusbar);

        retranslateUi(MWE_VideoSinkPainting);

        QMetaObject::connectSlotsByName(MWE_VideoSinkPainting);
    } // setupUi

    void retranslateUi(QMainWindow *MWE_VideoSinkPainting)
    {
        MWE_VideoSinkPainting->setWindowTitle(QCoreApplication::translate("MWE_VideoSinkPainting", "MWE_VideoSinkPainting", nullptr));
    } // retranslateUi

};

namespace Ui {
    class MWE_VideoSinkPainting: public Ui_MWE_VideoSinkPainting {};
} // namespace Ui

QT_END_NAMESPACE

#endif // UI_MWE_VIDEOSINKPAINTING_H

The answer is quite straightforward: you can use setVideoSink AND setVideoOutput .答案非常简单:您可以使用setVideoSinksetVideoOutput

The code I gave in OP is good, you just have to uncomment setVideoOutput(ui->videowidget);我在 OP 中提供的代码很好,您只需取消注释setVideoOutput(ui->videowidget); of mwe_videosinking.cpp and to call setVideoSink BEFORE calling setVideoOutput mwe_videosinking.cpp并在调用setVideoSink之前调用setVideoOutput

Since I cannot format code in a comment...由于我无法在评论中格式化代码...

So, you mean change this...所以,你的意思是改变这个...

    ui->setupUi(this);
    m_camera.reset(new QCamera(QMediaDevices::defaultVideoInput()));
    m_session.setCamera(m_camera.data());
    //m_session.setVideoOutput(ui->videowidget);
    connect(&mysink, &QVideoSink::videoFrameChanged, this, &MWE_VideoSinkPainting::processVideoFrame);
    m_session.setVideoSink(&mysink);
    m_camera->start();

...to this? ……到这个?

    ui->setupUi(this);
    m_camera.reset(new QCamera(QMediaDevices::defaultVideoInput()));
    m_session.setCamera(m_camera.data());
    m_session.setVideoSink(&mysink);
    m_session.setVideoOutput(ui->videowidget);
    connect(&mysink, &QVideoSink::videoFrameChanged, this, &MWE_VideoSinkPainting::processVideoFrame);
    m_camera->start();

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM