最近有个项目涉及海康威视的摄像头部分,需要在服务器端自动定时抓图,由于服务器端采用的是Windows系统,不熟Windows的api,所以直接采用Qt来编写。那么摄像头的激活配置什么的就不再介绍了,直接进入正题。
先下载海康的SDK,https://www.hikvision.com/cn/download_61.html,我的电脑系统是64位的,QT版本5.10.0,QT Creator是32位的,编译器是mingw32,介于编译器是32位,同时32位的程序能够在32、64位的系统下运行,但是64位的就只能在64位系统下运行,因此为了后期的兼容性,我下载了32位的SDK,至于能不能下载64位的SDK,可以自行尝试。
其次设置硬件设备,网络摄像头相当于一个服务器,我们的程序要访问它并获取视频数据,因此这个摄像头无论在内网还是外网,只要能访问到就行,最简单的方式就是ping它的IP,能通就行,我的IP是192.168.10.102,然后是服务端口,服务端口可以自行网页登陆摄像头查看,我这里是默认值,8000,然后是账号和密码,账号分为两种,管理员账号admin和普通账号,管理员账号的密码就是激活的时候让输入的密码,普通账号是激活后登陆摄像头自行设置的,我这里还是默认的admin和激活时候设置的密码。硬件环境大致就是这个样子。
然后新建一个Qt工程,我选择是继承于QWidget的类,当然其他基类也行,接着从下载的sdk里将头文件文件夹和库文件文件夹都拷到Qt工程目录下,分别修改为英文,我这里是include和libs,注意这里修改的名字应当和程序中对应;接下来是将库添加到QT工程中,使其编译时能够加载,库目录中有lib和dll,这两个都是库,这里插嘴说一下二者的区别,当Windows下一个工程编译成库时,可以有两种方式,第一种是静态库,第二种是动态库,静态库在编译时读取,动态库在运行时读取,生成静态库时只有lib文件,里边存放的就是函数的实现,生成动态库时,lib和dll文件都有,其中lib中没有函数的实现代码,只存放了该实现代码位于哪个dll中,dll中存放函数的实现代码;我们在调用静态库时,代码在链接阶段,将去对应lib中寻找实现代码,并进行链接,结束后,lib就没用了;而调用动态库时,代码在链接阶段也会去lib中寻找,不同的是,它只会留下标记,标识去哪里找运行代码,而不实际将dll中的实现链接进程序里,因此程序运行时不需要lib,但是必须有dll。言归正传,海康威视的SDK是动态库,我们需要指定lib文件的位置,运行时将dll文件放置在程序运行目录下即可,在QT中指定lib文件的位置是修改pro文件,添加如下代码:
LIBS +=-LH:/QTproject/HKDemo/HKDemo/libs -lHCCore -lHCNetSDK -lPlayCtrl
LIBS +=-LH:/QTproject/HKDemo/HKDemo/libs/HCNetSDKCom -lHCAlarm -lHCGeneralCfgMgr -lHCPreview
和Linux系统的加库方法非常像,-L表示库的搜索路径,-l表示链接哪个库,注意这里的路径要写全,我试过相对路径,没有成功,至此编译时的库加入到了QT工程中。
接下来要做的就是写代码了,我们可以根据海康威视提供的文档进行编写,其文档位于开发文档目录下,我使用的是设备网络SDK使用手册.chm,我们可以在里边看到使用流程,及一些接口的依赖关系:
整个大的框架就是初始化,注册,预览,停止预览,注销,释放资源,初始化SDK和释放SDK资源不必多说,是sdk的基础操作,然后注册就是登陆摄像头,注销就是退出登陆,注意抓图分为两种,预览抓图和设备抓图,预览抓图必须先开启预览,而设备抓图不用,因为我是做服务器端程序用的,就使用了设备抓图,即不需要预览就可以抓图。
初始化SDK和释放SDK资源
SDK中初始化资源函数为NET_DVR_Init(),资源释放函数为NET_DVR_Cleanup()。
由于这是SDK的基础操作,我们在窗体的构造函数执行初始化SDK的操作,在析构函数中执行释放SDK资源的操作,初始化SDK和释放SDK的代码如下:
bool Widget::initSDK()
{
bool isok = NET_DVR_Init();
if(isok == false)
{
qDebug()<< "NET_DVR_Init error;error number is " <
其余接口不再累述,自己查即可,这里我用的是NET_DVR_Login_V30函数和NET_DVR_Logout,代码如下:
void Widget::OnClickedLogin()
{
NET_DVR_DEVICEINFO_V30 DeviceInfoTmp;
userID=NET_DVR_Login_V30("192.168.10.102",8000,"admin","1234536asd",&DeviceInfoTmp);//参数依次为IP地址、服务端口、账号、密码,最后一个参数是为了获得硬件信息的
if(userID<0)
{
qDebug() << "NET_DVR_Login_V30 error;" << "error number is " << NET_DVR_GetLastError();
QMessageBox::warning(this,"error","NET_DVR_Login_V30 error;error number "+QString::number(NET_DVR_GetLastError()));
return;
}
qDebug()<<"Login Success,userID:" << userID<
预览函数需要获得窗体句柄,并且是Windows api中定义的句柄,本质上来讲,QT写出的窗体也是windows窗体,我们这里直接定义一个label控件,控件中有个成员函数是专门获得windows下的窗体句柄的,传入该句柄后,函数就会自动向窗体传递图像信息并显示,不需我们的任何操作,同时也不会阻塞在这个函数,应该是直接利用了windows api的消息机制,代码如下:
void Widget::OnClickedPreview()
{
if(userID==-1)
{
qDebug() <<"Need Login!"<label->winId();//HWND是windows api的窗体句柄类型
NET_DVR_PREVIEWINFO struPlayInfo;
previewID = play(hWnd,struPlayInfo);
if(previewID==-1)
{
return;
}
qDebug()<<"Preview Success!" << userID<
这里使用的是设备抓图,即可以在登录后直接抓图,不需要预览,但是这个函数只能获取jpeg图像,会损失精度,可以采用预览抓图函数,其可以设置获取BMP图像还是jpeg图像。注意文件路径必须包括文件名字,还有Windows下的要加双斜杠,另外文件路径中的文件夹应当在该函数之前创建,代码如下:
void Widget::OnClickedCapture()
{
if(userID!=-1)
{
NET_DVR_JPEGPARA IpJpegPara2;
IpJpegPara2.wPicQuality=0;
IpJpegPara2.wPicSize=0xff;
QDateTime current_date_time =QDateTime::currentDateTime();
QString current_date =current_date_time.toString("yyyy-MM-dd_hh-mm-ss_zzz");
current_date="C:\\Picture\\"+current_date+".jpg";
char* fileName;
QByteArray ba = current_date.toLatin1(); // must
fileName=ba.data();
if(NET_DVR_CaptureJPEGPicture(userID,1,&IpJpegPara2,fileName)==false)
{
qDebug() << "NET_DVR_CaptureJPEGPicture error;" << "error number is " << NET_DVR_GetLastError();
QMessageBox::warning(this,"error","NET_DVR_CaptureJPEGPicture error;error number "+QString::number(NET_DVR_GetLastError()));
return;
}
qDebug() <<"Capture Success!"<
整体流程就是这个样子,下面给出完整代码。
#include "widget.h"
#include
int main(int argc, char *argv[])
{
QApplication a(argc, argv);
Widget w;
w.show();
return a.exec();
}
#ifndef WIDGET_H
#define WIDGET_H
#include
#include
#include "include/HCNetSDK.h"
#include
#include
namespace Ui {
class Widget;
}
class Widget : public QWidget
{
Q_OBJECT
public:
explicit Widget(QWidget *parent = 0);
~Widget();
long play(HWND hWnd,NET_DVR_PREVIEWINFO struPlayInfo);
bool initSDK();
bool uninitSDK();
public slots:
void OnClickedLogin();
void OnClickedLogout();
void OnClickedPreview();
void OnClickedStopPreview();
void OnClickedCapture();
private:
Ui::Widget *ui;
long userID;
long previewID;
};
#endif // WIDGET_H
#include "widget.h"
#include "ui_widget.h"
#include
Widget::Widget(QWidget *parent) :
QWidget(parent),
ui(new Ui::Widget)
{
ui->setupUi(this);
userID=-1;
previewID=-1;
connect(ui->LoginButton,&QPushButton::clicked,this,&Widget::OnClickedLogin);
connect(ui->LogoutButton,&QPushButton::clicked,this,&Widget::OnClickedLogout);
connect(ui->PreviewButton,&QPushButton::clicked,this,&Widget::OnClickedPreview);
connect(ui->StopPreviewButton,&QPushButton::clicked,this,&Widget::OnClickedStopPreview);
connect(ui->CaptureButton,&QPushButton::clicked,this,&Widget::OnClickedCapture);
if(initSDK())
qDebug()<<"SDK init Success!"<label->winId();
NET_DVR_PREVIEWINFO struPlayInfo;
previewID = play(hWnd,struPlayInfo);
if(previewID==-1)
{
return;
}
qDebug()<<"Preview Success!" << userID<
发布应用应当以release模式编译,然后使用windeployqt 工具进行qt依赖项的导出,接着要将海康威视的dll也放入可执行文件目录下,这里为简单起见,直接将下载的海康威视的sdk中的库文件中的所有文件、目录复制到可执行文件目录下,如下图所示:
仍无法实现的,可以下载我打包好的,包括源代码和可执行文件及其依赖项。发布的应用里直接写死了IP和端口,可以在源码中修改为窗体输入。https://download.csdn.net/download/cx296649890/11574696