一、前言

把通用的视频控件搞定以后,后期增加新的内核方便多了,不需要在好多个文件复制粘贴之类的,接下来就是需要一个统一的类来管理视频监控系统中的16个通道或者32个通道,甚至64个通道也有可能,当然,通用通道管理也兼容各种监控内核,以前通道管理类,是每个内核写一个,也是很繁琐,大量的重复性代码,所以将通用视频监控控件整理好以后,顺其自然的要改造这个通用通道管理的类了。

通用通道管理的需求来源自实际的开发过程需要,比如断线重连机制,尽管每个视频监控控件自带了断线重连功能,很容易会出现极端的情况,比如网络断了以后,设备重新上线,会全部瞬间重新上线(如果设置的超时时间一致的话),这就给CPU造成很大压力,瞬间暴增,所以需要一个类专门管理所有的摄像机设备,由他来负责排队断线重连,加载打开设备,统一的截图机制,统一的视频存储机制。

通道管理基本功能:

  1. 设置地址集合(可以是配置文件读取也可以是数据库读取)、名称集合、控件集合。
  2. 所有通道或者指定通道的打开和关闭。
  3. 指定通道的抓拍截图。
  4. 设置视频通道数、超时时间。
  5. 设置打开视频的间隔、重连视频的间隔。
  6. 指定视频存储间隔和存储文件夹。

二、功能特点

  1. 支持多画面切换,全屏切换等,包括1+4+6+8+9+13+16+25+36+64画面切换。
  2. 支持alt+enter全屏,esc退出全屏。
  3. 自定义信息框+错误框+询问框+右下角提示框(包含多种格式)。
  4. 17套皮肤样式随意更换,所有样式全部统一,包括菜单等。
  5. 云台仪表盘鼠标移上去高亮,八个方位精准识别。
  6. 底部画面工具栏(画面分割切换+截图声音等设置)移上去高亮。
  7. 可在配置文件更改左上角logo+中文软件名称+英文软件名称。
  8. 封装了百度地图,视图切换,运动轨迹,设备点位,鼠标按下获取经纬度等。
  9. 支持图片地图,设备按钮可以在图片地图上自由拖动自动保存位置信息。
  10. 在百度地图和图片地图上,双击视频可以预览摄像头实时视频。
  11. 堆栈窗体,每个窗体都是个单独的qwidget,方便编写自己的代码。
  12. 顶部鼠标右键菜单,可动态控制时间CPU+左上角面板+左下角面板+右上角面板+右下角面板的显示和隐藏,支持恢复默认布局。
  13. 工具栏可以放置多个小图标和关闭图标。
  14. 左侧右侧可拖动拉伸,并自动记忆宽高位置,重启后恢复。
  15. 双击摄像机节点自动播放视频,双击节点自动依次添加视频,会自动跳到下一个,双击父节点自动添加该节点下的所有视频。
  16. 摄像机节点拖曳到对应窗体播放视频,同时支持拖曳本地文件直接播放。
  17. 视频画面窗体支持拖曳交换,瞬间响应。
  18. 双击节点+拖曳节点+拖曳窗体交换位置,均自动更新url.txt。
  19. 支持从url.txt中加载通道视频播放,自动记忆最后通道对应的视频,软件启动后自动打开播放。
  20. 右下角音量条控件,失去焦点自动隐藏,音量条带静音图标。
  21. 集成百度在线地图和离线地图,可以添加设备对应位置,自动生成地图,支持缩放和添加覆盖物等。
  22. 视频拖动到通道窗体外自动删除视频。
  23. 鼠标右键可删除当前+所有视频,截图当前+所有视频。
  24. 录像机管理、摄像机管理,可添加删除修改导入导出打印信息,立即应用新的设备信息生成树状列表,不需重启。
  25. 在pro文件中可以自由开启是否加载地图。
  26. 视频播放可选2种内核自由切换,vlc+ffmpeg,均可在pro中设置。
  27. 可设置1+4+9+16画面轮询,可设置轮询间隔以及轮询码流类型等,直接在主界面底部工具栏右侧单击启动轮询按钮即可,再次单击停止轮询。
  28. 默认超过10秒钟未操作自动隐藏鼠标指针。
  29. 支持onvif搜素设备,支持任意onvif摄像机,包括但不限于海康大华宇视天地伟业华为等。
  30. 支持onvif云台控制,可上下左右移动云台摄像机,包括复位和焦距调整等。
  31. 同时支持sqlite、mysql、postsql等数据库。
  32. 可保存视频,可选定时存储或者单文件存储,可选存储间隔时间。
  33. 可设置视频流通信方式tcp+udp,可设置视频解码是速度优先、质量优先、均衡等。
  34. 可设置硬解码类型,支持qsv、dxva2、d3d11va等。
  35. 默认采用opengl绘制视频,超低的CPU资源占用,支持yuyv和nv12两种格式绘制,很牛逼。
  36. 高度可定制化,用户可以很方便的在此基础上衍生自己的功能,支持linux和mac系统。

三、效果图

四、相关站点

  1. 国内站点:https://gitee.com/feiyangqingyun/QWidgetDemo
  2. 国际站点:https://github.com/feiyangqingyun/QWidgetDemo
  3. 个人主页:https://blog.csdn.net/feiyangqingyun
  4. 知乎主页:https://www.zhihu.com/people/feiyangqingyun/
  5. 体验地址:https://blog.csdn.net/feiyangqingyun/article/details/97565652

五、核心代码

#include "commonvideomanage.h"

#ifdef videovlc
#include "vlchelper.h"
#elif videoffmpeg
#include "ffmpeghelper.h"
#elif haikang
#include "haikanghelper.h"
#endif #define TIMEMS qPrintable(QTime::currentTime().toString("HH:mm:ss zzz"))
#define TIME qPrintable(QTime::currentTime().toString("HH:mm:ss"))
#define QDATE qPrintable(QDate::currentDate().toString("yyyy-MM-dd"))
#define QTIME qPrintable(QTime::currentTime().toString("HH-mm-ss"))
#define DATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd HH:mm:ss"))
#define STRDATETIME qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss"))
#define STRDATETIMEMS qPrintable(QDateTime::currentDateTime().toString("yyyy-MM-dd-HH-mm-ss-zzz")) QScopedPointer<CommonVideoManage> CommonVideoManage::self;
CommonVideoManage *CommonVideoManage::Instance()
{
if (self.isNull()) {
static QMutex mutex;
QMutexLocker locker(&mutex);
if (self.isNull()) {
self.reset(new CommonVideoManage);
}
} return self.data();
} CommonVideoManage::CommonVideoManage(QObject *parent) : QObject(parent)
{
timeout = 10;
openInterval = 1000;
checkInterval = 5;
videoCount = 16; saveVideo = false;
saveVideoInterval = 0;
savePath = qApp->applicationDirPath(); //打开视频定时器
timerOpen = new QTimer(this);
connect(timerOpen, SIGNAL(timeout()), this, SLOT(openVideo()));
timerOpen->setInterval(openInterval); //重连视频定时器
timerCheck = new QTimer(this);
connect(timerCheck, SIGNAL(timeout()), this, SLOT(checkVideo()));
timerCheck->setInterval(checkInterval * 1000); //新建目录
newDir("snap");
} CommonVideoManage::~CommonVideoManage()
{
this->stop();
} QString CommonVideoManage::getVersion2()
{
#if (defined videovlc) || (defined videoffmpeg) || (defined haikang)
return getVersion();
#else
return "1.0";
#endif
} void CommonVideoManage::newDir(const QString &dirName)
{
//如果路径中包含斜杠字符则说明是绝对路径
//linux系统路径字符带有 / windows系统 路径字符带有 :/
QString strDir = dirName;
if (!strDir.startsWith("/") && !strDir.contains(":/")) {
strDir = QString("%1/%2").arg(qApp->applicationDirPath()).arg(strDir);
} QDir dir(strDir);
if (!dir.exists()) {
dir.mkpath(strDir);
}
} void CommonVideoManage::openVideo()
{
if (index < videoCount) {
//取出一个进行打开,跳过为空的立即下一个
QString url = videoUrls.at(index);
if (!url.isEmpty()) {
this->open(index);
index++;
} else {
index++;
this->openVideo();
}
} else {
//全部取完则关闭定时器
timerOpen->stop();
}
} void CommonVideoManage::checkVideo()
{
//如果打开定时器还在工作则不用继续
if (timerOpen->isActive()) {
return;
} QDateTime now = QDateTime::currentDateTime();
for (int i = 0; i < videoCount; i++) {
//只有url不为空的才需要处理重连
if (videoUrls.at(i).isEmpty()) {
continue;
} //如果10秒内已经处理过重连则跳过当前这个,防止多个掉线一直处理第一个掉线的
if (lastTimes.at(i).secsTo(now) < 10) {
continue;
} //计算超时时间
QDateTime lastTime = videoWidgets.at(i)->getLastTime();
int sec = lastTime.secsTo(now);
if (sec >= timeout) {
//重连该设备
videoWidgets.at(i)->restart();
//每次重连记住最后重连时间
lastTimes[i] = now;
//break;
}
}
} void CommonVideoManage::snapImage(const QImage &image)
{
CommonVideoWidget *w = (CommonVideoWidget *)sender();
QString fileName = w->property("fileName").toString();
if (!image.isNull()) {
image.save(fileName, "jpg");
}
} void CommonVideoManage::setTimeout(int timeout)
{
if (timeout >= 5 && timeout < 60) {
this->timeout = timeout;
}
} void CommonVideoManage::setOpenInterval(int openInterval)
{
if (openInterval >= 0 && openInterval <= 2000) {
this->openInterval = openInterval;
timerOpen->setInterval(openInterval);
}
} void CommonVideoManage::setCheckInterval(int checkInterval)
{
if (checkInterval >= 5 && checkInterval <= 60) {
this->checkInterval = checkInterval;
timerCheck->setInterval(checkInterval * 1000);
}
} void CommonVideoManage::setVideoCount(int videoCount)
{
this->videoCount = videoCount;
} void CommonVideoManage::setSaveVideo(bool saveVideo)
{
this->saveVideo = saveVideo;
} void CommonVideoManage::setSaveVideoInterval(int saveVideoInterval)
{
this->saveVideoInterval = saveVideoInterval;
} void CommonVideoManage::setSavePath(const QString &savePath)
{
this->savePath = savePath;
} void CommonVideoManage::setUrls(const QList<QString> &videoUrls)
{
this->videoUrls = videoUrls;
} void CommonVideoManage::setNames(const QList<QString> &videoNames)
{
this->videoNames = videoNames;
} void CommonVideoManage::setWidgets(QList<CommonVideoWidget *> videoWidgets)
{
this->videoWidgets = videoWidgets;
} void CommonVideoManage::start()
{
if (videoWidgets.count() != videoCount) {
return;
} lastTimes.clear();
for (int i = 0; i < videoCount; i++) {
lastTimes.append(QDateTime::currentDateTime());
QString url = videoUrls.at(i);
if (!url.isEmpty()) {
CommonVideoWidget *w = videoWidgets.at(i);
#ifdef videoffmpeg
disconnect(w, SIGNAL(snapImage(QImage)), this, SLOT(snapImage(QImage)));
connect(w, SIGNAL(snapImage(QImage)), this, SLOT(snapImage(QImage)));
#endif //设置文件url地址
w->setUrl(url); //如果是USB摄像头则单独设置宽高
if (w->getIsUsbCamera()) {
w->setVideoWidth(640);
w->setVideoHeight(480);
} //设置OSD信息,可见+字体大小+文字+颜色+格式+位置
if (i < videoNames.count()) {
w->setOSD1Visible(true);
w->setOSD1FontSize(18);
w->setOSD1Text(videoNames.at(i));
w->setOSD1Color(Qt::yellow);
w->setOSD1Format(CommonVideoWidget::OSDFormat_Text);
w->setOSD1Position(CommonVideoWidget::OSDPosition_Right_Top); //还可以设置第二路OSD
#if 0
w->setOSD2Visible(true);
w->setOSD2FontSize(18);
w->setOSD2Color(Qt::yellow);
w->setOSD2Format(CommonVideoWidget::OSDFormat_DateTime);
w->setOSD2Position(CommonVideoWidget::OSDPosition_Left_Bottom);
#endif
} //设置是否存储文件
w->setSaveFile(saveVideo);
w->setSavePath(savePath);
w->setSaveInterval(saveVideoInterval);
if (saveVideo && saveVideoInterval == 0) {
QString path = QString("%1/%2").arg(savePath).arg(QDATE);
newDir(path);
QString fileName = QString("%1/Ch%2_%3.mp4").arg(path).arg(i + 1).arg(STRDATETIME);
w->setFileName(fileName);
} //打开间隔 = 0 毫秒则立即打开
if (openInterval == 0) {
this->open(i);
}
}
} //启动定时器挨个排队打开
if (openInterval > 0) {
index = 0;
timerOpen->start();
} //启动定时器排队处理重连
QTimer::singleShot(5000, timerCheck, SLOT(start()));
} void CommonVideoManage::stop()
{
if (videoWidgets.count() != videoCount) {
return;
} if (timerOpen->isActive()) {
timerOpen->stop();
} if (timerCheck->isActive()) {
timerCheck->stop();
} for (int i = 0; i < videoCount; i++) {
this->close(i);
}
} void CommonVideoManage::open(int index)
{
if (!videoUrls.at(index).isEmpty()) {
videoWidgets.at(index)->open();
}
} void CommonVideoManage::close(int index)
{
if (!videoUrls.at(index).isEmpty()) {
videoWidgets.at(index)->close();
}
} void CommonVideoManage::snap(int index, const QString &fileName)
{
if (videoUrls.at(index).isEmpty()) {
return;
} #ifdef videoffmpeg
CommonVideoWidget *w = videoWidgets.at(index);
w->setProperty("fileName", fileName);
QImage img = w->getImage();
if (!img.isNull()) {
img.save(fileName, "jpg");
}
#else
videoWidgets.at(index)->snap(fileName);
#endif
}

Qt音视频开发48-通用通道管理的更多相关文章

  1. 【秒懂音视频开发】02_Windows开发环境搭建

    音视频开发库的选择 每个主流平台基本都有自己的音视频开发库(API),用以处理音视频数据,比如: iOS:AVFoundation.AudioUnit等 Android:MediaPlayer.Med ...

  2. WebRTC 音视频开发

    WebRTC 音视频开发 webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译 ...

  3. 转:Android IOS WebRTC 音视频开发总结 (系列文章集合)

    随笔分类 - webrtc   Android IOS WebRTC 音视频开发总结(七八)-- 为什么WebRTC端到端监控很关键? 摘要: 本文主要介绍WebRTC端到端监控(我们翻译和整理的,译 ...

  4. Android开发 音视频开发需要了解的专业术语知识

    前言 在摸索一段时间的音视频开发后,越来越发现这个坑的深度真是特别的深. 除了了解Android自带的音视频处理API以外,还得了解一些视频与音频方面的知识.这篇博客就是主要讲解这方面的专业术语.内容 ...

  5. 音视频开发-FFmpeg

    音视频开发是个非常复杂的,庞大的开发话题,初涉其中,先看一下结合 OEIP(开源项目) 新增例子. 可以打开flv,mp4类型文件,以及rtmp协议音视频数据,声音的播放使用SDL. 把采集的麦/声卡 ...

  6. Python音视频开发:消除抖音短视频Logo的图形化工具实现

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 在<Python音视频开发:消除抖音短视频Logo和去电视台标的实现详解>节介绍了怎么通过Python+Moviepy+OpenCV实现 ...

  7. Python音视频开发:消除抖音短视频Logo和去电视台标

    ☞ ░ 前往老猿Python博文目录 ░ 一.引言 对于带Logo(如抖音Logo.电视台标)的视频,有三种方案进行Logo消除: 直接将对应区域用对应图像替换: 直接将对应区域模糊化: 通过变换将要 ...

  8. moviepy音视频开发:音频剪辑基类AudioClip

    ☞ ░ 前往老猿Python博文目录 ░ 一.背景知识介绍 1.1.声音三要素: 音调:人耳对声音高低的感觉称为音调(也叫音频).音调主要与声波的频率有关.声波的频率高,则音调也高. 音量:也就是响度 ...

  9. Android音视频开发(1):H264 基本原理

    前言 H264 视频压缩算法现在无疑是所有视频压缩技术中使用最广泛,最流行的.随着 x264/openh264 以及 ffmpeg 等开源库的推出,大多数使用者无需再对H264的细节做过多的研究,这大 ...

  10. Android IOS WebRTC 音视频开发总结(八十五)-- 使用WebRTC广播网络摄像头视频(下)

    本文主要介绍WebRTC (我们翻译和整理的,译者:weizhenwei,校验:blacker),最早发表在[编风网] 支持原创,转载必须注明出处,欢迎关注我的微信公众号blacker(微信ID:bl ...

随机推荐

  1. 云原生周刊:2024 扩展 K8s 集群指南 | 2024.1.2

    开源项目推荐 Jib Jib 无需 Docker 守护进程,也无需深入掌握 Docker 最佳实践,即可为您的 Java 应用程序构建优化的 Docker 和 OCI 映像.它可作为 Maven 和 ...

  2. HTML常见的文本语义标签

    列举HTML里一些常用的块内语义元素.可以通过F12来查看每个示例. <strong>:即"重点内容",通常是加粗,对应Markdown中的**加粗. 示例:归约数往往 ...

  3. 学习JavaScript第六天

    文章目录 1. JavaScript 中的垃圾回收机制(GC) 1.1 垃圾回收相关概念 ① 什么是垃圾 ② 什么是垃圾回收 ③ 垃圾没有及时回收的后果 ④ JavaScript 垃圾回收的常见算法 ...

  4. Mysql导出文本文件

    使用mysqldump命令导出文本文件 mysqldump -u root -pPassword -T 目标目录 dbname [tables] [option]; 其中: Password 参数表示 ...

  5. Linux 基础-查看进程命令 ps 和 top

    目录 1,使用 ps 命令找出 CPU 占用高的进程 2,通过 top 命令定位占用 cpu 高的进程 3,htop 系统监控与进程管理软件 4,参考资料 1,使用 ps 命令找出 CPU 占用高的进 ...

  6. 使用 FastGPT 工作流搭建 GitHub Issues 自动总结机器人

    如今任何项目开发节奏都很快,及时掌握项目动态是很重要滴,GitHub Issues 一般都是开发者和用户反馈问题的主要渠道. 然而,随着 Issue 数量的增加,及时跟进每一个问题会变得越来越困难. ...

  7. AI赋能-《用ChatGPT做软件测试》新书上市

    图书链接京东: https://item.jd.com/10121763192532.html当当: http://product.dangdang.com/29797547.html内容简介本书以目 ...

  8. vue3 js 学习笔记

    Vue3-js 学习笔记 目录 Vue3-js 学习笔记 目录 前言 reactive 数据绑定 事件绑定 生命函数周期 计算属性-computed props emit-自定义事件 ref-获取元素 ...

  9. 3.4 Linux文件(目录)命名规则

    介绍完 Linux 系统中目录结构之后,读者一定想知道如何为文件或目录命名. 我们知道,在 Linux 系统中,一切都是文件,既然是文件,就必须要有文件名.同其他系统相比,Linux 操作系统对文件或 ...

  10. AI 实战篇:Spring-AI再更新!细细讲下Advisors

    在2024年10月8日,Spring AI再次进行了更新,尽管当前版本仍为非稳定版本(1.0.0-M3),但博主将持续关注这些动态,并从流行的智能体视角深入解析其技术底层.目前,Spring AI仍处 ...