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

前言

监控,相信大家都不陌生。现在的监控技术发展迅速,国内以海康威视为首的智能视频监控提供商也层出不穷。现在,这些提供商都已经提供了相应的SDK以及API接口,能够很好的支撑我们进行摄像机的二次开发工作。相信大家都有接触过这么一个需求:利用手机可以自己进行摄像机的录像控制,然后在规定的时间内停止录制,然后将这个时间段内的视频保存下来。方便客户端的客户进行查看相应时间段的检控视频。这样就避免了视频一直录制,存储空间不足以承受如此之大的压力,而且也可以降低对于实时拉流的带宽要求。

本文关键词

JAVA、大华SDK、视频拉流、自定义拉流时长、录像保存、4G网络摄像机

硬件设备

本次所用到的摄像机是大华的4G网络摄像机,此摄像机支持利用SIM卡进行4G上网
大华SDK+JAVA+4g网络摄像头进行二次开发_第1张图片
现在不借助大华自带的软件来控制摄像机,自行开发软件来实现手机端访问摄像头进行拉流的触发,摄像头执行相应的程序进行一段时间的拉流,到了预定的时间进行自动保存视频数据到服务器的相应位置。

技术分析

首先,我们要明确的知道要实现这么一个简单的需求要具备哪些知识。
①手机怎么通过网络触发摄像机?
答:摄像头通过手机进行触发使用get请求的方式访问URL,输入设备ID,告诉服务器我要控制那台摄像头,服务器收到请求进行触发。(此技术属于基本的web开发的基本技能实现起来难度不大)
②通过什么样的方式让摄像头执行自己编写的程序?
答:程序运行在服务器上,同时需要让摄像头能够通过固定IP找到这台服务器。
③摄像头怎么找到服务器?
答:摄像头具备一个功能叫做自动注册(相当于第一次在服务器上使用需要创建一样,让服务器知道这个摄像机)主动注册时输入需要摄像头执行相应程序的服务器的固定IP,以及摄像机的端口号(可自定义)设备登录账号和设备登录密码即可完成注册,这个时候形成长连接就可以相互获取信息和传输数据了,服务器也就可以获取到设备的ID,与手机端发送的设备ID对应就可以触发相应的设备进行工作。

技术逻辑图

使用大华的SDK进行二次开发都会有相应的资料,内部也有很多逻辑流程图,这里提取出相关的那一块内容进行展示:
大华SDK+JAVA+4g网络摄像头进行二次开发_第2张图片

由上图可见,在我们利用手机触发摄像头的时候服务器内程序执行的逻辑是,首先要进行设备的初始化,然后服务器会寻找在记录中是否注册过本台摄像机,是否有此设备的信息,如果没有摄像机的信息需要进行主动注册。这个时候摄像机会执行访问本服务器固定IP的程序进行自动的注册,注册完成后服务器就会收到本台摄像机的设备ID(此时服务器与摄像头已经形成长连接),然后就可以等待客户端利用手机对摄像头进行触发时发出的信号做比对,让对应的摄像头开始工作。最后就是定义录制的时长,和自动保存的功能,完成后会清理内存。

代码实现

前期准备

首先打开我们心爱的idea(我这里使用的是2021 64位版本的)
大华SDK+JAVA+4g网络摄像头进行二次开发_第3张图片
进入到项目列表的窗口,选择New Project 新建立一个Spring Boot项目
大华SDK+JAVA+4g网络摄像头进行二次开发_第4张图片
然后,选择Spring Initializr,在右侧的对话框中进行项目名称的修改(我这里名字为CameraTest),然后根据自己的命名习惯将Group、Artifact、Package name名字做修改(这里仅修改了Grop的名称为com.ruoyi),最后注意自己的java的版本号。修改完后点击Next。
大华SDK+JAVA+4g网络摄像头进行二次开发_第5张图片
创建成功后我们会看到如图所示的界面
src/main/java/com.ruoyi.cameratest/CameraTestApplication
为springboot的启动程序。
大华SDK+JAVA+4g网络摄像头进行二次开发_第6张图片
然后我们需要修改下自己的maven的文件保存的路径,之后此项目所要用到的外部jar包都会保存在这些文件夹下。
①我们点击File/Settings

大华SDK+JAVA+4g网络摄像头进行二次开发_第7张图片
②在搜索框内搜索maven会出现如图所示的内容(Maven home path 为当时我们下载maven的源文件文件位置;User settings file首先找到自己的maven安装位置下conf文件下的settings.xml文件;Local repository找到自己maven安装位置下的自己创建的一个用于保存第三方jar包的文件夹)
大华SDK+JAVA+4g网络摄像头进行二次开发_第8张图片
例如我的maven安装在D:\SoftWares\apache-maven-3.6.1配置如下,完成后点击Apply,点击OK完成设置。
大华SDK+JAVA+4g网络摄像头进行二次开发_第9张图片
最后我们修改pom.xml文件
大华SDK+JAVA+4g网络摄像头进行二次开发_第10张图片
pom.xml

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 https://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>
    <groupId>com.ruoyi</groupId>
    <artifactId>CameraTest</artifactId>
    <version>0.0.1-SNAPSHOT</version>

    <name>CameraTest</name>
    <description>Demo project for Spring Boot</description>

    <properties>
        <java.version>1.8</java.version>
        <spring-boot.version>2.3.7.RELEASE</spring-boot.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter</artifactId>
        </dependency>

        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
            <scope>test</scope>
        </dependency>

        <dependency>
            <groupId>net.java.dev.jna</groupId>
            <artifactId>jna</artifactId>
            <version>5.4.0</version>
        </dependency>
        <dependency>
            <groupId>com.dahua.netsdk</groupId>
            <artifactId>dahua-netsdk-jni</artifactId>
            <version>1.0.0</version>
        </dependency>
        <!--lombok插件-->
        <dependency>
            <groupId>org.projectlombok</groupId>
            <artifactId>lombok</artifactId>
            <version>1.18.12</version>
        </dependency>
        <!-- Mysql驱动包 -->
        <dependency>
            <groupId>mysql</groupId>
            <artifactId>mysql-connector-java</artifactId>
        </dependency>
        <!-- web -->
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
    </dependencies>

    <dependencyManagement>
        <dependencies>
            <dependency>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-dependencies</artifactId>
                <version>${spring-boot.version}</version>
                <type>pom</type>
                <scope>import</scope>
            </dependency>
        </dependencies>
    </dependencyManagement>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

这里我们修改了pom.xml文件后需要进行刷新,最后测试一下是否可以成功启动springboot
大华SDK+JAVA+4g网络摄像头进行二次开发_第11张图片
再来就是进入大华公司的官网找到对应的SDK进行下载
在这里插入图片描述

1.消息接收实现类和接口

首先创建如下图所示的文件和类
大华SDK+JAVA+4g网络摄像头进行二次开发_第12张图片
instructController.java

package com.ruoyi.controller;


import com.ruoyi.service.instructService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.HashMap;
import java.util.Map;

@RestController
@RequestMapping("/test")
public class instructController {
    @Autowired
    private instructService service;

    public String VideoPath;
    public String getVideoPath() {
        return VideoPath;
    }
    public void setVideoPath(String videoPath) {
        VideoPath = videoPath;
    }

    @GetMapping("/ss")
    public Map<String,Object> SendMessage(PreInfo info){
        service.SendMessage(info);
        instructController instr = new instructController();

        Map<String, Object> result = new HashMap<String, Object>();
        result.put("videopath", instr.getVideoPath());
        return result;
    }


}

PreInfo.java

package com.ruoyi.controller;

import lombok.Data;

@Data
public class PreInfo {
    private String PreDeviceId;
}

instructService.java

package com.ruoyi.service;

import com.ruoyi.controller.PreInfo;

public interface instructService {
    void SendMessage(PreInfo info);
}

ServiceCB.java

package com.ruoyi.demo.frame;

import com.ruoyi.controller.PreInfo;
import com.ruoyi.service.instructService;
import org.springframework.stereotype.Component;

@Component
public class ServiceCB implements instructService {
    @Override
    public void SendMessage(PreInfo info) {
        System.out.println("成功触发!");
    }
}

2.主动注册

首先在src/mian/resources文件中将SDK中的dynamic-lib-load.xml文件复制过来,这个文件是为了满足不同平台下需要加载的C动态库

dynamic-lib-load.xml

<?xml version="1.0" encoding="UTF-8" ?>
<dynamic-lib>
    <win64>
        <lib>avnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>dhplay</lib>
        <lib>ImageAlg</lib>
        <lib>Infra</lib>
        <lib>IvsDrawer</lib>
        <lib>StreamConvertor</lib>
        <lib>jninetsdk</lib>
    </win64>
    <win32>
        <lib>avnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>dhplay</lib>
        <lib>Infra</lib>
        <lib>ImageAlg</lib>
        <lib>StreamConvertor</lib>
        <lib>jninetsdk</lib>
    </win32>
    <linux64>
        <lib>avnetsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>StreamConvertor</lib>
        <lib>jninetsdk</lib>
    </linux64>
    <linux32>
        <lib>avnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>StreamConvertor</lib>
        <lib>jninetsdk</lib>
    </linux32>
    <mac64>
        <lib>avnetsdk</lib>
        <lib>dhnetsdk</lib>
        <lib>dhconfigsdk</lib>
        <lib>StreamConvertor</lib>
    </mac64>
</dynamic-lib>

接着在项目目录中新建子文件夹com.ruoyi/lib
大华SDK+JAVA+4g网络摄像头进行二次开发_第13张图片

打开我们的大华SDK项目在一个新的窗口中,将我们需要的实现类拷贝过来。如下图是大华SDK-java-x64版本的文件目录。
大华SDK+JAVA+4g网络摄像头进行二次开发_第14张图片
首先我们将SDK中的lib文件中的所有文件全部拷贝到刚建立的lib中。(如果复制过来出现报错,检查import 中的文件路径名称,因为SDK中的项目名称为com.netsdk我们的不是所以需要修改)。后面功能实现类,由于官方的SDK中的实现类功能太多太杂,我做了整理,大家可以复制我整理后的代码。首先我们需要主动注册的实现类AutoRegisterModule.java,登录设备的实现类LoginModule.java,设备的信息类DeviceInfo.java

AutoRegisterModule.java

package com.ruoyi.demo.module;

import com.ruoyi.demo.module.LoginModule;
import com.ruoyi.lib.NetSDKLib.LLong;
import com.ruoyi.lib.NetSDKLib.fServiceCallBack;
import com.ruoyi.lib.NetSDKLib;
import com.ruoyi.lib.ToolKits;

public class AutoRegisterModule {

    // 监听服务句柄
    public static LLong mServerHandler = new LLong(0);

    // 设备信息
    public static NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();

    /**
     * 开启服务
     *
     * @param address  本地IP地址
     * @param port     本地端口, 可以任意
     * @param callback 回调函数
     */
    public static boolean startServer(String address, int port, fServiceCallBack callback) {
        //调用监听接口实现服务的开启
        mServerHandler = LoginModule.netsdk.CLIENT_ListenServer(address, port, 1000, callback, null);
        if (0 == mServerHandler.longValue()) {
            System.err.println("Failed to start server." + ToolKits.getErrorCodePrint());
        } else {
            System.out.printf("Start server, [Server address %s][Server port %d]\n", address, port);
        }
        return mServerHandler.longValue() != 0;
    }

    /**
     * 结束服务
     */
    public static boolean stopServer() {
        boolean bRet = false;

        if (mServerHandler.longValue() != 0) {
            bRet = LoginModule.netsdk.CLIENT_StopListenServer(mServerHandler);
            mServerHandler.setValue(0);
            System.out.println("Stop server!");
        }

        return bRet;
    }

}

LoginModule.java

package com.ruoyi.demo.module;

import com.ruoyi.lib.NetSDKLib;
import com.ruoyi.lib.NetSDKLib.LLong;
import com.ruoyi.lib.NetSDKLib.NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY;
import com.ruoyi.lib.NetSDKLib.NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY;
import com.ruoyi.lib.ToolKits;

import java.io.File;

/**
 * 登陆接口实现
 * 主要有 :初始化、登陆、登出功能
 */
public class LoginModule {

	public static NetSDKLib netsdk 		= NetSDKLib.NETSDK_INSTANCE;

	// 设备信息
	public static NetSDKLib.NET_DEVICEINFO_Ex m_stDeviceInfo = new NetSDKLib.NET_DEVICEINFO_Ex();
	
	// 登陆句柄
	public static LLong m_hLoginHandle = new LLong(0);   
	
	private static boolean bInit    = false;
	private static boolean bLogopen = false;
	
	/**
	 * 初始化
	 */
	public static boolean init(NetSDKLib.fDisConnect disConnect, NetSDKLib.fHaveReConnect haveReConnect) {	
		bInit = netsdk.CLIENT_Init(disConnect, null);
		if(!bInit) {
			System.out.println("Initialize SDK failed");
			return false;
		}

		//打开日志,可选
		NetSDKLib.LOG_SET_PRINT_INFO setLog = new NetSDKLib.LOG_SET_PRINT_INFO();
        File path = new File("./sdklog/");
        if (!path.exists()) {
            path.mkdir();
        }
		String logPath = path.getAbsoluteFile().getParent() + "\\sdklog\\" + ToolKits.getDate() + ".log";
		setLog.nPrintStrategy = 0;
		setLog.bSetFilePath = 1;
		System.arraycopy(logPath.getBytes(), 0, setLog.szLogFilePath, 0, logPath.getBytes().length);
		System.out.println(logPath);
		setLog.bSetPrintStrategy = 1;
		bLogopen = netsdk.CLIENT_LogOpen(setLog);
		if(!bLogopen ) {
			System.err.println("Failed to open NetSDK log");
		}
		
		// 设置断线重连回调接口,设置过断线重连成功回调函数后,当设备出现断线情况,SDK内部会自动进行重连操作
		// 此操作为可选操作,但建议用户进行设置
		netsdk.CLIENT_SetAutoReconnect(haveReConnect, null);
	    
		//设置登录超时时间和尝试次数,可选
		int waitTime = 5000; //登录请求响应超时时间设置为5S
		int 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默认1000ms
		netsdk.CLIENT_SetNetworkParam(netParam);	
		
		return true;
	}
	
	/**
	 * 清除环境
	 */
	public static void cleanup() {
		if(bLogopen) {
			netsdk.CLIENT_LogClose();
		}
		
		if(bInit) {
			netsdk.CLIENT_Cleanup();
		}
	}

	/**
	 * 登录设备
	 */
	public static boolean login(String m_strIp, int m_nPort, String m_strUser, String m_strPassword) {	
		//IntByReference nError = new IntByReference(0);
		//入参
		NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY pstInParam=new NET_IN_LOGIN_WITH_HIGHLEVEL_SECURITY();
		pstInParam.nPort=m_nPort;
		pstInParam.szIP=m_strIp.getBytes();
		pstInParam.szPassword=m_strPassword.getBytes();
		pstInParam.szUserName=m_strUser.getBytes();
		//出参
		NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY pstOutParam=new NET_OUT_LOGIN_WITH_HIGHLEVEL_SECURITY();
		pstOutParam.stuDeviceInfo=m_stDeviceInfo;
		//m_hLoginHandle = netsdk.CLIENT_LoginEx2(m_strIp, m_nPort, m_strUser, m_strPassword, 0, null, m_stDeviceInfo, nError);		
		m_hLoginHandle=netsdk.CLIENT_LoginWithHighLevelSecurity(pstInParam, pstOutParam);
		if(m_hLoginHandle.longValue() == 0) {
			System.err.printf("Login Device[%s] Port[%d]Failed. %s\n", m_strIp, m_nPort, ToolKits.getErrorCodePrint());
		} else {
			System.out.println("Login Success [ " + m_strIp + " ]");
		}	
		
		return m_hLoginHandle.longValue() == 0? false:true;
	}
	
	/**
	 * 登出设备
	 */
	public static boolean logout() {
		if(m_hLoginHandle.longValue() == 0) {
			return false;
		}
		
		boolean bRet = netsdk.CLIENT_Logout(m_hLoginHandle);
		if(bRet) {			
			m_hLoginHandle.setValue(0);	
		}
		
		return bRet;
	}
}

DeviceInfo.java

package com.ruoyi.demo.module;
import com.ruoyi.lib.NetSDKLib;
import com.ruoyi.lib.NetSDKLib.LLong;


public class DeviceInfo {

    
    private String m_strIp;
    private int m_nPort;
    private String m_strUser;
    private String m_strPassword;
    private String m_srtId;
    

    public DeviceInfo(String m_strIp, int m_nPort, String m_strUser, String m_strPassword) {
        this.m_strIp = m_strIp; //设备ip
        this.m_nPort = m_nPort; //设备端口号
        this.m_strUser = m_strUser;  //设备登录用户名
        this.m_strPassword = m_strPassword;  //设备登陆密码
    }

    public DeviceInfo() {
    }

    //获取设备ID
    public String getM_srtId() {
        return m_srtId;
    }
    //设置设备ID
    public void setM_srtId(String m_srtId) {
        this.m_srtId = m_srtId;
    }
    //获取设备用户名
    public String getM_strUser() {
        return m_strUser;
    }
    //设置设备用户名
    public void setM_strUser(String m_strUser) {
        this.m_strUser = m_strUser;
    }
    //获取设备登录密码
    public String getM_strPassword() {
        return m_strPassword;
    }
    //设置设备登录密码
    public void setM_strPassword(String m_strPassword) {
        this.m_strPassword = m_strPassword;
    }
    //获取设备IP
    public String getM_strIp() {
        return m_strIp;
    }
    //设置设备IP
    public void setM_strIp(String m_strIp) {
        this.m_strIp = m_strIp;
    }
    //获取设备端口号
    public int getM_nPort() {
        return m_nPort;
    }
    //设置设备端口号
    public void setM_nPort(int m_nPort) {
        this.m_nPort = m_nPort;
    }

    public static final NetSDKLib netSdk = NetSDKLib.NETSDK_INSTANCE;
    //获取登录句柄
    public LLong getLoginHandle() {
        return loginHandle;
    }

    // 登陆句柄
    private LLong loginHandle = new LLong(0);

}

3.主动注册监听

在编写主动监听实现类之前我们=将内部用到的回调监听的功能都封装成了类这样我们调用起来方便一些
大华SDK+JAVA+4g网络摄像头进行二次开发_第15张图片
DisConnectCallBack.java(设备断线监听回调用于监听设备的在线状态,如果掉线进行消息回传)

package com.ruoyi.demo.frame;

import com.ruoyi.lib.NetSDKLib;
import com.sun.jna.Pointer;

/**
 * 设备断线回调
 */

public class DisConnectCallBack implements NetSDKLib.fDisConnect {

    public DisConnectCallBack() {
    }

    private static class CallBackHolder {
        private static DisConnectCallBack instance = new DisConnectCallBack();
    }

    public static DisConnectCallBack getInstance() {
        return CallBackHolder.instance;
    }

    public void invoke(NetSDKLib.LLong lLoginID, String pchDVRIP, int nDVRPort, Pointer dwUser) {
        System.out.printf("Device[%s] Port[%d] DisConnect!\n", pchDVRIP, nDVRPort);
    }
}

HaveReConnectCallBack.java(设备重连回调,用与监听离线设备重连的状态,将重连成功后的设备信息进行回传)

package com.ruoyi.demo.frame;

import com.ruoyi.lib.NetSDKLib;
import com.sun.jna.Pointer;

/**
 * 设备重连回调
 */
public class HaveReConnectCallBack implements NetSDKLib.fHaveReConnect {
    public HaveReConnectCallBack() {
    }

    private static class CallBackHolder {
        private static HaveReConnectCallBack instance = new HaveReConnectCallBack();
    }

    public static HaveReConnectCallBack getInstance() {
        return CallBackHolder.instance;
    }

    public void invoke(NetSDKLib.LLong m_hLoginHandle, String pchDVRIP, int nDVRPort, Pointer dwUser) {
        System.out.printf("ReConnect Device[%s] Port[%d]\n", pchDVRIP, nDVRPort);

    }
}

CbfRealDataCallBackEx.java(在摄像机工作过程中进行工作状态的监听,将设备数据进行回传)

package com.ruoyi.demo.frame;

import com.ruoyi.lib.NetSDKLib;
import com.sun.jna.Pointer;

/**
 * 实时监视数据回调函数--扩展(pBuffer内存由SDK内部申请释放)
 */

public class CbfRealDataCallBackEx implements NetSDKLib.fRealDataCallBackEx {
    private static class CallBackHolder {
        private static CbfRealDataCallBackEx instance = new CbfRealDataCallBackEx();
    }

    public static CbfRealDataCallBackEx getInstance() {
        return CallBackHolder.instance;
    }

    @Override
    public void invoke(NetSDKLib.LLong lRealHandle, int dwDataType, Pointer pBuffer, int dwBufSize, int param, Pointer dwUser) {
        if (0 != lRealHandle.longValue()) {
            switch (dwDataType) {
                case 0:

                    break;
                case 1:
                    //标准视频数据
                    System.out.println("[1]"+"码流大小为" + dwBufSize + "\n" + "码流类型为原始音视频混合数据");
                    break;
                case 2:
                    //yuv 数据
                    System.out.println("[2]"+"码流大小为" + dwBufSize + "\n" + "码流类型为原始音视频混合数据");
                    break;
                case 3:
                    //pcm 音频数据
                    System.out.println("[3]"+"码流大小为" + dwBufSize + "\n" + "码流类型为原始音视频混合数据");
                    break;
                case 4:
                    //原始音频数据
                    System.out.println("[4]"+"码流大小为" + dwBufSize + "\n" + "码流类型为原始音视频混合数据");
                    break;
                default:
                    break;
            }
        }
    }
}

Run.java(主动注册实现函数,进行主动注册监听)

package com.ruoyi.demo.frame;

import com.ruoyi.demo.module.AutoRegisterModule;
import com.ruoyi.demo.module.LoginModule;

import javax.swing.*;
import java.net.InetAddress;
import java.net.UnknownHostException;

public class Run extends JFrame {
    // 主动注册监听回调
    private final ServiceCB servicCallback = new ServiceCB();
    // 设备断线通知回调
    private final DisConnectCallBack disConnectCallback = new DisConnectCallBack();

    /**
     * 主动注册
     * @param ServiceAddress 服务器公网IP
     * @param ServicePort  服务器注册端口默认为9500
     * */
    public boolean AutoListen(String ServiceAddress , int ServicePort){
        //window系统下监听设备返回信息函数,必须增加不增加无法返回注册设备信息
        pack();
        //初始化
        LoginModule.init(disConnectCallback,null);
        if (ServiceAddress == null){
            System.out.println("service is null");
        }
        if (ServicePort == 0){
            System.out.println("service port is zero");
        }
        //进行主动注册开启服务
        return(AutoRegisterModule.startServer(ServiceAddress, ServicePort, servicCallback));
    }
    /**
     * 获取本地地址
     * @return 获取本机ip
     */
    public String getHostAddress() {
        String address = "";
        try {
            InetAddress inetAddress = InetAddress.getLocalHost();
            address = inetAddress.getHostAddress();
        } catch (UnknownHostException e) {
            e.printStackTrace();
        }

        return address;
    }
}

ServiceCB.java(主动监听回调函数)

package com.ruoyi.demo.frame;

import com.ruoyi.conversion.CaseMenu;
import com.ruoyi.controller.PreInfo;
import com.ruoyi.conversion.RealPlayByDataTypeDemo;
import com.ruoyi.demo.module.DeviceInfo;
import com.ruoyi.lib.NetSDKLib;
import com.ruoyi.service.instructService;
import com.sun.jna.Pointer;
import org.springframework.stereotype.Component;

import java.io.UnsupportedEncodingException;
import java.util.HashSet;
import java.util.Set;

/**
 * 注册服务监听回调
 */
@Component
public class ServiceCB implements NetSDKLib.fServiceCallBack,instructService {
    //设备信息类对象用于获取注册后设备信息
    DeviceInfo info = new DeviceInfo();

    /**
     * 前端发送信息
     *
     * @param preInfo  前端发送信息
     */
    private static final Set<PreInfo> preInfoSet = new HashSet<>();

    @Override
    public int invoke(NetSDKLib.LLong lHandle, String pIp, int wPort,
                      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();
        }

        System.out.printf("Register Device Info [Device address %s][port %s][DeviceID %s] \n", pIp, wPort, deviceId);

        switch (lCommand) {
            case NetSDKLib.EM_LISTEN_TYPE.NET_DVR_DISCONNECT: {  // 验证期间设备断线回调
                info.setM_strIp("");
                info.setM_nPort(0);
                break;
            }
            case NetSDKLib.EM_LISTEN_TYPE.NET_DVR_SERIAL_RETURN: { // 设备注册携带序列号
                info.setM_strIp(pIp);
                info.setM_nPort(wPort);
                info.setM_srtId(deviceId);

                /**
                 * 判断前端是否传输数据
                 * */
                if (preInfoSet.size() != 0) {
                    //这判断设备id是否相同
                    preInfoSet.forEach(preInfo -> {
                        if (info.getM_srtId().equals(preInfo.getPreDeviceId())) {
                            preInfoSet.remove(preInfo);
                            System.out.println("前端触发ID:" + preInfo.getPreDeviceId());
                            System.out.println("设备当前ID:" + info.getM_srtId());
                            if (preInfo.getPreDeviceId().equals(info.getM_srtId())) {

                                new Thread(() -> {
                                    System.out.println("开始录制");
                                    info.setM_strUser("自己的大华相机用户名");
                                    info.setM_strPassword("自己的大华相机密码");

                                    RealPlayByDataTypeDemo demo = new RealPlayByDataTypeDemo();

                                    demo.addItem(new CaseMenu.Item(demo, "mp4", "attachFlv"));
                                    demo.addItem(new CaseMenu.Item(demo, "stop", "detach"));

                                    /**
                                     * SDK初始化
                                     * @param disConnect 断线回调函数
                                     * @param haveReConnect 重连回调函数
                                     * */
                                    DisConnectCallBack disConnect = new DisConnectCallBack();
                                    HaveReConnectCallBack haveReConnect = new HaveReConnectCallBack();
                                    demo.init(disConnect, haveReConnect);

                                    System.out.println(info.getM_strIp());
                                    System.out.println(info.getM_nPort());

                                    if (demo.login(info.getM_strIp(), info.getM_nPort(), info.getM_strUser(), info.getM_strPassword(),2,null,info.getM_srtId())) {
                                        System.out.println("登录成功");
                                        // 登录成功后
                                        demo.run(0,info.getM_srtId());
                                        try {
                                            /**
                                             * 视频录制60s
                                             * */
                                            Thread.sleep(60 * 1000);
                                            demo.run(1,null);
                                        } catch (InterruptedException e) {
                                            e.printStackTrace();
                                        }
                                    }
                                    // 登出设备
                                    demo.logout();
                                    // 测试结束,释放sdk资源
//                          demo.clean();
                                    System.out.println("录制结束");
                                }).start();
                            } else {
                                System.out.println("设备id不一致登录失败!");
                            }
                        }
                    });
                }
                break;
            }
            default:
                break;
        }
        return lCommand;
    }

    @Override
    //向前端进行摄像机触发成功的消息提醒
    public void SendMessage(PreInfo info) {
        preInfoSet.add(info);
        System.out.println("成功触发!");
    }

}

4.剩余包含的实现类

剩下的实现类均是大华提供的SDK中本身具备的我们只需要复制即可
大华SDK+JAVA+4g网络摄像头进行二次开发_第16张图片
大华SDK+JAVA+4g网络摄像头进行二次开发_第17张图片
由于摄像机不再旁边无法进行测试。我会将源文件上传到资源,感兴趣的可自行下载。

注意

主动注册使用了基于window.java的pack()监听函数实现实时监听设备的注册功能;在主动注册中的回调函数为多线程执行模式,每一个新注册的设备都单独运行在一个线程。
所有的业务逻辑需要书写在主动注册的回调函数中作为监听项。
在进行业务操作前必须要使用SDK初始化函数对SDK资源进行初始化,避免产生脏数据。
对于主动注册的登录操作需要使用“高安全的登录”方式,且需要传入设备ID作为参数,才可利用主动注册的登录方式成功登录服务器。
拉流保存的操作可以利用接口“NET_IN_REALPLAY_BY_DATA_TYPE(开始流数据解码)”与“NET_OUT_REALPLAY_BY_DATA_TYPE(停止流数据解码)”两个接口进行视频的录制与解码。(详细可见大华SDK开发文档)
部署服务器的时候需要注意服务器安全规则中的入站安全规则需要将“9500(主动注册端口)”,“Springboot访问端口xxxx”填入入站规则中,才可实现主动注册与远端触发功能。

你可能感兴趣的:(java,网络,java,服务器)