使用 QSCreen::grabWindow() 函数在 qt 中制作 window 预览

Making window previews in qt using QSCreen::grabWindow() function

我想制作一个屏幕投射应用程序,将 window 或屏幕直接捕获到 QQuickImage。为此,我制作了这个 header,它有一个线程 object,它在收到信号时经常更新屏幕截图(抱歉,文件是用糟糕的语言编写的)

class OtherThread : public QThread
{
    Q_OBJECT
    QString oldWritingFile = "filename.png";
    bool loaded = true;
public:
    int winId = 0;
    OtherThread(QObject *parent = nullptr):QThread(parent){};
    ~OtherThread(){
        if(isRunning()){
            requestInterruption();
            wait();
        }
    };
    void setParams(int id){
        winId = id;
    }
    void knowLoaded(){
        loaded =true;
    }
signals:
    void fileCacheChanged(QString);
protected:
    void run() override{
        while (!isInterruptionRequested()) {
            if(winId != 0 && loaded){
                bool success;
                loaded = false;
                if(oldWritingFile == QString::number(winId)+".png"){
                    if(qApp->primaryScreen()->grabWindow(winId).save(QString::number(winId)+"file.png")){
                        oldWritingFile = QString::number(winId)+"file.png";
                        success = true;
                    }else{success=false;}
                }else{
                    if(qApp->primaryScreen()->grabWindow(winId).save(QString::number(winId)+".png")){
                        oldWritingFile = QString::number(winId)+".png";
                        success = true;
                    }else{success = false;}
                }
                emit fileCacheChanged(oldWritingFile);
            }
        }
    };
};

class Controller : public QObject
{
    Q_OBJECT
    QString oldWritingFile;
public:
    Controller(QObject *parent = nullptr):QObject(parent) {}
    virtual ~Controller() {}
    void changeFile(QString message){
        oldWritingFile = message;
        emit newFile(message);
    };
public slots:
    void startThread(){
        OtherThread *thread =new OtherThread;
        connect(thread, &OtherThread::fileCacheChanged, this, &Controller::changeFile);
        connect(this, &Controller::stop, thread, &OtherThread::requestInterruption);
        connect(this, &Controller::changePrint, thread, &OtherThread::setParams);
        connect(this, &Controller::loaded, thread, &OtherThread::knowLoaded);
        thread->start();
    }
    QString getLoadedFile(){
        if(oldWritingFile != NULL){
            return "file:./"+oldWritingFile;
        }else{
            return "file:./index.png";
        }
    }
signals:
    void stop();
    void changePrint(int id);
    void newFile(QString);
    void loaded();
};

我用我的 qml 图像做了这个

Image {
        id: image
        anchors.fill: parent
        source: "images/kalaripayattu.svg"
        fillMode: Image.PreserveAspectFit
        cache:false
        Component.onCompleted: {
            justThread.newFile.connect(updateFunction)
        }
        function updateFunction(filename){
            source = "file:./"+filename;
            justThread.loaded()
        }
    }

    JustThread{
        signal stopThread
        id:justThread
        onStopThread: {
            stop()
        }
        Component.onCompleted: {
            startThread()
            changePrint(id)
        }
    }
    Component.onCompleted:{
        applicationWindow.closing.connect(justThread.stopThread)
    }

但它的 fps 真的很糟糕。 可以做些什么来增加 fps 吗? 有什么简单的方法可以做到这一点吗?

Image 并不是真正为显示频繁变化的图像而设计的。你要的是VideoOutput.

这是我会做的:

您需要创建一个 QObject 派生的 class 实例并将其设置为 VideoOutput 的源:http://doc.qt.io/qt-5/qml-qtmultimedia-videooutput.html#source-prop 您的 class 需要 Q_PROPERTY(QAbstractVideoSurface* videoSurface READ videoSurface WRITE setVideoSurface NOTIFY videoSurfaceChanged)

在你的 setVideoSurface 方法中,你需要以正确的格式调用 QAbstractVideoSurface 的启动方法(你的大小和像素格式,我猜桌面的像素格式应该是 QVideoFrame::Format_ARGB32)。

然后,当您想要更新 VideoOutput(例如通过 QTimer)时,您可以使用从 QScreen::grabWindow.[=19= 中获得的 QPixmap 构造的 QVideoFrame 调用 QAbstractVideoSurface 的当前方法]

为此,您可以使用 toImage and then convert it to QVideoFrame with theQVideoFrame::QVideoFrame(const QImage &image) constructor.

将 QPixmap 转换为 QImage