#pragma once
#include <QObject>
#include <QUrl>
#include <QtNetwork/QNetworkAccessManager>
#include <QtNetwork/QNetworkRequest>
#include <QtNetwork/QNetworkReply>
#include <QFile>
#include <QtCore/QElapsedTimer>
#include "Response.h"
#include "SoapHelper.h"
#include "OnvifDevice.h"
#include "OnvifDiscovery.h"
#include "OnvifMedia2Client.h"
#include "opencv2/opencv.hpp"
class CameraOnvif : public QObject
{
Q_OBJECT
public:
CameraOnvif();
~CameraOnvif();
QObject* _parent = nullptr;
public slots:
void discovery();
void snap();
private:
QElapsedTimer timer;
signals:
void msg(int id, QString s);
};
cpp
void CameraOnvif::snap()
{
//获取snap uri
auto cb = SoapCtx::Builder();
cb.SetSendTimeout(1000);
cb.SetReceiveTimeout(1000);
auto media = new OnvifMedia2Client(QUrl("http://192.168.1.9/onvif/device_service"), cb.Build(), nullptr);
Request<_tr2__GetSnapshotUri> request;
Request<_tr2__GetProfiles> request_2;
request.ProfileToken = "MediaProfile00000";
request.SetSoapAction("GetProfiles");
auto profile = media->GetProfiles(request_2);
request.SetSoapAction("GetSnapshotUri");
auto url = media->GetSnapshotUri(request);
qDebug() << "snap uri" << url.GetResultObject()->Uri;
//抓拍
QNetworkRequest r;
r.setUrl(QString("http://192.168.1.9/onvif/snapshot?channel=1&subtype=0"));
QNetworkAccessManager* manager = new QNetworkAccessManager();
if (timer.isValid())
timer.restart();
else
timer.start();
QNetworkReply* reply = manager->get(r);
connect(reply, &QNetworkReply::finished, this, [=]() {
auto a = reply->readAll();
std::vector<uchar> buf;
int len = a.size();
for (int i = 0;i < len;i++)
buf.push_back(a[i]);
//解码
cv::Mat img = cv::imdecode(buf,0);
//缩小
cv::Mat img_scaled;
cv::resize(img, img_scaled, cv::Size(img.cols / 4, img.rows / 4));
cv::imshow("img", img_scaled);
qDebug() << "time cost:" << timer.elapsed() << Qt::endl;
timer.invalidate();
QFile file("e:\\a.jpg");
file.open(QIODevice::WriteOnly);
file.write(a);
file.close();
});
}
缺外部链接符号,就检查附加依赖项,譬如用到OnvifMedia2Client函数,就要附加onvifmedia2.lib。
发现libonvif只能提供相机控制及信息查询,得到相机的endpoint地址及SnapshotUri后,如果抓拍图片,则需要使用Qt的network库。
libonvif库没有example,对于复制粘贴不是很方便。
注意QNetworkReply返回结果是QByte,要用opencv的imdecode函数转换到Mat格式,中间需要用vect数组复制一遍。
感觉用goap生成的c++函数搜索和查询相机的onvif参数,用libonvif更适合做相机控制、设置的程序。
大华提供的sdk进行抓拍,最多耗时间是1秒多一点,用onvif方式通用性有了,抓拍速度也不稳定,这相机性能一般,试试海康威视的相机。
对于海康相机(DS-2CD4A10FWD-IZ),GetProfiles和GetSnapshotUri执行都失败,提示:
Optional Action Not Implemented
应该是libonvif需要提供认证,我还不会用。
用ONVIF Device Test Tool查询得到海康相机的snapuri:
访问海康相机的抓拍地址,需要提供用户名和密码,但大华相机是不需要的。
因此用Qt的摘要认证:
cpp
//摘要认证
connect(manager, &QNetworkAccessManager::authenticationRequired, this, [=](QNetworkReply* reply, QAuthenticator* authenticator) {
authenticator->setUser("admin");
authenticator->setPassword("admin12345");
});
可以抓到画面。
耗时最多到1500ms,比大华还慢。