package com.netsdk.demo.customize.configuration; import com.netsdk.demo.util.CaseMenu; import com.netsdk.lib.NetSDKLib; import com.netsdk.lib.ToolKits; import com.netsdk.lib.Utils; import com.netsdk.lib.enumeration.EM_SERVER_OPTION; import com.netsdk.lib.enumeration.NET_EM_CFG_OPERATE_TYPE; import com.netsdk.lib.structure.*; import com.netsdk.module.ConfigModule; import com.netsdk.module.entity.DeviceInfo; import com.sun.jna.Memory; import com.sun.jna.Pointer; import java.nio.charset.Charset; /** * 第三方配置 获取/下发 * * @author 47040 * @version 1.0.0 * @since Created in 2021/3/9 13:50 */ public class ThirdPartyConfiguration { // netsdk 接口 private final NetSDKLib netSdkApi = NetSDKLib.NETSDK_INSTANCE; private final ConfigInitAndLogon initModule = new ConfigInitAndLogon(netSdkApi); // 多平台 编码 private Charset encode = Charset.forName(Utils.getPlatformEncode()); /** * 二次封装模块,包含一些设备配置的接口 */ private final ConfigModule configModule = new ConfigModule(netSdkApi); /** * 登录句柄值 */ private long loginHandler; //////////////////////////////////////////////////////////////////////////////////////////////// /////////////////////////////////// 第三方配置 获取/下发 ///////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// /** * 获取 平台接入配置(国标服务端) */ public void getVspGaysServerConfig() { NET_CFG_VSP_GAYS_SERVER_INFO config = new NET_CFG_VSP_GAYS_SERVER_INFO(); config.nSipServerInfoNum = 5; // 获取配置的数量 填最大值就行了 for (int i = 0; i < config.nSipServerInfoNum; i++) { int nChannelNum = initModule.deviceInfo.getByChanNum(); config.stuSipServerInfo[i].nChannelInfoNum = nChannelNum; // 这里填设备通道数就行 config.stuSipServerInfo[i].pstuChannelInfo = new Memory((new NET_CHANNEL_INFO().size()) * nChannelNum); config.stuSipServerInfo[i].nAlarmInfoNum = nChannelNum; // 这里也填设备通道数就行 config.stuSipServerInfo[i].pstuAlarmInfo = new Memory((new NET_ALARM_INFO().size()) * nChannelNum); config.stuSipServerInfo[i].nAudioOutputChnInfoNum = nChannelNum; // 这里还是填设备通道数 config.stuSipServerInfo[i].pstuAudioOutputChnInfo = new Memory((new NET_AUDIO_OUTPUT_CHANNEL_INFO().size()) * nChannelNum); } config = (NET_CFG_VSP_GAYS_SERVER_INFO) configModule.getConfig( loginHandler, // 登录句柄 NET_EM_CFG_OPERATE_TYPE.NET_EM_CFG_VSP_GAYS_SERVER, // 枚举->平台接入配置(国标服务端) config, // 配置结构体 -1 // 通道号 固定-1 ); if (config == null) { System.err.println("获取平台接入配置(国标服务端)失败:" + ToolKits.getErrorCode()); return; } // 打印展示 StringBuilder info = new StringBuilder().append("---------- 设备平台接入配置信息 ----------").append("\n"); int retCount = config.nRetSipServerInfoNum; info.append(String.format("( 共包含%d个配置 )", retCount)).append("\n"); for (int i = 0; i < retCount; i++) { String serverOption = EM_SERVER_OPTION.getEnum(config.stuSipServerInfo[i].emServerOption).getNote(); String sipSvrId = new String(config.stuSipServerInfo[i].szSipSvrId, encode).trim(); String domain = new String(config.stuSipServerInfo[i].szDomain, encode).trim(); String sipSvrIp = new String(config.stuSipServerInfo[i].szSipSvrIp, encode).trim(); String deviceId = new String(config.stuSipServerInfo[i].szDeviceId, encode).trim(); String password = new String(config.stuSipServerInfo[i].szPassword, encode).trim(); String localSipPort = String.valueOf(config.stuSipServerInfo[i].nLocalSipPort); String sipSvrPort = String.valueOf(config.stuSipServerInfo[i].nSipSvrPort); String sipRegExpires = String.valueOf(config.stuSipServerInfo[i].nSipRegExpires); String regInterval = String.valueOf(config.stuSipServerInfo[i].nRegInterval); String keepAliveCircle = String.valueOf(config.stuSipServerInfo[i].nKeepAliveCircle); String maxTimeoutTimes = String.valueOf(config.stuSipServerInfo[i].nMaxTimeoutTimes); String civilCode = new String(config.stuSipServerInfo[i].szCivilCode, encode).trim(); String interVideoID = new String(config.stuSipServerInfo[i].szIntervideoID).trim(); info.append(String.format(">>>>> 第【%d】配置详情 <<<<<", i + 1)).append("\n") .append(String.format(" %s: ", "启动选项")).append(serverOption).append("\n") .append(String.format(" %s: ", "SIP服务器编号")).append(sipSvrId).append("\n") .append(String.format(" %s: ", "SIP域")).append(domain).append("\n") .append(String.format(" %s: ", "SIP服务器 IP")).append(sipSvrIp).append("\n") .append(String.format(" %s: ", "设备编号")).append(deviceId).append("\n") .append(String.format(" %s: ", "注册密码")).append(password).append("\n") .append(String.format(" %s: ", "本地SIP服务端口")).append(localSipPort).append("\n") .append(String.format(" %s: ", "SIP服务器端口")).append(sipSvrPort).append("\n") .append(String.format(" %s: ", "注册有效期 单位秒")).append(sipRegExpires).append("\n") .append(String.format(" %s: ", "注册失败后重注册间隔 单位秒")).append(regInterval).append("\n") .append(String.format(" %s: ", "心跳周期 单位秒")).append(keepAliveCircle).append("\n") .append(String.format(" %s: ", "最大心跳超时次数")).append(maxTimeoutTimes).append("\n") .append(String.format(" %s: ", "行政区划代码")).append(civilCode).append("\n") .append(String.format(" %s: ", "接入模块识别码")).append(interVideoID).append("\n"); info.append(" ------ 通道信息 ------").append("\n"); int retChannelInfoCount = config.stuSipServerInfo[i].nRetChannelInfoNum; if (retChannelInfoCount != 0) { NET_CHANNEL_INFO[] channelInfos = new NET_CHANNEL_INFO[retChannelInfoCount]; for (int j = 0; j < retChannelInfoCount; j++) { channelInfos[j] = new NET_CHANNEL_INFO(); } Pointer pChannelInfo = config.stuSipServerInfo[i].pstuChannelInfo; ToolKits.GetPointerDataToStructArr(pChannelInfo, channelInfos); for (int j = 0; j < retChannelInfoCount; j++) { String channelID = new String(channelInfos[j].szID, encode); String alarmLevel = String.valueOf(channelInfos[j].nAlarmLevel); info.append(String.format(" >>> 第【%d】个通道信息详情 <<<", j + 1)).append("\n") .append(String.format(" %s: ", "通道编号")).append(channelID).append("\n") .append(String.format(" %s: ", "报警级别 范围 [1,6]")).append(alarmLevel).append("\n"); } } else { info.append(" 不存在通道信息").append("\n"); } info.append(" ------ 报警通道信息 ------").append("\n"); int retAlarmInfoCount = config.stuSipServerInfo[i].nRetAlarmInfoNum; if (retAlarmInfoCount != 0) { NET_ALARM_INFO[] alarmInfos = new NET_ALARM_INFO[retAlarmInfoCount]; for (int j = 0; j < retAlarmInfoCount; j++) { alarmInfos[j] = new NET_ALARM_INFO(); } Pointer pAlarmInfo = config.stuSipServerInfo[i].pstuAlarmInfo; ToolKits.GetPointerDataToStructArr(pAlarmInfo, alarmInfos); for (int j = 0; j < retAlarmInfoCount; j++) { String channelID = new String(alarmInfos[j].szID, encode); String alarmLevel = String.valueOf(alarmInfos[j].nAlarmLevel); info.append(String.format(" >>> 第【%d】个报警通道信息详情 <<<", j + 1)).append("\n") .append(String.format(" %s: ", "通道编号")).append(channelID).append("\n") .append(String.format(" %s: ", "报警级别 范围 [1,6]")).append(alarmLevel).append("\n"); } } else { info.append(" 不存在报警通道信息").append("\n"); } info.append(" ------ 音频输出通道信息 ------").append("\n"); int retAudioOutputChnInfoCount = config.stuSipServerInfo[i].nRetAudioOutputChnInfoNum; if (retAudioOutputChnInfoCount != 0) { NET_AUDIO_OUTPUT_CHANNEL_INFO[] audioChannelInfos = new NET_AUDIO_OUTPUT_CHANNEL_INFO[retAudioOutputChnInfoCount]; for (int j = 0; j < retAudioOutputChnInfoCount; j++) { audioChannelInfos[j] = new NET_AUDIO_OUTPUT_CHANNEL_INFO(); } Pointer pAudioOutputChnInfo = config.stuSipServerInfo[i].pstuAudioOutputChnInfo; ToolKits.GetPointerDataToStructArr(pAudioOutputChnInfo, audioChannelInfos); for (int j = 0; j < retAudioOutputChnInfoCount; j++) { String channelID = new String(audioChannelInfos[j].szID, encode); info.append(String.format(" >>> 第【%d】个音频输出通道信息详情 <<<", j + 1)).append("\n") .append(String.format(" %s: ", "通道编号")).append(channelID).append("\n"); } } else { info.append(" 不存在音频输出通道信息").append("\n"); } } System.out.println(info.toString()); } /** * 下发 平台接入配置(国标服务端) * Demo 里修改了配置组的第一个配置 */ public void setVspGaysServerConfig() { // 我们推荐修改配置的方式都是 获取->修改->下发 这样的流程 // 配置下发载体的结构体越复杂越得这么做,特别是很多早期的结构体,往往承担着复数个配置的收发任务,缺少关键字段容易引起设备异常 //////////////////////////////////////// 获取配置 ///////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// NET_CFG_VSP_GAYS_SERVER_INFO config = new NET_CFG_VSP_GAYS_SERVER_INFO(); config.nSipServerInfoNum = 5; // 获取配置的数量 填最大值就行了 int nChannelNum = initModule.deviceInfo.getByChanNum(); for (int i = 0; i < config.nSipServerInfoNum; i++) { config.stuSipServerInfo[i].nChannelInfoNum = nChannelNum; // 这里填设备通道数就行 config.stuSipServerInfo[i].pstuChannelInfo = new Memory((new NET_CHANNEL_INFO().size()) * nChannelNum); config.stuSipServerInfo[i].nAlarmInfoNum = nChannelNum; // 这里也填设备通道数就行 config.stuSipServerInfo[i].pstuAlarmInfo = new Memory((new NET_ALARM_INFO().size()) * nChannelNum); config.stuSipServerInfo[i].nAudioOutputChnInfoNum = nChannelNum; // 这里还是填设备通道数 config.stuSipServerInfo[i].pstuAudioOutputChnInfo = new Memory((new NET_AUDIO_OUTPUT_CHANNEL_INFO().size()) * nChannelNum); } config = (NET_CFG_VSP_GAYS_SERVER_INFO) configModule.getConfig(loginHandler, NET_EM_CFG_OPERATE_TYPE.NET_EM_CFG_VSP_GAYS_SERVER, config, -1); if (config == null) { System.err.println("获取平台接入配置(国标服务端)失败:" + ToolKits.getErrorCode()); return; } System.out.println("获取平台接入配置(国标服务端)成功"); //////////////////////////////////////// 修改配置 ///////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// // Demo这里只修改第1个配置 其他的配置原样下发 config.nSipServerInfoNum = config.nRetSipServerInfoNum; // 下发配置数量 设定成和接收时的一样 NET_SIP_SERVER_INFO sipServerInfo = config.stuSipServerInfo[0]; sipServerInfo.emServerOption = EM_SERVER_OPTION.EM_SERVER_OPTION_GB28181.getValue(); // 启用 28181 接入方式 byte[] bSipSvrId = "34020000002000000001".getBytes(encode); // SIP服务器编号 System.arraycopy(bSipSvrId, 0, sipServerInfo.szSipSvrId, 0, bSipSvrId.length); byte[] bDomain = "3402000000".getBytes(encode); // SIP域 System.arraycopy(bDomain, 0, sipServerInfo.szDomain, 0, bDomain.length); byte[] bSipSvrIp = "192.168.1.112".getBytes(encode); // SIP服务器 IP System.arraycopy(bSipSvrIp, 0, sipServerInfo.szSipSvrIp, 0, bSipSvrIp.length); byte[] bDeviceId = "34020000001320000001".getBytes(encode); // 设备编号 System.arraycopy(bDeviceId, 0, sipServerInfo.szDeviceId, 0, bDeviceId.length); ///// 注意: sdk虽然会发送修改后的密码,但可能该设置是无效的 这点请和产品、设备研发提前确认下 不同型号设备在实现上可能不同 ///// 因为配置是明文发送的 sdk拿到的密码大多时候是 ****** 这样的字符串 看不到真实密码 当然密码也确实不能暴露在TCP报文中 byte[] bPassword = "helloWorld".getBytes(encode); // 注册密码 System.arraycopy(bPassword, 0, sipServerInfo.szPassword, 0, bPassword.length); sipServerInfo.nLocalSipPort = 5060; // 本地SIP服务端口 sipServerInfo.nSipSvrPort = 5060; // SIP服务器端口 sipServerInfo.nSipRegExpires = 3600; // 注册有效期 单位秒 sipServerInfo.nRegInterval = 60; // 注册失败后重注册间隔 单位秒 sipServerInfo.nKeepAliveCircle = 60; // 心跳周期 单位秒 sipServerInfo.nMaxTimeoutTimes = 3; // 最大心跳超时次数 byte[] bCivilCode = "340200".getBytes(encode); // 行政区划代码 System.arraycopy(bCivilCode, 0, sipServerInfo.szCivilCode, 0, bCivilCode.length); byte[] bInterVideoID = "000001019".getBytes(encode); // 接入模块识别码 System.arraycopy(bInterVideoID, 0, sipServerInfo.szIntervideoID, 0, bInterVideoID.length); // Demo这里通道相关的信息都只修改第1个 // 通道信息 sipServerInfo.nChannelInfoNum = sipServerInfo.nRetChannelInfoNum; // 下发配置数量 设定成和接收的一样 NET_CHANNEL_INFO[] channelInfos = new NET_CHANNEL_INFO[sipServerInfo.nRetChannelInfoNum]; // 创建和数量相同的结构体组 for (int i = 0; i < sipServerInfo.nRetChannelInfoNum; i++) { channelInfos[i] = new NET_CHANNEL_INFO(); // 结构体组必须先分配内存 } if (sipServerInfo.nRetChannelInfoNum < nChannelNum) { // 如果获取配置时 实际获取量比设定的小 那直接 GetPointerDataToStructArr 全拷贝就会把指针数据拷贝到未知内存 // 解决的办法是 新建一个和获取量大小相同的新指针 复制数据后替换掉原来的 int size = (channelInfos[0].size()) * sipServerInfo.nRetChannelInfoNum; Pointer newStuChannelInfo = new Memory(size); newStuChannelInfo.write(0, sipServerInfo.pstuChannelInfo.getByteArray(0, size), 0, size); // 复制数据 sipServerInfo.pstuChannelInfo = newStuChannelInfo; // 替换原指针 } ToolKits.GetPointerDataToStructArr(sipServerInfo.pstuChannelInfo, channelInfos); // 把指针数据拷贝到结构体组里 // 现在修改它的第1个数据 byte[] bChannelID = "34020000001310000001".getBytes(encode); System.arraycopy(bChannelID, 0, channelInfos[0].szID, 0, bChannelID.length); channelInfos[0].nAlarmLevel = 1; ToolKits.SetStructArrToPointerData(channelInfos, sipServerInfo.pstuChannelInfo); // 再把修改了的数据拷贝回去 // 报警通道信息 sipServerInfo.nAlarmInfoNum = sipServerInfo.nRetAlarmInfoNum; // 下发配置数量 设定成和接收的一样; NET_ALARM_INFO[] alarmInfos = new NET_ALARM_INFO[sipServerInfo.nRetAlarmInfoNum]; for (int i = 0; i < sipServerInfo.nRetAlarmInfoNum; i++) { alarmInfos[i] = new NET_ALARM_INFO(); // 结构体组必须先分配内存 } if (sipServerInfo.nRetAlarmInfoNum < nChannelNum) { // 如果获取配置时 实际获取量比设定的小 那直接 GetPointerDataToStructArr 全拷贝就会把指针数据拷贝到未知内存 // 解决的办法是 新建一个和获取量大小相同的新指针 复制数据后替换掉原来的 int size = (alarmInfos[0].size()) * sipServerInfo.nRetAlarmInfoNum; Pointer newStuAlarmInfo = new Memory(size); newStuAlarmInfo.write(0, sipServerInfo.pstuAlarmInfo.getByteArray(0, size), 0, size); // 复制数据 sipServerInfo.pstuAlarmInfo = newStuAlarmInfo; // 替换原指针 } ToolKits.GetPointerDataToStructArr(sipServerInfo.pstuAlarmInfo, alarmInfos); // 把指针数据拷贝到结构体组里 // 现在修改它的第1个数据 byte[] bAlarmChannelID = "34020000002000000001".getBytes(encode); System.arraycopy(bAlarmChannelID, 0, alarmInfos[0].szID, 0, bAlarmChannelID.length); alarmInfos[0].nAlarmLevel = 1; ToolKits.SetStructArrToPointerData(alarmInfos, sipServerInfo.pstuAlarmInfo); // 再把修改了的数据拷贝回去 // 音频输出通道信息 sipServerInfo.nAudioOutputChnInfoNum = sipServerInfo.nRetAudioOutputChnInfoNum; // 下发配置数量 设定成和接收的一样; NET_AUDIO_OUTPUT_CHANNEL_INFO[] audioOutputChannelInfos = new NET_AUDIO_OUTPUT_CHANNEL_INFO[sipServerInfo.nRetAudioOutputChnInfoNum]; for (int i = 0; i < sipServerInfo.nRetAudioOutputChnInfoNum; i++) { audioOutputChannelInfos[i] = new NET_AUDIO_OUTPUT_CHANNEL_INFO(); // 结构体组必须先分配内存 } if (sipServerInfo.nRetAudioOutputChnInfoNum < nChannelNum) { // 如果获取配置时 实际获取量比设定的小 那直接 GetPointerDataToStructArr 全拷贝就会把指针数据拷贝到未知内存 // 解决的办法是 新建一个和获取量大小相同的新指针 复制数据后替换掉原来的 int size = (alarmInfos[0].size()) * sipServerInfo.nRetAudioOutputChnInfoNum; Pointer newStuAudioOutputInfo = new Memory(size); newStuAudioOutputInfo.write(0, sipServerInfo.pstuAudioOutputChnInfo.getByteArray(0, size), 0, size); // 复制数据 sipServerInfo.pstuAudioOutputChnInfo = newStuAudioOutputInfo; // 替换原指针 } ToolKits.GetPointerDataToStructArr(sipServerInfo.pstuAudioOutputChnInfo, audioOutputChannelInfos); // 把指针数据拷贝到结构体组里 // 现在修改它的第1个数据 byte[] bAudioOutputChannelID = "34020000002000000001".getBytes(encode); System.arraycopy(bAudioOutputChannelID, 0, audioOutputChannelInfos[0].szID, 0, bAudioOutputChannelID.length); ToolKits.SetStructArrToPointerData(audioOutputChannelInfos, sipServerInfo.pstuAudioOutputChnInfo); // 再把修改了的数据拷贝回去 //////////////////////////////////////// 下发配置 ///////////////////////////////////////////// ////////////////////////////////////////////////////////////////////////////////////////////// boolean ret = configModule.setConfig( loginHandler, // 登录句柄 NET_EM_CFG_OPERATE_TYPE.NET_EM_CFG_VSP_GAYS_SERVER, // 枚举->平台接入配置(28181国标服务端) config, // 配置结构体 -1 // 通道号 固定-1 ); if (!ret) { System.err.println("平台接入配置(国标服务端)下发失败:" + ToolKits.getErrorCode()); return; } System.out.println("平台接入配置(国标服务端)下发成功"); } //////////////////////////////////////////////////////////////////////////////////////////////// ///////////////////////////////////////// 简易控制台 //////////////////////////////////////////// //////////////////////////////////////////////////////////////////////////////////////////////// // 初始化测试 public void InitTest() { initModule.init(initModule.defaultDisconnectCB, initModule.defaultReconnectCB); // 初始化SDK库 loginHandler = initModule.loginWithHighSecurity(m_strIp, m_nPort, m_strUser, m_strPassword); // 高安全登录 if (loginHandler == 0) { System.err.println("登录失败, 请检查接口参数, See You.."); initModule.cleanAndExit(); } } // 加载测试内容 public void RunTest() { CaseMenu menu = new CaseMenu(); menu.addItem(new CaseMenu.Item(this, "获取 平台接入配置(国标服务端)", "getVspGaysServerConfig")); menu.addItem(new CaseMenu.Item(this, "下发 平台接入配置(国标服务端)", "setVspGaysServerConfig")); menu.run(); } // 结束测试 public void EndTest() { System.out.println("End Test"); initModule.logout(loginHandler); // 登出 System.out.println("See You..."); initModule.cleanAndExit(); // 清理资源并退出 } //////////////////// 配置登陆地址,端口,用户名,密码 /////////////////////// // private String m_strIp = "172.8.2.7"; private String m_strIp = "172.23.12.112"; private int m_nPort = 37777; private String m_strUser = "admin"; private String m_strPassword = "admin123"; ///////////////////////////////////////////////////////////////////////// public static void main(String[] args) { ThirdPartyConfiguration demo = new ThirdPartyConfiguration(); if (args.length == 4) { demo.m_strIp = args[0]; demo.m_nPort = Integer.parseInt(args[1]); demo.m_strUser = args[2]; demo.m_strPassword = args[3]; } demo.InitTest(); demo.RunTest(); demo.EndTest(); } }