您好,登錄后才能下訂單哦!
這篇文章主要講解了“Qt怎么實現視頻傳輸TCP版”,文中的講解內容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Qt怎么實現視頻傳輸TCP版”吧!
做音視頻開發,會遇到將音視頻重新轉發出去的需求,當然終極大法是推流轉發,還有一些簡單的場景是直接自定義協議將視頻傳出去就行,局域網的話速度還是不錯的。很多年前就做過類似的項目,無非就是將本地的圖片上傳到服務器,就這么簡單,其實用http的post上傳比較簡單容易,無需自定義協議,直接設置好二進制數據即可,而采用TCP或者UDP通信的話,必須自定義協議,因為不知道什么時候數據接收完了是完整的圖片數據,可能同時在發送很多圖片數據,而且還不能區分收到的圖片是哪個客戶端發來的,TCP長連接的話,還需要有心跳來檢測連接,所以必須自定義一套協議來支撐通信,這套協議采用的是上海監管平臺的通信協議格式,拓展性比較強,其中頭部信息包括了類型+當前完整包的數據長度,這個類型就是通信協議的標識,這樣下次來一個其他類型的比如樓宇對講可以叫IDOOR,服務端根據這個標識就能知道采用何種解析算法來處理后面的數據,而當前完整包的數據長度可以用來處理收到的數據,只有該長度的數據才表示接收完成一個完整的圖片數據,再去解碼處理。當傳輸的圖片到了一定速度的時候比如一秒鐘傳輸20張圖片,其實就相當于傳輸視頻了,一般人的肉眼看到一秒鐘20張圖片基本上認識就是視頻了。
TCP理論上是穩定的連接,不會丟包,也不會隨便一個包插入到一個包的中間,肯定能保證一個數據包的完整性,TCP連接也分兩種,一種是長連接,一旦連接了就一直通信,主要用在頻繁通信的場景中比如實時上傳,還有一種叫短連接,客戶端發完數據或者服務端接收完數據就立即斷開連接,主要用在不頻繁的通信場景中比如報警上傳,畢竟報警的情況在一天中很少發生,采用短連接為佳,可以省去很多系統的開銷,Qt對TCP的通信也是封裝的很好用,在一些小并發的就幾個幾十個連接的項目中,效率還是可以的,據說Qt5的QNetwork組件底層重新改寫了,效率比Qt4更高一些,本人也沒用去詳細的查看對應的源碼,只是聽說。
通信協議:
采用TCP長連接和UDP協議可選,默認通信端口6000。
采用自定義的xml通信協議。
所有傳輸加20個字節頭部:IIMAGE:0000000000000,IIMAGE:為固定頭部,后面接13個字節的 內容的長度(含20個頭部長度) 字符串。
下面協議部分省略了頭部字節。
服務端返回的數據中的uuid是對應接收到的消息的uuid。
服務端每次返回的時候都帶了當前時間,可用于客戶端校時。
客戶端發送心跳 <?xml version="1.0" encoding="UTF-8"?> <ImageClient Uuid="8AF12208-0356-434C-8A49-69A2170D9B5A" Flag="SHJC00000001"> <ClientHeart/> </ImageClient> 服務器收到心跳返回 <?xml version="1.0" encoding="UTF-8"?> <ImageServer Uuid="8AF12208-0356-434C-8A49-69A2170D9B5A" NowTime="2019-12-05 16:37:47"> Ok </ImageServer> 客戶端發送圖片 <?xml version="1.0" encoding="UTF-8"?> <ImageClient Uuid="66BCB44A-B567-48ED-8889-36B8FA6C4363" Flag="SHJC00000001"> <ClientImage>圖片base64編碼后的字符串/9j/4AAQSkZJRgABAQEAYABgAAD/2wBDAAgGBgcGBQgHBwcJCQgKDBQNDAsLDBkSEw8UHRofHh0aHBwgJC4nICIsIxwcKDcpLDAxNDQ0Hyc5PTgyPC4zNDL/2wBDAQkJCQwLDBgNDRgyIRwhMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjIyMjL/wAARCAJAAtADASIAAhEBAxEB/8QAHwAAAQUBAQEB...nvWsQRlXA61mTjmtWcdazLgcmrQ0U2plSMKjpDE7UtFFAwxRRRQAUuKWigQlFFFLcD//2Q==</ClientImage> </ImageClient> 服務端收到圖片返回 <?xml version="1.0" encoding="UTF-8"?> <ImageServer Uuid="66BCB44A-B567-48ED-8889-36B8FA6C4363" NowTime="2019-12-05 16:38:47"> Ack </ImageServer>
多線程收發圖片數據和解析圖片數據,不卡主界面。
同時支持TCP和UDP兩種模式,封裝了TCP模式以及UDP模式的客戶端類和服務端類。
圖片傳輸客戶端同時支持發送到多個服務端,可以作為一個教師機同屏發送到多個學生機的應用場景。
同時支持多個客戶端同時往服務端發送圖片,服務端每個連接都會自動開辟線程收發和解析圖片數據。
自定義label控件信號槽機制繪制圖片,不卡主界面。
自帶心跳機制判斷離線,自動重連服務器,可設置超時時間。
每個消息都有唯一的消息標識uuid,服務端收到以后會返回對應的uuid消息表示收到,客戶端可以根據此返回消息判斷服務端解析成功,不用再發,這樣可以確保發出去的數據服務器接收到了并解析成功。
每個消息都有唯一的圖片標識flag,相當于ID號,根據此標識判斷需要解析顯示到哪個界面。
圖片以base64的字符串格式發送,接收端接收到base64字符串的圖片數據解碼后重新生成圖片。
所有數據的收發都有信號發出去,方便輸出查看。
都提供單例類,方便只有一個的時候直接使用無需new。
采用自定義的xml協議,可以自由拓展其他屬性字段比如帶上圖片內容等。
#include "tcpimagesocket.h" #include "devicefun.h" TcpImageSocket::TcpImageSocket(QObject *parent) : QThread(parent) { stopped = false; tcpSocket = 0; //定時器解析收到的數據,可以自行調整間隔 timerData = new QTimer(this); connect(timerData, SIGNAL(timeout()), this, SLOT(checkData())); timerData->start(30); } TcpImageSocket::~TcpImageSocket() { } void TcpImageSocket::run() { while(!stopped) { //這里采用線程去處理,其實完全可以用定時器搞定,畢竟tcp的write是異步的,操作系統自動調度 //為了后期的拓展性,比如需要判斷是否發送成功之類的,需要同步處理,所以改成的線程去處理 //base64編碼數據轉圖片數據也需要時間的,主要的耗時在轉碼 //取出數據發送,這里需要加鎖,避免正在插入數據 if (imageFlags.count() > 0) { QMutexLocker locker(&mutexImage); QString imageFlag = imageFlags.takeFirst(); QString imageData = imageDatas.takeFirst(); QImage image = DeviceFun::getImage(imageData); emit receiveImage(imageFlag, image); } //要稍微休息下,否則CPU會被一直占用 msleep(1); } stopped = false; } void TcpImageSocket::readData() { //接收的數據存入buffer需要加鎖 QMutexLocker locker(&mutexData); //接收到的數據存入隊列,排隊處理 QByteArray data = tcpSocket->readAll(); buffer.append(data); emit receiveData(data); } void TcpImageSocket::checkData() { if (buffer.length() == 0) { return; } //取出數據處理需要加鎖,防止此時正在插入數據 QMutexLocker locker(&mutexData); QDomDocument dom; if (!DeviceFun::getReceiveXmlData(buffer, dom, "IIMAGE:", 11, true)) { return; } //逐個取出節點判斷數據 QDomElement element = dom.documentElement(); if (element.tagName() == "ImageClient") { QString uuid = element.attribute("Uuid"); QString flag = element.attribute("Flag"); QDomNode childNode = element.firstChild(); QString name = childNode.nodeName(); QString value = element.text(); //qDebug() << uuid << name << value; if (name == "ClientHeart") { sendHeart(uuid); } else if (name == "ClientImage") { sendAck(uuid); element = childNode.toElement(); value = element.text(); QMutexLocker locker(&mutexImage); if (this->isRunning() && imageFlags.count() < 10) { imageFlags << flag; imageDatas << value; } } } } void TcpImageSocket::stop() { buffer.clear(); imageFlags.clear(); imageDatas.clear(); stopped = true; this->wait(); if (tcpSocket != 0) { tcpSocket->disconnectFromHost(); } } void TcpImageSocket::setTcpSocket(QTcpSocket *tcpSocket) { if (this->tcpSocket == 0) { this->tcpSocket = tcpSocket; connect(tcpSocket, SIGNAL(disconnected()), this, SLOT(stop())); connect(tcpSocket, SIGNAL(error(QAbstractSocket::SocketError)), this, SLOT(stop())); connect(tcpSocket, SIGNAL(readyRead()), this, SLOT(readData())); } } void TcpImageSocket::writeData(const QString &body) { QString data = DeviceFun::getSendXmlData(body, "IIMAGE:"); QByteArray buffer = data.toUtf8(); tcpSocket->write(buffer); tcpSocket->flush(); emit sendData(buffer); } void TcpImageSocket::sendHeart(const QString &uuid) { //構建xml字符串 QStringList list; list.append(QString("<ImageServer Uuid=\"%1\" NowTime=\"%2\">").arg(uuid).arg(DATETIME)); list.append("Ok"); list.append("</ImageServer>"); writeData(list.join("")); } void TcpImageSocket::sendAck(const QString &uuid) { //構建xml字符串 QStringList list; list.append(QString("<ImageServer Uuid=\"%1\" NowTime=\"%2\">").arg(uuid).arg(DATETIME)); list.append("Ack"); list.append("</ImageServer>"); writeData(list.join("")); }
感謝各位的閱讀,以上就是“Qt怎么實現視頻傳輸TCP版”的內容了,經過本文的學習后,相信大家對Qt怎么實現視頻傳輸TCP版這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是億速云,小編將為大家推送更多相關知識點的文章,歡迎關注!
免責聲明:本站發布的內容(圖片、視頻和文字)以原創、轉載和分享為主,文章觀點不代表本網站立場,如果涉及侵權請聯系站長郵箱:is@yisu.com進行舉報,并提供相關證據,一經查實,將立刻刪除涉嫌侵權內容。