目录
1、流程图
1.1、录像查找
1.2、录下下载
2、按时间下载
2.1、开启下载
2.2、后台下载
2.2.1、方式一
2.2.2、方式二
3、问题整理
3.1、错误码34
3.2、错误码10
3.3、下载的文件大小为0kb
4、错误码
由于没有在官方文档中找到通过ISAPI协议透传实现按时间下载文件的方式。所以采取了SDK本地调用方式。
1、流程图
按照官方文档中描述的流程,下载文件分为按文件名和时间两种方式。本文仅以按时间段下载录像文件。
1.1、录像查找
1.2、录下下载
在官方的设备网络SDK使用手册_V6.1版本中,用例代码使用C++语法实现录像下载功能。实际上使用Java改写,调用时存在错误。
2、按时间下载
前期准备的初始化,登录模块请参考之前文章 的后半部分海康威视-NVR使用及ISAPI协议透传接入-CSDN博客
以下是核心代码:
public static void main(String[] args) {
boolean flag = init();
if(!flag){
return ;
}
// 登录
String sDeviceIP = "192.168.1.1";//登录设备IP地址
String sUsername = "admin";//设备用户名
String sPassword = "admin";//设备密码
flag = loginByV40(sDeviceIP, sUsername, sPassword);
if(!flag){
log.info("登录nvr失败");
return ;
}
// 设置查找范围
Calendar start = Calendar.getInstance();
start.setTime(DateUtil.parse("2024-06-15 08:00:00", DatePattern.NORM_DATETIME_PATTERN));
Calendar end = Calendar.getInstance();
end.setTime(DateUtil.parse("2024-06-15 08:00:10", DatePattern.NORM_DATETIME_PATTERN));
downloadVideoByRangeTime(1, start, end , "D:\\1.mp4");
logout();
}
2.1、开启下载
/**
* 下载指定时间段内的视频
* @param channel 通道号
* @param startTime
* @param endTime
* @param savePath 保存视频的绝对路径,mp4格式
*/
public static boolean downloadVideoByRangeTime(int channel, Calendar startTime, Calendar endTime, String savePath){
// NET_DVR_GetFileByTime_V40
HCNetSDK.NET_DVR_PLAYCOND playcond = new HCNetSDK.NET_DVR_PLAYCOND();
playcond.read();
//通道号 NVR设备路数小于32路的起始通道号从33开始,依次增加
playcond.dwChannel = channel + 32;
playcond.struStartTime.dwYear = startTime.get(Calendar.YEAR);
playcond.struStartTime.dwMonth = startTime.get(Calendar.MONTH)+1;
playcond.struStartTime.dwDay = startTime.get(Calendar.DAY_OF_MONTH);
playcond.struStartTime.dwHour = startTime.get(Calendar.HOUR_OF_DAY);
playcond.struStartTime.dwMinute = startTime.get(Calendar.MINUTE);
playcond.struStartTime.dwSecond = startTime.get(Calendar.SECOND);
//HCNetSDK.NET_DVR_TIME ed = new HCNetSDK.NET_DVR_TIME();
playcond.struStopTime.dwYear = endTime.get(Calendar.YEAR);
playcond.struStopTime.dwMonth = endTime.get(Calendar.MONTH)+1;
playcond.struStopTime.dwDay = endTime.get(Calendar.DAY_OF_MONTH);
playcond.struStopTime.dwHour = endTime.get(Calendar.HOUR_OF_DAY);
playcond.struStopTime.dwMinute = endTime.get(Calendar.MINUTE);
playcond.struStopTime.dwSecond = endTime.get(Calendar.SECOND);
playcond.write();
int code = hCNetSDK.NET_DVR_GetFileByTime_V40(lUserID, savePath, playcond);
if(code < 0){
log.info("初始化下载失败 channel={},playcond={}, code={}", channel, playcond, hCNetSDK.NET_DVR_GetLastError());
return false;
}
return playBackControl(code);
}
2.2、后台下载
2.2.1、方式一
通过循环检查Pos状态判断是否下载完成
private static boolean playBackControl(int hPlayback){
hCNetSDK.NET_DVR_PlayBackControl(hPlayback, HCNetSDK.NET_DVR_PLAYSTART, 0, null);
Date nowTime = new Date(System.currentTimeMillis());
SimpleDateFormat sdFormatter = new SimpleDateFormat("yyyy-MM-dd HH-mm-ss");
System.out.println("开始下载时间:"+sdFormatter.format(nowTime));
while(true){
IntByReference nPos = new IntByReference(0);
hCNetSDK.NET_DVR_PlayBackControl(hPlayback, HCNetSDK.NET_DVR_PLAYGETPOS, 0, nPos);
if (nPos.getValue() > 100)
{
System.out.println( "由于网络原因或DVR忙,下载异常终止!");
break;
}
if (nPos.getValue() == 100)
{
System.out.println("结束下载时间:"+sdFormatter.format(nowTime));
System.out.println("按时间下载结束!");
break;
}
try {
Thread.sleep(50);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
2.2.2、方式二
结合Pos状态,记录下载进度,主要针对大文件反馈结果。
/**
* 控制后台下载过程
*/
private static boolean playBackControl(int hPlayback){
boolean flag = hCNetSDK.NET_DVR_PlayBackControl_V40(hPlayback, HCNetSDK.NET_DVR_PLAYSTART, null, 0, null, null);
if(!flag) {
log.info("Play back control failed code=" + hCNetSDK.NET_DVR_GetLastError());
return false;
}
int nPos = 0;
for(nPos = 0; nPos < 100&&nPos>=0; nPos = hCNetSDK.NET_DVR_GetDownloadPos(hPlayback))
{
log.info("Be downloading... " + nPos);
try {
Thread.sleep(100);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
if(!hCNetSDK.NET_DVR_StopGetFile(hPlayback))
{
log.info("failed to stop get file " + hCNetSDK.NET_DVR_GetLastError());
return false;
}
if(nPos<0||nPos>100)
{
log.info("download err " + hCNetSDK.NET_DVR_GetLastError());
return false;
}
log.info("Be downloading..." + nPos);
return true;
}
3、问题整理
3.1、错误码34
描述:创建文件出错。本地录像、保存图片、获取配置文件和远程下载录像时创建文件失败。
原因:由于windows下,存储的文件路径指定到了C盘,造成无法写入。
解决:选择其他盘符即可
3.2、错误码10
描述:从设备接收数据超时。
原因:由于传入的channelID 设备通道号错误
解决:与ISAPI调用传入的channelID不同,在NVR设备路数小于32路的起始通道号从33开始,依次增加
// channel 为原始的 1、2、3...
playcond.dwChannel = channel + 32;
3.3、下载的文件大小为0kb
原因:传入的起止日期值错误,导致无法查找对应通道的回放录像
解决:由于我在代码中使用了java.util.Calendar 类,该类在查询日期字段值存在差异。
其中 Calendar.MONTH 以0为起点,需要+1
// 以下是年月日,时分秒的查询
playcond.struStartTime.dwYear = startTime.get(Calendar.YEAR);
playcond.struStartTime.dwMonth = startTime.get(Calendar.MONTH)+1;
playcond.struStartTime.dwDay = startTime.get(Calendar.DAY_OF_MONTH);
playcond.struStartTime.dwHour = startTime.get(Calendar.HOUR_OF_DAY);
playcond.struStartTime.dwMinute = startTime.get(Calendar.MINUTE);
playcond.struStartTime.dwSecond = startTime.get(Calendar.SECOND);
4、错误码
以下错误码仅代表 NET_DVR_GetFileByTime_V40 方法
错误类型 | 错误值 | 错误信息 |
---|---|---|
NET_DVR_NOERROR | 0 | 没有错误。 |
NET_DVR_PASSWORD_ERROR | 1 | 用户名密码错误。注册时输入的用户名或者密码错误。 |
NET_DVR_NOINIT | 3 | SDK未初始化。 |
NET_DVR_OVER_MAXLINK | 5 | 连接到设备的用户个数超过最大。 |
NET_DVR_NETWORK_FAIL_CONNECT | 7 | 连接设备失败。设备不在线或网络原因引起的连接超时等。 |
NET_DVR_NETWORK_SEND_ERROR | 8 | 向设备发送失败。 |
NET_DVR_NETWORK_RECV_ERROR | 9 | 从设备接收数据失败。 |
NET_DVR_NETWORK_RECV_TIMEOUT | 10 | 从设备接收数据超时。 |
NET_DVR_COMMANDTIMEOUT | 14 | 设备命令执行超时。 |
NET_DVR_PARAMETER_ERROR | 17 | 参数错误。SDK接口中给入的输入或输出参数为空。 |
NET_DVR_CREATEFILE_ERROR | 34 | 创建文件出错。本地录像、保存图片、获取配置文件和远程下载录像时创建文件失败。 |
NET_DVR_ALLOC_RESOURCE_ERROR | 41 | SDK资源分配错误。 |
NET_DVR_NOENOUGH_BUF | 43 | 缓冲区太小。接收设备数据的缓冲区或存放图片缓冲区不足。 |
NET_DVR_CREATESOCKET_ERROR | 44 | 创建SOCKET出错。 |
NET_DVR_USERNOTEXIST | 47 | 用户不存在。注册的用户ID已注销或不可用。 |
NET_DVR_BINDSOCKET_ERROR | 72 | 绑定套接字失败。 |
NET_DVR_SOCKETCLOSE_ERROR | 73 | socket连接中断,此错误通常是由于连接中断或目的地不可达。 |