Newtron Malayalam
Newtron Malayalam

Reputation: 337

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

I want to make a screen caster application that captures a window or a screen directly to QQuickImage. To do so I made this header which has a thread object which frequently updates screen shots when signal received (sorry file was written in a poor language)

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();
};

and with my qml image i did this

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)
    }

but it has a really horrible fps. can anything be done to increase fps? Is there any easy way to do this?

Upvotes: 0

Views: 615

Answers (1)

GrecKo
GrecKo

Reputation: 7160

Image is not really made for displaying frequently changing images. What you want is VideoOutput.

Here's what I would do :

You need to create a QObject derived class instance and set it as the source of the VideoOutput : http://doc.qt.io/qt-5/qml-qtmultimedia-videooutput.html#source-prop Your class needs to have a Q_PROPERTY(QAbstractVideoSurface* videoSurface READ videoSurface WRITE setVideoSurface NOTIFY videoSurfaceChanged)

In your setVideoSurface method, you need to call the start method of QAbstractVideoSurface with a correct format (your size and the pixelformat, pixelformat should be QVideoFrame::Format_ARGB32 for desktop I guess).

And then when you want to update the VideoOutput (via a QTimer for example), you call the present method of QAbstractVideoSurface with a QVideoFrame you constructed from the QPixmap you got in QScreen::grabWindow.

For that, you could convert the QPixmap to QImage with toImage and then convert it to QVideoFrame with theQVideoFrame::QVideoFrame(const QImage &image) constructor.

Upvotes: 1

Related Questions