Implementing Simple Screen Sharing with Qt and MQTT

Implementing Simple Screen Sharing with Qt and MQTT
Implementing Simple Screen Sharing with Qt and MQTT

Background

Implementing Simple Screen Sharing with Qt and MQTT

Why choose Qt + MQTT for implementation?

Initially, I used Electron + WebRTC + SRS for implementation, but WebRTC requires exchanging protocols, and obtaining protocol content in Electron’s WebRTC is done through Chromium, which is difficult to control. The key issue is that the Electron package is hard to manage, so I abandoned Electron.

Later, I researched a JavaFX solution based on JavaFX + JavaCV + Zlmediakit. The problem here is that merging images for streaming causes significant latency unless Zlmediakit is optimized, but this is a small feature that needs to consider both development and operational costs. Moreover, the Chinese character garbling in the JavaFX tray and the issues with packaging into an EXE are hard to handle; I didn’t succeed on my end. Even though the Wix environment was clearly installed, it said it couldn’t be found.

I also briefly researched the Flutter Windows solution, but considering that desktop screen capture is required and the current Flutter solution is not mature enough, I abandoned it as well.

Ultimately, I decided to use the Qt solution. Since the requirement is just screen sharing, I had previously researched remote desktop control solutions, which are actually based on desktop screen capture, so I directly used the MQTT solution for this.

Implementing Simple Screen Sharing with Qt and MQTT

MQTT

Implementing Simple Screen Sharing with Qt and MQTT

The Qt environment does not include MQTT by default, so we need to compile it ourselves and add it to the environment.

Source code link: https://github.com/qt/qtmqtt

Switch to the branch that matches the local Qt version and build it.

Implementing Simple Screen Sharing with Qt and MQTT

After the build is complete, simply copy the relevant files to the Qt environment.

The main directories include:

  • bin

  • include

  • lib

  • mkspecs

  • modules

Note: In the bin folder, only the DLL files need to be copied.

Implementing Simple Screen Sharing with Qt and MQTT

Implementing Simple Screen Sharing with Qt and MQTT

Project Code

Implementing Simple Screen Sharing with Qt and MQTT

01

mainwindow.h

#ifndef MAINWINDOW_H#define MAINWINDOW_H
#include <QLabel>#include <QMainWindow>#include <QMenu>#include <QMqttClient>#include <QScreen>#include <QSettings>#include <QSystemTrayIcon>
QT_BEGIN_NAMESPACEnamespace Ui { class MainWindow; }QT_END_NAMESPACE
class MainWindow : public QMainWindow{ Q_OBJECT
public: MainWindow(QWidget *parent = nullptr); ~MainWindow(); QString ipV4(); void connectMqtt(); QSettings *settings; void screenMonitor();
protected: void closeEvent(QCloseEvent *event) override;
private: Ui::MainWindow *ui; QScreen *screen; QLabel *imageLabel; QSystemTrayIcon *trayIcon; QMenu *trayMenu; QString ipv4; QMqttClient *mqttClient; bool monitoring;};#endif // MAINWINDOW_H

02

mainwindow.cpp

#include "mainwindow.h"#include "./ui_mainwindow.h"
#include <QSystemTrayIcon>#include <QMenu>#include <QCloseEvent>#include <QDebug>#include <QHostInfo>#include <QLabel>#include <QCoreApplication>#include <QGuiApplication>#include <QTimer>#include <QBuffer>#include <QMqttTopicName>#include <QMqttClient>#include <QImage>
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow) { ui->setupUi(this); settings = new QSettings("config.ini", QSettings::IniFormat); qDebug() << "mqtt hostname:" << settings->value("mqtt/hostname").toString(); ipv4 = this->ipV4(); mqttClient = new QMqttClient(this); this->setWindowTitle("Screen Sharing:" + ipv4);
imageLabel = new QLabel(this);

imageLabel->setScaledContents(true); // Set QLabel size policy to auto-adjust size imageLabel->setSizePolicy(QSizePolicy::Ignored, QSizePolicy::Ignored); // Set QLabel alignment in layout imageLabel->setAlignment(Qt::AlignCenter);

trayIcon = new QSystemTrayIcon(this); trayIcon->setIcon(QIcon(":/resources/assets/logo.png")); trayIcon->setToolTip("Screen Sharing"); trayMenu = new QMenu(this);
trayMenu->addAction("Open Window", this, [&]() { this->show(); }); trayMenu->addAction("Start", this, [&]() { monitoring = true; }); trayMenu->addAction("Stop", this, [&]() { monitoring = false; }); trayMenu->addAction("Exit", this, [&]() { QCoreApplication::quit(); });
trayIcon->setContextMenu(trayMenu); trayIcon->show();
screen = QGuiApplication::primaryScreen(); QPixmap screenshot = screen->grabWindow(0);
imageLabel->setPixmap(screenshot); screenshot.scaled(this->size(), Qt::KeepAspectRatio);

this->setCentralWidget(imageLabel); this->setMinimumWidth(920); this->setMinimumHeight(540);
connectMqtt(); screenMonitor();}

MainWindow::~MainWindow() { delete ui;}
void MainWindow::closeEvent(QCloseEvent *event) { if (trayIcon->isVisible()) { hide(); event->ignore(); } else { event->accept(); }}
void MainWindow::screenMonitor() { QTimer *timer = new QTimer(this); QObject::connect(timer, &QTimer::timeout, [&]() {

 // Capture screen content QPixmap screenshot = screen->grabWindow(0);
 // Set QLabel image imageLabel->setPixmap(screenshot); if (monitoring) { QByteArray byteArray; QBuffer buffer(&byteArray); buffer.open(QIODevice::WriteOnly); QImage image = screenshot.toImage();
 qDebug() << "origin size" << image.sizeInBytes(); int ratio = settings->value("image/ratio").toInt(); screenshot.save(&buffer, "JPEG", (ratio > 0 ? ratio : 20)); // Can be replaced with other image formats like "PNG"
 QString base64 = byteArray.toBase64(); // qDebug() << base64; QMqttTopicName topic("desktop/" + ipv4); qDebug() << mqttClient->state(); qDebug() << base64.toUtf8().size(); if (mqttClient->state() == QMqttClient::Connected) { mqttClient->publish(topic, base64.toUtf8()); }
 }

 });
 int interval = settings->value("timer/interval").toInt(); timer->setInterval(interval > 0 ? interval : 200); timer->start();}
QString MainWindow::ipV4() { QString hostName = QHostInfo::localHostName();
 // Get host information based on hostname QHostInfo hostInfo = QHostInfo::fromName(hostName);
 // Get IP address list QList <QHostAddress> addressList = hostInfo.addresses();
 // Iterate through IP address list foreach( const QHostAddress &address, addressList) {
 // Check if it is an IPv4 address if (address.protocol() == QAbstractSocket::IPv4Protocol) { qDebug() << "IPv4 Address:" << address.toString(); return address.toString(); }
 } return NULL;}
void MainWindow::connectMqtt() {
 mqttClient->setClientId("desktop_" + ipv4); mqttClient->setHostname(settings->value("mqtt/hostname").toString()); mqttClient->setPort(settings->value("mqtt/port").toInt()); mqttClient->setUsername(settings->value("mqtt/username").toString()); mqttClient->setPassword(settings->value("mqtt/password").toString()); mqttClient->setAutoKeepAlive(true);
 mqttClient->connectToHost(); QObject::connect(mqttClient, &QMqttClient::connected, [&]() { qDebug() << "Connected successfully"; });
}

03

The front end uses React + MQTT.js to display the image.

import {useEffect, useState} from "react";import * as mqtt from "mqtt";import {Image} from "antd";
export const IndexPage = () => { const [client, setClient] = useState<mqtt.MqttClient>() const [image,setImage] = useState<any>()
 useEffect(() => { const mqttClient = mqtt.connect("mqtt://localhost:8083/mqtt") mqttClient.on("connect", () => { mqttClient.subscribe("desktop/192.168.247.1", (err) => { }); }); mqttClient.on('message',(topic, payload, packet)=>{ setImage(`data:image/jpeg;base64,${payload}`); })
 setClient(mqttClient) }, [])
 return <> <Image src={image}/> </>}
Implementing Simple Screen Sharing with Qt and MQTT

Effect

Implementing Simple Screen Sharing with Qt and MQTT

On the left is the effect obtained by the front end, and on the right is the screen capture effect.

Implementing Simple Screen Sharing with Qt and MQTT

Implementing Simple Screen Sharing with Qt and MQTT

Deployment

Implementing Simple Screen Sharing with Qt and MQTT

Configure the Windows deployment environment variables.

Implementing Simple Screen Sharing with Qt and MQTT

Set up the deployment folder.

Implementing Simple Screen Sharing with Qt and MQTT

Execute the deployment.

Implementing Simple Screen Sharing with Qt and MQTT

Then you will see the generated EXE file in the target folder.

Implementing Simple Screen Sharing with Qt and MQTT

Use windeployqt6 to automatically copy the required dependency files.

Implementing Simple Screen Sharing with Qt and MQTT

Implementing Simple Screen Sharing with Qt and MQTT

Implementing Simple Screen Sharing with Qt and MQTT
Implementing Simple Screen Sharing with Qt and MQTT
Implementing Simple Screen Sharing with Qt and MQTT
Implementing Simple Screen Sharing with Qt and MQTT

Leave a Comment