成人午夜视频全免费观看高清-秋霞福利视频一区二区三区-国产精品久久久久电影小说-亚洲不卡区三一区三区一区

Qt怎么實現(xiàn)視頻傳輸TCP版

這篇文章主要講解了“Qt怎么實現(xiàn)視頻傳輸TCP版”,文中的講解內(nèi)容簡單清晰,易于學習與理解,下面請大家跟著小編的思路慢慢深入,一起來研究和學習“Qt怎么實現(xiàn)視頻傳輸TCP版”吧!

漢源網(wǎng)站制作公司哪家好,找創(chuàng)新互聯(lián)建站!從網(wǎng)頁設計、網(wǎng)站建設、微信開發(fā)、APP開發(fā)、成都響應式網(wǎng)站建設公司等網(wǎng)站項目制作,到程序開發(fā),運營維護。創(chuàng)新互聯(lián)建站于2013年創(chuàng)立到現(xiàn)在10年的時間,我們擁有了豐富的建站經(jīng)驗和運維經(jīng)驗,來保證我們的工作的順利進行。專注于網(wǎng)站建設就選創(chuàng)新互聯(lián)建站。

一、前言

做音視頻開發(fā),會遇到將音視頻重新轉(zhuǎn)發(fā)出去的需求,當然終極大法是推流轉(zhuǎn)發(fā),還有一些簡單的場景是直接自定義協(xié)議將視頻傳出去就行,局域網(wǎng)的話速度還是不錯的。很多年前就做過類似的項目,無非就是將本地的圖片上傳到服務器,就這么簡單,其實用http的post上傳比較簡單容易,無需自定義協(xié)議,直接設置好二進制數(shù)據(jù)即可,而采用TCP或者UDP通信的話,必須自定義協(xié)議,因為不知道什么時候數(shù)據(jù)接收完了是完整的圖片數(shù)據(jù),可能同時在發(fā)送很多圖片數(shù)據(jù),而且還不能區(qū)分收到的圖片是哪個客戶端發(fā)來的,TCP長連接的話,還需要有心跳來檢測連接,所以必須自定義一套協(xié)議來支撐通信,這套協(xié)議采用的是上海監(jiān)管平臺的通信協(xié)議格式,拓展性比較強,其中頭部信息包括了類型+當前完整包的數(shù)據(jù)長度,這個類型就是通信協(xié)議的標識,這樣下次來一個其他類型的比如樓宇對講可以叫IDOOR,服務端根據(jù)這個標識就能知道采用何種解析算法來處理后面的數(shù)據(jù),而當前完整包的數(shù)據(jù)長度可以用來處理收到的數(shù)據(jù),只有該長度的數(shù)據(jù)才表示接收完成一個完整的圖片數(shù)據(jù),再去解碼處理。當傳輸?shù)膱D片到了一定速度的時候比如一秒鐘傳輸20張圖片,其實就相當于傳輸視頻了,一般人的肉眼看到一秒鐘20張圖片基本上認識就是視頻了。

TCP理論上是穩(wěn)定的連接,不會丟包,也不會隨便一個包插入到一個包的中間,肯定能保證一個數(shù)據(jù)包的完整性,TCP連接也分兩種,一種是長連接,一旦連接了就一直通信,主要用在頻繁通信的場景中比如實時上傳,還有一種叫短連接,客戶端發(fā)完數(shù)據(jù)或者服務端接收完數(shù)據(jù)就立即斷開連接,主要用在不頻繁的通信場景中比如報警上傳,畢竟報警的情況在一天中很少發(fā)生,采用短連接為佳,可以省去很多系統(tǒng)的開銷,Qt對TCP的通信也是封裝的很好用,在一些小并發(fā)的就幾個幾十個連接的項目中,效率還是可以的,據(jù)說Qt5的QNetwork組件底層重新改寫了,效率比Qt4更高一些,本人也沒用去詳細的查看對應的源碼,只是聽說。

通信協(xié)議:

  1. 采用TCP長連接和UDP協(xié)議可選,默認通信端口6000。

  2. 采用自定義的xml通信協(xié)議。

  3. 所有傳輸加20個字節(jié)頭部:IIMAGE:0000000000000,IIMAGE:為固定頭部,后面接13個字節(jié)的 內(nèi)容的長度(含20個頭部長度) 字符串。

  4. 下面協(xié)議部分省略了頭部字節(jié)。

  5. 服務端返回的數(shù)據(jù)中的uuid是對應接收到的消息的uuid。

  6. 服務端每次返回的時候都帶了當前時間,可用于客戶端校時。

客戶端發(fā)送心跳
<?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>

客戶端發(fā)送圖片
<?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>

二、功能特點

  1. 多線程收發(fā)圖片數(shù)據(jù)和解析圖片數(shù)據(jù),不卡主界面。

  2. 同時支持TCP和UDP兩種模式,封裝了TCP模式以及UDP模式的客戶端類和服務端類。

  3. 圖片傳輸客戶端同時支持發(fā)送到多個服務端,可以作為一個教師機同屏發(fā)送到多個學生機的應用場景。

  4. 同時支持多個客戶端同時往服務端發(fā)送圖片,服務端每個連接都會自動開辟線程收發(fā)和解析圖片數(shù)據(jù)。

  5. 自定義label控件信號槽機制繪制圖片,不卡主界面。

  6. 自帶心跳機制判斷離線,自動重連服務器,可設置超時時間。

  7. 每個消息都有唯一的消息標識uuid,服務端收到以后會返回對應的uuid消息表示收到,客戶端可以根據(jù)此返回消息判斷服務端解析成功,不用再發(fā),這樣可以確保發(fā)出去的數(shù)據(jù)服務器接收到了并解析成功。

  8. 每個消息都有唯一的圖片標識flag,相當于ID號,根據(jù)此標識判斷需要解析顯示到哪個界面。

  9. 圖片以base64的字符串格式發(fā)送,接收端接收到base64字符串的圖片數(shù)據(jù)解碼后重新生成圖片。

  10. 所有數(shù)據(jù)的收發(fā)都有信號發(fā)出去,方便輸出查看。

  11. 都提供單例類,方便只有一個的時候直接使用無需new。

  12. 采用自定義的xml協(xié)議,可以自由拓展其他屬性字段比如帶上圖片內(nèi)容等。

三、效果圖

Qt怎么實現(xiàn)視頻傳輸TCP版

四、核心代碼

#include "tcpimagesocket.h"
#include "devicefun.h"

TcpImageSocket::TcpImageSocket(QObject *parent) : QThread(parent)
{
    stopped = false;
    tcpSocket = 0;

    //定時器解析收到的數(shù)據(jù),可以自行調(diào)整間隔
    timerData = new QTimer(this);
    connect(timerData, SIGNAL(timeout()), this, SLOT(checkData()));
    timerData->start(30);
}

TcpImageSocket::~TcpImageSocket()
{
}

void TcpImageSocket::run()
{
    while(!stopped) {
        //這里采用線程去處理,其實完全可以用定時器搞定,畢竟tcp的write是異步的,操作系統(tǒng)自動調(diào)度
        //為了后期的拓展性,比如需要判斷是否發(fā)送成功之類的,需要同步處理,所以改成的線程去處理
        //base64編碼數(shù)據(jù)轉(zhuǎn)圖片數(shù)據(jù)也需要時間的,主要的耗時在轉(zhuǎn)碼
        //取出數(shù)據(jù)發(fā)送,這里需要加鎖,避免正在插入數(shù)據(jù)
        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()
{
    //接收的數(shù)據(jù)存入buffer需要加鎖
    QMutexLocker locker(&mutexData);
    //接收到的數(shù)據(jù)存入隊列,排隊處理
    QByteArray data = tcpSocket->readAll();
    buffer.append(data);
    emit receiveData(data);
}

void TcpImageSocket::checkData()
{
    if (buffer.length() == 0) {
        return;
    }

    //取出數(shù)據(jù)處理需要加鎖,防止此時正在插入數(shù)據(jù)
    QMutexLocker locker(&mutexData);
    QDomDocument dom;
    if (!DeviceFun::getReceiveXmlData(buffer, dom, "IIMAGE:", 11, true)) {
        return;
    }

    //逐個取出節(jié)點判斷數(shù)據(jù)
    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怎么實現(xiàn)視頻傳輸TCP版”的內(nèi)容了,經(jīng)過本文的學習后,相信大家對Qt怎么實現(xiàn)視頻傳輸TCP版這一問題有了更深刻的體會,具體使用情況還需要大家實踐驗證。這里是創(chuàng)新互聯(lián),小編將為大家推送更多相關知識點的文章,歡迎關注!

本文標題:Qt怎么實現(xiàn)視頻傳輸TCP版
網(wǎng)站鏈接:http://jinyejixie.com/article40/pdcsho.html

成都網(wǎng)站建設公司_創(chuàng)新互聯(lián),為您提供網(wǎng)站營銷、服務器托管、企業(yè)網(wǎng)站制作、網(wǎng)站排名品牌網(wǎng)站制作、網(wǎng)站維護

廣告

聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)

成都定制網(wǎng)站建設
汤原县| 佛坪县| 来宾市| 璧山县| 岚皋县| 莎车县| 大城县| 文水县| 博客| 会同县| 岳阳市| 莱芜市| 浠水县| 武强县| 错那县| 衡山县| 淳安县| 冷水江市| 积石山| 淮北市| 新乡县| 巴塘县| 巴里| 兴化市| 开江县| 墨脱县| 固原市| 昌都县| 宁强县| 林州市| 依安县| 永和县| 咸阳市| 电白县| 西乌珠穆沁旗| 衡东县| 榆树市| 阿克苏市| 岑巩县| 阜南县| 淮北市|