屏幕共享程序的实现:
发送端(共享屏幕服务端) :
1. 获取桌面图像
QScreen *screen = QApplication::primaryScreen(); //获取当前程序的主窗口对象
QPixmap map = screen->grabWindow(QApplication::desktop()->winId()); //调用主窗口对象的捕捉窗口图像,并传递桌面窗口的id号
2. 编码成jpg
QByteArray ba;
QBuffer bf(&ba);
map.save(&bf, "jpg", 30) // 30表示压宿率,值从0 – 100, 值越小表示编码出来的图像文件就越小,当然也就越不清晰
3. 用udp组播发出.
组播的编程参考http://blog.csdn.net/jklinux/article/details/72123951
一张jpg大概100K, 每次传输4K字节, 需分成多次传输.
每次桌面图像编码成jpg, 图像大小是不固定的. 每张图像传输第一次时,发出"newImage:长度",
QUdpSocket成功发出个部分数据后, waitForBytesWritten等待发出,完成后再发出下一部分.
接收端:
1. 创建udp对象,加入组播
2. 接收数据到内存数据里,并且判断。 如接收到"newImage:长度"则表示上一张图像已传输完毕,可以把上一张图像刷出来了。并且也知道下一张图像的长度。
3. 接收完成一张图像后。使用QPixmap从内存数组里加载图像.
QPixmap map;
map.loadFromData((uchar *)data_pic, len_recv);
label->setPixmap(map);
完整工程源码:
发送端:
mywin.h
#ifndef MYWIN_H
#define MYWIN_H
#include <QWidget>
#include <QPushButton>
#include "mythread.h"
class MyWin : public QWidget
{
Q_OBJECT
private:
QPushButton *btn;
MyThread *thread;
public:
explicit MyWin(QWidget *parent = 0);
~MyWin();
signals:
public slots:
void slot_btn();
};
#endif // MYWIN_H
mywin.cpp
```cpp
#include "mywin.h"
MyWin::MyWin(QWidget *parent) : QWidget(parent)
{
btn = new QPushButton("start", this);
btn->setGeometry(10, 10, 150, 80);
connect(btn, SIGNAL(clicked(bool)), this, SLOT(slot_btn()));
thread = new MyThread;
thread->start();
}
MyWin::~MyWin()
{
delete btn;
thread->down();
thread->terminate();
thread->wait();
delete thread;
}
void MyWin::slot_btn()
{
if (btn->text() == "start")
{
thread->goOn();
btn->setText("pause");
}
else
{
thread->pause();
btn->setText("start");
}
}
mythread.h
#ifndef MYTHREAD_H
#define MYTHREAD_H
#include <QUdpSocket>
#include <QThread>
#include <QMutex>
class MyThread : public QThread
{
private:
QUdpSocket *udp;
char *data;
QMutex *mutex;
int flag;
public:
MyThread();
~MyThread();
void run();
void pause();
void goOn();
void down();
};
#endif // MYTHREAD_H
mythread.cpp
#include "mythread.h"
#include <QScreen>
#include <QApplication>
#include <QPixmap>
#include <QDesktopWidget>
#include <QBuffer>
#include <QNetworkInterface>
#include <QMessageBox>
#include <unistd.h>
#define MAX_LEN (3*1024*1024)
#define P_LEN 4000
#define PORT 7788
#define MULTI_ADDR "224.55.55.55"
MyThread::MyThread()
{
mutex = new QMutex;
mutex->lock();
flag = 0;
}
MyThread::~MyThread()
{
}
void MyThread::run()
{
QList<QNetworkInterface> list;
QNetworkInterface intf;
list = QNetworkInterface::allInterfaces(); //获取系统里所有的网卡对象
for (int i = 0; i < list.size(); i++)
{
intf = list.at(i);
QNetworkInterface::InterfaceFlags flags = intf.flags();
//找出处在执行状态,能支持组播的网卡对象
if ((flags & QNetworkInterface::IsRunning) && (flags & QNetworkInterface::CanMulticast))
break;
}
udp = new QUdpSocket;
if (!udp->bind(QHostAddress::AnyIPv4))
exit(0);
qDebug() << intf.name();
udp->setMulticastInterface(intf);
udp->setSocketOption(QUdpSocket::SendBufferSizeSocketOption, MAX_LEN*3);
data = new char[P_LEN];
QByteArray ba_pic;
int len_pic, len_sent;
while (1)
{
if (0 == flag)
mutex->lock();
if (2 == flag)
break;
QScreen *src = QApplication::primaryScreen();
QPixmap map = src->grabWindow(QApplication::desktop()->winId());
ba_pic.resize(0); //清除空间
QBuffer bf(&ba_pic);
map.save(&bf, "jpg", 30);
len_pic = ba_pic.size();
char *pdata = ba_pic.data();
len_sent = 0 ; //已发送长度
sprintf(data, "newImage:%d", len_pic);
int l, ret;
udp->writeDatagram(data, strlen(data), QHostAddress(MULTI_ADDR), PORT);
udp->waitForBytesWritten();
for (len_sent = 0; len_sent < len_pic; len_sent += P_LEN)
{
if (len_sent + P_LEN > len_pic)
l = len_pic - len_sent;
else
l = P_LEN;
ret = udp->writeDatagram(pdata+len_sent, l, QHostAddress(MULTI_ADDR), PORT);
udp->waitForBytesWritten();
}
}
}
void MyThread::pause()
{
flag = 0;
}
void MyThread::goOn()
{
flag = 1;
mutex->unlock();
}
void MyThread::down()
{
flag = 2;
mutex->unlock();
}
main.cpp
#include <QApplication>
#include "mywin.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyWin win;
win.show();
return a.exec();
}
接收端:
mywin.h
#ifndef MYWIN_H
#define MYWIN_H
#include <QWidget>
#include <QUdpSocket>
#include <QFile>
#include <QLabel>
class MyWin : public QWidget
{
Q_OBJECT
private:
QUdpSocket *udp;
int len_pic;
int len_recv;
char *data_pic;
char *data;
QLabel *label;
void resizeEvent(QResizeEvent *event);
public:
explicit MyWin(QWidget *parent = 0);
~MyWin();
signals:
public slots:
void slot_udp_read();
};
#endif // MYWIN_H
mywin.cpp
#include "mywin.h"
#include <QMessageBox>
#include <QPixmap>
#define MAX_LEN 5*1024*1024
#define P_LEN 4000
#define PORT 7788
#define MULTI_ADDR "224.55.55.55"
MyWin::MyWin(QWidget *parent) : QWidget(parent)
{
udp = new QUdpSocket(this);
if (!udp->bind(QHostAddress::AnyIPv4, PORT))
{
QMessageBox::critical(this, "error", "bind failed");
exit(0);
}
if (!udp->joinMulticastGroup(QHostAddress(MULTI_ADDR)))
{
QMessageBox::critical(this, "error", "join multicast group failed");
exit(0);
}
data_pic = new char[MAX_LEN];
data = new char[P_LEN];
label = new QLabel(this);
label->setScaledContents(true);
label->resize(this->size());
connect(udp, SIGNAL(readyRead()), this, SLOT(slot_udp_read()));
}
MyWin::~MyWin()
{
delete udp;
delete [] data_pic;
delete [] data;
}
void MyWin::resizeEvent(QResizeEvent *event)
{
label->resize(this->size());
}
void MyWin::slot_udp_read()
{
int ret;
ret = udp->readDatagram(data, P_LEN);
data[ret] = 0;
QByteArray ba(data, ret);
if (ba.contains("newImage:"))
{
len_pic = atoi(data+ba.indexOf("newImage:")+9);
qDebug() << len_pic;
len_recv = 0;
return;
}
memcpy(data_pic+len_recv, data, ret);
len_recv += ret;
if (len_recv >= len_pic)
{
QPixmap map;
map.loadFromData((uchar *)data_pic, len_recv);
label->setPixmap(map);
}
}
main.cpp
#include <QApplication>
#include "mywin.h"
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
MyWin win;
win.show();
return a.exec();
}