一、前言說明
搞完onvif設備模擬器,總想著把28181設備模擬也實現,因為之前已經花了大力氣把28181平臺軟件端實現了,為了實現這個組件,頭發掉了一大把,專門把國標文檔看了好幾遍,逐行閱讀,針對需要實現的交互協議,逐一實現,然后在N多實際現場測試,給最終用戶使用,不斷反饋迭代修改,以滿足更多的實際需求,比如有些廠家設備居然沒有ssrc,有些嚴格要求設備IP地址認證,有些只支持tcp被動方式,林林種種N多實際的特殊需求,都不斷調整以便適應,目前已經適配了市面上主流的設備,也有些非主流的也適配了,總之,能考慮到的都考慮了。
既然都已經熟悉了28181協議,那設備端的28181程序實現起來,輕車熟路,相對來說要簡單一些,因為設備模擬要實現的功能不多,能夠主動連接sip服務器,能夠應答設備信息和狀態,心跳保活,視頻點播,主要就這幾個,只不過sip通信鏈路可以是udp或者tcp,視頻點播可以支持udp、tcp主動、tcp被動三種方式。整個實現過程還是沒有碰到太多的難題,畢竟之前就已經把所有可能的坑都趟過去了。
二、效果圖
三、相關地址
- 國內站點:https://gitee.com/feiyangqingyun
- 國際站點:https://github.com/feiyangqingyun
- 個人作品:https://blog.csdn.net/feiyangqingyun/article/details/97565652
- 文件地址:https://pan.baidu.com/s/1d7TH_GEYl5nOecuNlWJJ7g 提取碼:01jf 文件名:bin_video_simulate。
四、功能特點
- 標準onvif協議,支持設備搜索、獲取參數、快照抓圖等。
- 支持264/265/aac等標準視音頻協議傳輸。
- 支持多路批量onvif設備模擬,每一路都獨立的端口。
- 支持本地攝像頭采集轉成onvif,可選擇不同的設備、分辨率、幀率等參數。
- 支持本地桌面采集轉成onvif,可選擇不同的屏幕、分辨率、幀率等參數。
- 支持各種視頻文件和視頻流轉成onvif,可重新設置編碼轉換以及分辨率轉換。
- 支持4K、8K等高清分辨率,不限制分辨率,非264/265會自動轉碼推流。
- 每一路都可以設置統一或者獨立的用戶驗證信息,為空則表示不驗證。
- 可以把任意內容接入到NVR以及視頻監控系統,方便保存錄像文件,以便回放可查。
- 也可作為壓力測試工具,比如模擬幾千路onvif設備,讓集成平臺軟件做接入壓力測試。
- 推出去的流不僅有rtsp格式,還支持rtmp、http、flv、ws-flv、webrtc等方式訪問,可以直接網頁查看。
- 在管理工具上可以看到每一路的推流狀況以及分辨率信息,非常直觀。
- 支持自動重連拉流,重連推流,保證7乘以24小時穩定運行。
- 可設置開機自啟動運行和后臺運行,不顯示在任務欄,作為后臺服務運行。
- 可批量添加文件、添加目錄,自動將目錄下的所有文件添加到模擬器。
- 多功能添加地址面板,可以選擇本地設備和監控設備,本地設備會自動識別攝像頭設備和桌面設備,監控設備可以選擇不同廠家,自動填充對應rtsp格式,填入用戶信息即可,可以批量遞增添加監控設備。
- 可無縫上傳到市面上所有的onvif協議設備,包括海康、大華、宇視、華為、天地偉業等,也支持ONVIF Device Manager國際onvif工具。
- 支持gb28181設備模擬,具備設備注冊、設備注銷、設備心跳、設備信息、設備配置、設備狀態應答等。
- 支持模擬報警和位置上報等,方便平臺側顯示對應設備的實時位置。
- 支持一鍵添加批量模擬28181設備,實時顯示已注冊和已注銷狀態。
- 支持將本地桌面、本地攝像頭、任意視頻文件、視頻流文件、手機攝像頭等轉換成28181設備,添加到NVR或者國標軟件平臺。
- sip協議同時支持udp和tcp兩種通信方式,視頻點播同時支持udp/tcp主動/tcp被動三種方式,涵蓋所有可能的場景需求。
- 無論是onvif設備模擬組件還是28181設備模擬組件,全部原創底層協議解析,純Qt實現,跨任意平臺。
- 代碼結構框架非常清晰,注釋詳細,代碼精簡不繁瑣,非常易于學習和移植,可以很容易拓展其他接口需求。
- 支持Qt4/Qt5/Qt6以及后續所有版本、所有編譯器、所有開發環境。
- 支持windows、linux、mac、國產OS、嵌入式linux、RK3588、樹莓派、香橙派等系統。
五、相關代碼
#include "gb28181devicebody.h"int GB28181DeviceBody::idlen = 5;
QString GB28181DeviceBody::getXml(const QString &type, const QStringList &body)
{QStringList list;list << QString("<?xml version=\"1.0\" encoding=\"GB2312\"?>");list << QString("<%1>").arg(type);list << body;list << QString("</%1>").arg(type);return list.join("\r\n");
}QStringList GB28181DeviceBody::getCommonXml(const QString &cmdType, const QString &deviceId, quint64 xmlSN)
{QStringList list;list << QString(" <CmdType>%1</CmdType>").arg(cmdType);list << QString(" <SN>%1</SN>").arg(xmlSN);list << QString(" <DeviceID>%1</DeviceID>").arg(deviceId);return list;
}QString GB28181DeviceBody::getResponseXml(const QString &cmdType, const QString &deviceId, quint64 xmlSN, const QStringList &body)
{QStringList list = GB28181DeviceBody::getCommonXml(cmdType, deviceId, xmlSN);list << " <Result>OK</Result>";list << body;return GB28181DeviceBody::getXml("Response", list);
}QString GB28181DeviceBody::getNotifyXml(const QString &cmdType, const QString &deviceId, quint64 xmlSN, const QStringList &body)
{QStringList list = GB28181DeviceBody::getCommonXml(cmdType, deviceId, xmlSN);list << body;return GB28181DeviceBody::getXml("Notify", list);
}QString GB28181DeviceBody::getKeepalive(GB28181DevicePara device, quint64 &xmlSN)
{QStringList list;list << QString(" <Status>OK</Status>");return GB28181DeviceBody::getNotifyXml("Keepalive", device.deviceId, xmlSN++, list);
}QString GB28181DeviceBody::getAlarm(GB28181DevicePara device, quint64 &xmlSN, int alarmPriority, int alarmMethod, int alarmType)
{QStringList list;list << QString(" <AlarmPriority>%1</AlarmPriority>").arg(alarmPriority);list << QString(" <AlarmMethod>%1</AlarmMethod>").arg(alarmMethod);list << QString(" <AlarmTime>%1</AlarmTime>").arg(SIPTIME);list << QString(" <Info>");list << QString(" <AlarmType>%1</AlarmType>").arg(alarmType);list << QString(" <Info>");return GB28181DeviceBody::getNotifyXml("Alarm", device.channelId, xmlSN++, list);
}QString GB28181DeviceBody::getMobilePosition(GB28181DevicePara device, quint64 &xmlSN, double longitude, double latitude, double altitude)
{QStringList list;list << QString(" <Time>%1</Time>").arg(SIPTIME);list << QString(" <Longitude>%1</Longitude>").arg(longitude);list << QString(" <Latitude>%1</Latitude>").arg(latitude);list << QString(" <Altitude>%1</Altitude>").arg(altitude);list << QString(" <Speed>%1</Speed>").arg(100);list << QString(" <Direction>%1</Direction>").arg(180);return GB28181DeviceBody::getNotifyXml("MobilePosition", device.channelId, xmlSN++, list);
}QString GB28181DeviceBody::getDeviceInfo(GB28181DevicePara device, quint64 xmlSN)
{QStringList list;list << QString(" <DeviceName>%1-%2</DeviceName>").arg("TX-Camera").arg(device.deviceId.right(idlen));list << QString(" <Manufacturer>%1</Manufacturer>").arg("TaiXue");list << QString(" <Model>%1</Model>").arg("TX-Camera-001");list << QString(" <Firmware>%1</Firmware>").arg("V20250808");list << QString(" <Channel>%1</Channel>").arg(1);return GB28181DeviceBody::getResponseXml("DeviceInfo", device.deviceId, xmlSN, list);
}QString GB28181DeviceBody::getDeviceStatus(GB28181DevicePara device, quint64 xmlSN)
{QStringList list;list << QString(" <Online>%1</Online>").arg("ONLINE");list << QString(" <Status>%1</Status>").arg("OK");list << QString(" <DeviceTime>%1</DeviceTime>").arg(SIPTIME);list << QString(" <Alarmstatus Num=\"%1\"></Alarmstatus>").arg(0);list << QString(" <Encode>%1</Encode>").arg("ON");list << QString(" <Record>%1</Record>").arg("OFF");return GB28181DeviceBody::getResponseXml("DeviceStatus", device.deviceId, xmlSN, list);
}QString GB28181DeviceBody::getCatalog(GB28181DevicePara device, quint64 xmlSN)
{QStringList list;list << QString(" <SumNum>%1</SumNum>").arg(1);list << QString(" <DeviceList Num=\"%1\">").arg("1");list << QString(" <Item>");list << QString(" <DeviceID>%1</DeviceID>").arg(device.channelId);list << QString(" <Name>%1-%2</Name>").arg("TX-Channel").arg(device.channelId.right(idlen));list << QString(" <Manufacturer>%1</Manufacturer>").arg("TaiXue");list << QString(" <Model>%1</Model>").arg("TX-Camera-001");list << QString(" <Owner>%1</Owner>").arg("Owner");list << QString(" <CivilCode>%1</CivilCode>").arg(device.serverRealm);list << QString(" <Address>%1</Address>").arg("Address");list << QString(" <Parental>%1</Parental>").arg(0);list << QString(" <ParentID>%1</ParentID>").arg(device.deviceId);list << QString(" <SafetyWay>%1</SafetyWay>").arg("0");list << QString(" <RegisterWay>%1</RegisterWay>").arg("1");list << QString(" <Secrecy>%1</Secrecy>").arg("0");list << QString(" <Status>%1</Status>").arg("ON");list << QString(" </Item>");list << QString(" </DeviceList>");return GB28181DeviceBody::getResponseXml("Catalog", device.deviceId, xmlSN, list);
}QString GB28181DeviceBody::getConfigDownload(GB28181DevicePara device, quint64 xmlSN)
{QStringList list;list << QString(" <BasicParam>");list << QString(" <Name>%1-%2</Name>").arg("TX-Camera").arg(device.deviceId.right(idlen));list << QString(" <DeviceID>%1</DeviceID>").arg(device.deviceId);list << QString(" <SIPServerID>%1</SIPServerID>").arg(device.serverId);list << QString(" <SIPServerIP>%1</SIPServerIP>").arg(device.serverIp);list << QString(" <SIPServerPort>%1</SIPServerPort>").arg(device.serverPort);list << QString(" <DomainName>%1</DomainName>").arg(device.serverRealm);list << QString(" <Expiration>%1</Expiration>").arg(device.registInterval);list << QString(" <Password>%1</Password>").arg(device.serverPwd);list << QString(" <HeartBeatInterval>%1</HeartBeatInterval>").arg(device.heartInterval);list << QString(" <HeartBeatCount>%1</HeartBeatCount>").arg(device.heartTimeout);list << QString(" <PositionCapability>%1</PositionCapability>").arg(0);list << QString(" </BasicParam>");return GB28181DeviceBody::getResponseXml("ConfigDownload", device.deviceId, xmlSN, list);
}