作者: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;
}