這里我們只是簡單學習下通過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; } }