Qt/C++開發監控GB28181系統/實時監測設備在線離線/視頻預覽自動重連/重新點播取流/低延遲

一、前言說明

一個好的視頻監控系統,設備掉線后能夠自動重連,也是一個重要的功能指標,如果監控系統只是個rtsp流地址,那非常好辦,只需要重新打開流地址即可,而gb28181中就變得復雜了很多,需要多個方便配合,主要涉及到sip交互指令,rtp解包,ffmpeg解碼,三個方面缺一不可,其中sip這邊負責讓設備發流數據,而且有多個交互指令,rtp這邊也需要先綁定地址監聽端口成功后,再把端口號一起sdp應答給設備,sip和rtp都完成后再打開ffmpeg準備收數據解碼,三個方面都要配合,只要有一個方面配合的不好就會失敗,關鍵是取流還支持三種方式,udp,tcp被動,tcp主動,三種方式都要能支持自動重連。網上很多國標的工具,幾乎都沒有這種重連的功能。

在做這個重連功能中,有幾個要點需要注意,一個是當用戶主動關閉了流,是不需要加入重連檢測的,不然永遠都關不掉,這個可以加個標志位來解決,當用戶打開流的時候標志位真,用戶關閉流的時候標志位假,重連定時器判斷標志位,為真才需要去檢測。還一個是資源的釋放,重連過程中必須先把之前的rtp解包線程的資源釋放,還有ffmpeg解碼這邊的資源全部釋放,不然一直累加會導致內存泄漏。

二、效果圖

在這里插入圖片描述
在這里插入圖片描述

三、相關地址

  1. 國內站點:https://gitee.com/feiyangqingyun
  2. 國際站點:https://github.com/feiyangqingyun
  3. 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
  4. 文件地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 文件名:bin_video_gb28181。

四、功能特點

  1. 支持設備注冊、注銷、心跳、校時、注冊認證、注銷認證等。
  2. 設備上線后可以手動獲取設備狀態、設備信息、配置信息、預置位信息等。
  3. 設備上線后自動獲取設備通道信息,包括中文通道名稱。識別到通道上線離線變化,會重新獲取該設備的所有通道信息。
  4. 支持視頻點播,可以分別點播主碼流和子碼流,內置rtp解包線程,解包后發給視頻播放組件解碼播放。
  5. 每個設備每個通道支持點播多個視頻,通過ssrc區分,支持共用端口和不同端口收流。
  6. 支持對某個設備下面所有通道、某個通道、某個通道對應的某個流分別關閉。
  7. 支持錄像文件查詢和回放,回放控制支持暫停播放、繼續播放、倍速播放、切換播放進度。
  8. 支持錄像文件下載,支持倍速比如8倍速下載,可同時多線程批量下載。
  9. 回放和下載同時支持IPC和NVR,比如攝像頭自帶的SD存儲卡錄像文件回放,NVR上的硬盤錄像文件回放。
  10. 支持云臺控制,向上、向下、向左、向右、左上、右上、左下、右下方位移動,鏡頭放大縮小,光圈放大縮小,鏡頭聚焦放焦。
  11. 支持預置位信息的查詢、調用、添加、修改、刪除等操作。
  12. 自動目錄訂閱功能,通道上線下線都有對應的信號通知。
  13. 內置定時讀取通道信息機制,以保證通道信息是最新的,比如有些NVR是不斷更新的通道信息。
  14. 內置訂閱警情和位置移動功能,訂閱后各種警情事件比如運動目標檢測報警、入侵檢測報警、徘徊檢測報警等自動上報。
  15. 支持語音對講功能,可以直接在視頻窗體的懸浮條上單擊語音對講按鈕,再次單擊關閉對講,對講期間懸浮條常駐顯示。
  16. 支持設備布防撤防,布防后警情信息會主動上報。
  17. 國標服務同時支持udp和tcp方式,可選只監聽一種或者兩種都監聽,tcp方式自動處理粘包問題。
  18. 國標拉流同時支持udp、tcp被動、tcp主動三種方式,每個通道都可以自由選擇何種拉流方式。
  19. 內置拉流端口池,每次拉流從中取出一個,關閉流自動回收端口號,重復利用。
  20. 收流端口自動糾錯,自動跳過被占用的端口,不會出現端口占用導致收流失敗的情況。
  21. 支持三種取流方式自動檢測離線重連,檢測到離線后,自動重啟點播拉流整個流程。
  22. 錄像文件回放,上一個完成后自動切換到下一個繼續回放,直到所有回放完成。支持高達8倍速回放。
  23. 視頻播放自適應硬解碼,極低資源占用,實時性極好,帶懸浮條顯示視頻流信息,可以直接在懸浮條單擊按鈕保存錄像文件到本地。
  24. 支持幾千路國標消息交互并發,實時視頻流支持64路同時顯示,可以拓展更多路數。
  25. 支持阿里云等云服務器,可以分別設置內網監聽地址和外網訪問地址,一般云服務器上是監聽地址用內網,對外訪問用外網地址。
  26. 支持視頻分發,也就是推流,視頻通道打開后可以自動推流到流媒體服務器,其他需要的地方拉流即可,支持rtsp、rtmp、hls、webrtc等方式拉流。
  27. 實時預覽和錄像回放都支持推流,推流支持疊加文字和圖片水印以及各種ffmpeg支持的濾鏡效果,支持多個水印同時疊加。
  28. 同時支持gb28181-2011、gb28181-2016、gb28181-2022以及后續可能的所有協議版本。
  29. SIP解析和交互采用純Qt底層代碼實現,udp/tcp通信交互,祖傳原創代碼解析,不依賴任何第三方。
  30. 代碼量少,gb28181交互部分共幾千行代碼,注釋詳細,接口友好,使用極其簡單,提供非常詳細的使用示例。
  31. 支持海康、大華、宇視、華為、天地偉業等所有國標設備,包括一些沒有ssrc的設備。
  32. 支持所有Qt版本和編譯器以及操作系統,包括但不限于win、linux、mac、android、嵌入式linux、樹莓派香橙派、國產os等。

五、相關代碼

#include "rtphelper.h"
#include "rtpthreadreceive.h"
#include "rtpthreadsend.h"
#include "gb28181widget.h"
#include "gb28181widgetmanage.h"
#include "gb28181server.h"
#include "urlhelper.h"bool GB28181Widget::checkSsrc = true;
GB28181Widget::GB28181Widget(QWidget *parent) : VideoWidget(parent)
{server = NULL;this->speed = 1;this->profile = 1;this->mode = TransmitMode_UdpServer;//創建視頻解碼線程rtpVideo = new RtpThreadReceive;rtpVideo->setProperty("running", false);connect(rtpVideo, SIGNAL(openOk(int)), this, SLOT(openOk(int)));connect(rtpVideo, SIGNAL(receiveData(QByteArray, qint64)), this, SLOT(receiveData(QByteArray, qint64)));//創建音頻發送線程rtpAudio = new RtpThreadSend;connect(rtpAudio, SIGNAL(openOk(int)), this, SLOT(openOk(int)));//關聯視頻窗體信號槽connect(this, SIGNAL(sig_receivePlayStart(int)), this, SLOT(receivePlayStartx(int)));connect(this, SIGNAL(sig_receivePlayFinsh()), this, SLOT(receivePlayFinshx()));
}void GB28181Widget::initPara(int scaleMode, int videoMode, int videoCore, int decodeType, const QString &hardware, int readTimeout, bool call, bool download)
{GB28181WidgetManage::initPara(this, scaleMode, videoMode, videoCore, decodeType, hardware, readTimeout, call, download);
}void GB28181Widget::clearPara()
{this->ssrcVideo = "";this->ssrcAudio = "";this->deviceId = "";this->channelId = "";this->startTime = "";this->endTime = "";
}qint64 GB28181Widget::getPts() const
{return rtpVideo->getPts();
}RtpThreadReceive *GB28181Widget::getThread() const
{return this->rtpVideo;
}QString GB28181Widget::getSsrcVideo() const
{return this->ssrcVideo;
}QString GB28181Widget::getSsrcAudio() const
{return this->ssrcAudio;
}QString GB28181Widget::getDeviceId() const
{return this->deviceId;
}QString GB28181Widget::getChannelId() const
{return this->channelId;
}void GB28181Widget::setServer(GB28181Server *server)
{this->server = server;connect(server, SIGNAL(startVideo(QString, int, QString, QString, QString, int, int)), this, SLOT(startVideo(QString, int, QString, QString, QString, int, int)), Qt::UniqueConnection);connect(server, SIGNAL(startAudio(QString, int, QString, QString, QString, int, int)), this, SLOT(startAudio(QString, int, QString, QString, QString, int, int)), Qt::UniqueConnection);
}void GB28181Widget::setPara(float speed, int profile, TransmitMode mode, const QString &serverIp, const QString &pushUrl, PlayType playType)
{this->speed = speed;this->profile = profile;this->mode = mode;this->serverIp = serverIp;this->pushUrl = pushUrl;this->playType = playType;this->setSpeed(speed);
}void GB28181Widget::setTime(const QString &startTime, const QString &endTime)
{this->startTime = startTime;this->endTime = endTime;
}void GB28181Widget::openVideo(const QString &deviceId, const QString &channelId)
{this->deviceId = deviceId;this->channelId = channelId;//啟動階段先要停止if (rtpVideo->isRunning()) {rtpVideo->stop();}//從端口池中取出一個端口lastPort = RtpHelper::takePort();rtpVideo->setProperty("running", true);rtpVideo->setPara(serverIp, lastPort, serverIp, lastPort, mode);rtpVideo->start();
}bool GB28181Widget::closeVideo(const QString &deviceId, const QString &channelId, const QString &ssrc)
{if (this->deviceId != deviceId) {return false;}if (!channelId.isEmpty() && this->channelId != channelId) {return false;}if (!ssrc.isEmpty() && this->ssrcVideo != ssrc) {return false;}this->stop();return true;
}void GB28181Widget::appendAudio(const QByteArray &data)
{rtpAudio->append(data);
}void GB28181Widget::openAudio(const QString &deviceId, const QString &channelId)
{//啟動階段先要停止if (rtpAudio->isRunning()) {rtpAudio->stop();}//從端口池中取出一個端口lastPort = RtpHelper::takePort();rtpAudio->setPara(serverIp, lastPort, serverIp, lastPort, mode);rtpAudio->start();
}bool GB28181Widget::closeAudio(const QString &deviceId, const QString &channelId, const QString &ssrc)
{if (this->deviceId != deviceId) {return false;}if (!channelId.isEmpty() && this->channelId != channelId) {return false;}if (!ssrc.isEmpty() && this->ssrcAudio != ssrc) {return false;}//停止語音對講和發流線程server->bye(deviceId, channelId, ssrcAudio);rtpAudio->stop();return true;
}void GB28181Widget::closeEvent(QCloseEvent *)
{this->stop();
}void GB28181Widget::openOk(int port)
{//收流端口打開成功才能繼續點播lastPort = port;if (sender() == rtpVideo) {bool playback = (playType == PlayType_Playback);ssrcVideo = server->invite(deviceId, channelId, port, startTime, endTime, speed, profile, mode, playback);} else {server->broadcast(deviceId, channelId, port);}
}void GB28181Widget::receiveData(const QByteArray &data, qint64 ssrc)
{if (videoThread) {
#if (QT_VERSION >= QT_VERSION_CHECK(5,0,0))videoThread->appendData(data);
#elseQMetaObject::invokeMethod(videoThread, "appendData", Q_ARG(QByteArray, data));
#endif}
}void GB28181Widget::startVideo(const QString &host, int port, const QString &deviceId, const QString &channelId, const QString &ssrc, int transmitMode, int transmitPort)
{//過濾下是不是當前窗體if (this->deviceId != deviceId || this->channelId != channelId || transmitMode != mode) {return;}//有些設備并沒有按照下發的ssrc作為標識/而是自己定義的/這里就需要去掉這個判斷if (checkSsrc && this->ssrcVideo != ssrc) {return;}//qDebug() << TIMEMS << "startVideo" << mode << host << port << deviceId << channelId << ssrc << transmitMode << transmitPort;if (mode == TransmitMode_TcpClient) {rtpVideo->setPara(serverIp, lastPort, host, transmitPort, mode);rtpVideo->setOk(true);}//打開視頻接收流數據/設置是否禁用解碼/僅推流不需要解碼/極大減少資源占用if (!this->getIsRunning()) {this->open(QString("stream://%1_%2").arg(deviceId).arg(channelId));this->getVideoThread()->setDisableDecode(this->property("disableDecode").toBool());}
}void GB28181Widget::startAudio(const QString &host, int port, const QString &deviceId, const QString &channelId, const QString &ssrc, int transmitMode, int transmitPort)
{//過濾下是不是當前窗體if (this->deviceId != deviceId || this->channelId != channelId) {return;}ssrcAudio = ssrc;rtpAudio->setPara(serverIp, lastPort, host, transmitPort, transmitMode, ssrc);rtpAudio->setOk(true);
}void GB28181Widget::receivePlayStartx(int)
{//設置播放速度this->setSpeed(speed);//執行保存文件或推流if (playType == PlayType_Download) {QString date = startTime.left(10);QString start = startTime.mid(11).replace(":", "-");QString end = endTime.mid(11).replace(":", "-");QString name = QString("%1_%2_%3_%4_%5.mp4").arg(deviceId).arg(channelId).arg(date).arg(start).arg(end);this->getVideoThread()->recordStart(qApp->applicationDirPath() + "/video/" + name);} else if (!pushUrl.isEmpty()) {QString url = this->getVideoPara().mediaUrl.mid(9);url = pushUrl + "/" + url + "_" + ssrcVideo;this->getVideoThread()->recordStart(url);//要轉成外網地址才能正常訪問QString ip = UrlHelper::getUrlIP(url);GB28181ServerPara para = server->getServerPara();url.replace(ip, para.serverHost);emit pushStart(this, url);}
}void GB28181Widget::receivePlayFinshx()
{//判斷唯一標識/一個通道可能打開了多個流/只需要關閉當前窗體對應的流if (!ssrcVideo.isEmpty()) {rtpVideo->setProperty("running", false);server->bye(deviceId, channelId, ssrcVideo);rtpVideo->stop();}if (!ssrcAudio.isEmpty()) {server->bye(deviceId, channelId, ssrcAudio);rtpAudio->stop();}if (!deviceId.isEmpty() && !channelId.isEmpty()) {emit pushStop(deviceId, channelId);}this->clearPara();
}

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/bicheng/92846.shtml
繁體地址,請注明出處:http://hk.pswp.cn/bicheng/92846.shtml
英文地址,請注明出處:http://en.pswp.cn/bicheng/92846.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

此芯p1開發板使用OpenHarmony時llama.cpp不同優化速度對比(GPU vs CPU)

硬件環境 Cix P1 SoC 瑞莎星睿 O6 開發板 rx580顯卡 產品介紹&#xff1a; https://docs.radxa.com/orion/o6/getting-started/introduction OpenHarmony 5.0.0 使用vulkan后端的llama.cpp &#xff08;GPU&#xff09; # ./llama-bench -m /data/qwen1_5-0_5b-chat-q2_k.…

Android 四大布局:使用方式與性能優化原理

一、四大布局基本用法與特點1. LinearLayout&#xff08;線性布局&#xff09;使用方式&#xff1a;<LinearLayoutandroid:orientation"vertical" <!-- 排列方向&#xff1a;vertical/horizontal -->android:layout_width"match_parent"android:…

Redis的BigKey問題

Redis的BigKey問題 什么是大Key問題&#xff1f; 大key問題其實可以說是大value問題&#xff0c;就是某個key對應的value所占據的存儲空間太大了&#xff0c;所以導致我們在操作這個key的時候花費的時間過長&#xff08;序列化\反序列化&#xff09;&#xff0c;從而降低了redi…

TDengine IDMP 產品基本概念

基本概念 元素 (Element) IDMP 通過樹狀層次結構來組織數據&#xff0c;樹狀結構里的每個節點被稱之為元素 (Element)。元素是一個物理的或邏輯的實體。它可以是具體的物理設備&#xff08;比如一臺汽車&#xff09;&#xff0c;物理設備的一個子系統&#xff08;比如一臺汽車的…

專題二_滑動窗口_將x減到0的最小操作數

一&#xff1a;題目解釋&#xff1a;每次只能移除數組的邊界&#xff0c;移除的邊界的總和為x&#xff0c;要求返回你移除邊界的最小操作數&#xff01;也就是說你最少花幾次移除邊界&#xff0c;就能夠讓這些移除的邊界的和為x&#xff0c;則返回這個次數&#xff01;所以這個…

CentOS 7 下通過 Anaconda3 運行llm大模型、deepseek大模型的完整指南

CentOS 7 下通過 Anaconda3 運行llm大模型、deepseek大模型的完整指南A1 CentOS 7 下通過 Anaconda3 運行大模型的完整指南一、環境準備二、創建專用環境三、模型部署與運行四、優化配置常見問題解決B1 CentOS 7 下通過 Anaconda3 使用 CPU 運行 DeepSeek 大模型的完整方案一、…

Flutter應用在Windows 8上正常運行

要讓Flutter應用在Windows 8上正常運行,需滿足以下前提條件,涵蓋系統環境、依賴配置、編譯設置等關鍵環節: 一、系統環境基礎要求 Windows 8版本 必須是 Windows 8.1(核心支持),不支持早期Windows 8(需升級到8.1,微軟已停止對原版Windows 8的支持)。 確認系統版本:右…

Redis實現消息隊列三種方式

參考 Redis隊列詳解&#xff08;springboot實戰&#xff09;_redis 隊列-CSDN博客 前言 MQ消息隊列有很多種&#xff0c;比如RabbitMQ,RocketMQ,Kafka等&#xff0c;但是也可以基于redis來實現&#xff0c;可以降低系統的維護成本和實現復雜度&#xff0c;本篇介紹redis中實現…

【C++動態版本號生成方案:實現類似C# 1.0.* 的自動構建號】

C動態版本號生成方案&#xff1a;實現類似C# 1.0.* 的自動構建號 在C#中&#xff0c;1.0.*版本號格式會在編譯時自動生成構建號和修訂號。本文將介紹如何在C項目中實現類似功能&#xff0c;通過MSBuild自動化生成基于編譯時間的版本號。 實現原理 版本號構成&#xff1a;主版本…

【算法題】:斐波那契數列

用 JavaScript 實現一個 fibonacci 函數&#xff0c;滿足&#xff1a; 輸入 n&#xff08;從0開始計數&#xff09;輸出第 n 個斐波那契數&#xff08;斐波那契數列從 1 開始&#xff1a;1,1,2,3,5,8,13,21…&#xff09; 示例&#xff1a; fibonacci(0) > 1fibonacci(4) &g…

【YOLOv13[基礎]】熱力圖可視化實踐 | 腳本升級 | 優化可視化效果 | 論文必備 | GradCAMPlusPlus, GradCAM, XGradCAM, EigenCAM等

本文將進行添加YOLOv13版本的升級版熱力圖可視化功能的實踐,支持圖像熱力圖可視化、優化可視化效果、 可以選擇使用GradCAMPlusPlus, GradCAM, XGradCAM, EigenCAM, HiResCAM, LayerCAM, RandomCAM, EigenGradCAM。一個參數即可設置是否顯示檢測框等。 原圖 結果圖

ElasticSearch相關術語介紹

1.RESTful風格程序REST(英文全稱為:"Representational State Transfer")指的是一組架構約束條件和原則。它是一種軟件架構風格&#xff08;約束條件和原則的集合&#xff0c;但并不是標準&#xff09;。 REST通過資源的角度觀察網絡&#xff0c;以URI對網絡資源進行…

《從零構建大語言模型》學習筆記4,注意力機制1

《從零構建大語言模型》學習筆記4&#xff0c;自注意力機制1 文章目錄《從零構建大語言模型》學習筆記4&#xff0c;自注意力機制1前言一、實現一個簡單的無訓練權重的自注意力機制二、實現具有可訓練權重的自注意力機制1. 分步計算注意力權重2.實現自注意力Python類三、將單頭…

昇思+昇騰開發板+DeepSeek模型推理和性能優化

昇思昇騰開發板DeepSeek模型推理和性能優化 模型推理 流程&#xff1a; 權重加載 -> 啟動推理 -> 效果比較與調優 -> 性能測試 -> 性能優化 權重加載 如微調章節介紹&#xff0c;最終的模型包含兩部分&#xff1a;base model 和 LoRA adapter&#xff0c;其中base …

未給任務“Fody.WeavingTask”的必需參數“IntermediateDir”賦值。 WpfTreeView

c#專欄記錄&#xff1a; 報錯 未給任務“Fody.WeavingTask”的必需參數“IntermediateDir”賦值。 WpfTreeView 生成 解決辦法 清理和重新生成項目 完成上述配置后&#xff0c;嘗試執行以下步驟&#xff1a; 清理項目&#xff1a;刪除 bin 和 obj 文件夾。 重新生成項目&…

[Linux]學習筆記系列 -- [arm][lib]

文章目錄arch/arm/lib/delay.cregister_current_timer_delay 注冊當前定時器延遲read_current_timer 讀取當前定時器drivers/clocksource/timer-stm32.cstm32_clocksource_init STM32 平臺上初始化時鐘源https://github.com/wdfk-prog/linux-study arch/arm/lib/delay.c regis…

harbor倉庫搭建(配置https)

目錄 1. 環境準備 2. 配置https的原因 3. 生成ca證書 4. 搭建harbor倉庫 5. 訪問harbor 6. 修改加密算法 1. 環境準備 需要提前安裝docker和docker-compose&#xff0c;harbor倉庫版本越新&#xff0c;對應的docker和docker-compose版本越新。 主機IP192.168.48.19dock…

C++多線程服務器

C多線程服務器 因為自己同時在看多本書&#xff0c;之前看過《TCP/IP 網絡編程》一書&#xff0c;其中有一個自己編寫一個多線程服務器的例子&#xff0c;于是就把代碼直接抄了一變。 在學習網絡編程前需要先了解網絡的7層模型。 具體代碼如下&#xff1a; 服務器端&#xff1a…

【Pandas】常用數據處理技巧

一. 數據讀取 1.pd.to_csv & pd.read_csv 細節&#xff1a; 1.pd.read_csv 需要 ignore_index True or ,index_col0 否則會有列Unnamed0 2.pickle具有更快的讀取速度&#xff0c;與更小的體積。 讀取前N行&#xff08;若不需獲取所有數據&#xff09; pd.read_csv(…

Docker Compose 部署高可用 MongoDB 副本集集群(含 Keepalived + HAProxy 負載均衡)

Docker Compose 部署高可用 MongoDB 副本集集群&#xff08;含 Keepalived HAProxy 負載均衡&#xff09;背景與目標&#x1f4cb; 環境規劃**服務器信息****軟件版本**部署步驟1. 創建目錄結構2、生成 keyFile&#xff08;三臺機器內容必須一致&#xff09;3. 準備 Keepalive…