作者:admin 日期:2023-09-10 瀏覽: 次
Qt編寫(xiě)視頻監(jiān)控系統(tǒng)71
最近遇到個(gè)需求是通過(guò)外網(wǎng)接入攝像機(jī)或者NVR,通用的做法是將視頻流推流到服務(wù)器,然后拉取rtmp視頻流,這樣就多了服務(wù)器的要求,而且實(shí)現(xiàn)的功能有限比如不能直接用onvif協(xié)議對(duì)設(shè)備獲取信息和配置信息,還有個(gè)做法就是將設(shè)備直接通過(guò)路由器端口映射到外網(wǎng),根據(jù)外網(wǎng)的IP地址和端口對(duì)應(yīng)填入即可。先不考慮安全問(wèn)題,因?yàn)楝F(xiàn)在設(shè)備廠家?guī)缀鯇?duì)安全性做的比較好,登錄和onvif交互等,都需要用戶驗(yàn)證,甚至還可以開(kāi)啟https機(jī)制。
通過(guò)端口映射后外網(wǎng)確實(shí)可以正常訪問(wèn)到設(shè)備,比如網(wǎng)頁(yè)打開(kāi)登錄,但是通過(guò)onvif消息交互就未必正確了,需要調(diào)整代碼,比如獲取到的視頻流地址是rtsp://192.168.0.5:554/1,由于經(jīng)過(guò)了端口映射,所以你需要將這個(gè)onvif交互中的IP地址和端口都要換成映射后的地址和端口,否則不能正常訪問(wèn)使用,由于設(shè)備本身并不知道你做過(guò)端口映射,他只會(huì)應(yīng)答自己本身的信息打包發(fā)出去,所以需要在onvif代碼中將對(duì)應(yīng)地址替換,取onvif地址中的IP地址即可,當(dāng)然也有可能是需要微調(diào)的,比如設(shè)備多個(gè)的時(shí)候經(jīng)過(guò)一個(gè)路由器端口有限,一個(gè)554端口只能對(duì)應(yīng)映射到一個(gè)設(shè)備的554端口,其他設(shè)備要改成其他端口,這就需要將獲取到的地址后的端口再手動(dòng)修改即可。
通過(guò)onvif機(jī)制和外網(wǎng)攝像頭設(shè)備交互,和內(nèi)網(wǎng)比較,就差一個(gè)不能實(shí)現(xiàn),那就是廣播搜索設(shè)備,因?yàn)樵谕饩W(wǎng)上,不存在發(fā)個(gè)廣播消息,設(shè)備可以接收到的,畢竟互聯(lián)網(wǎng)上的設(shè)備萬(wàn)億計(jì)算。除了這個(gè)無(wú)法實(shí)現(xiàn)以外,其他都正常,包括設(shè)置圖片參數(shù)(明亮度、飽和度、對(duì)比度、尖銳度),云臺(tái)控制、預(yù)置位相關(guān)操作都o(jì)k。由于無(wú)法廣播搜索設(shè)備,需要對(duì)外網(wǎng)的設(shè)備挨個(gè)單播搜索獲取詳細(xì)的設(shè)備信息,本系統(tǒng)封裝好的onvif組件支持單播搜索,填入IP地址和端口即可。
可以通過(guò)端口映射,外網(wǎng)訪問(wèn)攝像頭或者NVR等,基本步驟如下:
void OnvifQuery::checkAddr(const QString &ipport, QString &addr, bool checkOnvif, bool replacePort)
{
QStringList list = addr.split("/");
if (addr.isEmpty() || list.count() <= 3) {
return;
}
//將內(nèi)網(wǎng)的地址換成外網(wǎng)的(新增設(shè)備映射到外網(wǎng)后返回的數(shù)據(jù)中內(nèi)網(wǎng)地址轉(zhuǎn)換成外網(wǎng)的)
if (replacePort) {
list[2] = ipport;
} else {
//只替換IP地址
list[2] = ipport.split(":").first();
}
//將地址補(bǔ)全字符串(測(cè)試發(fā)現(xiàn)天地偉業(yè)的攝像機(jī)返回的地址都是沒(méi)有帶onvif字樣的需要補(bǔ)上才行)
//http://192.168.0.160:80/Media 要轉(zhuǎn)成 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;
//取出來(lái)時(shí)剛好按照順序一一對(duì)應(yīng)
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;
}
}
無(wú)錫數(shù)據(jù)恢復(fù)
}
return httpAddr;
}