65.QT-UDP組播實現多人共享桌面(同時支援收發顯示)

諾謙發表於2021-06-24

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

 

相關文章