【问题标题】:How to use QVideoSink in QML in Qt6Qt6中如何在QML中使用QVideoSink
【发布时间】:2021-11-24 16:03:06
【问题描述】:

我创建了自己的视频接收器,继承自 Qt 6 中的 QVideoSink。我想在 QML 端显示这个接收器的内容。我该怎么做?

VideoOutput QML 类型具有videoSink 属性,但它是只读的..

【问题讨论】:

    标签: qt qt6


    【解决方案1】:

    诸如 VideoOutput 和 QVideoWidget 之类的输出元素有一个 QVideoSink,所以你不应该创建一个,而是覆盖那个 QVideoSink:

    #ifndef PRODUCER_H
    #define PRODUCER_H
    
    #include <QObject>
    #include <QPointer>
    #include <QVideoSink>
    #include <QQmlEngine>
    #include <QTimer>
    
    class Producer : public QObject
    {
        Q_OBJECT
        QML_ELEMENT
        Q_PROPERTY(QVideoSink* videoSink READ videoSink WRITE setVideoSink NOTIFY videoSinkChanged)
    public:
        Producer(QObject *parent=nullptr);
        QVideoSink *videoSink() const;
        void setVideoSink(QVideoSink *newVideoSink);
        Q_INVOKABLE void start();
    signals:
        void videoSinkChanged();
    private:
        QPointer<QVideoSink> m_videoSink;
        void handleTimeout();
        QTimer m_timer;
    };
    
    #endif // PRODUCER_H
    
    #include "producer.h"
    
    #include <QImage>
    #include <QPainter>
    #include <QSize>
    #include <QVideoFrame>
    
    #include <QRandomGenerator>
    #include <QDateTime>
    
    Producer::Producer(QObject *parent):QObject(parent)
    {
        m_timer.setInterval(500);
        connect(&m_timer, &QTimer::timeout, this, &Producer::handleTimeout);
    }
    
    QVideoSink *Producer::videoSink() const
    {
        return m_videoSink.get();
    }
    
    void Producer::setVideoSink(QVideoSink *newVideoSink)
    {
        if (m_videoSink == newVideoSink)
            return;
        m_videoSink = newVideoSink;
        emit videoSinkChanged();
    }
    
    void Producer::start()
    {
        m_timer.start();
        handleTimeout();
    }
    
    void Producer::handleTimeout()
    {
        if(!m_videoSink)
            return;
        QVideoFrame video_frame(QVideoFrameFormat(QSize(640, 480),QVideoFrameFormat::Format_BGRA8888));
        if(!video_frame.isValid() || !video_frame.map(QVideoFrame::WriteOnly)){
            qWarning() << "QVideoFrame is not valid or not writable";
            return;
        }
        QImage::Format image_format = QVideoFrameFormat::imageFormatFromPixelFormat(video_frame.pixelFormat());
        if(image_format == QImage::Format_Invalid){
            qWarning() << "It is not possible to obtain image format from the pixel format of the videoframe";
            return;
        }
        int plane = 0;
        QImage image(video_frame.bits(plane), video_frame.width(),video_frame.height(), image_format);
        image.fill(QColor::fromRgb(QRandomGenerator::global()->generate()));
        QPainter painter(&image);
        painter.drawText(image.rect(), Qt::AlignCenter, QDateTime::currentDateTime().toString());
        painter.end();
    
        video_frame.unmap();
        m_videoSink->setVideoFrame(video_frame);
    }
    
    #include <QGuiApplication>
    #include <QQmlApplicationEngine>
    
    
    int main(int argc, char *argv[])
    {
        QGuiApplication app(argc, argv);
    
        QQmlApplicationEngine engine;
        const QUrl url(QStringLiteral("qrc:/main.qml"));
        QObject::connect(&engine, &QQmlApplicationEngine::objectCreated,
                         &app, [url](QObject *obj, const QUrl &objUrl) {
            if (!obj && url == objUrl)
                QCoreApplication::exit(-1);
        }, Qt::QueuedConnection);
        engine.load(url);
    
        return app.exec();
    }
    
    import QtQuick
    import QtQuick.Window
    import QtMultimedia
    
    import com.eyllanesc.multimedia
    
    Window {
        width: 640
        height: 480
        visible: true
        title: qsTr("Hello World")
    
        Producer{
            id: producer
            videoSink: videoOutput.videoSink
        }
        VideoOutput{
            id: videoOutput
            anchors.fill: parent
        }
        Component.onCompleted: producer.start()
    }
    

    完整的例子可以在here找到。

    【讨论】:

    • 如何从 QImage 设置 VideoFrame ?
    • @user31562 如果有一个QVideoFrameFormat::PixelFormat 与图像格式相关联(使用doc-snapshots.qt.io/qt6-dev/… 来验证)然后使用该pixelFormat 和图像的大小构建QVideoFrame,然后复制位使用std::memcpy 将图像发送到 QVideoFrame。如果没有关联的 PixelFormat,则它将图像格式转换为具有关联的格式。
    • @user31562 另一种选择是使用我的示例代码并使用 QPainter 将您拥有的 QImage 绘制到我示例的 QImage
    • 我已经用过这个 QPainter 画家(&image);画家.drawImage(0,0,frame);
    • @user31562 这就是我在之前的评论中指出的
    猜你喜欢
    • 2021-05-14
    • 2022-01-17
    • 2021-12-12
    • 1970-01-01
    • 2021-12-04
    • 2022-01-20
    • 2022-07-25
    • 2019-09-09
    • 2022-08-22
    相关资源
    最近更新 更多