特别说明:个人笔记,不惜勿喷。
Qt获取海康摄像头图像方法很多,比如可以只用RTSP的方式直接拉流获取。但是RTSP拉流的方式我也尝试过,需要用到FFMPEG,由于这个库东西比较多,而且里面很多流程需要自己去做解析,程序员本就是图方便和高效,与其去折腾,还不如直接用官方提供的SDK。这样省很多事情。
海康SDK开发心得:在预览图像的时候图像时延是非常低的,相比ffmpeg的rtsp要低很多,因此如果项目对于时延要去高的地方可以考虑用sdk来试试。
于是乎,自己研究了下,发现比较简单,此处仅作笔记,无技术含量。
1、首先工程文件添加海康SDK的库和头文件
LIBS += -L$$PWD/hiklib/lib/ -lHCNetSDK
INCLUDEPATH += $$PWD/hiklib/include
DEPENDPATH += $$PWD/hiklib/include
2、登录摄像头
NET_DVR_Init();
NET_DVR_SetConnectTime(2000, 1);
NET_DVR_SetReconnect(10000, true);
NET_DVR_USER_LOGIN_INFO struLoginInfo;
NET_DVR_DEVICEINFO_V40 struDeviceInfoV40;
memset(&struLoginInfo, 0x00, sizeof(NET_DVR_USER_LOGIN_INFO));
memset(&struDeviceInfoV40, 0x00, sizeof(NET_DVR_DEVICEINFO_V40));
struLoginInfo.bUseAsynLogin = 0; // 同步登录方式
strcpy(struLoginInfo.sDeviceAddress, "192.168.0.143"); // 设备IP地址
struLoginInfo.wPort = 8000 ; // 设备服务端口
strcpy_s(struLoginInfo.sUserName, "admin"); // 设备登录用户名
strcpy_s(struLoginInfo.sPassword, "123456"); // 设备登录密码
m_userId = NET_DVR_Login_V40(&struLoginInfo, &struDeviceInfoV40);
if (m_userId < 0)
{
qDebug() << "登录失败" << m_userId << NET_DVR_GetErrorMsg();
}
3、登录成功后就可以直接启动图像预览了。
NET_DVR_PREVIEWINFO struPreviewInfo;
memset(&struPreviewInfo, 0x00, sizeof(NET_DVR_PREVIEWINFO));
struPreviewInfo.lChannel = 1; // 通道id
struPreviewInfo.hPlayWnd = (HWND)ui->labelPreview->winId();
struPreviewInfo.dwStreamType = 0; // 码流类型: 0-主码流
struPreviewInfo.dwLinkMode = 0; // 链接方式: 0- TCP方式
struPreviewInfo.bBlocked = 0; // 0 - 非阻塞取流,1 - 阻塞取流
m_previewHandle = NET_DVR_RealPlay_V40(m_userId, &struPreviewInfo, NULL, NULL);
4、云台控制
#define START 0
#define STOP 1
NET_DVR_PTZControl(m_previewHandle, TILT_UP, START);
NET_DVR_PTZControl(m_previewHandle, TILT_UP, STOP);
/* 下 */
NET_DVR_PTZControl(m_previewHandle, TILT_DOWN, START);
NET_DVR_PTZControl(m_previewHandle, TILT_DOWN, STOP);
/* 其他方向可以自行查看头文件,方法一样 */
5、辅助功能:网络摄像头局域网扫描通信协议,方便在不知道摄像头ip的情况下进行
通过UDP报文广播的方式扫描在线摄像头
QString strData = QString("");
m_strUuid = QUuid::createUuid().toString(QUuid::WithoutBraces).toUpper();
strData += m_strUuid;
strData += " inquiry ";
qint64 len = m_udpSocket->writeDatagram(strData.toLatin1(), QHostAddress("239.255.255.250"), 37020);
qDebug() << "==>" << len << strData;
协议解析:
void ScanIPCameras::sltUdpSocketRead()
{
QByteArray byData;
// qDebug() << "udp recv" << m_udpSocket->pendingDatagramSize();
while (m_udpSocket->hasPendingDatagrams()) {
#if (QT_VERSION > QT_VERSION_CHECK(5, 8, 0))
QNetworkDatagram datagram = m_udpSocket->receiveDatagram();
QHostAddress host = datagram.senderAddress();
byData += datagram.data();
#else
QByteArray datagram;
datagram.resize(m_udpSocket->pendingDatagramSize());
m_udpSocket->readDatagram(datagram.data(), datagram.size());
byData += datagram;
#endif
}
parseXmlData(byData);
}
解析如下:
void ScanIPCameras::parseXmlData(const QByteArray &_byContent)
{
QDomDocument document;
QString error;
int row = 0, column = 0;
if (!document.setContent(_byContent, false, &error, &row, &column))
{
return;
}
if (document.isNull())
{
return;
}
QDomElement root = document.documentElement();
for(QDomNode node = root.firstChild(); !node.isNull(); node = node.nextSibling())
{
QDomElement e = node.toElement();
QString tag = node.nodeName();
QString value = e.text();
if ("Uuid" == tag) {
qDebug() << "value" << value;
qDebug() << "Uuid " << m_strUuid;
m_strUuid = value;
}
else if ("DeviceDescription" == tag) {
}
else if ("IPv4Address" == tag) {
emit signalCamera(value);
emit signalNotify(QString("摄像头扫描: 扫描完成,发现设备[%1]!~").arg(value));
}
else if ("CommandPort" == tag) {
}
else if ("HttpPort" == tag) {
}
else if ("DHCP" == tag) {
}
else if ("DeviceSN" == tag) {
}
else {
qDebug() << tag << value;
}
}
}
效果图: