├── 1.jpg
├── 2.wav
├── 3.gif
├── img
├── 1.png
├── 2.png
├── 3.png
├── 4.png
├── 5.png
├── 云会议.vsdx
└── architecture.png
├── AudioInput.cpp
├── AudioOutput.cpp
├── screen.h
├── resource.qrc
├── main.cpp
├── partner.h
├── screen.cpp
├── logqueue.h
├── recvsolve.h
├── AudioInput.h
├── README.md
├── AudioOutput.h
├── sendimg.h
├── sendtext.h
├── partner.cpp
├── mytextedit.h
├── recvsolve.cpp
├── .gitignore
├── myvideosurface.h
├── mytcpsocket.h
├── netheader.cpp
├── chatmessage.h
├── logqueue.cpp
├── CloudMeeting.pro
├── myvideosurface.cpp
├── widget.h
├── netheader.h
├── sendimg.cpp
├── sendtext.cpp
├── mytextedit.cpp
├── chatmessage.cpp
├── mytcpsocket.cpp
├── widget.cpp
└── widget.ui
/1.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/1.jpg
--------------------------------------------------------------------------------
/2.wav:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/2.wav
--------------------------------------------------------------------------------
/3.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/3.gif
--------------------------------------------------------------------------------
/img/1.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/img/1.png
--------------------------------------------------------------------------------
/img/2.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/img/2.png
--------------------------------------------------------------------------------
/img/3.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/img/3.png
--------------------------------------------------------------------------------
/img/4.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/img/4.png
--------------------------------------------------------------------------------
/img/5.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/img/5.png
--------------------------------------------------------------------------------
/img/云会议.vsdx:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/img/云会议.vsdx
--------------------------------------------------------------------------------
/AudioInput.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/AudioInput.cpp
--------------------------------------------------------------------------------
/AudioOutput.cpp:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/AudioOutput.cpp
--------------------------------------------------------------------------------
/img/architecture.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/muleimulei/CloudMeeting/HEAD/img/architecture.png
--------------------------------------------------------------------------------
/screen.h:
--------------------------------------------------------------------------------
1 | #ifndef SCREEN_H
2 | #define SCREEN_H
3 |
4 |
5 | class Screen
6 | {
7 | public:
8 | static int width;
9 | static int height;
10 |
11 | static void init();
12 | };
13 |
14 | #endif // SCREEN_H
15 |
--------------------------------------------------------------------------------
/resource.qrc:
--------------------------------------------------------------------------------
1 |
2 |
3 | 1.jpg
4 | 3.gif
5 |
6 |
7 | 2.wav
8 |
9 |
10 |
--------------------------------------------------------------------------------
/main.cpp:
--------------------------------------------------------------------------------
1 | #include
2 | #include "widget.h"
3 | #include "screen.h"
4 | #include
5 | int main(int argc, char* argv[])
6 | {
7 | QTextCodec::setCodecForLocale(QTextCodec::codecForName("UTF8"));
8 | QApplication app(argc, argv);
9 | Screen::init();
10 |
11 | Widget w;
12 | w.show();
13 | return app.exec();
14 | }
15 |
--------------------------------------------------------------------------------
/partner.h:
--------------------------------------------------------------------------------
1 | #ifndef PARTNER_H
2 | #define PARTNER_H
3 |
4 | #include
5 |
6 | class Partner : public QLabel
7 | {
8 | Q_OBJECT
9 | private:
10 | quint32 ip;
11 |
12 | void mousePressEvent(QMouseEvent *ev) override;
13 | int w;
14 | public:
15 | Partner(QWidget * parent = nullptr, quint32 = 0);
16 | void setpic(QImage img);
17 | signals:
18 | void sendip(quint32); //发送ip
19 | };
20 |
21 | #endif // PARTNER_H
22 |
--------------------------------------------------------------------------------
/screen.cpp:
--------------------------------------------------------------------------------
1 | #include "screen.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 |
7 | int Screen::width = -1;
8 | int Screen::height = -1;
9 |
10 |
11 | void Screen::init()
12 | {
13 | QScreen *s = QGuiApplication::primaryScreen();
14 |
15 |
16 | Screen::width = s->geometry().width();
17 | Screen::height = s->geometry().height();
18 |
19 | // qDebug() << Screen::width << " " << Screen::height;
20 | }
21 |
--------------------------------------------------------------------------------
/logqueue.h:
--------------------------------------------------------------------------------
1 | #ifndef LOGQUEUE_H
2 | #define LOGQUEUE_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "netheader.h"
8 |
9 | class LogQueue : public QThread
10 | {
11 | private:
12 | void run();
13 | QMutex m_lock;
14 | bool m_isCanRun;
15 |
16 | QUEUE_DATA log_queue;
17 | FILE *logfile;
18 | public:
19 | explicit LogQueue(QObject *parent = nullptr);
20 | void stopImmediately();
21 | void pushLog(Log*);
22 | };
23 |
24 | #endif // LOGQUEUE_H
25 |
--------------------------------------------------------------------------------
/recvsolve.h:
--------------------------------------------------------------------------------
1 | #ifndef RECVSOLVE_H
2 | #define RECVSOLVE_H
3 | #include "netheader.h"
4 | #include
5 | #include
6 | /*
7 | * 接收线程
8 | * 从接收队列拿取数据
9 | */
10 | class RecvSolve : public QThread
11 | {
12 | Q_OBJECT
13 | public:
14 | RecvSolve(QObject *par = NULL);
15 | void run() override;
16 | private:
17 | QMutex m_lock;
18 | bool m_isCanRun;
19 | signals:
20 | void datarecv(MESG *);
21 | public slots:
22 | void stopImmediately();
23 | };
24 |
25 | #endif // RECVSOLVE_H
26 |
--------------------------------------------------------------------------------
/AudioInput.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | class AudioInput : public QObject
7 | {
8 | Q_OBJECT
9 | private:
10 | QAudioInput *audio;
11 | QIODevice* inputdevice;
12 | char* recvbuf;
13 | public:
14 | AudioInput(QObject *par = 0);
15 | ~AudioInput();
16 | private slots:
17 | void onreadyRead();
18 | void handleStateChanged(QAudio::State);
19 | QString errorString();
20 | void setVolumn(int);
21 | public slots:
22 | void startCollect();
23 | void stopCollect();
24 | signals:
25 | void audioinputerror(QString);
26 | };
27 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # CloudMeeting
2 | 客户端基于QT,可以传输视频,音频,同时可以传输文本。涉及信号与槽,多线程,socket通信,Tcp/IP包解析. 服务器端地址:https://github.com/muleimulei/CloudMeeting_Server
3 |
4 | ## 初始界面
5 | 
6 |
7 | #### 输入服务器端地址与端口,点击连接按钮就可以连接到服务器, 然后可以点击创建会议按钮或者加入会议按钮。当点击创建会议按钮时,服务器会发送一个空闲的房间号(其实就是进程号)。然后把房间号给别人,别人就可以连接服务器,加入会议
8 |
9 | 
10 |
11 | #### 点击打开视频,就可以开启摄像头,向别人共享你的图像
12 |
13 | 
14 |
15 | #### 点击打开音频,就可以打开输入音频,向别人说话。(默认打开输出音频,别人说的话自己可以听到)
16 |
17 | 
18 |
19 | #### 点击聊天窗口,用户可以输入信息,发往会议里的所有人
20 |
21 | 
22 |
23 | #### 用户也可以@特定用户,特定用户端会发出提示音
24 |
25 | 
26 |
27 |
28 | ## 项目结构图
29 |
30 | 
--------------------------------------------------------------------------------
/AudioOutput.h:
--------------------------------------------------------------------------------
1 | #pragma once
2 |
3 | #include
4 | #include
5 | #include
6 | #include
7 | class AudioOutput : public QThread
8 | {
9 | Q_OBJECT
10 | private:
11 | QAudioOutput* audio;
12 | QIODevice* outputdevice;
13 | QMutex device_lock;
14 |
15 | volatile bool is_canRun;
16 | QMutex m_lock;
17 | void run();
18 | QString errorString();
19 | public:
20 | AudioOutput(QObject *parent = 0);
21 | ~AudioOutput();
22 | void stopImmediately();
23 | void startPlay();
24 | void stopPlay();
25 | private slots:
26 | void handleStateChanged(QAudio::State);
27 | void setVolumn(int);
28 | void clearQueue();
29 | signals:
30 | void audiooutputerror(QString);
31 | void speaker(QString);
32 | };
33 |
--------------------------------------------------------------------------------
/sendimg.h:
--------------------------------------------------------------------------------
1 | #ifndef SENDIMG_H
2 | #define SENDIMG_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 |
12 | class SendImg : public QThread
13 | {
14 | Q_OBJECT
15 | private:
16 | QQueue imgqueue;
17 | QMutex queue_lock;
18 | QWaitCondition queue_waitCond;
19 | void run() override;
20 | QMutex m_lock;
21 | volatile bool m_isCanRun;
22 | public:
23 | SendImg(QObject *par = NULL);
24 |
25 | void pushToQueue(QImage);
26 | public slots:
27 | void ImageCapture(QImage); //捕获到视频帧
28 | void clearImgQueue(); //线程结束时,清空视频帧队列
29 | void stopImmediately();
30 | };
31 |
32 | #endif // SENDIMG_H
33 |
--------------------------------------------------------------------------------
/sendtext.h:
--------------------------------------------------------------------------------
1 | #ifndef SENDTEXT_H
2 | #define SENDTEXT_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "netheader.h"
8 |
9 | struct M
10 | {
11 | QString str;
12 | MSG_TYPE type;
13 |
14 | M(QString s, MSG_TYPE e)
15 | {
16 | str = s;
17 | type = e;
18 | }
19 | };
20 |
21 | //发送文本数据
22 | class SendText : public QThread
23 | {
24 | Q_OBJECT
25 | private:
26 | QQueue textqueue;
27 | QMutex textqueue_lock; //队列锁
28 | QWaitCondition queue_waitCond;
29 | void run() override;
30 | QMutex m_lock;
31 | bool m_isCanRun;
32 | public:
33 | SendText(QObject *par = NULL);
34 | ~SendText();
35 | public slots:
36 | void push_Text(MSG_TYPE, QString str = "");
37 | void stopImmediately();
38 | };
39 |
40 | #endif // SENDTEXT_H
41 |
--------------------------------------------------------------------------------
/partner.cpp:
--------------------------------------------------------------------------------
1 | #include "partner.h"
2 | #include
3 | #include
4 | #include
5 | Partner::Partner(QWidget *parent, quint32 p):QLabel(parent)
6 | {
7 | // qDebug() <<"dsaf" << this->parent();
8 | ip = p;
9 |
10 | this->setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Minimum);
11 | w = ((QWidget *)this->parent())->size().width();
12 | this->setPixmap(QPixmap::fromImage(QImage(":/myImage/1.jpg").scaled(w-10, w-10)));
13 | this->setFrameShape(QFrame::Box);
14 |
15 | this->setStyleSheet("border-width: 1px; border-style: solid; border-color:rgba(0, 0 , 255, 0.7)");
16 | // this->setToolTipDuration(5);
17 |
18 | this->setToolTip(QHostAddress(ip).toString());
19 | }
20 |
21 |
22 | void Partner::mousePressEvent(QMouseEvent *)
23 | {
24 | emit sendip(ip);
25 | }
26 | void Partner::setpic(QImage img)
27 | {
28 | this->setPixmap(QPixmap::fromImage(img.scaled(w-10, w-10)));
29 | }
30 |
--------------------------------------------------------------------------------
/mytextedit.h:
--------------------------------------------------------------------------------
1 | #ifndef MYTEXTEDIT_H
2 | #define MYTEXTEDIT_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include
10 |
11 | class Completer: public QCompleter
12 | {
13 | Q_OBJECT
14 | public:
15 | explicit Completer(QWidget *parent= nullptr);
16 | };
17 |
18 | class MyTextEdit : public QWidget
19 | {
20 | Q_OBJECT
21 | private:
22 | QPlainTextEdit *edit;
23 | Completer *completer;
24 | QVector > ipspan;
25 | public:
26 | explicit MyTextEdit(QWidget *parent = nullptr);
27 | QString toPlainText();
28 | void setPlainText(QString);
29 | void setPlaceholderText(QString);
30 | void setCompleter(QStringList );
31 | private:
32 | QString textUnderCursor();
33 | bool eventFilter(QObject *, QEvent *);
34 |
35 | private slots:
36 | void changeCompletion(QString);
37 | public slots:
38 |
39 | void complete();
40 | signals:
41 | };
42 |
43 | #endif // MYTEXTEDIT_H
44 |
--------------------------------------------------------------------------------
/recvsolve.cpp:
--------------------------------------------------------------------------------
1 | #include "recvsolve.h"
2 | #include
3 | #include
4 | #include
5 | extern QUEUE_DATA queue_recv;
6 |
7 | void RecvSolve::stopImmediately()
8 | {
9 | QMutexLocker locker(&m_lock);
10 | m_isCanRun = false;
11 | }
12 |
13 | RecvSolve::RecvSolve(QObject *par):QThread(par)
14 | {
15 | qRegisterMetaType();
16 | m_isCanRun = true;
17 | }
18 |
19 | void RecvSolve::run()
20 | {
21 | WRITE_LOG("start solving data thread: 0x%p", QThread::currentThreadId());
22 | for(;;)
23 | {
24 | {
25 | QMutexLocker locker(&m_lock);
26 | if (m_isCanRun == false)
27 | {
28 | WRITE_LOG("stop solving data thread: 0x%p", QThread::currentThreadId());
29 | return;
30 | }
31 | }
32 | MESG * msg = queue_recv.pop_msg();
33 | if(msg == NULL) continue;
34 | /*else free(msg);
35 | qDebug() << "取出队列:" << msg->msg_type;*/
36 | emit datarecv(msg);
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This file is used to ignore files which are generated
2 | # ----------------------------------------------------------------------------
3 |
4 | *~
5 | *.autosave
6 | *.a
7 | *.core
8 | *.moc
9 | *.o
10 | *.obj
11 | *.orig
12 | *.rej
13 | *.so
14 | *.so.*
15 | *_pch.h.cpp
16 | *_resource.rc
17 | *.qm
18 | .#*
19 | *.*#
20 | core
21 | !core/
22 | tags
23 | .DS_Store
24 | .directory
25 | *.debug
26 | Makefile*
27 | *.prl
28 | *.app
29 | moc_*.cpp
30 | ui_*.h
31 | qrc_*.cpp
32 | Thumbs.db
33 | *.res
34 | *.rc
35 | /.qmake.cache
36 | /.qmake.stash
37 |
38 | # qtcreator generated files
39 | *.pro.user*
40 |
41 | # xemacs temporary files
42 | *.flc
43 |
44 | # Vim temporary files
45 | .*.swp
46 |
47 | # Visual Studio generated files
48 | *.ib_pdb_index
49 | *.idb
50 | *.ilk
51 | *.pdb
52 | *.sln
53 | *.suo
54 | *.vcproj
55 | *vcproj.*.*.user
56 | *.ncb
57 | *.sdf
58 | *.opensdf
59 | *.vcxproj
60 | *vcxproj.*
61 |
62 | # MinGW generated files
63 | *.Debug
64 | *.Release
65 |
66 | # Python byte code
67 | *.pyc
68 |
69 | # Binaries
70 | # --------
71 | *.dll
72 | *.exe
73 |
74 |
--------------------------------------------------------------------------------
/myvideosurface.h:
--------------------------------------------------------------------------------
1 | #ifndef MYVIDEOSURFACE_H
2 | #define MYVIDEOSURFACE_H
3 |
4 | #include
5 |
6 |
7 | class MyVideoSurface : public QAbstractVideoSurface
8 | {
9 | Q_OBJECT
10 | public:
11 | MyVideoSurface(QObject *parent = 0);
12 |
13 | //支持的像素格式
14 | QList supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const override;
15 |
16 | //检测视频流的格式是否合法,返回bool
17 | bool isFormatSupported(const QVideoSurfaceFormat &format) const override;
18 | bool start(const QVideoSurfaceFormat &format) override;
19 | bool present(const QVideoFrame &frame) override;
20 | QRect videoRect() const;
21 | void updateVideoRect();
22 | void paint(QPainter *painter);
23 |
24 | //private:
25 | // QWidget * widget_;
26 | // QImage::Format imageFormat_;
27 | // QRect targetRect_;
28 | // QSize imageSize_;
29 | // QVideoFrame currentFrame_;
30 |
31 | signals:
32 | void frameAvailable(QVideoFrame);
33 |
34 | };
35 |
36 | #endif // MYVIDEOSURFACE_H
37 |
--------------------------------------------------------------------------------
/mytcpsocket.h:
--------------------------------------------------------------------------------
1 | #ifndef MYTCPSOCKET_H
2 | #define MYTCPSOCKET_H
3 |
4 | #include
5 | #include
6 | #include
7 | #include "netheader.h"
8 | #ifndef MB
9 | #define MB (1024 * 1024)
10 | #endif
11 |
12 | typedef unsigned char uchar;
13 |
14 |
15 | class MyTcpSocket: public QThread
16 | {
17 | Q_OBJECT
18 | public:
19 | ~MyTcpSocket();
20 | MyTcpSocket(QObject *par=NULL);
21 | bool connectToServer(QString, QString, QIODevice::OpenModeFlag);
22 | QString errorString();
23 | void disconnectFromHost();
24 | quint32 getlocalip();
25 | private:
26 | void run() override;
27 | qint64 readn(char *, quint64, int);
28 | QTcpSocket *_socktcp;
29 | QThread *_sockThread;
30 | uchar *sendbuf;
31 | uchar* recvbuf;
32 | quint64 hasrecvive;
33 |
34 | QMutex m_lock;
35 | volatile bool m_isCanRun;
36 | private slots:
37 | bool connectServer(QString, QString, QIODevice::OpenModeFlag);
38 | void sendData(MESG *);
39 | void closeSocket();
40 |
41 | public slots:
42 | void recvFromSocket();
43 | void stopImmediately();
44 | void errorDetect(QAbstractSocket::SocketError error);
45 | signals:
46 | void socketerror(QAbstractSocket::SocketError);
47 | void sendTextOver();
48 | };
49 |
50 | #endif // MYTCPSOCKET_H
51 |
--------------------------------------------------------------------------------
/netheader.cpp:
--------------------------------------------------------------------------------
1 | #include "netheader.h"
2 | #include "logqueue.h"
3 | #include
4 | #include
5 |
6 | QUEUE_DATA queue_send; //�ı�����Ƶ���Ͷ���
7 | QUEUE_DATA queue_recv; //���ն���
8 | QUEUE_DATA audio_recv; //��Ƶ���ն���
9 |
10 | LogQueue *logqueue = nullptr;
11 |
12 | void log_print(const char *filename, const char *funcname, int line, const char *fmt, ...)
13 | {
14 | Log *log = (Log *) malloc(sizeof (Log));
15 | if(log == nullptr)
16 | {
17 | qDebug() << "malloc log fail";
18 | }
19 | else
20 | {
21 | memset(log, 0, sizeof (Log));
22 | log->ptr = (char *) malloc(1 * KB);
23 | if(log->ptr == nullptr)
24 | {
25 | free(log);
26 | qDebug() << "malloc log.ptr fail";
27 | return;
28 | }
29 | else
30 | {
31 | memset(log->ptr, 0, 1 * KB);
32 | time_t t = time(NULL);
33 | int pos = 0;
34 | int m = strftime(log->ptr + pos, KB - 2 - pos, "%F %H:%M:%S ", localtime(&t));
35 | pos += m;
36 |
37 | m = snprintf(log->ptr + pos, KB - 2 - pos, "%s:%s::%d>>>", filename, funcname, line);
38 | pos += m;
39 |
40 | va_list ap;
41 | va_start(ap, fmt);
42 | m = _vsnprintf(log->ptr + pos, KB - 2 - pos, fmt, ap);
43 | pos += m;
44 | va_end(ap);
45 | strcat_s(log->ptr+pos, KB-pos, "\n");
46 | log->len = strlen(log->ptr);
47 | logqueue->pushLog(log);
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/chatmessage.h:
--------------------------------------------------------------------------------
1 | #ifndef CHATMESSAGE_H
2 | #define CHATMESSAGE_H
3 |
4 | #include
5 | #include
6 |
7 | class ChatMessage:public QWidget
8 | {
9 | Q_OBJECT
10 | public:
11 | explicit ChatMessage(QWidget *parent = nullptr);
12 |
13 | enum User_Type{
14 | User_System,//系统
15 | User_Me, //自己
16 | User_She, //用户
17 | User_Time, //时间
18 | };
19 | void setTextSuccess();
20 | void setText(QString text, QString time, QSize allSize, QString ip = "", User_Type userType = User_Time);
21 |
22 | QSize getRealString(QString src);
23 | QSize fontRect(QString str);
24 |
25 | inline QString text() {return m_msg;}
26 | inline QString time() {return m_time;}
27 | inline User_Type userType() {return m_userType;}
28 | protected:
29 | void paintEvent(QPaintEvent *event);
30 | private:
31 | QString m_msg;
32 | QString m_time;
33 | QString m_curTime;
34 | QString m_ip;
35 |
36 | QSize m_allSize;
37 | User_Type m_userType = User_System;
38 |
39 | int m_kuangWidth;
40 | int m_textWidth;
41 | int m_spaceWid;
42 | int m_lineHeight;
43 |
44 | QRect m_ipLeftRect;
45 | QRect m_ipRightRect;
46 | QRect m_iconLeftRect;
47 | QRect m_iconRightRect;
48 | QRect m_sanjiaoLeftRect;
49 | QRect m_sanjiaoRightRect;
50 | QRect m_kuangLeftRect;
51 | QRect m_kuangRightRect;
52 | QRect m_textLeftRect;
53 | QRect m_textRightRect;
54 | QPixmap m_leftPixmap;
55 | QPixmap m_rightPixmap;
56 | QLabel* m_loading = Q_NULLPTR;
57 | QMovie* m_loadingMovie = Q_NULLPTR;
58 | bool m_isSending = false;
59 | };
60 |
61 | #endif // CHATMESSAGE_H
62 |
--------------------------------------------------------------------------------
/logqueue.cpp:
--------------------------------------------------------------------------------
1 | #include "logqueue.h"
2 | #include
3 |
4 | LogQueue::LogQueue(QObject *parent) : QThread(parent)
5 | {
6 |
7 | }
8 |
9 | void LogQueue::pushLog(Log* log)
10 | {
11 | log_queue.push_msg(log);
12 | }
13 |
14 | void LogQueue::run()
15 | {
16 | m_isCanRun = true;
17 | for(;;)
18 | {
19 | {
20 | QMutexLocker lock(&m_lock);
21 | if(m_isCanRun == false)
22 | {
23 | fclose(logfile);
24 | return;
25 | }
26 | }
27 | Log *log = log_queue.pop_msg();
28 | if(log == NULL || log->ptr == NULL) continue;
29 |
30 | //----------------write to logfile-------------------
31 | errno_t r = fopen_s(&logfile, "./log.txt", "a");
32 | if(r != 0)
33 | {
34 | qDebug() << "打开文件失败:" << r;
35 | continue;
36 | }
37 |
38 |
39 | qint64 hastowrite = log->len;
40 | qint64 ret = 0, haswrite = 0;
41 | while ((ret = fwrite( (char*)log->ptr + haswrite, 1 ,hastowrite - haswrite, logfile)) < hastowrite)
42 | {
43 | if (ret < 0 && (errno == EAGAIN || errno == EWOULDBLOCK) )
44 | {
45 | ret = 0;
46 | }
47 | else
48 | {
49 | qDebug() << "write logfile error";
50 | break;
51 | }
52 | haswrite += ret;
53 | hastowrite -= ret;
54 | }
55 |
56 | //free
57 | if(log->ptr) free(log->ptr);
58 | if(log) free(log);
59 |
60 | fflush(logfile);
61 | fclose(logfile);
62 | }
63 | }
64 |
65 | void LogQueue::stopImmediately()
66 | {
67 | QMutexLocker lock(&m_lock);
68 | m_isCanRun = false;
69 | }
70 |
--------------------------------------------------------------------------------
/CloudMeeting.pro:
--------------------------------------------------------------------------------
1 | QT += core gui multimedia multimediawidgets network
2 |
3 | greaterThan(QT_MAJOR_VERSION, 4): QT += widgets
4 |
5 | CONFIG += c++11
6 |
7 | # The following define makes your compiler emit warnings if you use
8 | # any Qt feature that has been marked deprecated (the exact warnings
9 | # depend on your compiler). Please consult the documentation of the
10 | # deprecated API in order to know how to port your code away from it.
11 | DEFINES += QT_DEPRECATED_WARNINGS
12 |
13 |
14 | win32-msvc*:QMAKE_CXXFLAGS += /wd"4819" /utf-8
15 |
16 | # You can also make your code fail to compile if it uses deprecated APIs.
17 | # In order to do so, uncomment the following line.
18 | # You can also select to disable deprecated APIs only up to a certain version of Qt.
19 | #DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
20 |
21 | SOURCES += \
22 | AudioInput.cpp \
23 | AudioOutput.cpp \
24 | chatmessage.cpp \
25 | logqueue.cpp \
26 | main.cpp \
27 | mytcpsocket.cpp \
28 | mytextedit.cpp \
29 | myvideosurface.cpp \
30 | netheader.cpp \
31 | partner.cpp \
32 | recvsolve.cpp \
33 | screen.cpp \
34 | sendimg.cpp \
35 | sendtext.cpp \
36 | widget.cpp
37 |
38 | HEADERS += \
39 | AudioInput.h \
40 | AudioOutput.h \
41 | chatmessage.h \
42 | logqueue.h \
43 | mytcpsocket.h \
44 | mytextedit.h \
45 | myvideosurface.h \
46 | netheader.h \
47 | partner.h \
48 | recvsolve.h \
49 | screen.h \
50 | sendimg.h \
51 | sendtext.h \
52 | widget.h
53 |
54 | FORMS += \
55 | widget.ui
56 |
57 | # Default rules for deployment.
58 | qnx: target.path = /tmp/$${TARGET}/bin
59 | else: unix:!android: target.path = /opt/$${TARGET}/bin
60 | !isEmpty(target.path): INSTALLS += target
61 |
62 | RESOURCES += \
63 | resource.qrc
64 |
--------------------------------------------------------------------------------
/myvideosurface.cpp:
--------------------------------------------------------------------------------
1 | #include "myvideosurface.h"
2 | #include
3 | #include
4 | MyVideoSurface::MyVideoSurface(QObject *parent):QAbstractVideoSurface(parent)
5 | {
6 |
7 | }
8 |
9 | QList MyVideoSurface::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const
10 | {
11 | if(handleType == QAbstractVideoBuffer::NoHandle)
12 | {
13 | return QList() << QVideoFrame::Format_RGB32
14 | << QVideoFrame::Format_ARGB32
15 | << QVideoFrame::Format_ARGB32_Premultiplied
16 | << QVideoFrame::Format_RGB565
17 | << QVideoFrame::Format_RGB555;
18 | }
19 | else
20 | {
21 | return QList();
22 | }
23 | }
24 |
25 |
26 | bool MyVideoSurface::isFormatSupported(const QVideoSurfaceFormat &format) const
27 | {
28 | // imageFormatFromPixelFormat: 返回与视频帧像素格式等效的图像格式
29 | //pixelFormat: 返回视频流中的像素格式
30 | return QVideoFrame::imageFormatFromPixelFormat(format.pixelFormat()) != QImage::Format_Invalid;
31 | }
32 |
33 | //将视频流中像素格式转换成格式对等的图片格式
34 | bool MyVideoSurface::start(const QVideoSurfaceFormat &format)
35 | {
36 | if(isFormatSupported(format) && !format.frameSize().isEmpty())
37 | {
38 | QAbstractVideoSurface::start(format);
39 | return true;
40 | }
41 | return false;
42 | }
43 |
44 |
45 | //捕获每一帧视频,每一帧都会到present
46 | bool MyVideoSurface::present(const QVideoFrame &frame)
47 | {
48 | if(!frame.isValid())
49 | {
50 | stop();
51 | return false;
52 | }
53 | if(frame.isMapped())
54 | {
55 | emit frameAvailable(frame);
56 | }
57 | else
58 | {
59 | QVideoFrame f(frame);
60 | f.map(QAbstractVideoBuffer::ReadOnly);
61 | emit frameAvailable(f);
62 | }
63 |
64 | return true;
65 | }
66 |
--------------------------------------------------------------------------------
/widget.h:
--------------------------------------------------------------------------------
1 | #ifndef WIDGET_H
2 | #define WIDGET_H
3 |
4 | #include "AudioInput.h"
5 | #include
6 | #include
7 | #include
8 | #include "mytcpsocket.h"
9 | #include
10 | #include "sendtext.h"
11 | #include "recvsolve.h"
12 | #include "partner.h"
13 | #include "netheader.h"
14 | #include
15 | #include "AudioOutput.h"
16 | #include "chatmessage.h"
17 | #include
18 |
19 | QT_BEGIN_NAMESPACE
20 | namespace Ui { class Widget; }
21 | QT_END_NAMESPACE
22 |
23 | class QCameraImageCapture;
24 | class MyVideoSurface;
25 | class SendImg;
26 | class QListWidgetItem;
27 | class Widget : public QWidget
28 | {
29 | Q_OBJECT
30 | private:
31 | static QRect pos;
32 | quint32 mainip; //主屏幕显示的IP图像
33 | QCamera *_camera; //摄像头
34 | QCameraImageCapture *_imagecapture; //截屏
35 | bool _createmeet; //是否创建会议
36 | bool _joinmeet; // 加入会议
37 | bool _openCamera; //是否开启摄像头
38 |
39 | MyVideoSurface *_myvideosurface;
40 |
41 | QVideoFrame mainshow;
42 |
43 | SendImg *_sendImg;
44 | QThread *_imgThread;
45 |
46 | RecvSolve *_recvThread;
47 | SendText * _sendText;
48 | QThread *_textThread;
49 | //socket
50 | MyTcpSocket * _mytcpSocket;
51 | void paintEvent(QPaintEvent *event);
52 |
53 | QMap partner; //用于记录房间用户
54 | Partner* addPartner(quint32);
55 | void removePartner(quint32);
56 | void clearPartner(); //退出会议,或者会议结束
57 | void closeImg(quint32); //根据IP重置图像
58 |
59 | void dealMessage(ChatMessage *messageW, QListWidgetItem *item, QString text, QString time, QString ip ,ChatMessage::User_Type type); //用户发送文本
60 | void dealMessageTime(QString curMsgTime); //处理时间
61 |
62 | //音频
63 | AudioInput* _ainput;
64 | QThread* _ainputThread;
65 | AudioOutput* _aoutput;
66 |
67 | QStringList iplist;
68 |
69 | public:
70 | Widget(QWidget *parent = nullptr);
71 | ~Widget();
72 |
73 | private slots:
74 | void on_createmeetBtn_clicked();
75 | void on_exitmeetBtn_clicked();
76 |
77 | void on_openVedio_clicked();
78 | void on_openAudio_clicked();
79 | void on_connServer_clicked();
80 | void cameraError(QCamera::Error);
81 | void audioError(QString);
82 | // void mytcperror(QAbstractSocket::SocketError);
83 | void datasolve(MESG *);
84 | void recvip(quint32);
85 | void cameraImageCapture(QVideoFrame frame);
86 | void on_joinmeetBtn_clicked();
87 |
88 | void on_horizontalSlider_valueChanged(int value);
89 | void speaks(QString);
90 |
91 | void on_sendmsg_clicked();
92 |
93 | void textSend();
94 |
95 | signals:
96 | void pushImg(QImage);
97 | void PushText(MSG_TYPE, QString = "");
98 | void stopAudio();
99 | void startAudio();
100 | void volumnChange(int);
101 | private:
102 | Ui::Widget *ui;
103 | };
104 | #endif // WIDGET_H
105 |
--------------------------------------------------------------------------------
/netheader.h:
--------------------------------------------------------------------------------
1 | #ifndef NETHEADER_H
2 | #define NETHEADER_H
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #define QUEUE_MAXSIZE 1500
9 | #ifndef MB
10 | #define MB 1024*1024
11 | #endif
12 |
13 | #ifndef KB
14 | #define KB 1024
15 | #endif
16 |
17 | #ifndef WAITSECONDS
18 | #define WAITSECONDS 2
19 | #endif
20 |
21 | #ifndef OPENVIDEO
22 | #define OPENVIDEO "打开视频"
23 | #endif
24 |
25 | #ifndef CLOSEVIDEO
26 | #define CLOSEVIDEO "关闭视频"
27 | #endif
28 |
29 | #ifndef OPENAUDIO
30 | #define OPENAUDIO "打开音频"
31 | #endif
32 |
33 | #ifndef CLOSEAUDIO
34 | #define CLOSEAUDIO "关闭音频"
35 | #endif
36 |
37 |
38 | #ifndef MSG_HEADER
39 | #define MSG_HEADER 11
40 | #endif
41 |
42 | enum MSG_TYPE
43 | {
44 | IMG_SEND = 0,
45 | IMG_RECV,
46 | AUDIO_SEND,
47 | AUDIO_RECV,
48 | TEXT_SEND,
49 | TEXT_RECV,
50 | CREATE_MEETING,
51 | EXIT_MEETING,
52 | JOIN_MEETING,
53 | CLOSE_CAMERA,
54 |
55 | CREATE_MEETING_RESPONSE = 20,
56 | PARTNER_EXIT = 21,
57 | PARTNER_JOIN = 22,
58 | JOIN_MEETING_RESPONSE = 23,
59 | PARTNER_JOIN2 = 24,
60 | RemoteHostClosedError = 40,
61 | OtherNetError = 41
62 | };
63 | Q_DECLARE_METATYPE(MSG_TYPE);
64 |
65 | struct MESG //消息结构体
66 | {
67 | MSG_TYPE msg_type;
68 | uchar* data;
69 | long len;
70 | quint32 ip;
71 | };
72 | Q_DECLARE_METATYPE(MESG *);
73 |
74 | //-------------------------------
75 |
76 | template
77 | struct QUEUE_DATA //消息队列
78 | {
79 | private:
80 | QMutex send_queueLock;
81 | QWaitCondition send_queueCond;
82 | QQueue send_queue;
83 | public:
84 | void push_msg(T* msg)
85 | {
86 | send_queueLock.lock();
87 | while(send_queue.size() > QUEUE_MAXSIZE)
88 | {
89 | send_queueCond.wait(&send_queueLock);
90 | }
91 | send_queue.push_back(msg);
92 | send_queueLock.unlock();
93 | send_queueCond.wakeOne();
94 | }
95 |
96 | T* pop_msg()
97 | {
98 | send_queueLock.lock();
99 | while(send_queue.size() == 0)
100 | {
101 | bool f = send_queueCond.wait(&send_queueLock, WAITSECONDS * 1000);
102 | if(f == false)
103 | {
104 | send_queueLock.unlock();
105 | return NULL;
106 | }
107 | }
108 | T* send = send_queue.front();
109 | send_queue.pop_front();
110 | send_queueLock.unlock();
111 | send_queueCond.wakeOne();
112 | return send;
113 | }
114 |
115 | void clear()
116 | {
117 | send_queueLock.lock();
118 | send_queue.clear();
119 | send_queueLock.unlock();
120 | }
121 | };
122 |
123 | struct Log
124 | {
125 | char *ptr;
126 | uint len;
127 | };
128 |
129 |
130 |
131 | void log_print(const char *, const char *, int, const char *, ...);
132 | #define WRITE_LOG(LOGTEXT, ...) do{ \
133 | log_print(__FILE__, __FUNCTION__, __LINE__, LOGTEXT, ##__VA_ARGS__);\
134 | }while(0);
135 |
136 |
137 |
138 | #endif // NETHEADER_H
139 |
--------------------------------------------------------------------------------
/sendimg.cpp:
--------------------------------------------------------------------------------
1 | #include "sendimg.h"
2 | #include "netheader.h"
3 | #include
4 | #include
5 | #include
6 |
7 | extern QUEUE_DATA queue_send;
8 |
9 | SendImg::SendImg(QObject *par):QThread(par)
10 | {
11 |
12 | }
13 |
14 | //消费线程
15 | void SendImg::run()
16 | {
17 | WRITE_LOG("start sending picture thread: 0x%p", QThread::currentThreadId());
18 | m_isCanRun = true;
19 | for(;;)
20 | {
21 | queue_lock.lock(); //加锁
22 |
23 | while(imgqueue.size() == 0)
24 | {
25 | //qDebug() << this << QThread::currentThreadId();
26 | bool f = queue_waitCond.wait(&queue_lock, WAITSECONDS * 1000);
27 | if (f == false) //timeout
28 | {
29 | QMutexLocker locker(&m_lock);
30 | if (m_isCanRun == false)
31 | {
32 | queue_lock.unlock();
33 | WRITE_LOG("stop sending picture thread: 0x%p", QThread::currentThreadId());
34 | return;
35 | }
36 | }
37 | }
38 |
39 | QByteArray img = imgqueue.front();
40 | // qDebug() << "取出队列:" << QThread::currentThreadId();
41 | imgqueue.pop_front();
42 | queue_lock.unlock();//解锁
43 | queue_waitCond.wakeOne(); //唤醒添加线程
44 |
45 |
46 | //构造消息体
47 | MESG* imgsend = (MESG*)malloc(sizeof(MESG));
48 | if (imgsend == NULL)
49 | {
50 | WRITE_LOG("malloc error");
51 | qDebug() << "malloc imgsend fail";
52 | }
53 | else
54 | {
55 | memset(imgsend, 0, sizeof(MESG));
56 | imgsend->msg_type = IMG_SEND;
57 | imgsend->len = img.size();
58 | qDebug() << "img size :" << img.size();
59 | imgsend->data = (uchar*)malloc(imgsend->len);
60 | if (imgsend->data == nullptr)
61 | {
62 | free(imgsend);
63 | WRITE_LOG("malloc error");
64 | qDebug() << "send img error";
65 | continue;
66 | }
67 | else
68 | {
69 | memset(imgsend->data, 0, imgsend->len);
70 | memcpy_s(imgsend->data, imgsend->len, img.data(), img.size());
71 | //加入发送队列
72 | queue_send.push_msg(imgsend);
73 | }
74 | }
75 | }
76 | }
77 |
78 | //添加线程
79 | void SendImg::pushToQueue(QImage img)
80 | {
81 | //压缩
82 | QByteArray byte;
83 | QBuffer buf(&byte);
84 | buf.open(QIODevice::WriteOnly);
85 | img.save(&buf, "JPEG");
86 | QByteArray ss = qCompress(byte);
87 | QByteArray vv = ss.toBase64();
88 | // qDebug() << "加入队列:" << QThread::currentThreadId();
89 | queue_lock.lock();
90 | while(imgqueue.size() > QUEUE_MAXSIZE)
91 | {
92 | queue_waitCond.wait(&queue_lock);
93 | }
94 | imgqueue.push_back(vv);
95 | queue_lock.unlock();
96 | queue_waitCond.wakeOne();
97 | }
98 |
99 | void SendImg::ImageCapture(QImage img)
100 | {
101 | pushToQueue(img);
102 | }
103 |
104 | void SendImg::clearImgQueue()
105 | {
106 | qDebug() << "清空视频队列";
107 | QMutexLocker locker(&queue_lock);
108 | imgqueue.clear();
109 | }
110 |
111 |
112 | void SendImg::stopImmediately()
113 | {
114 | QMutexLocker locker(&m_lock);
115 | m_isCanRun = false;
116 | }
117 |
--------------------------------------------------------------------------------
/sendtext.cpp:
--------------------------------------------------------------------------------
1 | #include "sendtext.h"
2 | #include
3 |
4 | extern QUEUE_DATA queue_send;
5 | #ifndef WAITSECONDS
6 | #define WAITSECONDS 2
7 | #endif
8 | SendText::SendText(QObject *par):QThread(par)
9 | {
10 |
11 | }
12 |
13 | SendText::~SendText()
14 | {
15 |
16 | }
17 |
18 | void SendText::push_Text(MSG_TYPE msgType, QString str)
19 | {
20 | textqueue_lock.lock();
21 | while(textqueue.size() > QUEUE_MAXSIZE)
22 | {
23 | queue_waitCond.wait(&textqueue_lock);
24 | }
25 | textqueue.push_back(M(str, msgType));
26 |
27 | textqueue_lock.unlock();
28 | queue_waitCond.wakeOne();
29 | }
30 |
31 |
32 | void SendText::run()
33 | {
34 | m_isCanRun = true;
35 | WRITE_LOG("start sending text thread: 0x%p", QThread::currentThreadId());
36 | for(;;)
37 | {
38 | textqueue_lock.lock(); //加锁
39 | while(textqueue.size() == 0)
40 | {
41 | bool f = queue_waitCond.wait(&textqueue_lock, WAITSECONDS * 1000);
42 | if(f == false) //timeout
43 | {
44 | QMutexLocker locker(&m_lock);
45 | if(m_isCanRun == false)
46 | {
47 | textqueue_lock.unlock();
48 | WRITE_LOG("stop sending text thread: 0x%p", QThread::currentThreadId());
49 | return;
50 | }
51 | }
52 | }
53 |
54 | M text = textqueue.front();
55 |
56 | // qDebug() << "取出队列:" << QThread::currentThreadId();
57 |
58 | textqueue.pop_front();
59 | textqueue_lock.unlock();//解锁
60 | queue_waitCond.wakeOne(); //唤醒添加线程
61 |
62 | //构造消息体
63 | MESG* send = (MESG*)malloc(sizeof(MESG));
64 | if (send == NULL)
65 | {
66 | WRITE_LOG("malloc error");
67 | qDebug() << __FILE__ <<__LINE__ << "malloc fail";
68 | continue;
69 | }
70 | else
71 | {
72 | memset(send, 0, sizeof(MESG));
73 |
74 | if (text.type == CREATE_MEETING || text.type == CLOSE_CAMERA)
75 | {
76 | send->len = 0;
77 | send->data = NULL;
78 | send->msg_type = text.type;
79 | queue_send.push_msg(send);
80 | }
81 | else if (text.type == JOIN_MEETING)
82 | {
83 | send->msg_type = JOIN_MEETING;
84 | send->len = 4; //房间号占4个字节
85 | send->data = (uchar*)malloc(send->len + 10);
86 |
87 | if (send->data == NULL)
88 | {
89 | WRITE_LOG("malloc error");
90 | qDebug() << __FILE__ << __LINE__ << "malloc fail";
91 | free(send);
92 | continue;
93 | }
94 | else
95 | {
96 | memset(send->data, 0, send->len + 10);
97 | quint32 roomno = text.str.toUInt();
98 | memcpy(send->data, &roomno, sizeof(roomno));
99 | //加入发送队列
100 |
101 | queue_send.push_msg(send);
102 | }
103 | }
104 | else if(text.type == TEXT_SEND)
105 | {
106 | send->msg_type = TEXT_SEND;
107 | QByteArray data = qCompress(QByteArray::fromStdString(text.str.toStdString())); //压缩
108 | send->len = data.size();
109 | send->data = (uchar *) malloc(send->len);
110 | if(send->data == NULL)
111 | {
112 | WRITE_LOG("malloc error");
113 | qDebug() << __FILE__ << __LINE__ << "malloc error";
114 | free(send);
115 | continue;
116 | }
117 | else
118 | {
119 | memset(send->data, 0, send->len);
120 | memcpy_s(send->data, send->len, data.data(), data.size());
121 | queue_send.push_msg(send);
122 | }
123 | }
124 | }
125 | }
126 | }
127 | void SendText::stopImmediately()
128 | {
129 | QMutexLocker locker(&m_lock);
130 | m_isCanRun = false;
131 | }
132 |
--------------------------------------------------------------------------------
/mytextedit.cpp:
--------------------------------------------------------------------------------
1 | #include "mytextedit.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | Completer::Completer(QWidget *parent): QCompleter(parent)
9 | {
10 |
11 | }
12 |
13 | MyTextEdit::MyTextEdit(QWidget *parent): QWidget(parent)
14 | {
15 | QVBoxLayout *layout = new QVBoxLayout(this);
16 | layout->setContentsMargins(0, 0, 0, 0);
17 | edit = new QPlainTextEdit();
18 | edit->setPlaceholderText(QString::fromUtf8("输入@可以向对应的IP发送数据"));
19 | layout->addWidget(edit);
20 | completer = nullptr;
21 | connect(edit, SIGNAL(textChanged()), this, SLOT(complete()));
22 | edit->installEventFilter(this);
23 | }
24 |
25 | QString MyTextEdit::textUnderCursor()
26 | {
27 | QTextCursor tc = edit->textCursor();
28 | tc.select(QTextCursor::WordUnderCursor);
29 | return tc.selectedText();
30 | }
31 |
32 | void MyTextEdit::complete()
33 | {
34 | if(edit->toPlainText().size() == 0 || completer == nullptr) return;
35 | QChar tail = edit->toPlainText().at(edit->toPlainText().size()-1);
36 | if(tail == '@')
37 | {
38 | completer->setCompletionPrefix(tail);
39 | QAbstractItemView *view = completer->popup();
40 | view->setCurrentIndex(completer->completionModel()->index(0, 0));
41 | QRect cr = edit->cursorRect();
42 | QScrollBar *bar = completer->popup()->verticalScrollBar();
43 | cr.setWidth(completer->popup()->sizeHintForColumn(0) + bar->sizeHint().width());
44 | completer->complete(cr);
45 | }
46 | }
47 |
48 | void MyTextEdit::changeCompletion(QString text)
49 | {
50 | QTextCursor tc = edit->textCursor();
51 | int len = text.size() - completer->completionPrefix().size();
52 | tc.movePosition(QTextCursor::EndOfWord);
53 | tc.insertText(text.right(len));
54 | edit->setTextCursor(tc);
55 | completer->popup()->hide();
56 |
57 | QString str = edit->toPlainText();
58 | int pos = str.size() - 1;
59 | while(str.at(pos) != '@') pos--;
60 |
61 | tc.clearSelection();
62 | tc.setPosition(pos, QTextCursor::MoveAnchor);
63 | tc.setPosition(str.size(), QTextCursor::KeepAnchor);
64 | // tc.movePosition(QTextCursor::Left, QTextCursor::KeepAnchor, str.size() - pos);
65 |
66 | QTextCharFormat fmt = tc.charFormat();
67 | QTextCharFormat fmt_back = fmt;
68 | fmt.setForeground(QBrush(Qt::white));
69 | fmt.setBackground(QBrush(QColor(0, 160, 233)));
70 | tc.setCharFormat(fmt);
71 | tc.clearSelection();
72 | tc.setCharFormat(fmt_back);
73 |
74 | tc.insertText(" ");
75 | edit->setTextCursor(tc);
76 |
77 | ipspan.push_back(QPair(pos, str.size()+1));
78 |
79 | }
80 |
81 | QString MyTextEdit::toPlainText()
82 | {
83 | return edit->toPlainText();
84 | }
85 |
86 | void MyTextEdit::setPlainText(QString str)
87 | {
88 | edit->setPlainText(str);
89 | }
90 |
91 | void MyTextEdit::setPlaceholderText(QString str)
92 | {
93 | edit->setPlaceholderText(str);
94 | }
95 |
96 | void MyTextEdit::setCompleter(QStringList stringlist)
97 | {
98 | if(completer == nullptr)
99 | {
100 | completer = new Completer(this);
101 | completer->setWidget(this);
102 | completer->setCompletionMode(QCompleter::PopupCompletion);
103 | completer->setCaseSensitivity(Qt::CaseInsensitive);
104 | connect(completer, SIGNAL(activated(QString)), this, SLOT(changeCompletion(QString)));
105 | }
106 | else
107 | {
108 | delete completer->model();
109 | }
110 | QStringListModel * model = new QStringListModel(stringlist, this);
111 | completer->setModel(model);
112 | }
113 |
114 | bool MyTextEdit::eventFilter(QObject *obj, QEvent *event)
115 | {
116 | if(obj == edit)
117 | {
118 | if(event->type() == QEvent::KeyPress)
119 | {
120 | QKeyEvent *keyevent = static_cast(event);
121 | QTextCursor tc = edit->textCursor();
122 | int p = tc.position();
123 | int i;
124 | for(i = 0; i < ipspan.size(); i++)
125 | {
126 | if( (keyevent->key() == Qt::Key_Backspace && p > ipspan[i].first && p <= ipspan[i].second ) || (keyevent->key() == Qt::Key_Delete && p >= ipspan[i].first && p < ipspan[i].second) )
127 | {
128 | tc.setPosition(ipspan[i].first, QTextCursor::MoveAnchor);
129 | if(p == ipspan[i].second)
130 | {
131 | tc.setPosition(ipspan[i].second, QTextCursor::KeepAnchor);
132 | }
133 | else
134 | {
135 | tc.setPosition(ipspan[i].second + 1, QTextCursor::KeepAnchor);
136 | }
137 | tc.removeSelectedText();
138 | ipspan.removeAt(i);
139 | return true;
140 | }
141 | else if(p >= ipspan[i].first && p <= ipspan[i].second)
142 | {
143 | QTextCursor tc = edit->textCursor();
144 | tc.setPosition(ipspan[i].second);
145 | edit->setTextCursor(tc);
146 | }
147 | }
148 | }
149 | }
150 | return QWidget::eventFilter(obj, event);
151 | }
152 |
--------------------------------------------------------------------------------
/chatmessage.cpp:
--------------------------------------------------------------------------------
1 | #include "chatmessage.h"
2 | #include
3 | #include
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 |
10 | ChatMessage::ChatMessage(QWidget *parent) : QWidget(parent)
11 | {
12 | QFont te_font = this->font();
13 | te_font.setFamily("MicrosoftYaHei");
14 | te_font.setPointSize(12);
15 | this->setFont(te_font);
16 | m_leftPixmap = QPixmap(":/myImage/1.jpg");
17 | m_rightPixmap = QPixmap(":/myImage/1.jpg");
18 |
19 | m_loadingMovie = new QMovie(this);
20 | m_loadingMovie->setFileName(":/myImage/3.gif");
21 | m_loading = new QLabel(this);
22 | m_loading->setMovie(m_loadingMovie);
23 | m_loading->setScaledContents(true);
24 | m_loading->resize(40,40);
25 | m_loading->setAttribute(Qt::WA_TranslucentBackground , true);
26 | }
27 |
28 | void ChatMessage::setTextSuccess()
29 | {
30 | m_loading->hide();
31 | m_loadingMovie->stop();
32 | m_isSending = true;
33 | }
34 |
35 | void ChatMessage::setText(QString text, QString time, QSize allSize, QString ip,ChatMessage::User_Type userType)
36 | {
37 | m_msg = text;
38 | m_userType = userType;
39 | m_time = time;
40 | m_curTime = QDateTime::fromTime_t(time.toInt()).toString("ddd hh:mm");
41 | m_allSize = allSize;
42 | m_ip = ip;
43 | if(userType == User_Me) {
44 | if(!m_isSending) {
45 | m_loading->move(m_kuangRightRect.x() - m_loading->width() - 10, m_kuangRightRect.y()+m_kuangRightRect.height()/2- m_loading->height()/2);
46 | // m_loading->move(0, 0);
47 | m_loading->show();
48 | m_loadingMovie->start();
49 | }
50 | } else {
51 | m_loading->hide();
52 | }
53 |
54 | this->update();
55 | }
56 |
57 | QSize ChatMessage::fontRect(QString str)
58 | {
59 | m_msg = str;
60 | int minHei = 30;
61 | int iconWH = 40;
62 | int iconSpaceW = 20;
63 | int iconRectW = 5;
64 | int iconTMPH = 10;
65 | int sanJiaoW = 6;
66 | int kuangTMP = 20;
67 | int textSpaceRect = 12;
68 | m_kuangWidth = this->width() - kuangTMP - 2*(iconWH+iconSpaceW+iconRectW);
69 | m_textWidth = m_kuangWidth - 2*textSpaceRect;
70 | m_spaceWid = this->width() - m_textWidth;
71 | m_iconLeftRect = QRect(iconSpaceW, iconTMPH + 10, iconWH, iconWH);
72 | m_iconRightRect = QRect(this->width() - iconSpaceW - iconWH, iconTMPH + 10, iconWH, iconWH);
73 |
74 |
75 | QSize size = getRealString(m_msg); // 整个的size
76 |
77 | qDebug() << "fontRect Size:" << size;
78 | int hei = size.height() < minHei ? minHei : size.height();
79 |
80 | m_sanjiaoLeftRect = QRect(iconWH+iconSpaceW+iconRectW, m_lineHeight/2 + 10, sanJiaoW, hei - m_lineHeight);
81 | m_sanjiaoRightRect = QRect(this->width() - iconRectW - iconWH - iconSpaceW - sanJiaoW, m_lineHeight/2+10, sanJiaoW, hei - m_lineHeight);
82 |
83 | if(size.width() < (m_textWidth+m_spaceWid)) {
84 | m_kuangLeftRect.setRect(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), m_lineHeight/4*3 + 10, size.width()-m_spaceWid+2*textSpaceRect, hei-m_lineHeight);
85 | m_kuangRightRect.setRect(this->width() - size.width() + m_spaceWid - 2*textSpaceRect - iconWH - iconSpaceW - iconRectW - sanJiaoW,
86 | m_lineHeight/4*3 + 10, size.width()-m_spaceWid+2*textSpaceRect, hei-m_lineHeight);
87 | } else {
88 | m_kuangLeftRect.setRect(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), m_lineHeight/4*3 + 10, m_kuangWidth, hei-m_lineHeight);
89 | m_kuangRightRect.setRect(iconWH + kuangTMP + iconSpaceW + iconRectW - sanJiaoW, m_lineHeight/4*3 + 10, m_kuangWidth, hei-m_lineHeight);
90 | }
91 | m_textLeftRect.setRect(m_kuangLeftRect.x()+textSpaceRect, m_kuangLeftRect.y()+iconTMPH,
92 | m_kuangLeftRect.width()-2*textSpaceRect, m_kuangLeftRect.height()-2*iconTMPH);
93 | m_textRightRect.setRect(m_kuangRightRect.x()+textSpaceRect, m_kuangRightRect.y()+iconTMPH,
94 | m_kuangRightRect.width()-2*textSpaceRect, m_kuangRightRect.height()-2*iconTMPH);
95 |
96 |
97 | m_ipLeftRect.setRect(m_kuangLeftRect.x(), m_kuangLeftRect.y()+iconTMPH - 20,
98 | m_kuangLeftRect.width()-2*textSpaceRect + iconWH*2, 20);
99 | m_ipRightRect.setRect(m_kuangRightRect.x(), m_kuangRightRect.y()+iconTMPH - 30,
100 | m_kuangRightRect.width()-2*textSpaceRect + iconWH*2 , 20);
101 | return QSize(size.width(), hei + 15);
102 | }
103 |
104 | QSize ChatMessage::getRealString(QString src)
105 | {
106 | QFontMetricsF fm(this->font());
107 | m_lineHeight = fm.lineSpacing();
108 | int nCount = src.count("\n");
109 | int nMaxWidth = 0;
110 | if(nCount == 0) {
111 | nMaxWidth = fm.width(src);
112 | QString value = src;
113 | if(nMaxWidth > m_textWidth) {
114 | nMaxWidth = m_textWidth;
115 | int size = m_textWidth / fm.width(" ");
116 | int num = fm.width(value) / m_textWidth;
117 | num = ( fm.width(value) ) / m_textWidth;
118 | nCount += num;
119 | QString temp = "";
120 | for(int i = 0; i < num; i++) {
121 | temp += value.mid(i*size, (i+1)*size) + "\n";
122 | }
123 | src.replace(value, temp);
124 | }
125 | } else {
126 | for(int i = 0; i < (nCount + 1); i++) {
127 | QString value = src.split("\n").at(i);
128 | nMaxWidth = fm.width(value) > nMaxWidth ? fm.width(value) : nMaxWidth;
129 | if(fm.width(value) > m_textWidth) {
130 | nMaxWidth = m_textWidth;
131 | int size = m_textWidth / fm.width(" ");
132 | int num = fm.width(value) / m_textWidth;
133 | num = ((i+num)*fm.width(" ") + fm.width(value)) / m_textWidth;
134 | nCount += num;
135 | QString temp = "";
136 | for(int i = 0; i < num; i++) {
137 | temp += value.mid(i*size, (i+1)*size) + "\n";
138 | }
139 | src.replace(value, temp);
140 | }
141 | }
142 | }
143 | return QSize(nMaxWidth+m_spaceWid, (nCount + 1) * m_lineHeight+2*m_lineHeight);
144 | }
145 |
146 | void ChatMessage::paintEvent(QPaintEvent *event)
147 | {
148 | Q_UNUSED(event);
149 |
150 | QPainter painter(this);
151 | painter.setRenderHints(QPainter::Antialiasing | QPainter::SmoothPixmapTransform);//消锯齿
152 | painter.setPen(Qt::NoPen);
153 | painter.setBrush(QBrush(Qt::gray));
154 |
155 | if(m_userType == User_Type::User_She) { // 用户
156 | //头像
157 | // painter.drawRoundedRect(m_iconLeftRect,m_iconLeftRect.width(),m_iconLeftRect.height());
158 | painter.drawPixmap(m_iconLeftRect, m_leftPixmap);
159 |
160 | //框加边
161 | QColor col_KuangB(234, 234, 234);
162 | painter.setBrush(QBrush(col_KuangB));
163 | painter.drawRoundedRect(m_kuangLeftRect.x()-1,m_kuangLeftRect.y()-1 + 10,m_kuangLeftRect.width()+2,m_kuangLeftRect.height()+2,4,4);
164 | //框
165 | QColor col_Kuang(255,255,255);
166 | painter.setBrush(QBrush(col_Kuang));
167 | painter.drawRoundedRect(m_kuangLeftRect,4,4);
168 |
169 | //三角
170 | QPointF points[3] = {
171 | QPointF(m_sanjiaoLeftRect.x(), 40),
172 | QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 35),
173 | QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 45),
174 | };
175 | QPen pen;
176 | pen.setColor(col_Kuang);
177 | painter.setPen(pen);
178 | painter.drawPolygon(points, 3);
179 |
180 | //三角加边
181 | QPen penSanJiaoBian;
182 | penSanJiaoBian.setColor(col_KuangB);
183 | painter.setPen(penSanJiaoBian);
184 | painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 24));
185 | painter.drawLine(QPointF(m_sanjiaoLeftRect.x() - 1, 30), QPointF(m_sanjiaoLeftRect.x()+m_sanjiaoLeftRect.width(), 36));
186 |
187 | //ip
188 | //ip
189 | QPen penIp;
190 | penIp.setColor(Qt::darkGray);
191 | painter.setPen(penIp);
192 | QFont f = this->font();
193 | f.setPointSize(10);
194 | QTextOption op(Qt::AlignHCenter | Qt::AlignVCenter);
195 | painter.setFont(f);
196 | painter.drawText(m_ipLeftRect, m_ip, op);
197 |
198 | //内容
199 | QPen penText;
200 | penText.setColor(QColor(51,51,51));
201 | painter.setPen(penText);
202 | QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
203 | option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
204 | painter.setFont(this->font());
205 | painter.drawText(m_textLeftRect, m_msg,option);
206 | } else if(m_userType == User_Type::User_Me) { // 自己
207 | //头像
208 | // painter.drawRoundedRect(m_iconRightRect,m_iconRightRect.width(),m_iconRightRect.height());
209 | painter.drawPixmap(m_iconRightRect, m_rightPixmap);
210 |
211 | //框
212 | QColor col_Kuang(75,164,242);
213 | painter.setBrush(QBrush(col_Kuang));
214 | painter.drawRoundedRect(m_kuangRightRect,4,4);
215 |
216 | //三角
217 | QPointF points[3] = {
218 | QPointF(m_sanjiaoRightRect.x()+m_sanjiaoRightRect.width(), 40),
219 | QPointF(m_sanjiaoRightRect.x(), 35),
220 | QPointF(m_sanjiaoRightRect.x(), 45),
221 | };
222 | QPen pen;
223 | pen.setColor(col_Kuang);
224 | painter.setPen(pen);
225 | painter.drawPolygon(points, 3);
226 |
227 |
228 | //ip
229 | QPen penIp;
230 | penIp.setColor(Qt::black);
231 | painter.setPen(penIp);
232 | QFont f = this->font();
233 | f.setPointSize(10);
234 | QTextOption op(Qt::AlignHCenter | Qt::AlignVCenter);
235 | painter.setFont(f);
236 | painter.drawText(m_ipRightRect, m_ip, op);
237 |
238 | //内容
239 | QPen penText;
240 | penText.setColor(Qt::white);
241 | painter.setPen(penText);
242 | QTextOption option(Qt::AlignLeft | Qt::AlignVCenter);
243 | option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
244 | painter.setFont(this->font());
245 | painter.drawText(m_textRightRect,m_msg,option);
246 | } else if(m_userType == User_Type::User_Time) { // 时间
247 | QPen penText;
248 | penText.setColor(QColor(153,153,153));
249 | painter.setPen(penText);
250 | QTextOption option(Qt::AlignCenter);
251 | option.setWrapMode(QTextOption::WrapAtWordBoundaryOrAnywhere);
252 | QFont te_font = this->font();
253 | te_font.setFamily("MicrosoftYaHei");
254 | te_font.setPointSize(10);
255 | painter.setFont(te_font);
256 | painter.drawText(this->rect(),m_curTime,option);
257 | }
258 | };
259 |
--------------------------------------------------------------------------------
/mytcpsocket.cpp:
--------------------------------------------------------------------------------
1 | #include "mytcpsocket.h"
2 | #include "netheader.h"
3 | #include
4 | #include
5 | #include
6 | #include
7 |
8 | extern QUEUE_DATA queue_send;
9 | extern QUEUE_DATA queue_recv;
10 | extern QUEUE_DATA audio_recv;
11 |
12 | void MyTcpSocket::stopImmediately()
13 | {
14 | {
15 | QMutexLocker lock(&m_lock);
16 | if(m_isCanRun == true) m_isCanRun = false;
17 | }
18 | //关闭read
19 | _sockThread->quit();
20 | _sockThread->wait();
21 | }
22 |
23 | void MyTcpSocket::closeSocket()
24 | {
25 | if (_socktcp && _socktcp->isOpen())
26 | {
27 | _socktcp->close();
28 | }
29 | }
30 |
31 | MyTcpSocket::MyTcpSocket(QObject *par):QThread(par)
32 | {
33 | qRegisterMetaType();
34 | _socktcp = nullptr;
35 |
36 | _sockThread = new QThread(); //发送数据线程
37 | this->moveToThread(_sockThread);
38 | connect(_sockThread, SIGNAL(finished()), this, SLOT(closeSocket()));
39 | sendbuf =(uchar *) malloc(4 * MB);
40 | recvbuf = (uchar*)malloc(4 * MB);
41 | hasrecvive = 0;
42 |
43 | }
44 |
45 |
46 | void MyTcpSocket::errorDetect(QAbstractSocket::SocketError error)
47 | {
48 | qDebug() <<"Sock error" <msg_type = RemoteHostClosedError;
60 | }
61 | else
62 | {
63 | msg->msg_type = OtherNetError;
64 | }
65 | queue_recv.push_msg(msg);
66 | }
67 | }
68 |
69 |
70 |
71 | void MyTcpSocket::sendData(MESG* send)
72 | {
73 | if (_socktcp->state() == QAbstractSocket::UnconnectedState)
74 | {
75 | emit sendTextOver();
76 | if (send->data) free(send->data);
77 | if (send) free(send);
78 | return;
79 | }
80 | quint64 bytestowrite = 0;
81 | //构造消息头
82 | sendbuf[bytestowrite++] = '$';
83 |
84 | //消息类型
85 | qToBigEndian(send->msg_type, sendbuf + bytestowrite);
86 | bytestowrite += 2;
87 |
88 | //发送者ip
89 | quint32 ip = _socktcp->localAddress().toIPv4Address();
90 | qToBigEndian(ip, sendbuf + bytestowrite);
91 | bytestowrite += 4;
92 |
93 | if (send->msg_type == CREATE_MEETING || send->msg_type == AUDIO_SEND || send->msg_type == CLOSE_CAMERA || send->msg_type == IMG_SEND || send->msg_type == TEXT_SEND) //创建会议,发送音频,关闭摄像头,发送图片
94 | {
95 | //发送数据大小
96 | qToBigEndian(send->len, sendbuf + bytestowrite);
97 | bytestowrite += 4;
98 | }
99 | else if (send->msg_type == JOIN_MEETING)
100 | {
101 | qToBigEndian(send->len, sendbuf + bytestowrite);
102 | bytestowrite += 4;
103 | uint32_t room;
104 | memcpy(&room, send->data, send->len);
105 | qToBigEndian(room, send->data);
106 | }
107 |
108 | //将数据拷入sendbuf
109 | memcpy(sendbuf + bytestowrite, send->data, send->len);
110 | bytestowrite += send->len;
111 | sendbuf[bytestowrite++] = '#'; //结尾字符
112 |
113 | //----------------write to server-------------------------
114 | qint64 hastowrite = bytestowrite;
115 | qint64 ret = 0, haswrite = 0;
116 | while ((ret = _socktcp->write((char*)sendbuf + haswrite, hastowrite - haswrite)) < hastowrite)
117 | {
118 | if (ret == -1 && _socktcp->error() == QAbstractSocket::TemporaryError)
119 | {
120 | ret = 0;
121 | }
122 | else if (ret == -1)
123 | {
124 | qDebug() << "network error";
125 | break;
126 | }
127 | haswrite += ret;
128 | hastowrite -= ret;
129 | }
130 |
131 | _socktcp->waitForBytesWritten();
132 |
133 | if(send->msg_type == TEXT_SEND)
134 | {
135 | emit sendTextOver(); //成功往内核发送文本信息
136 | }
137 |
138 |
139 | if (send->data)
140 | {
141 | free(send->data);
142 | }
143 | //free
144 | if (send)
145 | {
146 | free(send);
147 | }
148 | }
149 |
150 | /*
151 | * 发送线程
152 | */
153 | void MyTcpSocket::run()
154 | {
155 | //qDebug() << "send data" << QThread::currentThreadId();
156 | m_isCanRun = true; //标记可以运行
157 | /*
158 | *$_MSGType_IPV4_MSGSize_data_# //
159 | * 1 2 4 4 MSGSize 1
160 | *底层写数据线程
161 | */
162 | for(;;)
163 | {
164 | {
165 | QMutexLocker locker(&m_lock);
166 | if(m_isCanRun == false) return; //在每次循环判断是否可以运行,如果不行就退出循环
167 | }
168 |
169 | //构造消息体
170 | MESG * send = queue_send.pop_msg();
171 | if(send == NULL) continue;
172 | QMetaObject::invokeMethod(this, "sendData", Q_ARG(MESG*, send));
173 | }
174 | }
175 |
176 |
177 | qint64 MyTcpSocket::readn(char * buf, quint64 maxsize, int n)
178 | {
179 | quint64 hastoread = n;
180 | quint64 hasread = 0;
181 | do
182 | {
183 | qint64 ret = _socktcp->read(buf + hasread, hastoread);
184 | if(ret < 0)
185 | {
186 | return -1;
187 | }
188 | if(ret == 0)
189 | {
190 | return hasread;
191 | }
192 | hasread += ret;
193 | hastoread -= ret;
194 | }while(hastoread > 0 && hasread < maxsize);
195 | return hasread;
196 | }
197 |
198 |
199 | void MyTcpSocket::recvFromSocket()
200 | {
201 |
202 | //qDebug() << "recv data socket" <bytesAvailable();
207 | if (availbytes <=0 )
208 | {
209 | return;
210 | }
211 | qint64 ret = _socktcp->read((char *) recvbuf + hasrecvive, availbytes);
212 | if (ret <= 0)
213 | {
214 | qDebug() << "error or no more data";
215 | return;
216 | }
217 | hasrecvive += ret;
218 |
219 | //数据包不够
220 | if (hasrecvive < MSG_HEADER)
221 | {
222 | return;
223 | }
224 | else
225 | {
226 | quint32 data_size;
227 | qFromBigEndian(recvbuf + 7, 4, &data_size);
228 | if ((quint64)data_size + 1 + MSG_HEADER <= hasrecvive) //收够一个包
229 | {
230 | if (recvbuf[0] == '$' && recvbuf[MSG_HEADER + data_size] == '#') //且包结构正确
231 | {
232 | MSG_TYPE msgtype;
233 | uint16_t type;
234 | qFromBigEndian(recvbuf + 1, 2, &type);
235 | msgtype = (MSG_TYPE)type;
236 | qDebug() << "recv data type: " << msgtype;
237 | if (msgtype == CREATE_MEETING_RESPONSE || msgtype == JOIN_MEETING_RESPONSE || msgtype == PARTNER_JOIN2)
238 | {
239 | if (msgtype == CREATE_MEETING_RESPONSE)
240 | {
241 | qint32 roomNo;
242 | qFromBigEndian(recvbuf + MSG_HEADER, 4, &roomNo);
243 |
244 | MESG* msg = (MESG*)malloc(sizeof(MESG));
245 |
246 | if (msg == NULL)
247 | {
248 | qDebug() << __LINE__ << " CREATE_MEETING_RESPONSE malloc MESG failed";
249 | }
250 | else
251 | {
252 | memset(msg, 0, sizeof(MESG));
253 | msg->msg_type = msgtype;
254 | msg->data = (uchar*)malloc((quint64)data_size);
255 | if (msg->data == NULL)
256 | {
257 | free(msg);
258 | qDebug() << __LINE__ << "CREATE_MEETING_RESPONSE malloc MESG.data failed";
259 | }
260 | else
261 | {
262 | memset(msg->data, 0, (quint64)data_size);
263 | memcpy(msg->data, &roomNo, data_size);
264 | msg->len = data_size;
265 | queue_recv.push_msg(msg);
266 | }
267 |
268 | }
269 | }
270 | else if (msgtype == JOIN_MEETING_RESPONSE)
271 | {
272 | qint32 c;
273 | memcpy(&c, recvbuf + MSG_HEADER, data_size);
274 |
275 | MESG* msg = (MESG*)malloc(sizeof(MESG));
276 |
277 | if (msg == NULL)
278 | {
279 | qDebug() << __LINE__ << "JOIN_MEETING_RESPONSE malloc MESG failed";
280 | }
281 | else
282 | {
283 | memset(msg, 0, sizeof(MESG));
284 | msg->msg_type = msgtype;
285 | msg->data = (uchar*)malloc(data_size);
286 | if (msg->data == NULL)
287 | {
288 | free(msg);
289 | qDebug() << __LINE__ << "JOIN_MEETING_RESPONSE malloc MESG.data failed";
290 | }
291 | else
292 | {
293 | memset(msg->data, 0, data_size);
294 | memcpy(msg->data, &c, data_size);
295 |
296 | msg->len = data_size;
297 | queue_recv.push_msg(msg);
298 | }
299 | }
300 | }
301 | else if (msgtype == PARTNER_JOIN2)
302 | {
303 | MESG* msg = (MESG*)malloc(sizeof(MESG));
304 | if (msg == NULL)
305 | {
306 | qDebug() << "PARTNER_JOIN2 malloc MESG error";
307 | }
308 | else
309 | {
310 | memset(msg, 0, sizeof(MESG));
311 | msg->msg_type = msgtype;
312 | msg->len = data_size;
313 | msg->data = (uchar*)malloc(data_size);
314 | if (msg->data == NULL)
315 | {
316 | free(msg);
317 | qDebug() << "PARTNER_JOIN2 malloc MESG.data error";
318 | }
319 | else
320 | {
321 | memset(msg->data, 0, data_size);
322 | uint32_t ip;
323 | int pos = 0;
324 | for (int i = 0; i < data_size / sizeof(uint32_t); i++)
325 | {
326 | qFromBigEndian(recvbuf + MSG_HEADER + pos, sizeof(uint32_t), &ip);
327 | memcpy_s(msg->data + pos, data_size - pos, &ip, sizeof(uint32_t));
328 | pos += sizeof(uint32_t);
329 | }
330 | queue_recv.push_msg(msg);
331 | }
332 |
333 | }
334 | }
335 | }
336 | else if (msgtype == IMG_RECV || msgtype == PARTNER_JOIN || msgtype == PARTNER_EXIT || msgtype == AUDIO_RECV || msgtype == CLOSE_CAMERA || msgtype == TEXT_RECV)
337 | {
338 | //read ipv4
339 | quint32 ip;
340 | qFromBigEndian(recvbuf + 3, 4, &ip);
341 |
342 | if (msgtype == IMG_RECV)
343 | {
344 | //QString ss = QString::fromLatin1((char *)recvbuf + MSG_HEADER, data_len);
345 | QByteArray cc((char *) recvbuf + MSG_HEADER, data_size);
346 | QByteArray rc = QByteArray::fromBase64(cc);
347 | QByteArray rdc = qUncompress(rc);
348 | //将消息加入到接收队列
349 | // qDebug() << roomNo;
350 |
351 | if (rdc.size() > 0)
352 | {
353 | MESG* msg = (MESG*)malloc(sizeof(MESG));
354 | if (msg == NULL)
355 | {
356 | qDebug() << __LINE__ << " malloc failed";
357 | }
358 | else
359 | {
360 | memset(msg, 0, sizeof(MESG));
361 | msg->msg_type = msgtype;
362 | msg->data = (uchar*)malloc(rdc.size()); // 10 = format + width + width
363 | if (msg->data == NULL)
364 | {
365 | free(msg);
366 | qDebug() << __LINE__ << " malloc failed";
367 | }
368 | else
369 | {
370 | memset(msg->data, 0, rdc.size());
371 | memcpy_s(msg->data, rdc.size(), rdc.data(), rdc.size());
372 | msg->len = rdc.size();
373 | msg->ip = ip;
374 | queue_recv.push_msg(msg);
375 | }
376 | }
377 | }
378 | }
379 | else if (msgtype == PARTNER_JOIN || msgtype == PARTNER_EXIT || msgtype == CLOSE_CAMERA)
380 | {
381 | MESG* msg = (MESG*)malloc(sizeof(MESG));
382 | if (msg == NULL)
383 | {
384 | qDebug() << __LINE__ << " malloc failed";
385 | }
386 | else
387 | {
388 | memset(msg, 0, sizeof(MESG));
389 | msg->msg_type = msgtype;
390 | msg->ip = ip;
391 | queue_recv.push_msg(msg);
392 | }
393 | }
394 | else if (msgtype == AUDIO_RECV)
395 | {
396 | //解压缩
397 | QByteArray cc((char*)recvbuf + MSG_HEADER, data_size);
398 | QByteArray rc = QByteArray::fromBase64(cc);
399 | QByteArray rdc = qUncompress(rc);
400 |
401 | if (rdc.size() > 0)
402 | {
403 | MESG* msg = (MESG*)malloc(sizeof(MESG));
404 | if (msg == NULL)
405 | {
406 | qDebug() << __LINE__ << "malloc failed";
407 | }
408 | else
409 | {
410 | memset(msg, 0, sizeof(MESG));
411 | msg->msg_type = AUDIO_RECV;
412 | msg->ip = ip;
413 |
414 | msg->data = (uchar*)malloc(rdc.size());
415 | if (msg->data == nullptr)
416 | {
417 | free(msg);
418 | qDebug() << __LINE__ << "malloc msg.data failed";
419 | }
420 | else
421 | {
422 | memset(msg->data, 0, rdc.size());
423 | memcpy_s(msg->data, rdc.size(), rdc.data(), rdc.size());
424 | msg->len = rdc.size();
425 | msg->ip = ip;
426 | audio_recv.push_msg(msg);
427 | }
428 | }
429 | }
430 | }
431 | else if(msgtype == TEXT_RECV)
432 | {
433 | //解压缩
434 | QByteArray cc((char *)recvbuf + MSG_HEADER, data_size);
435 | std::string rr = qUncompress(cc).toStdString();
436 | if(rr.size() > 0)
437 | {
438 | MESG* msg = (MESG*)malloc(sizeof(MESG));
439 | if (msg == NULL)
440 | {
441 | qDebug() << __LINE__ << "malloc failed";
442 | }
443 | else
444 | {
445 | memset(msg, 0, sizeof(MESG));
446 | msg->msg_type = TEXT_RECV;
447 | msg->ip = ip;
448 | msg->data = (uchar*)malloc(rr.size());
449 | if (msg->data == nullptr)
450 | {
451 | free(msg);
452 | qDebug() << __LINE__ << "malloc msg.data failed";
453 | }
454 | else
455 | {
456 | memset(msg->data, 0, rr.size());
457 | memcpy_s(msg->data, rr.size(), rr.data(), rr.size());
458 | msg->len = rr.size();
459 | queue_recv.push_msg(msg);
460 | }
461 | }
462 | }
463 | }
464 | }
465 | }
466 | else
467 | {
468 | qDebug() << "package error";
469 | }
470 | memmove_s(recvbuf, 4 * MB, recvbuf + MSG_HEADER + data_size + 1, hasrecvive - ((quint64)data_size + 1 + MSG_HEADER));
471 | hasrecvive -= ((quint64)data_size + 1 + MSG_HEADER);
472 | }
473 | else
474 | {
475 | return;
476 | }
477 | }
478 | }
479 |
480 |
481 | MyTcpSocket::~MyTcpSocket()
482 | {
483 | delete sendbuf;
484 | delete _sockThread;
485 | }
486 |
487 |
488 |
489 | bool MyTcpSocket::connectServer(QString ip, QString port, QIODevice::OpenModeFlag flag)
490 | {
491 | if(_socktcp == nullptr) _socktcp = new QTcpSocket(); //tcp
492 | _socktcp->connectToHost(ip, port.toUShort(), flag);
493 | connect(_socktcp, SIGNAL(readyRead()), this, SLOT(recvFromSocket()), Qt::UniqueConnection); //接受数据
494 | //处理套接字错误
495 | connect(_socktcp, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(errorDetect(QAbstractSocket::SocketError)),Qt::UniqueConnection);
496 |
497 | if(_socktcp->waitForConnected(5000))
498 | {
499 | return true;
500 | }
501 | _socktcp->close();
502 | return false;
503 | }
504 |
505 |
506 | bool MyTcpSocket::connectToServer(QString ip, QString port, QIODevice::OpenModeFlag flag)
507 | {
508 | _sockThread->start(); // 开启链接,与接受
509 | bool retVal;
510 | QMetaObject::invokeMethod(this, "connectServer", Qt::BlockingQueuedConnection, Q_RETURN_ARG(bool, retVal),
511 | Q_ARG(QString, ip), Q_ARG(QString, port), Q_ARG(QIODevice::OpenModeFlag, flag));
512 |
513 | if (retVal)
514 | {
515 | this->start() ; //写数据
516 | return true;
517 | }
518 | else
519 | {
520 | return false;
521 | }
522 | }
523 |
524 | QString MyTcpSocket::errorString()
525 | {
526 | return _socktcp->errorString();
527 | }
528 |
529 | void MyTcpSocket::disconnectFromHost()
530 | {
531 | //write
532 | if(this->isRunning())
533 | {
534 | QMutexLocker locker(&m_lock);
535 | m_isCanRun = false;
536 | }
537 |
538 | if(_sockThread->isRunning()) //read
539 | {
540 | _sockThread->quit();
541 | _sockThread->wait();
542 | }
543 |
544 | //清空 发送 队列,清空接受队列
545 | queue_send.clear();
546 | queue_recv.clear();
547 | audio_recv.clear();
548 | }
549 |
550 |
551 | quint32 MyTcpSocket::getlocalip()
552 | {
553 | if(_socktcp->isOpen())
554 | {
555 | return _socktcp->localAddress().toIPv4Address();
556 | }
557 | else
558 | {
559 | return -1;
560 | }
561 | }
562 |
--------------------------------------------------------------------------------
/widget.cpp:
--------------------------------------------------------------------------------
1 | #include "widget.h"
2 | #include "ui_widget.h"
3 | #include "screen.h"
4 | #include
5 | #include
6 | #include
7 | #include
8 | #include
9 | #include "myvideosurface.h"
10 | #include "sendimg.h"
11 | #include
12 | #include
13 | #include
14 | #include
15 | #include
16 | #include
17 | #include "logqueue.h"
18 | #include
19 | #include
20 | #include
21 | #include
22 | QRect Widget::pos = QRect(-1, -1, -1, -1);
23 |
24 | extern LogQueue *logqueue;
25 |
26 | Widget::Widget(QWidget *parent)
27 | : QWidget(parent)
28 | , ui(new Ui::Widget)
29 | {
30 |
31 |
32 | //开启日志线程
33 | logqueue = new LogQueue();
34 | logqueue->start();
35 |
36 | qDebug() << "main: " <();
38 |
39 | WRITE_LOG("-------------------------Application Start---------------------------");
40 | WRITE_LOG("main UI thread id: 0x%p", QThread::currentThreadId());
41 | //ui界面
42 | _createmeet = false;
43 | _openCamera = false;
44 | _joinmeet = false;
45 | Widget::pos = QRect(0.1 * Screen::width, 0.1 * Screen::height, 0.8 * Screen::width, 0.8 * Screen::height);
46 |
47 | ui->setupUi(this);
48 |
49 | ui->openAudio->setText(QString(OPENAUDIO).toUtf8());
50 | ui->openVedio->setText(QString(OPENVIDEO).toUtf8());
51 |
52 | this->setGeometry(Widget::pos);
53 | this->setMinimumSize(QSize(Widget::pos.width() * 0.7, Widget::pos.height() * 0.7));
54 | this->setMaximumSize(QSize(Widget::pos.width(), Widget::pos.height()));
55 |
56 |
57 | ui->exitmeetBtn->setDisabled(true);
58 | ui->joinmeetBtn->setDisabled(true);
59 | ui->createmeetBtn->setDisabled(true);
60 | ui->openAudio->setDisabled(true);
61 | ui->openVedio->setDisabled(true);
62 | ui->sendmsg->setDisabled(true);
63 | mainip = 0; //主屏幕显示的用户IP图像
64 |
65 | //-------------------局部线程----------------------------
66 | //创建传输视频帧线程
67 | _sendImg = new SendImg();
68 | _imgThread = new QThread();
69 | _sendImg->moveToThread(_imgThread); //新起线程接受视频帧
70 | _sendImg->start();
71 | //_imgThread->start();
72 | //处理每一帧数据
73 |
74 | //--------------------------------------------------
75 |
76 |
77 | //数据处理(局部线程)
78 | _mytcpSocket = new MyTcpSocket(); // 底层线程专管发送
79 | connect(_mytcpSocket, SIGNAL(sendTextOver()), this, SLOT(textSend()));
80 | //connect(_mytcpSocket, SIGNAL(socketerror(QAbstractSocket::SocketError)), this, SLOT(mytcperror(QAbstractSocket::SocketError)));
81 |
82 |
83 | //----------------------------------------------------------
84 | //文本传输(局部线程)
85 | _sendText = new SendText();
86 | _textThread = new QThread();
87 | _sendText->moveToThread(_textThread);
88 | _textThread->start(); // 加入线程
89 | _sendText->start(); // 发送
90 |
91 | connect(this, SIGNAL(PushText(MSG_TYPE,QString)), _sendText, SLOT(push_Text(MSG_TYPE,QString)));
92 | //-----------------------------------------------------------
93 |
94 | //配置摄像头
95 | _camera = new QCamera(this);
96 | //摄像头出错处理
97 | connect(_camera, SIGNAL(error(QCamera::Error)), this, SLOT(cameraError(QCamera::Error)));
98 | _imagecapture = new QCameraImageCapture(_camera);
99 | _myvideosurface = new MyVideoSurface(this);
100 |
101 |
102 | connect(_myvideosurface, SIGNAL(frameAvailable(QVideoFrame)), this, SLOT(cameraImageCapture(QVideoFrame)));
103 | connect(this, SIGNAL(pushImg(QImage)), _sendImg, SLOT(ImageCapture(QImage)));
104 |
105 |
106 | //监听_imgThread退出信号
107 | connect(_imgThread, SIGNAL(finished()), _sendImg, SLOT(clearImgQueue()));
108 |
109 |
110 | //------------------启动接收数据线程-------------------------
111 | _recvThread = new RecvSolve();
112 | connect(_recvThread, SIGNAL(datarecv(MESG*)), this, SLOT(datasolve(MESG*)), Qt::BlockingQueuedConnection);
113 | _recvThread->start();
114 |
115 | //预览窗口重定向在MyVideoSurface
116 | _camera->setViewfinder(_myvideosurface);
117 | _camera->setCaptureMode(QCamera::CaptureStillImage);
118 |
119 | //音频
120 | _ainput = new AudioInput();
121 | _ainputThread = new QThread();
122 | _ainput->moveToThread(_ainputThread);
123 |
124 |
125 | _aoutput = new AudioOutput();
126 | _ainputThread->start(); //获取音频,发送
127 | _aoutput->start(); //播放
128 |
129 | connect(this, SIGNAL(startAudio()), _ainput, SLOT(startCollect()));
130 | connect(this, SIGNAL(stopAudio()), _ainput, SLOT(stopCollect()));
131 | connect(_ainput, SIGNAL(audioinputerror(QString)), this, SLOT(audioError(QString)));
132 | connect(_aoutput, SIGNAL(audiooutputerror(QString)), this, SLOT(audioError(QString)));
133 | connect(_aoutput, SIGNAL(speaker(QString)), this, SLOT(speaks(QString)));
134 | //设置滚动条
135 | ui->scrollArea->verticalScrollBar()->setStyleSheet("QScrollBar:vertical { width:8px; background:rgba(0,0,0,0%); margin:0px,0px,0px,0px; padding-top:9px; padding-bottom:9px; } QScrollBar::handle:vertical { width:8px; background:rgba(0,0,0,25%); border-radius:4px; min-height:20; } QScrollBar::handle:vertical:hover { width:8px; background:rgba(0,0,0,50%); border-radius:4px; min-height:20; } QScrollBar::add-line:vertical { height:9px;width:8px; border-image:url(:/images/a/3.png); subcontrol-position:bottom; } QScrollBar::sub-line:vertical { height:9px;width:8px; border-image:url(:/images/a/1.png); subcontrol-position:top; } QScrollBar::add-line:vertical:hover { height:9px;width:8px; border-image:url(:/images/a/4.png); subcontrol-position:bottom; } QScrollBar::sub-line:vertical:hover { height:9px;width:8px; border-image:url(:/images/a/2.png); subcontrol-position:top; } QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical { background:rgba(0,0,0,10%); border-radius:4px; }");
136 | ui->listWidget->setStyleSheet("QScrollBar:vertical { width:8px; background:rgba(0,0,0,0%); margin:0px,0px,0px,0px; padding-top:9px; padding-bottom:9px; } QScrollBar::handle:vertical { width:8px; background:rgba(0,0,0,25%); border-radius:4px; min-height:20; } QScrollBar::handle:vertical:hover { width:8px; background:rgba(0,0,0,50%); border-radius:4px; min-height:20; } QScrollBar::add-line:vertical { height:9px;width:8px; border-image:url(:/images/a/3.png); subcontrol-position:bottom; } QScrollBar::sub-line:vertical { height:9px;width:8px; border-image:url(:/images/a/1.png); subcontrol-position:top; } QScrollBar::add-line:vertical:hover { height:9px;width:8px; border-image:url(:/images/a/4.png); subcontrol-position:bottom; } QScrollBar::sub-line:vertical:hover { height:9px;width:8px; border-image:url(:/images/a/2.png); subcontrol-position:top; } QScrollBar::add-page:vertical,QScrollBar::sub-page:vertical { background:rgba(0,0,0,10%); border-radius:4px; }");
137 |
138 | QFont te_font = this->font();
139 | te_font.setFamily("MicrosoftYaHei");
140 | te_font.setPointSize(12);
141 |
142 | ui->listWidget->setFont(te_font);
143 |
144 | ui->tabWidget->setCurrentIndex(1);
145 | ui->tabWidget->setCurrentIndex(0);
146 | }
147 |
148 |
149 | void Widget::cameraImageCapture(QVideoFrame frame)
150 | {
151 | // qDebug() << QThread::currentThreadId() << this;
152 |
153 | if(frame.isValid() && frame.isReadable())
154 | {
155 | QImage videoImg = QImage(frame.bits(), frame.width(), frame.height(), QVideoFrame::imageFormatFromPixelFormat(frame.pixelFormat()));
156 |
157 | QTransform matrix;
158 | matrix.rotate(180.0);
159 |
160 | QImage img = videoImg.transformed(matrix, Qt::FastTransformation).scaled(ui->mainshow_label->size());
161 |
162 | if(partner.size() > 1)
163 | {
164 | emit pushImg(img);
165 | }
166 |
167 | if(_mytcpSocket->getlocalip() == mainip)
168 | {
169 | ui->mainshow_label->setPixmap(QPixmap::fromImage(img).scaled(ui->mainshow_label->size()));
170 | }
171 |
172 | Partner *p = partner[_mytcpSocket->getlocalip()];
173 | if(p) p->setpic(img);
174 |
175 | //qDebug()<< "format: " << videoImg.format() << "size: " << videoImg.size() << "byteSIze: "<< videoImg.sizeInBytes();
176 | }
177 | frame.unmap();
178 | }
179 |
180 | Widget::~Widget()
181 | {
182 | //终止底层发送与接收线程
183 |
184 | if(_mytcpSocket->isRunning())
185 | {
186 | _mytcpSocket->stopImmediately();
187 | _mytcpSocket->wait();
188 | }
189 |
190 | //终止接收处理线程
191 | if(_recvThread->isRunning())
192 | {
193 | _recvThread->stopImmediately();
194 | _recvThread->wait();
195 | }
196 |
197 | if(_imgThread->isRunning())
198 | {
199 | _imgThread->quit();
200 | _imgThread->wait();
201 | }
202 |
203 | if(_sendImg->isRunning())
204 | {
205 | _sendImg->stopImmediately();
206 | _sendImg->wait();
207 | }
208 |
209 | if(_textThread->isRunning())
210 | {
211 | _textThread->quit();
212 | _textThread->wait();
213 | }
214 |
215 | if(_sendText->isRunning())
216 | {
217 | _sendText->stopImmediately();
218 | _sendText->wait();
219 | }
220 |
221 | if (_ainputThread->isRunning())
222 | {
223 | _ainputThread->quit();
224 | _ainputThread->wait();
225 | }
226 |
227 | if (_aoutput->isRunning())
228 | {
229 | _aoutput->stopImmediately();
230 | _aoutput->wait();
231 | }
232 | WRITE_LOG("-------------------Application End-----------------");
233 |
234 | //关闭日志
235 | if(logqueue->isRunning())
236 | {
237 | logqueue->stopImmediately();
238 | logqueue->wait();
239 | }
240 |
241 | delete ui;
242 | }
243 |
244 | void Widget::on_createmeetBtn_clicked()
245 | {
246 | if(false == _createmeet)
247 | {
248 | ui->createmeetBtn->setDisabled(true);
249 | ui->openAudio->setDisabled(true);
250 | ui->openVedio->setDisabled(true);
251 | ui->exitmeetBtn->setDisabled(true);
252 | emit PushText(CREATE_MEETING); //将 “创建会议"加入到发送队列
253 | WRITE_LOG("create meeting");
254 | }
255 | }
256 |
257 | void Widget::paintEvent(QPaintEvent *event)
258 | {
259 | Q_UNUSED(event);
260 | /*
261 | * 触发事件(3条, 一般使用第二条进行触发)
262 | * 1. 窗口部件第一次显示时,系统会自动产生一个绘图事件。从而强制绘制这个窗口部件,主窗口起来会绘制一次
263 | * 2. 当重新调整窗口部件的大小时,系统也会产生一个绘制事件--QWidget::update()或者QWidget::repaint()
264 | * 3. 当窗口部件被其它窗口部件遮挡,然后又再次显示出来时,就会对那些隐藏的区域产生一个绘制事件
265 | */
266 | }
267 |
268 |
269 | //退出会议(1,加入的会议, 2,自己创建的会议)
270 | void Widget::on_exitmeetBtn_clicked()
271 | {
272 | if(_camera->status() == QCamera::ActiveStatus)
273 | {
274 | _camera->stop();
275 | }
276 |
277 | ui->createmeetBtn->setDisabled(true);
278 | ui->exitmeetBtn->setDisabled(true);
279 | _createmeet = false;
280 | _joinmeet = false;
281 | //-----------------------------------------
282 | //清空partner
283 | clearPartner();
284 | // 关闭套接字
285 |
286 | //关闭socket
287 | _mytcpSocket->disconnectFromHost();
288 | _mytcpSocket->wait();
289 |
290 | ui->outlog->setText(tr("已退出会议"));
291 |
292 | ui->connServer->setDisabled(false);
293 | ui->groupBox_2->setTitle(QString("主屏幕"));
294 | // ui->groupBox->setTitle(QString("副屏幕"));
295 | //清除聊天记录
296 | while(ui->listWidget->count() > 0)
297 | {
298 | QListWidgetItem *item = ui->listWidget->takeItem(0);
299 | ChatMessage *chat = (ChatMessage *) ui->listWidget->itemWidget(item);
300 | delete item;
301 | delete chat;
302 | }
303 | iplist.clear();
304 | ui->plainTextEdit->setCompleter(iplist);
305 |
306 |
307 | WRITE_LOG("exit meeting");
308 |
309 | QMessageBox::warning(this, "Information", "退出会议" , QMessageBox::Yes, QMessageBox::Yes);
310 |
311 | //-----------------------------------------
312 | }
313 |
314 | void Widget::on_openVedio_clicked()
315 | {
316 | if(_camera->status() == QCamera::ActiveStatus)
317 | {
318 | _camera->stop();
319 | WRITE_LOG("close camera");
320 | if(_camera->error() == QCamera::NoError)
321 | {
322 | _imgThread->quit();
323 | _imgThread->wait();
324 | ui->openVedio->setText("开启摄像头");
325 | emit PushText(CLOSE_CAMERA);
326 | }
327 | closeImg(_mytcpSocket->getlocalip());
328 | }
329 | else
330 | {
331 | _camera->start(); //开启摄像头
332 | WRITE_LOG("open camera");
333 | if(_camera->error() == QCamera::NoError)
334 | {
335 | _imgThread->start();
336 | ui->openVedio->setText("关闭摄像头");
337 | }
338 | }
339 | }
340 |
341 |
342 | void Widget::on_openAudio_clicked()
343 | {
344 | if (!_createmeet && !_joinmeet) return;
345 | if (ui->openAudio->text().toUtf8() == QString(OPENAUDIO).toUtf8())
346 | {
347 | emit startAudio();
348 | ui->openAudio->setText(QString(CLOSEAUDIO).toUtf8());
349 | }
350 | else if(ui->openAudio->text().toUtf8() == QString(CLOSEAUDIO).toUtf8())
351 | {
352 | emit stopAudio();
353 | ui->openAudio->setText(QString(OPENAUDIO).toUtf8());
354 | }
355 | }
356 |
357 | void Widget::closeImg(quint32 ip)
358 | {
359 | if (!partner.contains(ip))
360 | {
361 | qDebug() << "close img error";
362 | return;
363 | }
364 | Partner * p = partner[ip];
365 | p->setpic(QImage(":/myImage/1.jpg"));
366 |
367 | if(mainip == ip)
368 | {
369 | ui->mainshow_label->setPixmap(QPixmap::fromImage(QImage(":/myImage/1.jpg").scaled(ui->mainshow_label->size())));
370 | }
371 | }
372 |
373 | void Widget::on_connServer_clicked()
374 | {
375 | QString ip = ui->ip->text(), port = ui->port->text();
376 | ui->outlog->setText("正在连接到" + ip + ":" + port);
377 | repaint();
378 |
379 | QRegExp ipreg("((2{2}[0-3]|2[01][0-9]|1[0-9]{2}|0?[1-9][0-9]|0{0,2}[1-9])\\.)((25[0-5]|2[0-4][0-9]|[01]?[0-9]{0,2})\\.){2}(25[0-5]|2[0-4][0-9]|[01]?[0-9]{1,2})");
380 |
381 | QRegExp portreg("^([0-9]|[1-9]\\d|[1-9]\\d{2}|[1-9]\\d{3}|[1-5]\\d{4}|6[0-4]\\d{3}|65[0-4]\\d{2}|655[0-2]\\d|6553[0-5])$");
382 | QRegExpValidator ipvalidate(ipreg), portvalidate(portreg);
383 | int pos = 0;
384 | if(ipvalidate.validate(ip, pos) != QValidator::Acceptable)
385 | {
386 | QMessageBox::warning(this, "Input Error", "Ip Error", QMessageBox::Yes, QMessageBox::Yes);
387 | return;
388 | }
389 | if(portvalidate.validate(port, pos) != QValidator::Acceptable)
390 | {
391 | QMessageBox::warning(this, "Input Error", "Port Error", QMessageBox::Yes, QMessageBox::Yes);
392 | return;
393 | }
394 |
395 | if(_mytcpSocket ->connectToServer(ip, port, QIODevice::ReadWrite))
396 | {
397 | ui->outlog->setText("成功连接到" + ip + ":" + port);
398 | ui->openAudio->setDisabled(true);
399 | ui->openVedio->setDisabled(true);
400 | ui->createmeetBtn->setDisabled(false);
401 | ui->exitmeetBtn->setDisabled(true);
402 | ui->joinmeetBtn->setDisabled(false);
403 | WRITE_LOG("succeeed connecting to %s:%s", ip.toStdString().c_str(), port.toStdString().c_str());
404 | QMessageBox::warning(this, "Connection success", "成功连接服务器" , QMessageBox::Yes, QMessageBox::Yes);
405 | ui->sendmsg->setDisabled(false);
406 | ui->connServer->setDisabled(true);
407 | }
408 | else
409 | {
410 | ui->outlog->setText("连接失败,请重新连接...");
411 | WRITE_LOG("failed to connenct %s:%s", ip.toStdString().c_str(), port.toStdString().c_str());
412 | QMessageBox::warning(this, "Connection error", _mytcpSocket->errorString() , QMessageBox::Yes, QMessageBox::Yes);
413 | }
414 | }
415 |
416 |
417 | void Widget::cameraError(QCamera::Error)
418 | {
419 | QMessageBox::warning(this, "Camera error", _camera->errorString() , QMessageBox::Yes, QMessageBox::Yes);
420 | }
421 |
422 | void Widget::audioError(QString err)
423 | {
424 | QMessageBox::warning(this, "Audio error", err, QMessageBox::Yes);
425 | }
426 |
427 | void Widget::datasolve(MESG *msg)
428 | {
429 | if(msg->msg_type == CREATE_MEETING_RESPONSE)
430 | {
431 | int roomno;
432 | memcpy(&roomno, msg->data, msg->len);
433 |
434 | if(roomno != 0)
435 | {
436 | QMessageBox::information(this, "Room No", QString("房间号:%1").arg(roomno), QMessageBox::Yes, QMessageBox::Yes);
437 |
438 | ui->groupBox_2->setTitle(QString("主屏幕(房间号: %1)").arg(roomno));
439 | ui->outlog->setText(QString("创建成功 房间号: %1").arg(roomno) );
440 | _createmeet = true;
441 | ui->exitmeetBtn->setDisabled(false);
442 | ui->openVedio->setDisabled(false);
443 | ui->joinmeetBtn->setDisabled(true);
444 |
445 | WRITE_LOG("succeed creating room %d", roomno);
446 | //添加用户自己
447 | addPartner(_mytcpSocket->getlocalip());
448 | mainip = _mytcpSocket->getlocalip();
449 | ui->groupBox_2->setTitle(QHostAddress(mainip).toString());
450 | ui->mainshow_label->setPixmap(QPixmap::fromImage(QImage(":/myImage/1.jpg").scaled(ui->mainshow_label->size())));
451 | }
452 | else
453 | {
454 | _createmeet = false;
455 | QMessageBox::information(this, "Room Information", QString("无可用房间"), QMessageBox::Yes, QMessageBox::Yes);
456 | ui->outlog->setText(QString("无可用房间"));
457 | ui->createmeetBtn->setDisabled(false);
458 | WRITE_LOG("no empty room");
459 | }
460 | }
461 | else if(msg->msg_type == JOIN_MEETING_RESPONSE)
462 | {
463 | qint32 c;
464 | memcpy(&c, msg->data, msg->len);
465 | if(c == 0)
466 | {
467 | QMessageBox::information(this, "Meeting Error", tr("会议不存在") , QMessageBox::Yes, QMessageBox::Yes);
468 | ui->outlog->setText(QString("会议不存在"));
469 | WRITE_LOG("meeting not exist");
470 | ui->exitmeetBtn->setDisabled(true);
471 | ui->openVedio->setDisabled(true);
472 | ui->joinmeetBtn->setDisabled(false);
473 | ui->connServer->setDisabled(true);
474 | _joinmeet = false;
475 | }
476 | else if(c == -1)
477 | {
478 | QMessageBox::warning(this, "Meeting information", "成员已满,无法加入" , QMessageBox::Yes, QMessageBox::Yes);
479 | ui->outlog->setText(QString("成员已满,无法加入"));
480 | WRITE_LOG("full room, cannot join");
481 | }
482 | else if (c > 0)
483 | {
484 | QMessageBox::warning(this, "Meeting information", "加入成功" , QMessageBox::Yes, QMessageBox::Yes);
485 | ui->outlog->setText(QString("加入成功"));
486 | WRITE_LOG("succeed joining room");
487 | //添加用户自己
488 | addPartner(_mytcpSocket->getlocalip());
489 | mainip = _mytcpSocket->getlocalip();
490 | ui->groupBox_2->setTitle(QHostAddress(mainip).toString());
491 | ui->mainshow_label->setPixmap(QPixmap::fromImage(QImage(":/myImage/1.jpg").scaled(ui->mainshow_label->size())));
492 | ui->joinmeetBtn->setDisabled(true);
493 | ui->exitmeetBtn->setDisabled(false);
494 | ui->createmeetBtn->setDisabled(true);
495 | _joinmeet = true;
496 | }
497 | }
498 | else if(msg->msg_type == IMG_RECV)
499 | {
500 | QHostAddress a(msg->ip);
501 | qDebug() << a.toString();
502 | QImage img;
503 | img.loadFromData(msg->data, msg->len);
504 | if(partner.count(msg->ip) == 1)
505 | {
506 | Partner* p = partner[msg->ip];
507 | p->setpic(img);
508 | }
509 | else
510 | {
511 | Partner* p = addPartner(msg->ip);
512 | p->setpic(img);
513 | }
514 |
515 | if(msg->ip == mainip)
516 | {
517 | ui->mainshow_label->setPixmap(QPixmap::fromImage(img).scaled(ui->mainshow_label->size()));
518 | }
519 | repaint();
520 | }
521 | else if(msg->msg_type == TEXT_RECV)
522 | {
523 | QString str = QString::fromStdString(std::string((char *)msg->data, msg->len));
524 | //qDebug() << str;
525 | QString time = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
526 | ChatMessage *message = new ChatMessage(ui->listWidget);
527 | QListWidgetItem *item = new QListWidgetItem();
528 | dealMessageTime(time);
529 | dealMessage(message, item, str, time, QHostAddress(msg->ip).toString() ,ChatMessage::User_She);
530 | if(str.contains('@' + QHostAddress(_mytcpSocket->getlocalip()).toString()))
531 | {
532 | QSound::play(":/myEffect/2.wav");
533 | }
534 | }
535 | else if(msg->msg_type == PARTNER_JOIN)
536 | {
537 | Partner* p = addPartner(msg->ip);
538 | if(p)
539 | {
540 | p->setpic(QImage(":/myImage/1.jpg"));
541 | ui->outlog->setText(QString("%1 join meeting").arg(QHostAddress(msg->ip).toString()));
542 | iplist.append(QString("@") + QHostAddress(msg->ip).toString());
543 | ui->plainTextEdit->setCompleter(iplist);
544 | }
545 | }
546 | else if(msg->msg_type == PARTNER_EXIT)
547 | {
548 | removePartner(msg->ip);
549 | if(mainip == msg->ip)
550 | {
551 | ui->mainshow_label->setPixmap(QPixmap::fromImage(QImage(":/myImage/1.jpg").scaled(ui->mainshow_label->size())));
552 | }
553 | if(iplist.removeOne(QString("@") + QHostAddress(msg->ip).toString()))
554 | {
555 | ui->plainTextEdit->setCompleter(iplist);
556 | }
557 | else
558 | {
559 | qDebug() << QHostAddress(msg->ip).toString() << "not exist";
560 | WRITE_LOG("%s not exist",QHostAddress(msg->ip).toString().toStdString().c_str());
561 | }
562 | ui->outlog->setText(QString("%1 exit meeting").arg(QHostAddress(msg->ip).toString()));
563 | }
564 | else if (msg->msg_type == CLOSE_CAMERA)
565 | {
566 | closeImg(msg->ip);
567 | }
568 | else if (msg->msg_type == PARTNER_JOIN2)
569 | {
570 | uint32_t ip;
571 | int other = msg->len / sizeof(uint32_t), pos = 0;
572 | for (int i = 0; i < other; i++)
573 | {
574 | memcpy_s(&ip, sizeof(uint32_t), msg->data + pos , sizeof(uint32_t));
575 | pos += sizeof(uint32_t);
576 | Partner* p = addPartner(ip);
577 | if (p)
578 | {
579 | p->setpic(QImage(":/myImage/1.jpg"));
580 | iplist << QString("@") + QHostAddress(ip).toString();
581 | }
582 | }
583 | ui->plainTextEdit->setCompleter(iplist);
584 | ui->openVedio->setDisabled(false);
585 | }
586 | else if(msg->msg_type == RemoteHostClosedError)
587 | {
588 |
589 | clearPartner();
590 | _mytcpSocket->disconnectFromHost();
591 | _mytcpSocket->wait();
592 | ui->outlog->setText(QString("关闭与服务器的连接"));
593 | ui->createmeetBtn->setDisabled(true);
594 | ui->exitmeetBtn->setDisabled(true);
595 | ui->connServer->setDisabled(false);
596 | ui->joinmeetBtn->setDisabled(true);
597 | //清除聊天记录
598 | while(ui->listWidget->count() > 0)
599 | {
600 | QListWidgetItem *item = ui->listWidget->takeItem(0);
601 | ChatMessage *chat = (ChatMessage *)ui->listWidget->itemWidget(item);
602 | delete item;
603 | delete chat;
604 | }
605 | iplist.clear();
606 | ui->plainTextEdit->setCompleter(iplist);
607 | if(_createmeet || _joinmeet) QMessageBox::warning(this, "Meeting Information", "会议结束" , QMessageBox::Yes, QMessageBox::Yes);
608 | }
609 | else if(msg->msg_type == OtherNetError)
610 | {
611 | QMessageBox::warning(NULL, "Network Error", "网络异常" , QMessageBox::Yes, QMessageBox::Yes);
612 | clearPartner();
613 | _mytcpSocket->disconnectFromHost();
614 | _mytcpSocket->wait();
615 | ui->outlog->setText(QString("网络异常......"));
616 | }
617 | if(msg->data)
618 | {
619 | free(msg->data);
620 | msg->data = NULL;
621 | }
622 | if(msg)
623 | {
624 | free(msg);
625 | msg = NULL;
626 | }
627 | }
628 |
629 | Partner* Widget::addPartner(quint32 ip)
630 | {
631 | if (partner.contains(ip)) return NULL;
632 | Partner *p = new Partner(ui->scrollAreaWidgetContents ,ip);
633 | if (p == NULL)
634 | {
635 | qDebug() << "new Partner error";
636 | return NULL;
637 | }
638 | else
639 | {
640 | connect(p, SIGNAL(sendip(quint32)), this, SLOT(recvip(quint32)));
641 | partner.insert(ip, p);
642 | ui->verticalLayout_3->addWidget(p, 1);
643 |
644 | //当有人员加入时,开启滑动条滑动事件,开启输入(只有自己时,不打开)
645 | if (partner.size() > 1)
646 | {
647 | connect(this, SIGNAL(volumnChange(int)), _ainput, SLOT(setVolumn(int)), Qt::UniqueConnection);
648 | connect(this, SIGNAL(volumnChange(int)), _aoutput, SLOT(setVolumn(int)), Qt::UniqueConnection);
649 | ui->openAudio->setDisabled(false);
650 | ui->sendmsg->setDisabled(false);
651 | _aoutput->startPlay();
652 | }
653 | return p;
654 | }
655 | }
656 |
657 | void Widget::removePartner(quint32 ip)
658 | {
659 | if(partner.contains(ip))
660 | {
661 | Partner *p = partner[ip];
662 | disconnect(p, SIGNAL(sendip(quint32)), this, SLOT(recvip(quint32)));
663 | ui->verticalLayout_3->removeWidget(p);
664 | delete p;
665 | partner.remove(ip);
666 |
667 | //只有自已一个人时,关闭传输音频
668 | if (partner.size() <= 1)
669 | {
670 | disconnect(_ainput, SLOT(setVolumn(int)));
671 | disconnect(_aoutput, SLOT(setVolumn(int)));
672 | _ainput->stopCollect();
673 | _aoutput->stopPlay();
674 | ui->openAudio->setText(QString(OPENAUDIO).toUtf8());
675 | ui->openAudio->setDisabled(true);
676 | }
677 | }
678 | }
679 |
680 | void Widget::clearPartner()
681 | {
682 | ui->mainshow_label->setPixmap(QPixmap());
683 | if(partner.size() == 0) return;
684 |
685 | QMap::iterator iter = partner.begin();
686 | while (iter != partner.end())
687 | {
688 | quint32 ip = iter.key();
689 | iter++;
690 | Partner *p = partner.take(ip);
691 | ui->verticalLayout_3->removeWidget(p);
692 | delete p;
693 | p = nullptr;
694 | }
695 |
696 | //关闭传输音频
697 | disconnect(_ainput, SLOT(setVolumn(int)));
698 | disconnect(_aoutput, SLOT(setVolumn(int)));
699 | //关闭音频播放与采集
700 | _ainput->stopCollect();
701 | _aoutput->stopPlay();
702 | ui->openAudio->setText(QString(CLOSEAUDIO).toUtf8());
703 | ui->openAudio->setDisabled(true);
704 |
705 |
706 | //关闭图片传输线程
707 | if(_imgThread->isRunning())
708 | {
709 | _imgThread->quit();
710 | _imgThread->wait();
711 | }
712 | ui->openVedio->setText(QString(OPENVIDEO).toUtf8());
713 | ui->openVedio->setDisabled(true);
714 | }
715 |
716 | void Widget::recvip(quint32 ip)
717 | {
718 | if (partner.contains(mainip))
719 | {
720 | Partner* p = partner[mainip];
721 | p->setStyleSheet("border-width: 1px; border-style: solid; border-color:rgba(0, 0 , 255, 0.7)");
722 | }
723 | if (partner.contains(ip))
724 | {
725 | Partner* p = partner[ip];
726 | p->setStyleSheet("border-width: 1px; border-style: solid; border-color:rgba(255, 0 , 0, 0.7)");
727 | }
728 | ui->mainshow_label->setPixmap(QPixmap::fromImage(QImage(":/myImage/1.jpg").scaled(ui->mainshow_label->size())));
729 | mainip = ip;
730 | ui->groupBox_2->setTitle(QHostAddress(mainip).toString());
731 | qDebug() << ip;
732 | }
733 |
734 | /*
735 | * 加入会议
736 | */
737 |
738 | void Widget::on_joinmeetBtn_clicked()
739 | {
740 | QString roomNo = ui->meetno->text();
741 |
742 | QRegExp roomreg("^[1-9][0-9]{1,4}$");
743 | QRegExpValidator roomvalidate(roomreg);
744 | int pos = 0;
745 | if(roomvalidate.validate(roomNo, pos) != QValidator::Acceptable)
746 | {
747 | QMessageBox::warning(this, "RoomNo Error", "房间号不合法" , QMessageBox::Yes, QMessageBox::Yes);
748 | }
749 | else
750 | {
751 | //加入发送队列
752 | emit PushText(JOIN_MEETING, roomNo);
753 | }
754 | }
755 |
756 | void Widget::on_horizontalSlider_valueChanged(int value)
757 | {
758 | emit volumnChange(value);
759 | }
760 |
761 | void Widget::speaks(QString ip)
762 | {
763 | ui->outlog->setText(QString(ip + " 正在说话").toUtf8());
764 | }
765 |
766 | void Widget::on_sendmsg_clicked()
767 | {
768 | QString msg = ui->plainTextEdit->toPlainText().trimmed();
769 | if(msg.size() == 0)
770 | {
771 | qDebug() << "empty";
772 | return;
773 | }
774 | qDebug()<plainTextEdit->setPlainText("");
776 | QString time = QString::number(QDateTime::currentDateTimeUtc().toTime_t());
777 | ChatMessage *message = new ChatMessage(ui->listWidget);
778 | QListWidgetItem *item = new QListWidgetItem();
779 | dealMessageTime(time);
780 | dealMessage(message, item, msg, time, QHostAddress(_mytcpSocket->getlocalip()).toString() ,ChatMessage::User_Me);
781 | emit PushText(TEXT_SEND, msg);
782 | ui->sendmsg->setDisabled(true);
783 | }
784 |
785 | void Widget::dealMessage(ChatMessage *messageW, QListWidgetItem *item, QString text, QString time, QString ip ,ChatMessage::User_Type type)
786 | {
787 | ui->listWidget->addItem(item);
788 | messageW->setFixedWidth(ui->listWidget->width());
789 | QSize size = messageW->fontRect(text);
790 | item->setSizeHint(size);
791 | messageW->setText(text, time, size, ip, type);
792 | ui->listWidget->setItemWidget(item, messageW);
793 | }
794 |
795 | void Widget::dealMessageTime(QString curMsgTime)
796 | {
797 | bool isShowTime = false;
798 | if(ui->listWidget->count() > 0) {
799 | QListWidgetItem* lastItem = ui->listWidget->item(ui->listWidget->count() - 1);
800 | ChatMessage* messageW = (ChatMessage *)ui->listWidget->itemWidget(lastItem);
801 | int lastTime = messageW->time().toInt();
802 | int curTime = curMsgTime.toInt();
803 | qDebug() << "curTime lastTime:" << curTime - lastTime;
804 | isShowTime = ((curTime - lastTime) > 60); // 两个消息相差一分钟
805 | // isShowTime = true;
806 | } else {
807 | isShowTime = true;
808 | }
809 | if(isShowTime) {
810 | ChatMessage* messageTime = new ChatMessage(ui->listWidget);
811 | QListWidgetItem* itemTime = new QListWidgetItem();
812 | ui->listWidget->addItem(itemTime);
813 | QSize size = QSize(ui->listWidget->width() , 40);
814 | messageTime->resize(size);
815 | itemTime->setSizeHint(size);
816 | messageTime->setText(curMsgTime, curMsgTime, size);
817 | ui->listWidget->setItemWidget(itemTime, messageTime);
818 | }
819 | }
820 |
821 | void Widget::textSend()
822 | {
823 | qDebug() << "send text over";
824 | QListWidgetItem* lastItem = ui->listWidget->item(ui->listWidget->count() - 1);
825 | ChatMessage* messageW = (ChatMessage *)ui->listWidget->itemWidget(lastItem);
826 | messageW->setTextSuccess();
827 | ui->sendmsg->setDisabled(false);
828 | }
829 |
--------------------------------------------------------------------------------
/widget.ui:
--------------------------------------------------------------------------------
1 |
2 |
3 | Widget
4 |
5 |
6 | true
7 |
8 |
9 |
10 | 0
11 | 0
12 | 1296
13 | 784
14 |
15 |
16 |
17 |
18 | 0
19 | 0
20 |
21 |
22 |
23 |
24 | 900
25 | 600
26 |
27 |
28 |
29 | 云会议
30 |
31 |
32 |
33 | 3
34 |
35 |
36 | 3
37 |
38 |
39 | 3
40 |
41 |
42 | 3
43 |
44 |
45 | 3
46 |
47 | -
48 |
49 |
50 | 9
51 |
52 |
53 | 5
54 |
55 |
56 | 6
57 |
58 |
59 | 5
60 |
61 |
62 | 3
63 |
64 |
-
65 |
66 |
67 |
68 | 0
69 | 0
70 |
71 |
72 |
73 | ArrowCursor
74 |
75 |
76 | 加入会议
77 |
78 |
79 |
80 | 3
81 |
82 |
83 | 3
84 |
85 |
86 | 3
87 |
88 |
89 | 3
90 |
91 |
92 | 3
93 |
94 |
-
95 |
96 |
97 | 会议编号:
98 |
99 |
100 |
101 | -
102 |
103 |
104 | -
105 |
106 |
107 | ArrowCursor
108 |
109 |
110 | 加入
111 |
112 |
113 |
114 |
115 |
116 |
117 | -
118 |
119 |
120 |
121 | 0
122 | 0
123 |
124 |
125 |
126 | ArrowCursor
127 |
128 |
129 | 创建会议
130 |
131 |
132 |
133 | 3
134 |
135 |
136 | 3
137 |
138 |
139 | 3
140 |
141 |
142 | 3
143 |
144 |
145 | 3
146 |
147 |
-
148 |
149 |
150 | ArrowCursor
151 |
152 |
153 | 创建
154 |
155 |
156 |
157 |
158 |
159 |
160 | -
161 |
162 |
163 |
164 | 0
165 | 0
166 |
167 |
168 |
169 | ArrowCursor
170 |
171 |
172 | 退出会议
173 |
174 |
175 |
176 | 3
177 |
178 |
179 | 3
180 |
181 |
182 | 3
183 |
184 |
185 | 3
186 |
187 |
188 | 3
189 |
190 |
-
191 |
192 |
193 | ArrowCursor
194 |
195 |
196 | 退出
197 |
198 |
199 |
200 |
201 |
202 |
203 | -
204 |
205 |
206 | 连接服务器
207 |
208 |
209 |
210 | 3
211 |
212 |
213 | 3
214 |
215 |
216 | 3
217 |
218 |
219 | 3
220 |
221 |
222 | 3
223 |
224 |
-
225 |
226 |
227 | IP:
228 |
229 |
230 |
231 | -
232 |
233 |
234 |
235 | 0
236 | 0
237 |
238 |
239 |
240 |
241 | 0
242 | 0
243 |
244 |
245 |
246 | 192.168.1.126
247 |
248 |
249 |
250 | -
251 |
252 |
253 | Port:
254 |
255 |
256 |
257 | -
258 |
259 |
260 |
261 | 0
262 | 0
263 |
264 |
265 |
266 |
267 | 300
268 | 16777215
269 |
270 |
271 |
272 | 8888
273 |
274 |
275 |
276 | -
277 |
278 |
279 | 连接
280 |
281 |
282 |
283 |
284 |
285 |
286 |
287 |
288 | -
289 |
290 |
291 | 1
292 |
293 |
-
294 |
295 |
296 |
297 | 0
298 | 0
299 |
300 |
301 |
302 |
303 | 400
304 | 0
305 |
306 |
307 |
308 |
309 |
310 |
311 |
312 |
313 | 255
314 | 0
315 | 0
316 |
317 |
318 |
319 |
320 |
321 |
322 | 255
323 | 0
324 | 0
325 |
326 |
327 |
328 |
329 |
330 |
331 | 255
332 | 0
333 | 0
334 |
335 |
336 |
337 |
338 |
339 |
340 |
341 |
342 | 255
343 | 0
344 | 0
345 |
346 |
347 |
348 |
349 |
350 |
351 | 255
352 | 0
353 | 0
354 |
355 |
356 |
357 |
358 |
359 |
360 | 255
361 | 0
362 | 0
363 |
364 |
365 |
366 |
367 |
368 |
369 |
370 |
371 | 120
372 | 120
373 | 120
374 |
375 |
376 |
377 |
378 |
379 |
380 | 120
381 | 120
382 | 120
383 |
384 |
385 |
386 |
387 |
388 |
389 | 0
390 | 0
391 | 0
392 |
393 |
394 |
395 |
396 |
397 |
398 |
399 | QTabWidget::Rounded
400 |
401 |
402 | 1
403 |
404 |
405 | true
406 |
407 |
408 |
409 | 副屏幕
410 |
411 |
412 |
413 | 0
414 |
415 |
416 | 0
417 |
418 |
419 | 0
420 |
421 |
422 | 0
423 |
424 |
425 | 0
426 |
427 |
-
428 |
429 |
430 | 0
431 |
432 |
433 | true
434 |
435 |
436 |
437 |
438 | 0
439 | 0
440 | 392
441 | 585
442 |
443 |
444 |
445 |
446 | 0
447 | 0
448 |
449 |
450 |
451 |
452 | 3
453 |
454 |
455 | 0
456 |
457 |
458 | 0
459 |
460 |
461 | 0
462 |
463 |
464 | 0
465 |
466 |
467 |
468 |
469 |
470 |
471 |
472 |
473 |
474 |
475 | 0
476 | 0
477 |
478 |
479 |
480 | 聊天窗口
481 |
482 |
483 |
484 | 2
485 |
486 |
487 | 0
488 |
489 |
490 | 0
491 |
492 |
493 | 0
494 |
495 |
496 | 0
497 |
498 | -
499 |
500 |
501 | Qt::NoFocus
502 |
503 |
504 | Qt::ScrollBarAlwaysOff
505 |
506 |
507 | QAbstractScrollArea::AdjustToContents
508 |
509 |
510 | 0
511 |
512 |
513 | Qt::AlignLeading|Qt::AlignLeft|Qt::AlignRight|Qt::AlignTrailing
514 |
515 |
516 |
517 | -
518 |
519 |
520 |
521 | 0
522 | 0
523 |
524 |
525 |
526 |
527 | 16777215
528 | 100
529 |
530 |
531 |
532 |
533 | 0
534 |
535 |
536 | 0
537 |
538 |
539 | 0
540 |
541 |
542 | 0
543 |
544 |
545 | 0
546 |
547 |
-
548 |
549 |
550 |
551 | 0
552 | 0
553 |
554 |
555 |
556 |
557 | 16777215
558 | 80
559 |
560 |
561 |
562 |
563 |
564 |
565 |
566 |
567 | 0
568 | 0
569 | 0
570 |
571 |
572 |
573 |
574 |
575 |
576 | 0
577 | 0
578 | 0
579 |
580 |
581 |
582 |
583 |
584 |
585 | 0
586 | 0
587 | 0
588 |
589 |
590 |
591 |
592 |
593 |
594 |
595 |
596 | 0
597 | 0
598 | 0
599 |
600 |
601 |
602 |
603 |
604 |
605 | 0
606 | 0
607 | 0
608 |
609 |
610 |
611 |
612 |
613 |
614 | 0
615 | 0
616 | 0
617 |
618 |
619 |
620 |
621 |
622 |
623 |
624 |
625 | 120
626 | 120
627 | 120
628 |
629 |
630 |
631 |
632 |
633 |
634 | 120
635 | 120
636 | 120
637 |
638 |
639 |
640 |
641 |
642 |
643 | 0
644 | 0
645 | 0
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 | Microsoft YaHei
655 | 12
656 |
657 |
658 |
659 | 输入@可以向对应的IP发送数据
660 |
661 |
662 |
663 | -
664 |
665 |
666 | 发送
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 | -
678 |
679 |
680 |
681 | 0
682 | 0
683 |
684 |
685 |
686 |
687 |
688 |
689 |
690 |
691 | 255
692 | 0
693 | 0
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 |
702 | 255
703 | 0
704 | 0
705 |
706 |
707 |
708 |
709 |
710 |
711 |
712 |
713 | 120
714 | 120
715 | 120
716 |
717 |
718 |
719 |
720 |
721 |
722 |
723 | 主屏幕
724 |
725 |
726 |
727 | 0
728 |
729 |
730 | 0
731 |
732 |
733 | 0
734 |
735 |
736 | 0
737 |
738 |
739 | 0
740 |
741 |
-
742 |
743 |
744 |
745 | 0
746 | 0
747 |
748 |
749 |
750 |
751 |
752 |
753 | false
754 |
755 |
756 |
757 |
758 |
759 |
760 |
761 |
762 | -
763 |
764 |
-
765 |
766 |
767 |
768 | 0
769 | 0
770 |
771 |
772 |
773 | 视频
774 |
775 |
776 |
-
777 |
778 |
779 | 开启摄像头
780 |
781 |
782 |
783 |
784 |
785 |
786 | -
787 |
788 |
789 | 音频
790 |
791 |
792 |
-
793 |
794 |
795 |
796 | 0
797 | 0
798 |
799 |
800 |
801 | 开启音频
802 |
803 |
804 |
805 | -
806 |
807 |
808 | 音量:
809 |
810 |
811 |
812 | -
813 |
814 |
815 | Qt::Horizontal
816 |
817 |
818 |
819 |
820 |
821 |
822 |
823 |
824 | -
825 |
826 |
827 |
828 | 0
829 | 0
830 |
831 |
832 |
833 | 输出日志
834 |
835 |
836 |
837 | 6
838 |
839 |
840 | 9
841 |
842 |
843 | 9
844 |
845 |
846 | 9
847 |
848 |
849 | 9
850 |
851 |
-
852 |
853 |
854 |
855 | 0
856 | 0
857 |
858 |
859 |
860 |
861 |
862 |
863 |
864 |
865 | 255
866 | 0
867 | 0
868 |
869 |
870 |
871 |
872 |
873 |
874 |
875 |
876 | 255
877 | 0
878 | 0
879 |
880 |
881 |
882 |
883 |
884 |
885 |
886 |
887 | 120
888 | 120
889 | 120
890 |
891 |
892 |
893 |
894 |
895 |
896 |
897 |
898 |
899 |
900 |
901 |
902 |
903 |
904 |
905 |
906 |
907 |
908 | MyTextEdit
909 | QPlainTextEdit
910 |
911 |
912 |
913 |
914 |
915 |
916 |
--------------------------------------------------------------------------------