千家信息网

如何用Qt音视频开发实现通用通道管理

发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,本篇内容主要讲解"如何用Qt音视频开发实现通用通道管理",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何用Qt音视频开发实现通用通道管理"吧!一、前言把通
千家信息网最后更新 2025年01月24日如何用Qt音视频开发实现通用通道管理

本篇内容主要讲解"如何用Qt音视频开发实现通用通道管理",感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习"如何用Qt音视频开发实现通用通道管理"吧!

一、前言

把通用的视频控件搞定以后,后期增加新的内核方便多了,不需要在好多个文件复制粘贴之类的,接下来就是需要一个统一的类来管理视频监控系统中的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系统。

三、效果图

四、核心代码

#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::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 &videoUrls){    this->videoUrls = videoUrls;}void CommonVideoManage::setNames(const QList &videoNames){    this->videoNames = videoNames;}void CommonVideoManage::setWidgets(QList 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音视频开发实现通用通道管理"有了更深的了解,不妨来实际操作一番吧!这里是网站,更多相关内容可以进入相关频道进行查询,关注我们,继续学习!

0