Qt编写安防视频监控系统40-onvif线程处理

一、前言

整个onvif模块大部分的功能都有了以后,除了在demo上点点按钮可以执行获取结果显示外,最终还是要应用到视频监控中,在按钮上点点和系统中后台自动运行是两码事,比如onvif校时和事件订阅,不会说是傻到在监控系统界面上提供按钮给用户点击才去执行,最多做的应该是系统设置中提供两个开关比如自动校时、事件订阅,可以方便的开启这几个功能。开启以后等监控系统启动后自动去处理,比如挨个对摄像机进行校时处理以及订阅事件,为了能够做到添加摄像机后自动立即应用,特意改成了在打开摄像机视频画面的时候,主动去实例化DeviceOnvif类(每个摄像机都对应一个实例)

最开始的做法是采用定时器去处理要指定的指令队列,后面发现速度不好控制,毕竟网络请求受网速和网络环境的影响,有时候100毫秒就执行完成了,有时候又需要300毫秒不等,尽管网络请求的时候已经设置了超时时间(这个时间一般设置成2-3秒,保证请求有足够的时候返回),这个时间有点大,如果按照这个网络请求超时时间来设定定时器,设备数量很多的时候太慢了,监控系统一般几十个设备是有的,这蜗牛一样的速度要处理到何年马月,而且每个摄像机有多个指令需要处理比如自动校时、事件订阅等。

那有没有一种机制可以尽最快的速度排队处理呢,答案是当然,这不就是线程擅长干的事情吗,使劲的干,休息多久自由msleep控制即可,网络环境好的情况下,20个设备的指令基本上在1s内完成的,这就能够满足用户的需求,毕竟用户打开软件后,大概率不想等待太长时间,就像能够看到所有摄像机时间自动校准好了,搞个摄像机报警也能立即通过onvif协议上报,该处理的都尽快处理完了。

QNetworkAccessManager类如果一开始不是在线程中new出来的,会提示不能在其他线程执行,这就需要在线程的run函数中调用QMetaObject::invokeMethod来执行对应的处理,一个万能的处理方法就是将需要执行的全部放在work函数中,搞个iswork标志位,进入该开始的时候将标志位iswork=true,处理结束后iswork=false,在run中先判断标志位是否为假,为假表示当前不在工作,则去调用work函数处理。这就规避了在线程中执行其他线程类对象函数的错误提示。

基本的处理思路

  • 查询出所有的摄像机信息。
  • 过滤摄像机信息,找出所有具备onvif地址的,只有具备onvif地址的才是需要去处理的。
  • 从deviceonvif链表中找到当前onvif地址的设备类对象,该方法同时肩带new出实例在没有找到对应实例的情况下。
  • 将对应的处理转成命令指令队列,带有onvif地址标识,交给onvifthread线程类专门处理。
  • 所有的方法在该实例中都有对应方法进行处理,对该实例调用对应的方法比如校时、事件订阅、抓图等。
  • 处理完成后将对应的结果信号发出去,对应三个参数分别表示onvif地址、指令、结果数据(QVariant类型)。

三、在线文档

feiyangqingyun.gitee.io/qwidgetdemo…

四、效果图

在这里插入图片描述

五、核心代码

#include "onvifthread.h"

QScopedPointer<OnvifThread> OnvifThread::self;
OnvifThread *OnvifThread::Instance()
{
    if (self.isNull()) {
        static QMutex mutex;
        QMutexLocker locker(&mutex);
        if (self.isNull()) {
            self.reset(new OnvifThread);
        }
    }

    return self.data();
}

OnvifThread::OnvifThread(QObject *parent) : QThread(parent)
{
    stopped = false;
    working = false;
}

OnvifThread::~OnvifThread()
{
    this->stop();
    this->wait();
}

void OnvifThread::run()
{
    while (!stopped) {
        //先判断是否在工作
        if (!working) {
            //异步执行
            QMetaObject::invokeMethod(this, "work");
        }

        //可以自行调整休息的时间
        msleep(100);
    }

    stopped = false;
    working = false;
}

void OnvifThread::stop()
{
    stopped = true;
}

void OnvifThread::work()
{
    //设置正在工作标志位
    working = true;

    if (devices.count() > 0) {
        mutex.lock();
        OnvifDevice *device = devices.takeFirst();
        QString cmd = cmds.takeFirst();
        QVariant data = datas.takeFirst();
        mutex.unlock();

        QList<QVariant> list = data.toList();
        QString url = device->getOnvifAddr();
        QString ip = OnvifHelper::getIP(url);
        if (cmd == "remove") {
            bool ok = OnvifHelper::onvifDevices.removeOne(device);
            device->deleteLater();
            qDebug() << TIMEMS << "执行移除对象" << ip << ok;
        } else if (cmd == "systemReboot") {
            QString result = device->systemReboot();
            qDebug() << TIMEMS << "远程重启设备" << ip << result;
        } else if (cmd == "setDateTime") {
            //两种方式都设置下 一种是直接设置日期时间字符串 一种是触发NTP同步
            bool ok = device->setDateTime(QDateTime::currentDateTime().toUTC());
            //bool ok = device->setDateTime(QDateTime::currentDateTime().toUTC(), true);
            qDebug() << TIMEMS << "设置设备时间" << ip << ok;
        } else if (cmd == "getEvent") {
            QString result = device->getEvent();
            qDebug() << TIMEMS << "订阅报警事件" << ip << result;
        } else if (cmd == "getProfile") {
            QString result = device->getProfile();
            qDebug() << TIMEMS << "获取配置文件" << ip << result;
        } else if (cmd == "snapImage") {
            QImage image = device->snapImage(device->getProfile());
            emit receiveImage(url, image);
            qDebug() << TIMEMS << "手动抓拍图片" << ip << (!image.isNull());
        } else if (cmd == "getVideoSource") {
            QString result = device->getVideoSource();
            emit receiveResult(url, cmd, result);
            qDebug() << TIMEMS << "获取视频参数" << ip << result;
        } else if (cmd == "getImageSetting") {
            int brightness, colorSaturation, contrast;
            QString result = device->getImageSetting(brightness, colorSaturation, contrast);
            QVariant data = (QList<QVariant>() << brightness << colorSaturation << contrast);
            emit receiveResult(url, cmd, data);
            qDebug() << TIMEMS << "获取图片参数" << ip << result;
        } else if (cmd == "setImageSetting") {
            int brightness = list.at(0).toInt();
            int colorSaturation = list.at(1).toInt();
            int contrast = list.at(2).toInt();
            bool ok = device->setImageSetting(brightness, colorSaturation, contrast);
            qDebug() << TIMEMS << "设置图片参数" << ip << ok;
        }
    }

    working = false;
}

void OnvifThread::append(const OnvifDeviceUser &deviceUser, const QString &cmd, const QVariant &data)
{
    mutex.lock();

    //先解绑以及重新绑定事件
    OnvifDevice *device = OnvifHelper::getOnvifDevice(deviceUser);
    disconnect(device, SIGNAL(receiveEvent(QString, OnvifEventInfo)), this, SIGNAL(receiveEvent(QString, OnvifEventInfo)));
    connect(device, SIGNAL(receiveEvent(QString, OnvifEventInfo)), this, SIGNAL(receiveEvent(QString, OnvifEventInfo)));

    //添加到队列等待处理
    devices << device;
    cmds << cmd;
    datas << data;

    mutex.unlock();
}
复制代码
© 版权声明
THE END
喜欢就支持一下吧
点赞0 分享