65.QT-UDP组播实现多人共享桌面(同时支持收发显示)

2021-06-29 10:59:00 浏览数 (1)

这里我们只是简单学习下通过udp组播如何共享桌面demo.帧率上面比较低,毕竟没有用推流,只是简单的将图片发送到组播地址,而加入组播地址的客户端去取数据显示而已.

主要是为了学习UDP知识而写的,真的想要做共享桌面的话,建议还是使用qt FFmpeg推流.速度上会快很多(后续有时间再来出)

1.Demo介绍

截图如下所示:

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

功能介绍

  • 一份代码同时支持收数据处理发数据处理.
  • 自动检查帧率每帧图片字节大小
  • 代码中使用了多线程队列协助QWidget显示.
  • 当接收共享时,会在线程中不停接收数据,直到接收到完整的一份数据时,则放到队列中,然后供QWidget提取数据.
  • 当开启共享时,则在线程中抓取桌面数据,实时发送,并备份一个QPixmap供QWidget显示数据

代码和可以直接运行的程序都放在群里,需要的自行下载:

2.sharescreenthread.cpp代码如下所示

代码语言:javascript复制
#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代码如下所示

代码语言:javascript复制
#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;
    }
}

0 人点赞