写在前面

gitee地址如下: Gitee仓库传送门
大华SDK分语言和操作系统,不同的SDK实现方式不太一样,需要先根据自己的需求进行SDK的选取。比如,很多线上服务是部署在Linux系统的,所以开发的时候就尽量少折腾windows系统的东西。

首先,先将大华SDK开发所需的jar包引入,可以参考官网的SDK demo。官网传送门大华SDK官网

以下代码逻辑都是基于大华SDK Linux_64_java 版本的demo进行开发的。

有的人开发是直接把NetSDKLib 这个类里的代码全部复制一遍,这样也行,不过导入的东西太多太麻烦了。我是直接将SDK打包再引入整个包文件(顺带吐槽一下,64位java版本的Linux demo,官方竟然连个jar包都没有!我是自己使用maven打包然后丢到公司的maven仓库,一同丢进本公司maven仓库的还有dahua-netsdk-jni包。然后再通过maven引入),引包如下:

<dependency><groupId>net.java.dev.jna</groupId><artifactId>jna</artifactId><version>5.4.0</version></dependency><!-- 这是Linux版本的demo--><dependency><groupId>com.dahua.netsdk</groupId><artifactId>dahua-netsdk-linux</artifactId><version>1.0.0</version><classifier>demo</classifier></dependency><!--1.1.0是linux的jni包 --><dependency><groupId>com.dahua.netsdk</groupId><artifactId>dahua-netsdk-jni</artifactId><version>1.1.0</version></dependency>

注:以上3个jar包,除了第一个是在maven的公共仓库都存在的,后面两个都是我从demo里得来,然后扔到自己公司的私服再引入的。

对于订阅智能事件的流程,我引用官网demo的文档里的流程图,如下:

通过流程图可以清晰得看到整个事件的过程。不多说,直接上代码:


@Component
public class DaHuaConnectorManagerImpl implements DaHuaConnectorManager {// 服务是否开启private boolean isStarted = false;private Logger logger = LoggerFactory.getLogger(getClass());// 服务监听回调private StartServiceCallBack serviceCallBack = StartServiceCallBack.getInstance();// 网络断线处理private static DisConnect disConnect = new DisConnect();// 设备连接恢复,实现设备连接恢复接口private static HaveReConnect haveReConnect = new HaveReConnect();@PostConstructpublic void startServer() {// 先初始化sdk,再开启服务的监听SdkUtils.init(disConnect, haveReConnect);logger.info("初始化SDK结束");String ip = this.getIp();int port = Integer.parseInt(System.getenv("PORT"));try {boolean start = SdkUtils.startServer(ip, port, serviceCallBack);if (!start) {logger.error("开启服务失败");} else {isStarted = true;}} catch (Exception e) {logger.error("开启服务异常", e);}}/*** 获取本机ip** @return*/private String getIp(){try{InetAddress addr = InetAddress.getLocalHost();if (addr != null){return addr.getHostAddress();}}catch (Exception e){logger.error("获取本机IP异常",e);}return "";}
}

初始化SDK和开启服务是在springboot项目启动时执行的,在初始化SDK时传入了两个回调disConnecthaveReConnect 即断线回调和重连回调。这两个回调不是必须参数,根据业务场景选择是否传入。我的业务场景是在设备断线或者重连时向自己的平台上报设备状态,所以必传。不过很意外的是,在相机断连时断线回调会执行,但重连后重连回调竟然没有执行啊(暂时未找到原因),所以我只能在相机登录成功后向自己的平台上报一次设备状态。两个回调具体实现如下:

  /*** 设备断线回调: 通过 CLIENT_Init 设置该回调函数,当设备出现断线时,SDK会调用该函数*/public static class DisConnect implements NetSDKLib.fDisConnect {@Overridepublic void invoke(NetSDKLib.LLong m_hLoginHandle, String deviceIP, int port, Pointer dwUser) {logger.info("设备({})端口号({})断连", deviceIP, port);ConcurrentMap<String, DeviceInfo> deviceMap = serviceCallBack.deviceMap;String status = "offline";// 获取设备IDString deviceID = getDeviceIDByIP(deviceMap, deviceIP, status);// 上报设备状态到物联网平台if (!StringUtils.isEmpty(deviceID)) {reportDeviceStatus(deviceID, status);} else {logger.error("通过设备IP获取设备ID失败,设备ID为空,deviceIP={}", deviceIP);}}}
/*** 网络连接恢复,设备重连成功回调,通过 CLIENT_SetAutoReconnect 设置该回调函数,当已断线的设备重连成功时,SDK会调用该函数*/public static class HaveReConnect implements NetSDKLib.fHaveReConnect {@Overridepublic void invoke(NetSDKLib.LLong m_hLoginHandle, String deviceIP, int port, Pointer dwUser) {logger.info("设备({})端口号({})重连", deviceIP, port);ConcurrentMap<String, DeviceInfo> deviceMap = serviceCallBack.deviceMap;String status = "online";// 获取设备IDString deviceID = getDeviceIDByIP(deviceMap, deviceIP, status);// 上报设备状态到物联网平台if (!StringUtils.isEmpty(deviceID)) {reportDeviceStatus(deviceID, status);} else {logger.error("通过设备IP获取设备ID失败,设备ID为空,deviceIP={}", deviceIP);}}}

SdkUtils中服务初始化和开启监听如下:

 /*** 初始化SDK** @param disConnect* @param haveReConnect* @return*/public static boolean init(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect haveReConnect) {bInit = netsdk.CLIENT_Init(disConnect, null);if (!bInit) {logger.info("初始化SDK失败");return false;}// 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,SDK内部会自动进行重连操作netsdk.CLIENT_SetAutoReconnect(haveReConnect, null);//设置登录超时时间和尝试次数,可选int waitTime = 5000; //登录请求响应超时时间设置为5Sint tryTimes = 1;    //登录时尝试建立链接1次netsdk.CLIENT_SetConnectTime(waitTime, tryTimes);// 设置更多网络参数,NET_PARAM的nWaittime,nConnectTryNum成员与CLIENT_SetConnectTime// 接口设置的登录设备超时时间和尝试次数意义相同,可选NetSDKLib.NET_PARAM netParam = new NetSDKLib.NET_PARAM();netParam.nConnectTime = 10000;      // 登录时尝试建立链接的超时时间netParam.nGetConnInfoTime = 3000;   // 设置子连接的超时时间netParam.nGetDevInfoTime = 3000;//获取设备信息超时时间,为0默认1000msnetsdk.CLIENT_SetNetworkParam(netParam);return true;}
 /*** 开启服务** @param address* @param port* @param callback* @return*/public static boolean startServer(String address, int port, fServiceCallBack callback) {mServerHandler = netsdk.CLIENT_ListenServer(address, port, 1000, callback, (Pointer) null);if (0L == mServerHandler.longValue()) {logger.error("开启服务失败,error={},address={},port={}", ToolKits.getErrorCodePrint(),address,port);} else {logger.info("开启服务成功,address={},port={}", address, port);}return mServerHandler.longValue() != 0L;}

服务开启和后还需要开启监听回调函数StartServiceCallBack,实现如下:

package cn.com.egova.connector.module.utils;import cn.com.egova.connector.bean.DeviceInfo;
import com.netsdk.lib.NetSDKLib;
import com.sun.jna.Pointer;
import java.io.UnsupportedEncodingException;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;public class StartServiceCallBack implements NetSDKLib.fServiceCallBack {public static ConcurrentMap<String, DeviceInfo> deviceMap = new ConcurrentHashMap<>();private Logger logger = LoggerFactory.getLogger(getClass());private StartServiceCallBack() {System.out.println("监听回调函数初始化");}private static class CallBackHolder {private static StartServiceCallBack instance = new StartServiceCallBack();}public static StartServiceCallBack getInstance() {return StartServiceCallBack.CallBackHolder.instance;}@Overridepublic int invoke(NetSDKLib.LLong lHandle, String ip, int port, int lCommand, Pointer pParam, int dwParamLen, Pointer dwUserData) {// 将 pParam 转化为序列号byte[] buffer = new byte[dwParamLen];pParam.read(0, buffer, 0, dwParamLen);String deviceId = "";try {deviceId = new String(buffer, "GBK").trim();} catch (UnsupportedEncodingException e) {e.printStackTrace();}DeviceInfo deviceInfo = new DeviceInfo();deviceInfo.setDeviceIp(ip);deviceInfo.setPort(port);// 用户名和密码暂时写死,所有的相机都一样deviceInfo.setUserName("admin");deviceInfo.setPassword("admin");if (deviceMap.containsKey(deviceId)) { // 设备断连后重连deviceInfo.setReconnectStatus(true);}deviceMap.put(deviceId, deviceInfo);logger.info("当前时间:{},注册设备地址:{},端口:{},deviceID:{}", SdkUtils.getTime(System.currentTimeMillis()), ip, port, deviceId);return 0;}
}

import java.io.Serializable;
import lombok.Data;/*** 设备信息** @author :* @date :Created in 2022/5/17 18:00*/
@Data
public class DeviceInfo implements Serializable {private String deviceIp;private int port;private String userName;private String password;private int eventType;private boolean reconnectStatus;
}

当大华相机开启主动注册的功能后,相机通电通网,会执行这个回调,在回调中我打印了相机的相关信息并将相机存到map中,以供后续的逻辑使用。当然,如果没有什么需求,这个回调可以什么都不写,直接返回一个int值。

接下来是相机的登录,因为线上可能是很多个相机连接同一个服务,而相机什么时候连接也是未知的,所以我的思路是,先在上一步的回调中将相机的基本信息保存下来,然后开启一个定时器任务,每分钟执行一次相机登录的定时器任务,遍历map,去登录相机。代码如下:

mport java.util.Timer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.CommandLineRunner;
import org.springframework.stereotype.Component;@Component
public class StartTask implements CommandLineRunner {@Autowiredprivate ServiceTask serviceTask;@Overridepublic void run(String... args) throws Exception {Timer timer = new Timer();// 服务启动后间隔1分钟执行一次,以后每分钟会执行一次timer.scheduleAtFixedRate(serviceTask, 60 * 1000, 60 * 1000);}
}
package cn.com.egova.connector.module.task;/*** @author :* @date :Created in 2022/7/6 17:41*/import cn.com.egova.connector.bean.DeviceInfo;
import cn.com.egova.connector.module.utils.AnalyzerDataCallBack;
import cn.com.egova.connector.module.utils.SdkUtils;
import cn.com.egova.connector.module.utils.StartServiceCallBack;
import com.netsdk.lib.NetSDKLib;
import com.netsdk.lib.NetSDKLib.LLong;
import java.util.HashMap;
import java.util.Map;
import java.util.TimerTask;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;@Component
public class ServiceTask extends TimerTask {private static Logger logger = LoggerFactory.getLogger(ServiceTask.class);// 设备信息public static ConcurrentMap<String, DeviceInfo> deviceMap = new ConcurrentHashMap<>();// 设备是否登录public static Map<String, Boolean> deviceLoginMap = new HashMap<>();// 服务监听回调private StartServiceCallBack serviceCallBack = StartServiceCallBack.getInstance();// 订阅智能事件回调private AnalyzerDataCallBack dataCallBack = AnalyzerDataCallBack.getInstance();@Overridepublic void run() {long currentTimeStamp = System.currentTimeMillis();deviceMap = serviceCallBack.deviceMap;if (!deviceMap.isEmpty()) {  // 设备太多可以考虑开多线程实现for (String deviceID : deviceMap.keySet()) {try {Boolean isLogin = deviceLoginMap.getOrDefault(deviceID, false);DeviceInfo deviceInfo = deviceMap.getOrDefault(deviceID, null);if (deviceInfo != null) {if (!isLogin || deviceInfo.isReconnectStatus()) { // 未登录相机或相机重连时需要登录// 登录相机LLong loginHandle = SdkUtils.login(deviceInfo.getDeviceIp(), deviceInfo.getPort(), deviceInfo.getUserName(), deviceInfo.getPassword(),deviceID);if (loginHandle.intValue() == 0) {logger.error("登录相机失败,deviceID={},ip={},port={}", deviceID, deviceInfo.getDeviceIp(),deviceInfo.getPort());continue;} else {// 登录成功后将相机登录状态保存在map中deviceLoginMap.put(deviceID, true);deviceInfo.setReconnectStatus(false);// 登录成功后开启订阅事件int channelID = 0; // 默认通道IDint bNeedPicture = 1; // 是否有图 0-无图 1-有图LLong attachHandle = SdkUtils.realLoadPicture(loginHandle, channelID, NetSDKLib.EVENT_IVS_ALL, // 订阅所有的事件bNeedPicture, dataCallBack, deviceID);if (attachHandle.intValue() == 0) {logger.error("开启智能订阅事件失败,deviceID={}", deviceID);continue;}                       }}}} catch (Exception e) {logger.error("相机登录异常,deviceID={}", deviceID, e);}}}
//        logger.info("结束执行相机登录的定时器任务,耗时{}ms",System.currentTimeMillis()-currentTimeStamp);}
}
 /*** 登录** @param ip* @param port* @param userName* @param password* @param deviceIDs* @return*/public static LLong login(String ip, int port, String userName, String password, String deviceIDs) {//入参NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam = new NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();Pointer deviceId = ToolKits.GetGBKStringToPointer(deviceIDs);pstInParam.nPort = port;pstInParam.szIP = ip.getBytes();pstInParam.szPassword = password.getBytes();pstInParam.szUserName = userName.getBytes();pstInParam.emSpecCap = NetSDKLib.EM_LOGIN_SPAC_CAP_TYPE.EM_LOGIN_SPEC_CAP_SERVER_CONN; // 主动注册登录pstInParam.pCapParam = deviceId;// 出参NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam = new NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();try {m_stDeviceInfo.sSerialNumber = deviceIDs.getBytes("GBK");} catch (Exception e) {logger.error("转换设备ID异常,deviceID={}", deviceIDs, e);}pstOutParam.stuDeviceInfo = m_stDeviceInfo;m_hLoginHandle = netsdk.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);long value = m_hLoginHandle.longValue();if (value == 0) {logger.error("登录失败,ip={},port={},error={}", ip, port, ToolKits.getErrorCodePrint());} else {logger.info("设备登录成功。ip={},port={}", ip, port);// 登录成功后像物联网平台上报设备状态变化reportDeviceStatus(deviceIDs, "online");}return m_hLoginHandle;}

在相机登录成功后订阅事件,我订阅的是所有的事件,所以当相机触发了相应事件后,会通过回调将事件的一些信息返回回来。比如车位事件,会将车位上车的信息以指针形式返回,然后由开发者自己去实现一些相应的功能。车位事件的回调如下:

public class AnalyzerDataCallBack implements NetSDKLib.fAnalyzerDataCallBack {private static class CallBackHolder {private static AnalyzerDataCallBack instance = new AnalyzerDataCallBack();}public static AnalyzerDataCallBack getInstance() {return CallBackHolder.instance;}private static Logger logger = LoggerFactory.getLogger(AnalyzerDataCallBack.class);@Overridepublic int invoke(LLong lAnalyzerHandle, int eventType,Pointer alarmInfo, Pointer pBuffer, int dwBufSize,Pointer dwUser, int nSequence, Pointer reserved) {logger.info("订阅智能事件回调,订阅类型:{}", getEventType(eventType));if (lAnalyzerHandle.intValue() == 0) {return -1;}if (eventType == NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACEPARKING || eventType ==NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACENOPARKING) { // 车位有车和无车TrafficInfo trafficInfo = getTrafficInfo(alarmInfo, eventType);if (trafficInfo == null) {return -1;}// 转换车位图片,String picture = revertPicture(pBuffer, dwBufSize);if (!StringUtils.isEmpty(picture)) {trafficInfo.setPictures(picture);}// 将车位事件上报到云平台reportCameraRecord(trafficInfo, eventType);}return 0;}/*** 转换车位事件数据信息** @param alarmInfo* @param eventType* @return*/public TrafficInfo getTrafficInfo(Pointer alarmInfo, int eventType) {logger.info("转换车位事件数据,事件类型:{}", getEventType(eventType));TrafficInfo trafficInfo = new TrafficInfo();if (alarmInfo == null) {return trafficInfo;}switch (eventType) {case (NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACEPARKING): // 车位有车{NetSDKLib.DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO msg = new NetSDKLib.DEV_EVENT_TRAFFIC_PARKINGSPACEPARKING_INFO();ToolKits.GetPointerData(alarmInfo, msg);trafficInfo.eventName = Res.string().getEventName(NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACEPARKING);try {// windows 是GBK,Linux是UTF-8trafficInfo.plateNumber = new String(msg.stuObject.szText, "UTF-8").trim();} catch (Exception e) {logger.error("车位有车时转换车牌号失败", e);}trafficInfo.plateType = new String(msg.stTrafficCar.szPlateType).trim();trafficInfo.fileCount = String.valueOf(msg.stuFileInfo.bCount);trafficInfo.fileIndex = String.valueOf(msg.stuFileInfo.bIndex);trafficInfo.groupID = String.valueOf(msg.stuFileInfo.nGroupId);trafficInfo.illegalPlace = ToolKits.GetPointerDataToByteArr(msg.stTrafficCar.szDeviceAddress);trafficInfo.laneNumber = String.valueOf(msg.nLane);trafficInfo.plateColor = new String(msg.stTrafficCar.szPlateColor).trim();trafficInfo.vehicleColor = new String(msg.stTrafficCar.szVehicleColor).trim();trafficInfo.vehicleType = this.convertCarType(new String(msg.stuVehicle.szObjectSubType).trim());trafficInfo.vehicleSize = Res.string().getTrafficSize(msg.stTrafficCar.nVehicleSize);trafficInfo.happenTime = this.getTime(msg.UTC);trafficInfo.picEnable = msg.stuObject.bPicEnble;trafficInfo.offSet = msg.stuObject.stPicInfo.dwOffSet;trafficInfo.fileLength = msg.stuObject.stPicInfo.dwFileLenth;trafficInfo.boundingBox = msg.stuObject.BoundingBox;trafficInfo.actionCredible = msg.stuObject.nConfidence;trafficInfo.parkingSpaceCode = new String(msg.szParkingNum).replaceAll("\\u0000", "");String deviceID = getDeviceID(trafficInfo.parkingSpaceCode);if (StringUtils.isEmpty(deviceID)) {logger.error("获取设备ID失败,设备ID为空");return null;} else {trafficInfo.deviceID = deviceID;}break;}case (NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACENOPARKING): // 车位无车{NetSDKLib.DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO msg = new NetSDKLib.DEV_EVENT_TRAFFIC_PARKINGSPACENOPARKING_INFO();ToolKits.GetPointerData(alarmInfo, msg);trafficInfo.eventName = Res.string().getEventName(NetSDKLib.EVENT_IVS_TRAFFIC_PARKINGSPACENOPARKING);try {// windows 是GBK,Linux是UTF-8trafficInfo.plateNumber = new String(msg.stuObject.szText, "UTF-8").trim();} catch (Exception e) {logger.error("车位无车时转换车牌号失败", e);}trafficInfo.plateType = new String(msg.stTrafficCar.szPlateType).trim();trafficInfo.fileCount = String.valueOf(msg.stuFileInfo.bCount);trafficInfo.fileIndex = String.valueOf(msg.stuFileInfo.bIndex);trafficInfo.groupID = String.valueOf(msg.stuFileInfo.nGroupId);trafficInfo.illegalPlace = ToolKits.GetPointerDataToByteArr(msg.stTrafficCar.szDeviceAddress);trafficInfo.laneNumber = String.valueOf(msg.nLane);trafficInfo.plateColor = new String(msg.stTrafficCar.szPlateColor).trim();trafficInfo.vehicleColor = new String(msg.stTrafficCar.szVehicleColor).trim();trafficInfo.vehicleType = this.convertCarType(new String(msg.stuVehicle.szObjectSubType).trim());trafficInfo.vehicleSize = Res.string().getTrafficSize(msg.stTrafficCar.nVehicleSize);trafficInfo.happenTime = this.getTime(msg.UTC);trafficInfo.picEnable = msg.stuObject.bPicEnble;trafficInfo.offSet = msg.stuObject.stPicInfo.dwOffSet;trafficInfo.fileLength = msg.stuObject.stPicInfo.dwFileLenth;trafficInfo.boundingBox = msg.stuObject.BoundingBox;trafficInfo.actionCredible = msg.stuObject.nConfidence;trafficInfo.parkingSpaceCode = new String(msg.szParkingNum).replaceAll("\\u0000", "");String deviceID = getDeviceID(trafficInfo.parkingSpaceCode);if (StringUtils.isEmpty(deviceID)) {logger.error("获取设备ID失败,设备ID为空");return null;} else {trafficInfo.deviceID = deviceID;}break;}}return trafficInfo;}/** 车位事件信息 */@Datapublic static class TrafficInfo {@SuppressWarnings("FieldCanBeLocal")private String eventName;               // 事件名称private String plateNumber;             // 车牌号private String plateType;               // 车牌类型private String plateColor;              // 车牌颜色private String vehicleColor;            // 车身颜色private String vehicleType;             // 车身类型private String vehicleSize;             // 车辆大小private String fileCount;               // 文件总数private String fileIndex;               // 文件编号private String groupID;                 // 组IDprivate String illegalPlace;            // 违法地点private String laneNumber;              // 通道号private String happenTime;              // 事件时间private int picEnable;                  // 车牌对应信息,BOOL类型private int offSet;                     // 车牌偏移量private int fileLength;                 // 文件大小private NetSDKLib.DH_RECT boundingBox;  // 包围盒private int actionCredible;             // 置信度private String parkingSpaceCode;        // 车位编号private String pictures;                // 全景图private String deviceID;                // 设备IDprivate double[] coordinate;            // 坐标}/*** 从UTC结构体中转换时间** @param utc* @return*/private String getTime(NET_TIME_EX utc) {String time = "";if (utc != null) {time = utc.dwYear + "-" + convertNumber(utc.dwMonth) + "-" + convertNumber(utc.dwDay) + " " + convertNumber(utc.dwHour) + ":" +convertNumber(utc.dwMinute) + ":" + convertNumber(utc.dwSecond);}return time;}/*** 转换标识日期的数字,小于10的数字前面补0** @param number* @return*/private String convertNumber(int number) {if (number < 10) {return "0" + number;}return String.valueOf(number);}/*** 事件类型转换** @param eventType* @return*/private String getEventType(int eventType) {if (eventType == 299) {return "车位有车事件";} else if (eventType == 300) {return "车位无车事件";}else if(eventType == 264){return  "违停事件";}else if(eventType == 358){return "逆行事件";}else if(eventType == 261){return "掉头事件";}else if(eventType == 257 || eventType == 267){return "压线事件";}else if(eventType == 266){return "变道事件";}return "未知事件";}/*** 转换车牌颜色** @param color* @return*/private int convertPlateColor(String color){switch (color){case "blue":return 1;case "white":return 2;case "black":return 3;case "yellow":return 4;case "green":return 5;default:return 0;}}}

说下个人踩过的坑:
1 Windows和Linux版本的jni不一样,如果是项目上线环境是ubuntu或者cent os,建议开发时不要折腾Windows版本了哈。
2 相机登录时的ip和端口号以回调中的为准,相机的端口号在未登录前会不断变化,所以不要指定相机登录时的port,不然会登录不上哈。当然了,回调的前提是在大华相机后台开启了主动注册功能,如下图:

3 部署到测试环境或线上环境,不同的镜像可能也会影响sdk内部jna的运行。我打包的dockfile文件内容如下:

其中用的java jre是 openjdk:8-jre。

写在后面

大华SDK java实现车位和违停事件相关推荐

  1. 大华SDK+JAVA+4g网络摄像头进行二次开发

    前言 监控,相信大家都不陌生.现在的监控技术发展迅速,国内以海康威视为首的智能视频监控提供商也层出不穷.现在,这些提供商都已经提供了相应的SDK以及API接口,能够很好的支撑我们进行摄像机的二次开发工 ...

  2. java对接大华SDK摄像头监控

    java对接大华SDK摄像头监控 SDK对接 java对接大华SDK摄像头监控 前言 获取SDK 配置环境变量 拉取实时监控数据 补充 前言 小编在接到对接大华摄像头实时监控在网页显示直播视频,先是在 ...

  3. 大华 sdk java_[日常总结] 大华SDK基于Java的二次开发实现Restful Api

    最近半个月在对接大华摄像头,要实现摄像头的实时预览.云台控制和截图等功能.在网上找了很多资源也没有找到想要的,所以自己写了工具类.简单陈述一下基于大华的官网SDK实现摄像头的云台控制和抓图.无非就是调 ...

  4. 大华sdk转springboot项目 (刷卡、刷人脸、刷二维码、刷身份证)

    最近公司有个项目需要对接设备扫粤康码! 管理层选择了大华, 拿回来了大华的1台设备给我调试, 因健康码不允许转发 大华那边无法帮我处理, 所以只能申请数字平台后自己对接, 目前我这边的业务流程是: 二 ...

  5. 大华摄像头java开发之抓图

    大华摄像头java开发之抓图 继上一个海康的项目之后,这个月开始了大华网络摄像头的二次开发,在写正式的内容之前先容我吐槽一下: 首先就是从官网下载的Demo,里面只有C写的,让我一个java开发怎么看 ...

  6. 大华硬盘录像机通过大华SDK接入EasyCVR,为何出现无法在线的情况?

    EasyCVR视频融合云平台开放度高.兼容性强.可支持灵活拓展与第三方集成,目前已经成为安防市场主流的视频能力层服务平台.平台可支持多协议.多类型的设备接入,包括国标GB28181.RTSP/Onvi ...

  7. 视频融合协议安防监控系统EasyCVR支持大华SDK接入设备录像下载流程说明

    上一篇我们讲了EasyCVR视频协议融合平台已经支持了大华SDK协议设备的接入,并且分享了录像回放流程,目前其他私有协议仍在拓展和开发当中.本文我们再分享一下EasyCVR接入大华SDK协议设备后的录 ...

  8. 海康SDK/大华SDK安防视频智能分析平台EasyCVR如何将通道视频流推送至CDN分发?

    EasyCVR视频协议融合平台不仅支持常规协议的接入,我们还拓展了海康SDK.大华SDK.Ehome等私有协议.如果前端设备协议不同且数量较多,则可以通过EasyCVR平台来对所有视频通道做整合和统一 ...

  9. EasyCVR通过大华SDK接入设备不能进行云台控制,如何判断是否为设备问题?

    EasyCVR视频融合云服务支持多协议.多类型的设备接入,包括IPC.NVR.编码器.单兵设备.移动执法仪等常见的视频源设备,可覆盖市面上大多数厂商的视频监控等设备.平台具备强大的视频汇聚与管理.数据 ...

最新文章

  1. python编程入门经典 评分-豆瓣评分爆炸!Python+机器学习经典图书
  2. PostgreSQL的 array_to_string 功能
  3. 1.12 Java空对象(null)是怎么回事?
  4. SpringBoot中使用fastjson将map转换成json
  5. php从入门到精通+视频,PHP从入门到精通2015版(初级)
  6. linux源码安装必备条件,玩转Linux必备的金钥匙之源码安装mysql
  7. JAVA面试-HIBERNATE与 MYBATIS的对比
  8. 2015年6月24日日报
  9. Encoding非常用编码转换
  10. hwclock(Linux)
  11. 钉钉总裁不穷:周末最烦写周报还有被人钉
  12. 网络运维工程师常见的面试题及需掌握的知识点
  13. 谈谈蛋疼的问题:里式替换原则:正方形是长方形吗?
  14. 秀米的对话框格子可以变大吗_更新丨秀米图文可以一键兼容多格式发布到其他平台了!...
  15. Python实现摄像头实时人脸检测
  16. 成就系统实现(四)-测试和总结
  17. 2021年部分漏洞整合+检测工具
  18. 生成带logo图像的二维码
  19. 关于WIN7输入法的小问题
  20. 三羊献瑞-第六届蓝桥杯省赛

热门文章

  1. 优化算法 - 动量法
  2. Spring 之官方中文文档
  3. Apache Geode 2.11 运行Geode服务器进程
  4. Wikidata 模型分析+实体抽取+数据处理
  5. java计算机毕业设计网络游戏论坛平台源码+系统+数据库+lw文档+mybatis+运行部署
  6. 大数据画像 | 爬下十万产品经理信息,我了解到...(多图表)
  7. python矩阵转置_867. 矩阵转置(Python)
  8. 祖国 你好的python语句_“祖国,你好!”综合性学习活动试题及答案
  9. 盈透证券 简单API 实战
  10. Java泛型概念及作用