Qt/C++開發監控GB28181系統/錄像回放/切換播放進度立即跳轉/支持8倍速播放/倍速和跳轉進度無縫切換

一、前言說明

在國標監控系統中,錄像回放過程中,需要切換播放進度,對比過很過國標系統,絕大部分尤其是網頁版的監控系統,在切換進度過程中都會黑屏,這個體驗就很不友好了,明明gb28181協議中就有切換進度的指令,切換完成后,會立即發送對應進度開始的音視頻流數據,只要繼續解碼就行,用抓包工具查看數據,發現居然是重新發起請求,也就是按照當前時間點的視頻錄像文件,重新發起請求,整個交互過程一般可能需要1-2s,導致中間黑屏,是解碼不好處理?因為切換進度后對應的pts突然之間變了,尤其是從一個大的時間點切換到一個小的時間點,pts居然變小了,其中不乏一些著名的國標平臺,為何不能做成一個平滑的無縫切換呢?中間還涉及到重新推拉流?

用純Qt開發這個國標監控組件,就沒有這個黑屏的問題,切換進度直接執行指令,然后清空緩存數據,收到新的數據繼續解碼播放即可,非常的流暢和穩定,切換到哪里就立即是顯示哪里,往前往后切換都能立即響應,跳轉進度無縫切換,要的就是這種效果。網頁版的國標監控系統還有一個問題就是倍速,基本上只能做到4倍速,別說八倍速了,連八倍速的選項都沒有,是擔心網頁解碼顯示性能不足?其實很多時候查閱錄像文件,基本上需要倍速播放,播放快到了需要的地方才會切換到正常速度或者滿倍速慢慢找。其實還有個問題,就是倍速傳過來的其實是完整的流數據,只是解碼這邊要求快一點解碼,比如fps25幀,如果八倍速就是每秒鐘給了25乘以8等于200幀的數據,然后要求1秒鐘解碼200幀的數據顯示,確實壓力夠大,查看了對應的CPU和GPU占用,確實高了不少,對電腦的配置要求也比較高,那是不是后面可以改成只推送關鍵幀過來?這個應該需要設備端的配合才行。

二、效果圖

在這里插入圖片描述

三、相關地址

  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 "frmvideoplayback.h"
#include "ui_frmvideoplayback.h"
#include "qthelper.h"
#include "videoutil.h"
#include "gb28181server.h"
#include "gb28181helper.h"
#include "gb28181widgetmanage.h"frmVideoPlayback::frmVideoPlayback(QWidget *parent) : QWidget(parent), ui(new Ui::frmVideoPlayback)
{ui->setupUi(this);this->initForm();this->initConfig();
}frmVideoPlayback::~frmVideoPlayback()
{delete ui;
}void frmVideoPlayback::closeAll()
{this->setId("", "");ui->listWidget->clear();ui->btnStop->click();
}void frmVideoPlayback::setServer(GB28181Server *server)
{this->server = server;ui->gb28181Widget->setServer(server);
}void frmVideoPlayback::setId(const QString &deviceId, const QString &channelId)
{this->deviceId = deviceId;this->channelId = channelId;
}void frmVideoPlayback::initForm()
{server = NULL;ui->sliderPosition->setRange(0, 0);ui->gb28181Widget->setBgText("視頻回放");ui->gb28181Widget->setProperty("onSync", AppConfig::RecordOnSync);connect(ui->btnPlay, SIGNAL(clicked(bool)), this, SLOT(playControl()));connect(ui->btnStop, SIGNAL(clicked(bool)), this, SLOT(playControl()));connect(ui->sliderPosition, SIGNAL(clicked()), this, SLOT(playControl()));connect(ui->cboxSpeed, SIGNAL(currentIndexChanged(int)), this, SLOT(playControl()));connect(ui->gb28181Widget, SIGNAL(sig_receivePlayStart(int)), this, SLOT(receivePlayStart(int)));connect(ui->gb28181Widget, SIGNAL(sig_receivePlayFinsh()), this, SLOT(receivePlayFinsh()));connect(ui->gb28181Widget, SIGNAL(pushStart(GB28181Widget *, QString)), GB28181WidgetManage::Instance(), SIGNAL(pushStart(GB28181Widget *, QString)));connect(ui->gb28181Widget, SIGNAL(pushStop(QString, QString)), GB28181WidgetManage::Instance(), SIGNAL(pushStop(QString, QString)));//進度定時器timer = new QTimer(this);connect(timer, SIGNAL(timeout()), this, SLOT(setPosition()));timer->setInterval(300);
}void frmVideoPlayback::initConfig()
{//ui->cboxSpeed->setEnabled(false);VideoUtil::loadSpeed(ui->cboxSpeed);ui->cboxSpeed->setCurrentIndex(ui->cboxSpeed->findData(AppConfig::RecordSpeed));connect(ui->cboxSpeed, SIGNAL(currentIndexChanged(int)), this, SLOT(saveConfig()));ui->dateStart->setDateTime(QDateTime::fromString(AppConfig::RecordStart, "yyyy-MM-ddThh:mm:ss"));connect(ui->dateStart, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(saveConfig()));ui->dateEnd->setDateTime(QDateTime::fromString(AppConfig::RecordEnd, "yyyy-MM-ddThh:mm:ss"));connect(ui->dateEnd, SIGNAL(dateTimeChanged(QDateTime)), this, SLOT(saveConfig()));
}void frmVideoPlayback::saveConfig()
{AppConfig::RecordSpeed = ui->cboxSpeed->itemData(ui->cboxSpeed->currentIndex()).toString();AppConfig::RecordStart = ui->dateStart->dateTime().toString("yyyy-MM-ddThh:mm:ss");AppConfig::RecordEnd = ui->dateEnd->dateTime().toString("yyyy-MM-ddThh:mm:ss");AppConfig::writeConfig();
}void frmVideoPlayback::setPosition()
{qint64 pts = ui->gb28181Widget->getPts();VideoUtil::setPosition(ui->labPosition, ui->sliderPosition, pts * 100);
}void frmVideoPlayback::receivePlayStart(int)
{//取出時間QListWidgetItem *item = ui->listWidget->currentItem();QString startTime = item->data(Qt::UserRole + 1).toString();QString endTime = item->data(Qt::UserRole + 2).toString();//計算時長qint64 duration = GB28181Helper::getDuration(startTime, endTime) * 1000;VideoUtil::setDuration(ui->labDuration, ui->sliderPosition, duration);
}void frmVideoPlayback::receivePlayFinsh()
{timer->stop();ui->btnPlay->setText("暫停");VideoUtil::resetDuration(ui->labDuration, ui->labPosition, ui->sliderPosition);//暫停期間不用繼續if (ui->btnPlay->text() == "播放") {return;}//如果還有下一個自動觸發雙擊播放/過濾下剛剛關閉觸發的播放結束int row = ui->listWidget->currentRow();qint64 offset = lastTime.msecsTo(QDateTime::currentDateTime());if (offset > 1000 && row < ui->listWidget->count() - 1) {ui->listWidget->setCurrentRow(row + 1);on_listWidget_itemDoubleClicked(ui->listWidget->currentItem());}
}void frmVideoPlayback::playControl()
{QString ssrc = ui->gb28181Widget->getSsrcVideo();if (!server || ssrc.isEmpty()) {return;}QObject *obj = sender();if (obj == ui->btnPlay) {if (ui->btnPlay->text() == "暫停") {ui->btnPlay->setText("播放");server->playControl(deviceId, channelId, PlayControl_Pause, 0, ssrc);} else {ui->btnPlay->setText("暫停");server->playControl(deviceId, channelId, PlayControl_Play, 0, ssrc);}} else if (obj == ui->btnStop) {lastTime = QDateTime::currentDateTime();ui->gb28181Widget->stop();} else if (obj == ui->sliderPosition) {timer->stop();timer->start();ui->btnPlay->setText("暫停");qint64 sec = ui->sliderPosition->value() / 1000;server->playControl(deviceId, channelId, PlayControl_Position, sec);} else if (obj == ui->cboxSpeed) {float speed = ui->cboxSpeed->itemData(ui->cboxSpeed->currentIndex()).toFloat();server->playControl(deviceId, channelId, PlayControl_Scale, speed, ssrc);}
}void frmVideoPlayback::receiveStatus(GB28181Status status)
{if (status.deviceId != deviceId || status.channelId != channelId || status.notifyType != 121) {return;}//不是當前窗體產生的不用處理if (ui->gb28181Widget->getSsrcVideo() == status.ssrc) {ui->gb28181Widget->stop();}
}void frmVideoPlayback::receiveRecord(const QList<GB28181Record> &records)
{if (!this->isVisible()) {return;}foreach (GB28181Record record, records) {QString startTime = record.startTime;QString endTime = record.endTime;QString date = startTime.mid(0, 10);QString start = startTime.mid(11, 5);QString end = endTime.mid(11, 5);QListWidgetItem *item = new QListWidgetItem;item->setData(Qt::UserRole + 1, startTime);item->setData(Qt::UserRole + 2, endTime);item->setText(QString("%0 %1 - %2").arg(date).arg(start).arg(end));ui->listWidget->addItem(item);}
}void frmVideoPlayback::on_btnQueryRecord_clicked()
{ui->listWidget->clear();QDateTime dateStart = ui->dateStart->dateTime();QDateTime dateEnd = ui->dateEnd->dateTime();if (dateStart >= dateEnd) {QtHelper::showMessageBoxError("開始時間必須小于結束時間, 請重新選擇!");return;}server->queryRecord(deviceId, channelId, dateStart, dateEnd);
}void frmVideoPlayback::on_listWidget_itemDoubleClicked(QListWidgetItem *item)
{//取出時間QString startTime = item->data(Qt::UserRole + 1).toString();QString endTime = item->data(Qt::UserRole + 2).toString();//停止播放lastTime = QDateTime::currentDateTime();ui->gb28181Widget->stop();qApp->processEvents();timer->start();//啟動播放ui->gb28181Widget->initPara(AppConfig::ScaleMode, AppConfig::VideoMode, AppConfig::VideoCore, AppConfig::DecodeType, AppConfig::Hardware, AppConfig::ReadTimeout, false, false);ui->gb28181Widget->setPara(AppConfig::RecordSpeed.toFloat(), AppConfig::RecordProfile, (TransmitMode)AppConfig::TransmitMode, AppConfig::ServerIp, AppConfig::RecordPush, GB28181Widget::PlayType_Playback);ui->gb28181Widget->setTime(startTime, endTime);ui->gb28181Widget->openVideo(deviceId, channelId);
}

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

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

相關文章

【11】大恒相機SDK C++開發 ——原圖像數據IFrameData內存中上下顛倒,怎么裁剪ROI 實時顯示在pictureBox中

文章目錄3 當內存中的 圖像數據是垂直翻轉的時候怎么截取ROI 并顯示3.1 對ROI在原圖中的位置做轉換3.2 將ROI的最后一行當做開始位置&#xff0c;從底部向上復制數據3.3 完整代碼3.4 圖像數據在內存中上下顛倒的情況3.5 調用驗證4 unsafe代碼 解釋及注意事項 看我另一篇文章5 C…

小架構step系列29:校驗注解的組合

1 概述如果遇到某些屬性需要多種校驗&#xff0c;比如需要非空、符合某正則表達式、長度不能超過某值等&#xff0c;如果這種屬性只有有限幾個&#xff0c;那么手工把對應的校驗注解都加上即可。但如果這種屬性比較多&#xff0c;那么重復加這些校驗注解&#xff0c;也是一種代…

網絡基礎19:OSPF多區域實驗

一、拓撲結構1. 網絡拓撲&#xff1a;骨干區域&#xff08;Area 0&#xff09;&#xff1a;連接核心設備&#xff08;AR1、AR2、AR3、AR4、AR5、AR6&#xff09;。非骨干區域&#xff1a;Area 1&#xff1a;AR5 ? AR9Area 2&#xff1a;AR5 ? AR10Area 3&#xff1a;AR6 ? A…

goland編寫go語言導入自定義包出現: package xxx is not in GOROOT (/xxx/xxx) 的解決方案

問題 寫了個自定義的包 calc.go&#xff0c;在路徑 $GOPATH/go_project/src/demo_51_package/com/目錄下&#xff0c;其中main.go 是main方法的入口代碼 main.go 代碼如下 package main import "demo_51_package/com" func main() {add : calc.Add(1, 2)println(add)…

HLS視頻切片音頻中斷問題分析與解決方案

HLS視頻切片音頻中斷問題分析與解決方案 問題背景 在使用FFmpeg進行HLS視頻切片并通過hls.js前端播放時&#xff0c;開發者經常遇到一個典型問題&#xff1a;第一個視頻切片播放正常且有聲音&#xff0c;但后續切片卻突然失去音頻。這種現象在直播和點播場景中均有出現&#xf…

【Linux網絡編程】網絡層協議 - IP

目錄 背景補充 協議頭格式 IP報文的分片與組裝 網段劃分 網段劃分是什么&#xff1f;為什么要進行網段劃分&#xff1f; 怎么進行網段劃分&#xff1f; 路由 路由表生成算法 背景補充 假設現在主機B要給主機C發送消息。在我們前面的學習中&#xff0c;一直都是將數據拷…

從“救火”到“先知”:潤建曲尺運維大模型如何重構網絡運維價值鏈

“7月18號&#xff0c;北京&#xff0c;晴&#xff0c;最高溫度38攝氏度。”天氣預報緩緩播報&#xff0c;商場、地鐵、辦公樓無不歌頌著威利斯開利的貢獻&#xff0c;但這份涼爽的背后&#xff0c;離不開 “電” 的無聲托舉。5G毫秒級下載、絲滑的移動支付、智能電表、智能家居…

Element表格單元格類名動態設置

在 Element UI 的 el-table 組件中&#xff0c;cell-class-name 屬性用于動態自定義表格單元格的 CSS 類名&#xff0c;通常用于根據數據條件設置樣式。1. 基本用法在 el-table 上綁定 :cell-class-name 屬性&#xff0c;值為一個函數。該函數接收一個對象參數&#xff0c;返回…

利用容器適配器實現stack和queue外加deque的介紹(STL)

文章目錄前言什么是容器適配器&#xff1f;觀察庫中的源碼那么該如何使用容器適配器呢&#xff1f;deque的簡單介紹(了解)deque的原理介紹deque的優缺為什么選擇deque作為stack和queue的底層默認容器&#xff1f;&#xff08;重點&#xff09;利用容器適配器實現我們自己的棧和…

【因子動物園巡禮】第12章:機器學習在因子投資中的應用(中文翻譯)

【因子動物園巡禮】第12章&#xff1a;機器學習在因子投資中的應用&#xff08;中文翻譯&#xff09;第12章 因子投資中的機器學習12.1 量化金融中的人工智能12.2 量化因子投資的AI化組件&#xff1a;解剖學視角12.2.1 數據源拓展與預處理12.2.2 因子研究12.2.3 因子模型12.2.4…

【Golang】用官方rate包構造簡單IP限流器

文章目錄使用 Go 實現基于 IP 地址的限流機制什么是 IP 限流&#xff1f;基于 rate.Limiter 實現 IP 限流1. 設計思路2. 代碼實現3. 限流中間件4. 在 Gin 中使用中間件代碼解釋使用 Go 實現基于 IP 地址的限流機制 在高流量的服務中&#xff0c;限流是一個至關重要的環節。它不…

力扣 Pandas 挑戰(6)---數據合并

本文圍繞力扣的Pandas簡單題集&#xff0c;解析如何用Pandas完成基礎數據處理任務&#xff0c;適合Pandas初學者學習。題目1&#xff1a;1050. 合作過至少三次的演員和導演題目描述&#xff1a;ActorDirector 表&#xff1a;---------------------- | Column Name | Type | …

隨筆之TDengine基準測試示例

文章目錄一、基本信息二、基準測試策略三、基準測試過程1. 模擬高并發寫入場景2. 模擬并發查詢場景四、基準測試結論一、基本信息 TDengine 版本&#xff1a;3.3.6.13&#xff08;目前最新版本&#xff09;服務器配置&#xff1a;16核CPU&#xff0c;32GB內存&#xff0c;高IO…

【IQA技術專題】DISTS代碼講解

本文是對DISTS圖像質量評價指標的代碼解讀&#xff0c;原文解讀請看DISTS文章講解。 本文的代碼來源于IQA-Pytorch工程。 1、原文概要 以前的一些IQA方法對于捕捉紋理上的感知一致性有所欠缺&#xff0c;魯棒性不足。基于此&#xff0c;作者開發了一個能夠在圖像結構和圖像紋…

2024年SEVC SCI2區,一致性虛擬領航者跟蹤群集算法GDRRT*-PSO+多無人機路徑規劃,深度解析+性能實測

目錄1.摘要2.算法背景3.GDRRT*-PSO與虛擬領航者跟蹤算法4.結果展示5.參考文獻6.算法輔導應用定制讀者交流1.摘要 隨著無人機技術的快速發展及其卓越的運動和機動性能&#xff0c;無人機在社會和軍事等諸多領域得到了廣泛應用。多無人機協同作業&#xff0c;能夠顯著提升任務執…

鏈特異性文庫是什么?為什么它在轉錄組測序中越來越重要?

鏈特異性文庫是什么&#xff1f;為什么它在轉錄組測序中越來越重要&#xff1f; 在現代分子生物學研究中&#xff0c;RNA測序&#xff08;RNA-seq&#xff09; 是一種廣泛應用的技術&#xff0c;用于分析基因在不同條件下的表達情況。而在RNA-seq的眾多技術細節中&#xff0c;有…

ClickHouse vs PostgreSQL:數據分析領域的王者之爭,誰更勝一籌?

文章概要 作為一名數據架構師&#xff0c;我經常被問到一個問題&#xff1a;在眾多數據庫選擇中&#xff0c;ClickHouse和PostgreSQL哪一個更適合我的項目&#xff1f;本文將深入探討這兩種數據庫系統的核心差異、性能對比、適用場景以及各自的優缺點&#xff0c;幫助您在技術選…

面向對象系統的單元測試層次

面向對象系統的單元測試層次面向對象&#xff08;Object-Oriented, OO&#xff09;編程范式引入了封裝、繼承和多態等核心概念&#xff0c;這使得傳統的、基于函數的單元測試方法不再充分。面向對象系統的單元測試必須適應其獨特的結構和行為特性&#xff0c;從單一方法擴展到類…

如何用USRP捕獲手機信號波形(上)系統及知識準備

目錄&#xff1a; 如何用USRP捕獲手機信號波形&#xff08;上&#xff09;系統及知識準備 如何用USRP捕獲手機信號波形&#xff08;中&#xff09;手機/基站通信 如何用USRP捕獲手機信號波形&#xff08;下&#xff09;協議分析 一、手機通信參數獲取 首先用Cellular-z網絡…

C語言-數組:數組(定義、初始化、元素的訪問、遍歷)內存和內存地址、數組的查找算法和排序算法;

本章概述思維導圖&#xff1a;C語言數組在C語言中&#xff0c;數組是一種固定大小的、相同類型元素的有序集合&#xff0c;通過索引&#xff08;下標&#xff09;訪問。數組數組&#xff1a;是一種容器&#xff0c;可以用來存儲同種數據類型的多個值&#xff1b;數組特點&#…