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