之前已经实现了基本的服务器端的架构,并且通过浏览器我们已经可以看见实时的视屏画面了,现在要做的是,把视屏画面显示在自己编写的一个客户端上面,而不是一直使用浏览器查看。
环境:acer 4752G ubuntu 12.04
工具:vi Qt 4.7
这里首先需要解决一个非常关键的问题:
当我们的服务器段,通过摄像头采集到了数据之后,我们把它经过编码之后形成的是一张一张的图片帧,然后发往浏览器,这时我们采用的http协议,发往浏览器,然后浏览器可以解析我们的http协议,去掉基本的数据封装的头部和尾部,但是现在我们使用的是自己编写的客户端,这样的话我们就必须得自己接写服务器端发来的数据,自己去除数据帧的帧头,以及我们在服务器端发出的一帧数据的开始和结尾除,自己删除多余的信息,然后去除相应的图片,绘制到我们的客户端截面。
当然,如果当当是为了实现一个自己写的客户端可以接收数据的话,我们完全可以自己定义协议,然后封装数据发往客户端,客户端做相应的解析即可。
这里呢,我测试的是使用http协议,
首先发送的是一个http头部 [HEADER]…..\r\n\r\n… \n\n..[data]\r\n
\n\n..[data]\r\n
\n\n..[data]\r\n
这是我们发出的每一针的数据的封装格式,
然后我们在解析的时候就是要
第一步:删除http报头
第二部:判断是否有一个数据结束的标记已经之后还附加的多余的数据 \r\n–
第三部:当等待有一帧完整的数据后,我们就从数据帧的结尾处开始取数据,区到开头,然后吧取到的数据发送给绘制函数。update()我们的屏幕。绘制完之后,删除现有的数据,准备接收下一帧的数据
解析过程是最终要的。!!
一下是完整的实现代码:
1 2 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 |
#ifndef DIALOG_H #define DIALOG_H #include <QDialog> #include <QLineEdit> #include <QPushButton> #include <QLabel> class Dialog : public QDialog { Q_OBJECT public: Dialog(QWidget *parent = 0); ~Dialog(); //获取IP地址和端口 QString getHost(); int getPort(); private: QLabel *lbIp; QLabel *lbPort; QLineEdit *lineEditIp; QLineEdit *lineEditPort; QPushButton *btnOk; QPushButton *btnCancel; }; #endif // DIALOG_H #ifndef SCREEN_H #define SCREEN_H #include <QWidget> #include "dialog.h" #include <QImage> #include "thread.h" class Screen : public QWidget { Q_OBJECT public: Screen(QWidget *parent = 0); ~Screen(); private: QImage image; Dialog *dialog; Thread *thread; protected: void paintEvent(QPaintEvent *); signals: public slots: void showDialog(); void showImages(QImage image); void connectToServer();//链接到服务器,响应对话框发出的accept信号 }; #endif // SCREEN_H #ifndef THREAD_H #define THREAD_H #include <QThread> #include <QDebug> #include <QtNetwork/QTcpSocket> #include <QThread> #include <QImage> class Thread : public QThread { Q_OBJECT public: Thread(); void setIpPort(QString,int); private: QString ip; int port; signals: void sendCurrentImage(QImage); protected: void run(); }; #endif // THREAD_H #ifndef WIDGET_H #define WIDGET_H #include <QtGui/QWidget> #include <QPushButton> #include <QImage> #include "screen.h" class Widget : public QWidget { Q_OBJECT public: Widget(QWidget *parent = 0); ~Widget(); private: QPushButton *btnStart; QPushButton *btnStop; QPushButton *btnQuit; Screen *screen; }; #endif // WIDGET_H |
接下来是具体的实现每个函数和信号之间的关联
提醒一点,资源文件自己去找,然后项目文件什么的,记得加network模块,否则编译会报错。
1 2 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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 |
#include "dialog.h" #include <QHBoxLayout> #include <QVBoxLayout> #include <QDebug> Dialog::Dialog(QWidget *parent) { lbIp = new QLabel(this); lbIp->setText("HostIP:"); lbPort = new QLabel(this); lbPort->setText(" Port:"); lineEditIp = new QLineEdit("127.0.0.1",this); lineEditPort = new QLineEdit("8000",this); btnOk = new QPushButton(this); btnOk->setText("OK"); btnCancel = new QPushButton(this); btnCancel->setText("Cancel"); QHBoxLayout *ipHBox = new QHBoxLayout; ipHBox->addWidget(lbIp); ipHBox->addWidget(lineEditIp); QHBoxLayout *portHBox = new QHBoxLayout; portHBox->addWidget(lbPort); portHBox->addWidget(lineEditPort); QHBoxLayout *btnHBox = new QHBoxLayout; btnHBox->addWidget(btnOk); btnHBox->addWidget(btnCancel); QVBoxLayout *vBox = new QVBoxLayout; vBox->addLayout(ipHBox); vBox->addLayout(portHBox); vBox->addLayout(btnHBox); setLayout(vBox); connect(btnOk,SIGNAL(clicked()),this,SLOT(accept())); connect(btnCancel,SIGNAL(clicked()),this,SLOT(reject())); } QString Dialog::getHost() { qDebug()<<"dialog"<<lineEditIp->text(); return lineEditIp->text(); } int Dialog::getPort() { qDebug()<<"dialog"<<lineEditPort->text(); return lineEditPort->text().toInt(); } Dialog::~Dialog() { } #include "screen.h" #include <QPainter> #include <QRect> #include <QDebug> #include <QDialog> Screen::Screen(QWidget *parent) :QWidget(parent) { resize(960,540); image.load(":/images/wugui.jpg"); dialog = new Dialog(this); dialog->setModal(true); //设置当对话框弹出时,背景不能点击 thread = new Thread; //注册accepted信号 connect(dialog,SIGNAL(accepted()),this,SLOT(connectToServer()));//注册在dialog里面发出的信号的响应 connect(thread,SIGNAL(sendCurrentImage(QImage)),this,SLOT(showImages(QImage))); //响应信号sendCurrentImage刷新图片 } void Screen::connectToServer() { qDebug()<<"qqqqqq"<<dialog->getHost()<<dialog->getPort(); thread->setIpPort(dialog->getHost(),dialog->getPort()); //设置链接的服务器IP和PORT thread->start(); //开启线程 } void Screen::paintEvent(QPaintEvent *) { QPainter painter(this); QRect rect(0,0,this->width(),this->height()); //设置绘制的矩形区域 painter.drawImage(rect,image); } void Screen::showImages(QImage image) { this->image =image; update(); } void Screen::showDialog() { dialog->show(); } Screen::~Screen() { } #include "thread.h" #include "screen.h" #include <QDebug> QImage image; Thread::Thread() { } void Thread::setIpPort(QString ip, int port) { this->ip = ip; this->port = port; qDebug()<<ip<<port; } void Thread::run() { QTcpSocket tcpSocket; tcpSocket.connectToHost(ip,port); //链接到服务器 qDebug()<<ip<<port; if(tcpSocket.waitForConnected()) //链接 qDebug()<<"connected..."<<endl; QByteArray streamData; // streamData.clear(); //清空数据 streamData = QByteArray("action=stream"); //定义数据头 tcpSocket.write(streamData); //发往服务器,请求数据 if(!tcpSocket.waitForBytesWritten()) { qDebug()<<"Write action failed!"<<endl; //写入头失败 } else { qDebug()<<"Write action success"<<endl; } //去除报头 //QByteArray head="\r\n\r\n"; //QByreArray start="\n\n"; //数据开始 //图片数据区 data //QByteArray end="\r\n--"; //数据结尾 while(!streamData.contains("\r\n\r\n")) //判断数据段是否包含有\r\n\r\n数据 { //数据头是\r\n\r\n tcpSocket.waitForReadyRead(); //准备读取数据 streamData.append(tcpSocket.readAll()); //读取数据到streamData } int index = streamData.indexOf("\r\n\r\n"); //找到开始位置 streamData.remove(0,index+4); //删除报头 QByteArray photoData; while(1) { photoData.clear();//清空数据 while(!streamData.contains("\r\n--")) //是否有数据尾 { tcpSocket.waitForReadyRead(); streamData.append(tcpSocket.readAll()); } index = streamData.indexOf("\n\n"); streamData.remove(0,index+2); //删除数据尾 index = streamData.indexOf("\r\n--"); photoData = streamData.left(index); //截取左边的长度,也就是目前得到的一张图片 image.loadFromData(photoData); //转换格式 emit sendCurrentImage(image); //发送信号 streamData.remove(0,index+4); //再次取出数据头 } } #include "widget.h" #include <QHBoxLayout> #include <QVBoxLayout> #include "screen.h" #include "dialog.h" Widget::Widget(QWidget *parent) : QWidget(parent) { resize(960,580); screen = new Screen(this); btnStart = new QPushButton(this); btnStart->setText("Start"); btnStop = new QPushButton(this); btnStop->setText("Stop"); btnQuit = new QPushButton(this); btnQuit->setText("Quit"); QHBoxLayout *btnHBox =new QHBoxLayout; btnHBox->addWidget(btnStart); btnHBox->addWidget(btnStop); btnHBox->addWidget(btnQuit); QVBoxLayout *vBox = new QVBoxLayout; vBox->addWidget(screen); vBox->addLayout(btnHBox); setLayout(vBox); connect(btnStart,SIGNAL(clicked()),screen,SLOT(showDialog())); connect(btnQuit,SIGNAL(clicked()),this,SLOT(close())); } Widget::~Widget() { } |
以上便是初学测试的代码。话说有点坑跌,。将就下吧。觉得不好的就请猛拍砖就好。