一、说明
最近做的一个项目需要调用一个海康威视的摄像头,获取每一帧的数据。刚拿到手的时候有些不知所措,花了两三个小时看了下官方文档,下面分享一下代码。
二、运行环境
- OpenCV库
- 摄像头依赖
其中摄像头依赖可在海康威视官网找到,内容如下:
1 | #头文件 |
三、代码(提供两种方案)
- 方案一:通过“抓拍”接口抓取每一帧(实际基本不能实时,大概最快一秒一帧)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
using namespace std;
int main_old()
{
// 初始化设备
NET_DVR_Init();
//设置连接时间与重连时间
NET_DVR_SetConnectTime(2000, 3); //等待时间,重连次数
NET_DVR_SetReconnect(10000, true); //重连间隔,是否重连
//注册设备
NET_DVR_DEVICEINFO_V30 struDeviceInfo; //定义设备参数的结构体,NET_DVR_Login_V30中初始化
char ip[] = "192.168.0.64";
int port = 8000;
char user[] = "account";
char password[] = "password";
LONG lUserID = NET_DVR_Login_V30(ip, port,user,password, &struDeviceInfo); //注册设备,参数存储在struDeviceInfo中,返回用户ID>=0号
//注册失败
if (lUserID < 0)
{
printf("Login error, %d\n", NET_DVR_GetLastError());
NET_DVR_Cleanup();
return 0;
}
NET_DVR_JPEGPARA JpegPara;
JpegPara.wPicSize = 19; //1920*1080
JpegPara.wPicQuality = 0; //最好质量
const DWORD maxBuffer = 1000000;
char *jpeg = (char*)calloc(sizeof(char), maxBuffer);
DWORD size = 0;
//计算时间
for (int i = 0; i < 100; i++)
{
clock_t start = clock();
if (!NET_DVR_CaptureJPEGPicture_NEW(lUserID, 1, &JpegPara, jpeg, maxBuffer, &size))
{
std::cout << "error:" << NET_DVR_GetLastError() << std::endl;
continue;
}
clock_t end = clock();
std::cout << "帧花费时间:" << ((double)(end - start)) / CLOCKS_PER_SEC << "s" << endl;
}
//图片写入磁盘
if (_access("Pictures", 0) == -1)
{
int i = _mkdir("Pictures");
}
for (int i = 0; i < 1000; i++)
{
if (!NET_DVR_CaptureJPEGPicture_NEW(lUserID, 1, &JpegPara, jpeg, maxBuffer, &size))
{
std::cout << "error:" << NET_DVR_GetLastError() << std::endl;
continue;
}
std::cout <<"size of "<<i<<".jpeg: "<< size <<"Byte" << std::endl;
ofstream outFile;
char fileName[100];
sprintf(fileName, "Pictures\\%d.jpeg", i);
outFile.open(fileName, ios::binary | ios::out);
if (!outFile.is_open())
{
std::cout << "fail to create/open Pictures\\" << i << ".jpeg" << std::endl;
continue;
}
outFile.write(jpeg, size);
outFile.close();
std::cout << std::endl;
}
free(jpeg);
//注销用户
NET_DVR_Logout(lUserID);
//释放SDK资源
NET_DVR_Cleanup();
system("pause");
return 0;
} - 方案二:通过实时回调获取每一帧(基本能做到实时)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
using namespace std;
using namespace cv;
//回调函数声明
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2); // 二级回调
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser); // 数据触发。(V40句柄,数据类型,缓冲区指针,缓冲区大小,用户数据)
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser); // 异常时触发
//void CALLBACK CBFun(long nPort, char *pBuf, long nSize, long nWidth, long nHeight, long nStamp, long nType, long nReceved);
clock_t startTime, endTime;
int main()
{
//初始化摄像头
NET_DVR_Init();
NET_DVR_SetConnectTime(2000, 1); //连接等待2s,重连1次
NET_DVR_SetReconnect(10000, true); //10秒重连,启动重连
//图片写入磁盘,测试使用
if (_access("Pictures_in_T_YV12", 0) == -1)
{
int i = _mkdir("Pictures_in_T_YV12");
}
//定义登录相关参数
char ip[] = "192.168.0.64";
int port = 8000;
char user[] = "account";
char password[] = "password";
NET_DVR_DEVICEINFO_V30 struDeviceInfo; //存储设备相关参数
//启动登录
LONG userID = NET_DVR_Login_V30(ip, port, user, password, &struDeviceInfo); //注册设备,参数存储在struDeviceInfo中,返回用户ID>=0号
//登录结果判断
if (userID < 0)
{
printf("登录摄像头出错, 错误代码:%d\n", NET_DVR_GetLastError());
NET_DVR_Cleanup();
system("pause");
return 0;
}
NET_DVR_SetExceptionCallBack_V30(0, NULL, g_ExceptionCallBack, NULL); //设置异常回调
//定义预览参数
NET_DVR_PREVIEWINFO struPlayInfo = { 0 };
struPlayInfo.hPlayWnd = NULL; //不使用预览界面
struPlayInfo.lChannel = 1; //预览通道号 = 1
struPlayInfo.dwStreamType = 0; //0:主码流
struPlayInfo.dwLinkMode = 0; //0- TCP方式,1- UDP方式,2- 多播方式,3- RTP方式,4-RTP/RTSP,5-RSTP/HTTP
//设置实时回调
LONG handle = NET_DVR_RealPlay_V40(userID, &struPlayInfo, fRealDataCallBack, NULL); //启动实时预览
//预览判断
if (handle < 0)
{
printf("实时预览调用出错,错误代码:%d\n", NET_DVR_GetLastError());
NET_DVR_Logout(userID);
NET_DVR_Cleanup();
system("pause");
return 0;
}
//waitKey();
Sleep(-1);
//相关释放
NET_DVR_StopRealPlay(handle); //关闭预览
NET_DVR_Logout(userID); //注销用户
NET_DVR_Cleanup(); //释放SDK资源
system("pause");
return 0;
}
//实时数据触发调用 (V40句柄,数据类型,缓冲区指针,缓冲区大小,用户数据)
void CALLBACK fRealDataCallBack(LONG lRealHandle, DWORD dwDataType, BYTE *pBuffer, DWORD dwBufSize, void *pUser)
{
static LONG nPort = -1; //实时预览通道号
switch (dwDataType)
{
case NET_DVR_SYSHEAD: //头部数据
if (!PlayM4_GetPort(&nPort)) //申请播放port,存为全局变量,供后面使用
{
break;
}
if (dwBufSize > 0) //数据长度>0
{
if (!PlayM4_SetStreamOpenMode(nPort, STREAME_REALTIME)) //设置流处理模式 STREAME_REALTIME:尽力实时,不阻塞 STREAME_FILE:按照时间戳
{
break;
}
if (!PlayM4_OpenStream(nPort, pBuffer, dwBufSize, 1024 * 100000)) //打开流接口,缓冲区设置为最大
{
break;
}
if (!PlayM4_Play(nPort, NULL)) //开始处理,不设置窗口句柄
{
break;
}
if (!PlayM4_SetDecCallBack(nPort, DecCBFun)) //设置自定义的解码回调函数 DecCBFun
{
break;
}
}
break;
case NET_DVR_STREAMDATA: //流数据
if (dwBufSize > 0 && nPort != -1)
{
if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))
{
cout << "error" << PlayM4_GetLastError(nPort) << endl;
break;
}
}
break;
default: //其他数据
if (dwBufSize > 0 && nPort != -1)
{
if (!PlayM4_InputData(nPort, pBuffer, dwBufSize))
{
break;
}
}
break;
}
}
//实时解码回调
void CALLBACK DecCBFun(long nPort, char * pBuf, long nSize, FRAME_INFO * pFrameInfo, long nReserved1, long nReserved2)
{
static int number = 0;
if (pFrameInfo->nType == T_YV12) //YV12:视频格式 PCM:音频
{
Mat src(pFrameInfo->nHeight + pFrameInfo->nHeight / 2, pFrameInfo->nWidth, CV_8UC1, pBuf);
cvtColor(src, src, CV_YUV2BGR_YV12);
char name[100];
sprintf_s(name, "Pictures_in_T_YV12\\%d.jpg", number); //保存在磁盘为jpg
imwrite(name, src);
number++;
}
}
//异常触发
void CALLBACK g_ExceptionCallBack(DWORD dwType, LONG lUserID, LONG lHandle, void *pUser)
{
switch (dwType)
{
case EXCEPTION_RECONNECT: //预览时触发"重连"信号
printf("设备重新连接,当前时间为:%d\n", (int)time(NULL));
break;
default: //默认不作处理
break;
}
}