这里我们只是简单学习下通过udp组播如何共享桌面demo.帧率上面比较低,毕竟没有用推流,只是简单的将图片发送到组播地址,而加入组播地址的客户端去取数据显示而已.
主要是为了学习UDP知识而写的,真的想要做共享桌面的话,建议还是使用qt FFmpeg推流.速度上会快很多(后续有时间再来出)
1.Demo介绍
截图如下所示:

gif效果如下所示(有点大,加载有点久):

功能介绍
- 一份代码同时支持收数据处理和发数据处理.
- 自动检查帧率和每帧图片字节大小
- 代码中使用了多线程和队列协助QWidget显示.
- 当接收共享时,会在线程中不停接收数据,直到接收到完整的一份数据时,则放到队列中,然后供QWidget提取数据.
- 当开启共享时,则在线程中抓取桌面数据,实时发送,并备份一个QPixmap供QWidget显示数据
代码和可以直接运行的程序都放在群里,需要的自行下载:

2.sharescreenthread.cpp代码如下所示
- #include "sharescreenthread.h"
- ShareScreenThread::ShareScreenThread(QThread *parent) : QThread(parent),
- m_state(ShareScreen_None),
- groupAddress("239.255.43.21"),
- m_runCnt(0),
- m_canRead(false),
- m_sendQuality(20)
- {
- m_recvQueue.clear();
- }
- bool ShareScreenThread::startGrabWindow()
- {
- QMutexLocker locker(&m_mutex);
- if (m_state == ShareScreen_Stop || m_state == ShareScreen_SendRunning) {
- m_state = ShareScreen_SendRunning;
- emit stateChange();
- return true;
- }
- return false;
- }
- bool ShareScreenThread::stopGrabWindow()
- {
- QMutexLocker locker(&m_mutex);
- if (m_state == ShareScreen_SendRunning || m_state == ShareScreen_Stop) {
- m_state = ShareScreen_EnterStop;
- return true;
- }
- return false;
- }
- void ShareScreenThread::run()
- {
- m_udp = new QUdpSocket();
- qDebug()<<"绑定:"<<m_udp->bind(QHostAddress::AnyIPv4, 44544, QUdpSocket::ShareAddress | QUdpSocket::ReuseAddressHint);
- qDebug()<<"加入:"<<m_udp->joinMulticastGroup(groupAddress);
- while(1) {
- switch (m_state) {
- case ShareScreen_None:
- m_runCnt++;
- if (m_runCnt > 100) {
- m_state = ShareScreen_Stop;
- m_preGetWindowMSec = QDateTime::currentDateTime().toMSecsSinceEpoch(); //记录时间
- emit stateChange();
- }
- msleep(10);
- if(m_udp->hasPendingDatagrams() ) {
- m_state = ShareScreen_RecvRunning;
- emit stateChange();
- m_preGetWindowMSec = QDateTime::currentDateTime().toMSecsSinceEpoch(); //记录时间
- getWindow();
- }
- break;
- case ShareScreen_Stop:
- if(m_udp->hasPendingDatagrams()) {
- m_state = ShareScreen_RecvRunning;
- emit stateChange();
- getWindow();
- }
- break;
- case ShareScreen_RecvRunning:
- getWindow();
- break;
- case ShareScreen_SendRunning:
- grabWindow();
- break;
- case ShareScreen_EnterStop: // 由于广播,自己会受到自己消息,需要清空
- if (m_udp->hasPendingDatagrams() ) {
- m_udp->receiveDatagram();
- } else {
- m_state = ShareScreen_Stop;
- emit stateChange();
- }
- break;
- default: break;
- }
- }
- }
3.widget.cpp代码如下所示
- #include "widget.h"
- #include "ui_widget.h"
- Widget::Widget(QWidget *parent)
- : QWidget(parent),
- ui(new Ui::Widget)
- {
- ui->setupUi(this);
- connect(&m_thread, SIGNAL(stateChange()), this, SLOT(onStateChange()));
- m_thread.start();
- connect(&m_updateShow, SIGNAL(timeout()), this, SLOT(onUpdateShow()));
- setWindowTitle("UDP共享屏幕");
- }
- Widget::~Widget()
- {
- m_thread.terminate();
- delete ui;
- }
- void Widget::onStateChange()
- {
- qDebug()<<"onStateChange"<<m_thread.state();
- switch (m_thread.state()) {
- case ShareScreenThread::ShareScreen_None: break;
- case ShareScreenThread::ShareScreen_Stop: ui->labelHint->setText("等待共享..."); cleanShow(); ui->comboQuality->setEnabled(true); break;
- case ShareScreenThread::ShareScreen_RecvRunning: ui->labelHint->setText("有人正在共享中?");
- m_pressMSec = QDateTime::currentDateTime().toMSecsSinceEpoch(); //记录的时间
- m_updateShowCnt = 0;
- m_updateShow.start(25);
- ui->comboQuality->setEnabled(false);
- break;
- case ShareScreenThread::ShareScreen_SendRunning: ui->labelHint->setText("您正在共享中?"); ui->comboQuality->setEnabled(true); break;
- default: break;
- }
- }
- void Widget::cleanShow()
- {
- ui->labelShow->clear();
- ui->labelByte->setText(QString("每帧: %1KB").arg(0));
- ui->labelFPS->setText("当前FPS: "+ QString("%1").arg(0));
- }
- void Widget::onUpdateShow()
- {
- bool getOk = false;
- int size = 0;
- QPixmap pix(m_thread.getPixmap(getOk, size));
- QSize imageSize =pix.size();
- if (size!=0)
- ui->labelByte->setText(QString("每帧: %1KB").arg(size/1024));
- if (getOk == false)
- return;
- pix = pix.scaled(ui->labelShow->size(), Qt::KeepAspectRatio);
- if (m_thread.state() == ShareScreenThread::ShareScreen_RecvRunning) {
- QPainter painter(&pix);
- painter.setRenderHints(QPainter::Antialiasing);
- QPixmap mouse(":/mouse");
- double xratio = pix.width() / (double)imageSize.width();
- double yratio = pix.height() / (double)imageSize.height();
- painter.drawPixmap(m_thread.getMousePos().x()*xratio, m_thread.getMousePos().y()*yratio , 25*xratio, 25*yratio, mouse);
- }
- ui->labelShow->setPixmap(pix);
- if (m_updateShowCnt++ >= 10) {
- qint64 tmp = QDateTime::currentDateTime().toMSecsSinceEpoch();
- qint64 durationMs = tmp - m_pressMSec;
- int fps = m_updateShowCnt * 1000/durationMs;
- ui->labelFPS->setText("当前FPS: "+ QString("%1").arg(fps));
- m_updateShowCnt = 0;
- m_pressMSec = tmp;
- }
- }
- void Widget::on_btnStartShare_clicked()
- {
- bool question;
- switch (m_thread.state()) {
- case ShareScreenThread::ShareScreen_None: customDialog::ShowMessageErr(this,"提示", "正在初始化中!"); return;
- case ShareScreenThread::ShareScreen_Stop: cleanShow(); break;
- case ShareScreenThread::ShareScreen_RecvRunning: customDialog::ShowMessageInfo(this,"提示", "有人正在共享中!"); return;
- case ShareScreenThread::ShareScreen_SendRunning: question = customDialog::ShowMessageQuestion(this,"询问", "是否取消共享?");
- if (!question)
- return;
- }
- bool myStartd = ui->btnStartShare->text().contains("停止");
- if (myStartd) {
- m_thread.stopGrabWindow();
- ui->btnStartShare->setText("开始共享");
- ui->labelFPS->setText("当前FPS: 0");
- m_updateShow.stop();
- ui->labelShow->setPixmap(QPixmap());
- } else {
- m_thread.startGrabWindow();
- ui->btnStartShare->setText("停止共享");
- m_pressMSec = QDateTime::currentDateTime().toMSecsSinceEpoch(); //记录的时间
- m_updateShowCnt = 0;
- m_updateShow.start(12);
- }
- }
- void Widget::keyPressEvent(QKeyEvent *event)
- {
- if (ui->control->isHidden() && event->key() == Qt::Key_Escape) {
- ui->control->show();
- showMaximized();
- }
- }
- void Widget::on_btnFull_clicked()
- {
- ui->control->hide();
- showFullScreen();
- }
- void Widget::on_comboQuality_currentIndexChanged(int index)
- {
- switch (index) {
- case 0 : m_thread.setQuality(20); break;
- case 1 : m_thread.setQuality(38); break;
- case 2 : m_thread.setQuality(50); break;
- }
- }