作者:admin 日期:2023-09-10 浏览: 次
Qt编写视频监控系统71
最近遇到个需求是通过外网接入摄像机或者NVR,通用的做法是将视频流推流到服务器,然后拉取rtmp视频流,这样就多了服务器的要求,而且实现的功能有限比如不能直接用onvif协议对设备获取信息和配置信息,还有个做法就是将设备直接通过路由器端口映射到外网,根据外网的IP地址和端口对应填入即可。先不考虑安全问题,因为现在设备厂家几乎对安全性做的比较好,登录和onvif交互等,都需要用户验证,甚至还可以开启https机制。
通过端口映射后外网确实可以正常访问到设备,比如网页打开登录,但是通过onvif消息交互就未必正确了,需要调整代码,比如获取到的视频流地址是rtsp://192.168.0.5:554/1,由于经过了端口映射,所以你需要将这个onvif交互中的IP地址和端口都要换成映射后的地址和端口,否则不能正常访问使用,由于设备本身并不知道你做过端口映射,他只会应答自己本身的信息打包发出去,所以需要在onvif代码中将对应地址替换,取onvif地址中的IP地址即可,当然也有可能是需要微调的,比如设备多个的时候经过一个路由器端口有限,一个554端口只能对应映射到一个设备的554端口,其他设备要改成其他端口,这就需要将获取到的地址后的端口再手动修改即可。
通过onvif机制和外网摄像头设备交互,和内网比较,就差一个不能实现,那就是广播搜索设备,因为在外网上,不存在发个广播消息,设备可以接收到的,毕竟互联网上的设备万亿计算。除了这个无法实现以外,其他都正常,包括设置图片参数(明亮度、饱和度、对比度、尖锐度),云台控制、预置位相关操作都ok。由于无法广播搜索设备,需要对外网的设备挨个单播搜索获取详细的设备信息,本系统封装好的onvif组件支持单播搜索,填入IP地址和端口即可。
可以通过端口映射,外网访问摄像头或者NVR等,基本步骤如下:
void OnvifQuery::checkAddr(const QString &ipport, QString &addr, bool checkOnvif, bool replacePort)
{
QStringList list = addr.split("/");
if (addr.isEmpty() || list.count() <= 3) {
return;
}
//将内网的地址换成外网的(新增设备映射到外网后返回的数据中内网地址转换成外网的)
if (replacePort) {
list[2] = ipport;
} else {
//只替换IP地址
list[2] = ipport.split(":").first();
}
//将地址补全字符串(测试发现天地伟业的摄像机返回的地址都是没有带onvif字样的需要补上才行)
//http://192.168.0.160:80/Media 要转成 http://192.168.0.160:80/onvif/Media
if (checkOnvif && !addr.contains("/onvif/")) {
list.insert(3, "onvif");
}
addr = list.join("/");
}
OnvifHttpAddr OnvifQuery::getServices(const QString &ipport)
{
//<tds:Namespace>http://www.onvif.org/ver10/media/wsdl</tds:Namespace>
//<tds:XAddr>http://192.168.0.160:80/Media</tds:XAddr>
//<tds:Namespace>http://www.onvif.org/ver20/ptz/wsdl</tds:Namespace>
//<tds:XAddr>http://192.168.0.160:80/PTZ</tds:XAddr>
OnvifHttpAddr httpAddr;
//取出来时刚好按照顺序一一对应
QDomNodeList nodeNamespace = doc.elementsByTagName(QString("%1:Namespace").arg(wsdlAddr.wsdlDevice));
QDomNodeList nodeXAddr = doc.elementsByTagName(QString("%1:XAddr").arg(wsdlAddr.wsdlDevice));
int size1 = nodeNamespace.size();
int size2 = nodeXAddr.size();
if (size1 == size2) {
for (int i = 0; i < size1; ++i) {
QString name = nodeNamespace.at(i).toElement().text();
QString addr = nodeXAddr.at(i).toElement().text();
this->checkAddr(ipport, addr, true, true);
//可以自行增加其他的
if (name == "http://www.onvif.org/ver10/device/wsdl") {
httpAddr.addrDevice = addr;
} else if (name == "http://www.onvif.org/ver10/media/wsdl") {
httpAddr.addrMedia = addr;
} else if (name == "http://www.onvif.org/ver20/media/wsdl") {
httpAddr.addrMedia2 = addr;
} else if (name == "http://www.onvif.org/ver20/ptz/wsdl") {
httpAddr.addrPtz = addr;
} else if (name == "http://www.onvif.org/ver20/imaging/wsdl") {
httpAddr.addrImaging = addr;
} else if (name == "http://www.onvif.org/ver10/events/wsdl") {
httpAddr.addrEvents = addr;
} else if (name == "http://www.onvif.org/ver20/analytics/wsdl") {
httpAddr.addrAnalytics = addr;
}
}
无锡数据恢复
}
return httpAddr;
}