diff --git a/README.md b/README.md index 22551a054..08a058cf2 100644 --- a/README.md +++ b/README.md @@ -40,6 +40,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git ![3-1](doc/_media/3-1.png "3-1.png") ![3-2](doc/_media/3-2.png "3-2.png") ![build_1](https://images.gitee.com/uploads/images/2022/0304/101919_ee5b8c79_1018729.png "2022-03-04_10-13.png") +![运维中心](doc/_media/log.jpg "log.jpg") # 功能特性 - [X] 集成web界面 diff --git a/doc/_content/ability/_media/img_19.png b/doc/_content/ability/_media/img_19.png new file mode 100644 index 000000000..5cf2d42a1 Binary files /dev/null and b/doc/_content/ability/_media/img_19.png differ diff --git a/doc/_content/ability/_media/img_20.png b/doc/_content/ability/_media/img_20.png new file mode 100644 index 000000000..387eb6947 Binary files /dev/null and b/doc/_content/ability/_media/img_20.png differ diff --git a/doc/_content/ability/_media/img_21.png b/doc/_content/ability/_media/img_21.png new file mode 100644 index 000000000..19c7762ab Binary files /dev/null and b/doc/_content/ability/_media/img_21.png differ diff --git a/doc/_content/ability/_media/img_22.png b/doc/_content/ability/_media/img_22.png new file mode 100644 index 000000000..f6aa8c4cf Binary files /dev/null and b/doc/_content/ability/_media/img_22.png differ diff --git a/doc/_content/ability/_media/img_23.png b/doc/_content/ability/_media/img_23.png new file mode 100644 index 000000000..91be357ef Binary files /dev/null and b/doc/_content/ability/_media/img_23.png differ diff --git a/doc/_content/ability/_media/img_24.png b/doc/_content/ability/_media/img_24.png new file mode 100644 index 000000000..e5224819e Binary files /dev/null and b/doc/_content/ability/_media/img_24.png differ diff --git a/doc/_content/ability/_media/img_25.png b/doc/_content/ability/_media/img_25.png new file mode 100644 index 000000000..35900bd83 Binary files /dev/null and b/doc/_content/ability/_media/img_25.png differ diff --git a/doc/_content/ability/device.md b/doc/_content/ability/device.md index cc604576f..87dd35047 100644 --- a/doc/_content/ability/device.md +++ b/doc/_content/ability/device.md @@ -33,6 +33,10 @@ ![_media/img_11.png](_media/img_11.png) +### 3. 宇视科技 + +![_media/img_25.png](_media/img_25.png) + ### 3. 艾科威视摄像头 ![_media/img_15.png](_media/img_15.png) diff --git a/doc/_content/ability/node_manger.md b/doc/_content/ability/node_manager.md similarity index 100% rename from doc/_content/ability/node_manger.md rename to doc/_content/ability/node_manager.md diff --git a/doc/_content/introduction/compile.md b/doc/_content/introduction/compile.md index b61d6453b..038eb315e 100644 --- a/doc/_content/introduction/compile.md +++ b/doc/_content/introduction/compile.md @@ -112,7 +112,7 @@ mvn package -P war ``` 编译如果报错, 一般都是网络问题, 导致的依赖包下载失败 -编译完成后在target目录下出现wvp-pro-***.jar/wvp-pro-***.war。 +编译完成后在target目录下出现 `wvp-pro-VERSION.jar` 和 `wvp-pro-VERSION.war` 文件。 接下来[配置服务](./_content/introduction/config.md) diff --git a/doc/_media/log.jpg b/doc/_media/log.jpg new file mode 100644 index 000000000..18c57d0f5 Binary files /dev/null and b/doc/_media/log.jpg differ diff --git a/pom.xml b/pom.xml index 5d818fcda..53bf517fd 100644 --- a/pom.xml +++ b/pom.xml @@ -107,6 +107,12 @@ org.springframework.boot spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-websocket + + org.springframework.boot spring-boot-configuration-processor @@ -374,6 +380,18 @@ 1.18.30 provided + + + + + + + + + io.github.sevdokimov.logviewer + log-viewer-spring-boot + 1.0.10 + org.springframework.boot @@ -401,6 +419,13 @@ 1.8 1.8 + + + org.projectlombok + lombok + 1.18.30 + + diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java index e838cc8eb..acd6092fd 100644 --- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java +++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java @@ -39,7 +39,7 @@ public class VManageBootstrap extends SpringBootServletInitializer { }else { log.info("构建版本: {}", gitUtil.getBuildVersion()); log.info("构建时间: {}", gitUtil.getBuildDate()); - log.info("GIT最后提交时间: {}", gitUtil.getCommitTime()); + log.info("GIT信息: 分支: {}, ID: {}, 时间: {}", gitUtil.getBranch(), gitUtil.getCommitIdShort(), gitUtil.getCommitTime()); } } // 项目重启 diff --git a/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java b/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java deleted file mode 100644 index ed1c2b9b5..000000000 --- a/src/main/java/com/genersoft/iot/vmp/common/ApiSaveConstant.java +++ /dev/null @@ -1,198 +0,0 @@ -package com.genersoft.iot.vmp.common; - -/** - * 为API重命名, 方便向数据库记录数据的时候展示 - * @author lin - */ -public class ApiSaveConstant { - - public static String getVal(String key) { - String[] keyItemArray = key.split("/"); - if (keyItemArray.length <= 1 || !"api".equals(keyItemArray[1])) { - return null; - } - if (keyItemArray.length >= 4) { - switch (keyItemArray[2]) { - case "alarm": - if ("delete".equals(keyItemArray[3])) { - return "删除报警"; - } - break; - case "device": - switch (keyItemArray[3]) { - case "config": - if (keyItemArray.length >= 5 && "basicParam".equals(keyItemArray[4])) { - return "[设备配置] 基本配置设置命令"; - } - break; - case "control": - switch (keyItemArray[4]) { - case "teleboot": - return "[设备控制] 远程启动"; - case "record": - return "[设备控制] 录像控制"; - case "guard": - return "[设备控制] 布防/撤防命令"; - case "reset_alarm": - return "[设备控制] 报警复位"; - case "i_frame": - return "[设备控制] 强制关键帧"; - case "home_position": - return "[设备控制] 看守位控制"; - default: - return ""; - } - case "query": - if (keyItemArray.length <= 5) { - return null; - } - switch (keyItemArray[4]) { - case "devices": - if (keyItemArray.length < 7) { - return null; - } - switch (keyItemArray[6]) { - case "sync": - return "[设备查询] 同步设备通道"; - case "delete": - return "[设备查询] 移除设备"; - default: - return ""; - } - case "channel": - return "[设备查询] 更新通道信息"; - case "transport": - return "[设备查询] 修改数据流传输模式"; - default: - return ""; - } - default: - return ""; - } - - break; - case "gbStream": - switch (keyItemArray[3]) { - case "del": - return "移除通道与国标的关联"; - case "add": - return "添加通道与国标的关联"; - default: - return ""; - } - case "media": - break; - case "position": - if ("subscribe".equals(keyItemArray[3])) { - return "订阅位置信息"; - } - break; - case "platform": - switch (keyItemArray[3]) { - case "save": - return "添加上级平台"; - case "delete": - return "移除上级平台"; - case "update_channel_for_gb": - return "向上级平台添加国标通道"; - case "del_channel_for_gb": - return "从上级平台移除国标通道"; - default: - return ""; - } - case "platform_gb_stream": - break; - case "play": - switch (keyItemArray[3]) { - case "start": - return "开始点播"; - case "stop": - return "停止点播"; - case "convert": - return "转码"; - case "convertStop": - return "结束转码"; - case "broadcast": - return "语音广播"; - default: - return ""; - } - case "download": - switch (keyItemArray[3]) { - case "start": - return "开始历史媒体下载"; - case "stop": - return "停止历史媒体下载"; - default: - return ""; - } - case "playback": - switch (keyItemArray[3]) { - case "start": - return "开始视频回放"; - case "stop": - return "停止视频回放"; - default: - return ""; - } - case "ptz": - switch (keyItemArray[3]) { - case "control": - return "云台控制"; - case "front_end_command": - return "通用前端控制命令"; - default: - return ""; - } - case "gb_record": - break; - case "onvif": - break; - case "server": - if ("restart".equals(keyItemArray[3])) { - return "重启流媒体服务"; - } - break; - case "proxy": - switch (keyItemArray[3]) { - case "save": - return "保存代理"; - case "del": - return "移除代理"; - case "start": - return "启用代理"; - case "stop": - return "停用代理"; - default: - return ""; - } - case "push": - switch (keyItemArray[3]) { - case "save_to_gb": - return "将推流添加到国标"; - case "remove_form_gb": - return "将推流移出到国标"; - default: - return ""; - } - case "user": - switch (keyItemArray[3]) { - case "login": - return "登录"; - case "changePassword": - return "修改密码"; - case "add": - return "添加用户"; - case "delete": - return "删除用户"; - default: - return ""; - } - default: - return ""; - } - } - return null; - } -} - diff --git a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java index 8c91dc82a..adf9643fd 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/common/InviteInfo.java @@ -29,8 +29,14 @@ public class InviteInfo { private StreamInfo streamInfo; + private String mediaServerId; - public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, + private Long expirationTime; + + private Long createTime; + + + public static InviteInfo getInviteInfo(String deviceId, Integer channelId, String stream, SSRCInfo ssrcInfo, String mediaServerId, String receiveIp, Integer receivePort, String streamMode, InviteSessionType type, InviteSessionStatus status) { InviteInfo inviteInfo = new InviteInfo(); @@ -43,6 +49,7 @@ public class InviteInfo { inviteInfo.setStreamMode(streamMode); inviteInfo.setType(type); inviteInfo.setStatus(status); + inviteInfo.setMediaServerId(mediaServerId); return inviteInfo; } diff --git a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java index 3d5597563..442709918 100644 --- a/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java +++ b/src/main/java/com/genersoft/iot/vmp/common/VideoManagerConstants.java @@ -12,58 +12,31 @@ public class VideoManagerConstants { public static final String WVP_SERVER_STREAM_PREFIX = "VMP_SIGNALLING_STREAM_"; - public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER:"; + public static final String MEDIA_SERVER_PREFIX = "VMP_MEDIA_SERVER_INFO:"; public static final String ONLINE_MEDIA_SERVERS_PREFIX = "VMP_ONLINE_MEDIA_SERVERS:"; - public static final String DEVICE_PREFIX = "VMP_DEVICE_"; + public static final String DEVICE_PREFIX = "VMP_DEVICE_INFO"; - // 设备同步完成 - public static final String DEVICE_SYNC_PREFIX = "VMP_DEVICE_SYNC_"; - - public static final String CACHEKEY_PREFIX = "VMP_CHANNEL_"; - - public static final String KEEPLIVEKEY_PREFIX = "VMP_KEEPALIVE_"; - - // TODO 此处多了一个_,暂不修改 - public static final String INVITE_PREFIX = "VMP_INVITE_INFO"; - public static final String PLAYER_PREFIX = "VMP_INVITE_PLAY_"; - public static final String PLAY_BLACK_PREFIX = "VMP_INVITE_PLAYBACK_"; - public static final String DOWNLOAD_PREFIX = "VMP_INVITE_DOWNLOAD_"; - - public static final String PLATFORM_KEEPALIVE_PREFIX = "VMP_PLATFORM_KEEPALIVE_"; + public static final String INVITE_PREFIX = "VMP_GB_INVITE_INFO"; public static final String PLATFORM_CATCH_PREFIX = "VMP_PLATFORM_CATCH_"; - public static final String PLATFORM_REGISTER_PREFIX = "VMP_PLATFORM_REGISTER_"; - public static final String PLATFORM_REGISTER_INFO_PREFIX = "VMP_PLATFORM_REGISTER_INFO_"; - public static final String SEND_RTP_INFO = "VMP_SEND_RTP_INFO:"; - public static final String SEND_RTP_INFO_CALLID = "VMP_SEND_RTP_INFO:CALL_ID"; - public static final String SEND_RTP_INFO_STREAM = "VMP_SEND_RTP_INFO:STREAM"; - public static final String SEND_RTP_INFO_CHANNEL = "VMP_SEND_RTP_INFO:CHANNEL"; + public static final String SEND_RTP_PORT = "VM_SEND_RTP_PORT:"; + public static final String SEND_RTP_INFO_CALLID = "VMP_SEND_RTP_INFO:CALL_ID:"; + public static final String SEND_RTP_INFO_STREAM = "VMP_SEND_RTP_INFO:STREAM:"; + public static final String SEND_RTP_INFO_CHANNEL = "VMP_SEND_RTP_INFO:CHANNEL:"; - public static final String EVENT_ONLINE_REGISTER = "1"; + public static final String SIP_INVITE_SESSION = "VMP_SIP_INVITE_SESSION_INFO:"; + public static final String SIP_INVITE_SESSION_CALL_ID = SIP_INVITE_SESSION + "CALL_ID:"; + public static final String SIP_INVITE_SESSION_STREAM = SIP_INVITE_SESSION + "STREAM:"; - public static final String EVENT_ONLINE_MESSAGE = "3"; - - public static final String EVENT_OUTLINE_UNREGISTER = "1"; - - public static final String EVENT_OUTLINE_TIMEOUT = "2"; - - public static final String MEDIA_SSRC_USED_PREFIX = "VMP_MEDIA_USED_SSRC_"; - - public static final String SIP_INVITE_SESSION = "VMP_SIP_INVITE_SESSION:"; - public static final String SIP_INVITE_SESSION_CALL_ID = SIP_INVITE_SESSION + "CALL_ID" + ":"; - public static final String SIP_INVITE_SESSION_STREAM = SIP_INVITE_SESSION + "STREAM" + ":"; - - public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY_"; + public static final String MEDIA_STREAM_AUTHORITY = "VMP_MEDIA_STREAM_AUTHORITY:"; public static final String SIP_CSEQ_PREFIX = "VMP_SIP_CSEQ_"; - public static final String SIP_SN_PREFIX = "VMP_SIP_SN_"; - public static final String SIP_SUBSCRIBE_PREFIX = "VMP_SIP_SUBSCRIBE_"; public static final String SYSTEM_INFO_CPU_PREFIX = "VMP_SYSTEM_INFO_CPU_"; @@ -79,7 +52,7 @@ public class VideoManagerConstants { public static final String PUSH_STREAM_LIST = "VMP_PUSH_STREAM_LIST_"; public static final String WAITE_SEND_PUSH_STREAM = "VMP_WAITE_SEND_PUSH_STREAM:"; public static final String START_SEND_PUSH_STREAM = "VMP_START_SEND_PUSH_STREAM:"; - public static final String PUSH_STREAM_ONLINE = "VMP_PUSH_STREAM_ONLINE:"; + public static final String SSE_TASK_KEY = "SSE_TASK_"; diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java deleted file mode 100644 index e55c8ecf2..000000000 --- a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java +++ /dev/null @@ -1,118 +0,0 @@ -package com.genersoft.iot.vmp.conf; - -import com.genersoft.iot.vmp.common.ApiSaveConstant; -import com.genersoft.iot.vmp.conf.security.SecurityUtils; -import com.genersoft.iot.vmp.service.ILogService; -import com.genersoft.iot.vmp.storager.dao.dto.LogDto; -import com.genersoft.iot.vmp.utils.DateUtil; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.StringUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.http.HttpStatus; -import org.springframework.stereotype.Component; -import org.springframework.util.ObjectUtils; -import org.springframework.web.filter.OncePerRequestFilter; - -import javax.servlet.FilterChain; -import javax.servlet.ServletException; -import javax.servlet.annotation.WebFilter; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; -import java.io.IOException; - -/** - * @author lin - */ -@Slf4j -@WebFilter(filterName = "ApiAccessFilter", urlPatterns = "/api/*", asyncSupported=true) -@Component -public class ApiAccessFilter extends OncePerRequestFilter { - - @Autowired - private UserSetting userSetting; - - @Autowired - private ILogService logService; - - - @Override - protected void doFilterInternal(HttpServletRequest servletRequest, HttpServletResponse servletResponse, FilterChain filterChain) throws ServletException, IOException { - String username = null; - if (SecurityUtils.getUserInfo() == null) { - username = servletRequest.getParameter("username"); - }else { - username = SecurityUtils.getUserInfo().getUsername(); - } - long start = System.currentTimeMillis(); // 请求进入时间 - String uriName = ApiSaveConstant.getVal(servletRequest.getRequestURI()); - - filterChain.doFilter(servletRequest, servletResponse); - - if (uriName != null && userSetting != null && userSetting.getLogInDatabase() != null && userSetting.getLogInDatabase()) { - - LogDto logDto = new LogDto(); - logDto.setName(uriName); - if (ObjectUtils.isEmpty(username)) { - username = ""; - } - logDto.setUsername(username); - logDto.setAddress(servletRequest.getRemoteAddr()); - logDto.setResult(HttpStatus.valueOf(servletResponse.getStatus()).toString()); - logDto.setTiming(System.currentTimeMillis() - start); - logDto.setType(servletRequest.getMethod()); - logDto.setUri(servletRequest.getRequestURI()); - logDto.setCreateTime(DateUtil.getNow()); - logService.add(logDto); - - - } - } - - /** - * 获取IP地址 - * - * @param request 请求 - * @return request发起客户端的IP地址 - */ - private String getIP(HttpServletRequest request) { - if (request == null) { - return "0.0.0.0"; - } - - String Xip = request.getHeader("X-Real-IP"); - String XFor = request.getHeader("X-Forwarded-For"); - - String UNKNOWN_IP = "unknown"; - if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) { - //多次反向代理后会有多个ip值,第一个ip才是真实ip - int index = XFor.indexOf(","); - if (index != -1) { - return XFor.substring(0, index); - } else { - return XFor; - } - } - - XFor = Xip; - if (StringUtils.isNotEmpty(XFor) && !UNKNOWN_IP.equalsIgnoreCase(XFor)) { - return XFor; - } - - if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { - XFor = request.getHeader("Proxy-Client-IP"); - } - if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { - XFor = request.getHeader("WL-Proxy-Client-IP"); - } - if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { - XFor = request.getHeader("HTTP_CLIENT_IP"); - } - if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { - XFor = request.getHeader("HTTP_X_FORWARDED_FOR"); - } - if (StringUtils.isBlank(XFor) || UNKNOWN_IP.equalsIgnoreCase(XFor)) { - XFor = request.getRemoteAddr(); - } - return XFor; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java b/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java index 56573fe58..761250e02 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/MediaStatusTimerTask.java @@ -8,7 +8,7 @@ import org.springframework.scheduling.annotation.Scheduled; public class MediaStatusTimerTask { - @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次 +// @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次 public void execute(){ } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java index 622c419a0..55e4aff9d 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java @@ -50,7 +50,7 @@ public class SpringDocConfig { public GroupedOpenApi publicApi() { return GroupedOpenApi.builder() .group("1. 全部") - .packagesToScan("com.genersoft.iot.vmp.vmanager", "com.genersoft.iot.vmp.jt1078.controller") + .packagesToScan("com.genersoft.iot.vmp") .build(); } @@ -58,7 +58,7 @@ public class SpringDocConfig { public GroupedOpenApi publicApi2() { return GroupedOpenApi.builder() .group("2. 国标28181") - .packagesToScan("com.genersoft.iot.vmp.vmanager.gb28181") + .packagesToScan("com.genersoft.iot.vmp.gb28181") .build(); } @@ -66,7 +66,7 @@ public class SpringDocConfig { public GroupedOpenApi publicApi3() { return GroupedOpenApi.builder() .group("3. 拉流转发") - .packagesToScan("com.genersoft.iot.vmp.vmanager.streamProxy") + .packagesToScan("com.genersoft.iot.vmp.streamProxy") .build(); } @@ -74,7 +74,7 @@ public class SpringDocConfig { public GroupedOpenApi publicApi4() { return GroupedOpenApi.builder() .group("4. 推流管理") - .packagesToScan("com.genersoft.iot.vmp.vmanager.streamPush") + .packagesToScan("com.genersoft.iot.vmp.streamPush") .build(); } @@ -82,7 +82,7 @@ public class SpringDocConfig { public GroupedOpenApi publicApi5() { return GroupedOpenApi.builder() .group("4. 服务管理") - .packagesToScan("com.genersoft.iot.vmp.vmanager.server") + .packagesToScan("com.genersoft.iot.vmp.server") .build(); } @@ -90,7 +90,7 @@ public class SpringDocConfig { public GroupedOpenApi publicApi6() { return GroupedOpenApi.builder() .group("5. 用户管理") - .packagesToScan("com.genersoft.iot.vmp.vmanager.user") + .packagesToScan("com.genersoft.iot.vmp.user") .build(); } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java index 1d4293ba6..604c8c56a 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.conf; +import lombok.Data; import org.springframework.boot.context.properties.ConfigurationProperties; import org.springframework.core.annotation.Order; import org.springframework.stereotype.Component; @@ -13,326 +14,160 @@ import java.util.List; @Component @ConfigurationProperties(prefix = "user-settings", ignoreInvalidFields = true) @Order(0) +@Data public class UserSetting { + /** + * 是否保存位置的历史记录(轨迹) + */ private Boolean savePositionHistory = Boolean.FALSE; + /** + * 是否开始自动点播: 请求流为未拉起的流时,自动开启点播, 需要rtp.enable=true + */ private Boolean autoApplyPlay = Boolean.FALSE; + /** + * [可选] 部分设备需要扩展SDP,需要打开此设置,一般设备无需打开 + */ private Boolean seniorSdp = Boolean.FALSE; + /** + * 点播/录像回放 等待超时时间,单位:毫秒 + */ private Integer playTimeout = 10000; + /** + * 上级点播等待超时时间,单位:毫秒 + */ private int platformPlayTimeout = 20000; + /** + * 是否开启接口鉴权 + */ private Boolean interfaceAuthentication = Boolean.TRUE; - private Boolean recordPushLive = Boolean.TRUE; - - private Boolean recordSip = Boolean.TRUE; - - private Boolean logInDatabase = Boolean.FALSE; - - private Boolean usePushingAsStatus = Boolean.FALSE; - - private Boolean useSourceIpAsStreamIp = Boolean.FALSE; - - private Boolean sipUseSourceIpAsRemoteAddress = Boolean.FALSE; - - private Boolean streamOnDemand = Boolean.TRUE; - - private Boolean pushAuthority = Boolean.TRUE; - - private Boolean syncChannelOnDeviceOnline = Boolean.FALSE; - - private Boolean sipLog = Boolean.FALSE; - private Boolean sqlLog = Boolean.FALSE; - private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE; - - private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE; - - private Boolean deviceStatusNotify = Boolean.TRUE; - private Boolean useCustomSsrcForParentInvite = Boolean.TRUE; - - private Boolean docEnable = Boolean.TRUE; - - private String serverId = "000000"; - - private String thirdPartyGBIdReg = "[\\s\\S]*"; - - private String broadcastForPlatform = "UDP"; - - private String civilCodeFile = "classpath:civilCode.csv"; - + /** + * 接口鉴权例外的接口, 即不进行接口鉴权的接口,尽量详细书写,尽量不用/**,至少两级目录 + */ private List interfaceAuthenticationExcludes = new ArrayList<>(); + /** + * 推流直播是否录制 + */ + private Boolean recordPushLive = Boolean.TRUE; + + /** + * 国标是否录制 + */ + private Boolean recordSip = Boolean.TRUE; + + /** + * 使用推流状态作为推流通道状态 + */ + private Boolean usePushingAsStatus = Boolean.FALSE; + + /** + * 使用来源请求ip作为streamIp,当且仅当你只有zlm节点它与wvp在一起的情况下开启 + */ + private Boolean useSourceIpAsStreamIp = Boolean.FALSE; + + /** + * 是否使用设备来源Ip作为回复IP, 不设置则为 false + */ + private Boolean sipUseSourceIpAsRemoteAddress = Boolean.FALSE; + + /** + * 国标点播 按需拉流, true:有人观看拉流,无人观看释放, false:拉起后不自动释放 + */ + private Boolean streamOnDemand = Boolean.TRUE; + + /** + * 推流鉴权, 默认开启 + */ + private Boolean pushAuthority = Boolean.TRUE; + + /** + * 设备上线时是否自动同步通道 + */ + private Boolean syncChannelOnDeviceOnline = Boolean.FALSE; + + /** + * 是否开启sip日志 + */ + private Boolean sipLog = Boolean.FALSE; + + /** + * 是否开启mybatis-sql日志 + */ + private Boolean sqlLog = Boolean.FALSE; + + /** + * 消息通道功能-缺少国标ID是否给所有上级发送消息 + */ + private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE; + + /** + * 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息 + */ + private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE; + + /** + * 设备/通道状态变化时发送消息 + */ + private Boolean deviceStatusNotify = Boolean.TRUE; + + /** + * 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 + */ + private Boolean useCustomSsrcForParentInvite = Boolean.TRUE; + + /** + * 开启接口文档页面。 默认开启,生产环境建议关闭,遇到swagger相关的漏洞时也可以关闭 + */ + private Boolean docEnable = Boolean.TRUE; + + /** + * 服务ID,不写则为000000 + */ + private String serverId = "000000"; + + + /** + * 国标级联语音喊话发流模式 * UDP:udp传输 TCP-ACTIVE:tcp主动模式 TCP-PASSIVE:tcp被动模式 + */ + private String broadcastForPlatform = "UDP"; + + /** + * 行政区划信息文件,系统启动时会加载到系统里 + */ + private String civilCodeFile = "classpath:civilCode.csv"; + + /** + * 跨域配置,不配置此项则允许所有跨域请求,配置后则只允许配置的页面的地址请求, 可以配置多个 + */ private List allowedOrigins = new ArrayList<>(); + /** + * 设置notify缓存队列最大长度,超过此长度的数据将返回486 BUSY_HERE,消息丢弃, 默认100000 + */ private int maxNotifyCountQueue = 100000; + /** + * 国标级联离线后多久重试一次注册 + */ private int registerAgainAfterTime = 60; + /** + * 国标续订方式,true为续订,每次注册在同一个会话里,false为重新注册,每次使用新的会话 + */ private boolean registerKeepIntDialog = false; + /** + * # 国标设备离线后的上线策略, + * # 0: 国标标准实现,设备离线后不回复心跳,直到设备重新注册上线, + * # 1(默认): 对于离线设备,收到心跳就把设备设置为上线,并更新注册时间为上次这次心跳的时间。防止过期时间判断异常 + */ private int gbDeviceOnline = 1; - public Boolean getSavePositionHistory() { - return savePositionHistory; - } - - public Boolean isSavePositionHistory() { - return savePositionHistory; - } - - public Boolean isAutoApplyPlay() { - return autoApplyPlay; - } - - public Boolean isSeniorSdp() { - return seniorSdp; - } - - public Integer getPlayTimeout() { - return playTimeout; - } - - public Boolean isInterfaceAuthentication() { - return interfaceAuthentication; - } - - public Boolean isRecordPushLive() { - return recordPushLive; - } - - public List getInterfaceAuthenticationExcludes() { - return interfaceAuthenticationExcludes; - } - - public void setSavePositionHistory(Boolean savePositionHistory) { - this.savePositionHistory = savePositionHistory; - } - - public void setAutoApplyPlay(Boolean autoApplyPlay) { - this.autoApplyPlay = autoApplyPlay; - } - - public void setSeniorSdp(Boolean seniorSdp) { - this.seniorSdp = seniorSdp; - } - - public void setPlayTimeout(Integer playTimeout) { - this.playTimeout = playTimeout; - } - - public void setInterfaceAuthentication(boolean interfaceAuthentication) { - this.interfaceAuthentication = interfaceAuthentication; - } - - public void setRecordPushLive(Boolean recordPushLive) { - this.recordPushLive = recordPushLive; - } - - public void setInterfaceAuthenticationExcludes(List interfaceAuthenticationExcludes) { - this.interfaceAuthenticationExcludes = interfaceAuthenticationExcludes; - } - - public Boolean getLogInDatabase() { - return logInDatabase; - } - - public void setLogInDatabase(Boolean logInDatabase) { - this.logInDatabase = logInDatabase; - } - - public String getServerId() { - return serverId; - } - - public void setServerId(String serverId) { - this.serverId = serverId; - } - - public String getThirdPartyGBIdReg() { - return thirdPartyGBIdReg; - } - - public void setThirdPartyGBIdReg(String thirdPartyGBIdReg) { - this.thirdPartyGBIdReg = thirdPartyGBIdReg; - } - - public Boolean getRecordSip() { - return recordSip; - } - - public void setRecordSip(Boolean recordSip) { - this.recordSip = recordSip; - } - - public int getPlatformPlayTimeout() { - return platformPlayTimeout; - } - - public void setPlatformPlayTimeout(int platformPlayTimeout) { - this.platformPlayTimeout = platformPlayTimeout; - } - - public Boolean isUsePushingAsStatus() { - return usePushingAsStatus; - } - - public void setUsePushingAsStatus(Boolean usePushingAsStatus) { - this.usePushingAsStatus = usePushingAsStatus; - } - - public Boolean getStreamOnDemand() { - return streamOnDemand; - } - - public void setStreamOnDemand(Boolean streamOnDemand) { - this.streamOnDemand = streamOnDemand; - } - - public Boolean getUseSourceIpAsStreamIp() { - return useSourceIpAsStreamIp; - } - - public void setUseSourceIpAsStreamIp(Boolean useSourceIpAsStreamIp) { - this.useSourceIpAsStreamIp = useSourceIpAsStreamIp; - } - - public Boolean getPushAuthority() { - return pushAuthority; - } - - public void setPushAuthority(Boolean pushAuthority) { - this.pushAuthority = pushAuthority; - } - - public Boolean getSyncChannelOnDeviceOnline() { - return syncChannelOnDeviceOnline; - } - - public void setSyncChannelOnDeviceOnline(Boolean syncChannelOnDeviceOnline) { - this.syncChannelOnDeviceOnline = syncChannelOnDeviceOnline; - } - - public String getBroadcastForPlatform() { - return broadcastForPlatform; - } - - public void setBroadcastForPlatform(String broadcastForPlatform) { - this.broadcastForPlatform = broadcastForPlatform; - } - - public Boolean getSipUseSourceIpAsRemoteAddress() { - return sipUseSourceIpAsRemoteAddress; - } - - public void setSipUseSourceIpAsRemoteAddress(Boolean sipUseSourceIpAsRemoteAddress) { - this.sipUseSourceIpAsRemoteAddress = sipUseSourceIpAsRemoteAddress; - } - - public Boolean getSipLog() { - return sipLog; - } - - public void setSipLog(Boolean sipLog) { - this.sipLog = sipLog; - } - - public List getAllowedOrigins() { - return allowedOrigins; - } - - public void setAllowedOrigins(List allowedOrigins) { - this.allowedOrigins = allowedOrigins; - } - - public Boolean getSendToPlatformsWhenIdLost() { - return sendToPlatformsWhenIdLost; - } - - public void setSendToPlatformsWhenIdLost(Boolean sendToPlatformsWhenIdLost) { - this.sendToPlatformsWhenIdLost = sendToPlatformsWhenIdLost; - } - - public Boolean getRefuseChannelStatusChannelFormNotify() { - return refuseChannelStatusChannelFormNotify; - } - - public void setRefuseChannelStatusChannelFormNotify(Boolean refuseChannelStatusChannelFormNotify) { - this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify; - } - - public int getMaxNotifyCountQueue() { - return maxNotifyCountQueue; - } - - public void setMaxNotifyCountQueue(int maxNotifyCountQueue) { - this.maxNotifyCountQueue = maxNotifyCountQueue; - } - - public Boolean getDeviceStatusNotify() { - return deviceStatusNotify; - } - - public void setDeviceStatusNotify(Boolean deviceStatusNotify) { - this.deviceStatusNotify = deviceStatusNotify; - } - - public Boolean getUseCustomSsrcForParentInvite() { - return useCustomSsrcForParentInvite; - } - - public void setUseCustomSsrcForParentInvite(Boolean useCustomSsrcForParentInvite) { - this.useCustomSsrcForParentInvite = useCustomSsrcForParentInvite; - } - - public Boolean getSqlLog() { - return sqlLog; - } - - public void setSqlLog(Boolean sqlLog) { - this.sqlLog = sqlLog; - } - - public String getCivilCodeFile() { - return civilCodeFile; - } - - public void setCivilCodeFile(String civilCodeFile) { - this.civilCodeFile = civilCodeFile; - } - - public int getRegisterAgainAfterTime() { - return registerAgainAfterTime; - } - - public void setRegisterAgainAfterTime(int registerAgainAfterTime) { - this.registerAgainAfterTime = registerAgainAfterTime; - } - - public boolean isRegisterKeepIntDialog() { - return registerKeepIntDialog; - } - - public void setRegisterKeepIntDialog(boolean registerKeepIntDialog) { - this.registerKeepIntDialog = registerKeepIntDialog; - } - - public Boolean getDocEnable() { - return docEnable; - } - - public void setDocEnable(Boolean docEnable) { - this.docEnable = docEnable; - } - - public int getGbDeviceOnline() { - return gbDeviceOnline; - } - - public void setGbDeviceOnline(int gbDeviceOnline) { - this.gbDeviceOnline = gbDeviceOnline; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java b/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java index 229aeabb7..6da0caf3a 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/WVPTimerTask.java @@ -19,7 +19,7 @@ public class WVPTimerTask { @Autowired private SipConfig sipConfig; - @Scheduled(fixedRate = 2 * 1000) //每3秒执行一次 + @Scheduled(fixedDelay = 2 * 1000) //每3秒执行一次 public void execute(){ JSONObject jsonObject = new JSONObject(); jsonObject.put("ip", sipConfig.getShowIp()); diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java index ebeea987d..3246097a3 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java @@ -25,6 +25,8 @@ import java.util.ArrayList; @Component public class JwtAuthenticationFilter extends OncePerRequestFilter { + private final static String WSHeader = "sec-websocket-protocol"; + @Autowired private UserSetting userSetting; @@ -44,17 +46,29 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter { return; } - if (!userSetting.isInterfaceAuthentication()) { + if (!userSetting.getInterfaceAuthentication()) { UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() ); SecurityContextHolder.getContext().setAuthentication(token); chain.doFilter(request, response); return; } + + + String jwt = request.getHeader(JwtUtils.getHeader()); // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的 // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口 + + // websocket 鉴权信息默认存储在这里 + String secWebsocketProtocolHeader = request.getHeader(WSHeader); if (StringUtils.isBlank(jwt)) { - jwt = request.getParameter(JwtUtils.getHeader()); + + if (secWebsocketProtocolHeader != null) { + jwt = secWebsocketProtocolHeader; + response.setHeader(WSHeader, secWebsocketProtocolHeader); + }else { + jwt = request.getParameter(JwtUtils.getHeader()); + } if (StringUtils.isBlank(jwt)) { jwt = request.getHeader(JwtUtils.getApiKeyHeader()); if (StringUtils.isBlank(jwt)) { diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java index d5642d98d..2bdad1ff3 100644 --- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java @@ -25,6 +25,7 @@ import org.springframework.web.cors.UrlBasedCorsConfigurationSource; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; +import java.util.List; /** * 配置Spring Security @@ -62,7 +63,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { **/ @Override public void configure(WebSecurity web) { - if (userSetting.isInterfaceAuthentication()) { + if (userSetting.getInterfaceAuthentication()) { ArrayList matchers = new ArrayList<>(); matchers.add("/"); matchers.add("/#/**"); @@ -104,6 +105,16 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { @Override protected void configure(HttpSecurity http) throws Exception { + + List defaultExcludes = userSetting.getInterfaceAuthenticationExcludes(); + defaultExcludes.add("/api/user/login"); + defaultExcludes.add("/index/hook/**"); + defaultExcludes.add("/api/device/query/snap/**"); + defaultExcludes.add("/index/hook/abl/**"); + defaultExcludes.add("/swagger-ui/**"); + defaultExcludes.add("/doc.html#/**"); +// defaultExcludes.add("/channel/log"); + http.headers().contentTypeOptions().disable() .and().cors().configurationSource(configurationSource()) .and().csrf().disable() @@ -114,8 +125,7 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter { .and() .authorizeRequests() .requestMatchers(CorsUtils::isPreFlightRequest).permitAll() - .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll() - .antMatchers("/api/user/login", "/index/hook/**","/index/hook/abl/**", "/swagger-ui/**", "/doc.html#/**").permitAll() + .antMatchers(defaultExcludes.toArray(new String[0])).permitAll() .anyRequest().authenticated() // 异常处理器 .and() diff --git a/src/main/java/com/genersoft/iot/vmp/conf/webLog/LogChannel.java b/src/main/java/com/genersoft/iot/vmp/conf/webLog/LogChannel.java new file mode 100644 index 000000000..e999b2717 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/webLog/LogChannel.java @@ -0,0 +1,65 @@ +package com.genersoft.iot.vmp.conf.webLog; + +import lombok.extern.slf4j.Slf4j; + +import javax.websocket.*; +import javax.websocket.server.ServerEndpoint; +import java.io.IOException; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; + + @ServerEndpoint(value = "/channel/log") +@Slf4j +public class LogChannel { + + public static final ConcurrentMap CHANNELS = new ConcurrentHashMap<>(); + + private Session session; + + @OnMessage(maxMessageSize = 1) // MaxMessage 1 byte + public void onMessage(String message) { + + try { + this.session.close(new CloseReason(CloseReason.CloseCodes.TOO_BIG, "此节点不接收任何客户端信息")); + } catch (IOException e) { + log.error("[Web-Log] 连接关闭失败: id={}, err={}", this.session.getId(), e.getMessage()); + } + } + + @OnOpen + public void onOpen(Session session, EndpointConfig endpointConfig) { + this.session = session; + this.session.setMaxIdleTimeout(0); + CHANNELS.put(this.session.getId(), this); + + log.info("[Web-Log] 连接已建立: id={}", this.session.getId()); + } + + @OnClose + public void onClose(CloseReason closeReason) { + + log.info("[Web-Log] 连接已断开: id={}, err={}", this.session.getId(), closeReason); + CHANNELS.remove(this.session.getId()); + } + + @OnError + public void onError(Throwable throwable) throws IOException { + log.info("[Web-Log] 连接错误: id={}, err= {}", this.session.getId(), throwable.getMessage()); + if (this.session.isOpen()) { + this.session.close(new CloseReason(CloseReason.CloseCodes.UNEXPECTED_CONDITION, throwable.getMessage())); + } + } + + /** + * Push messages to all clients + * + * @param message + */ + public static void push(String message) { + CHANNELS.values().stream().forEach(endpoint -> { + if (endpoint.session.isOpen()) { + endpoint.session.getAsyncRemote().sendText(message); + } + }); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/webLog/WebSocketAppender.java b/src/main/java/com/genersoft/iot/vmp/conf/webLog/WebSocketAppender.java new file mode 100644 index 000000000..000ab86e9 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/webLog/WebSocketAppender.java @@ -0,0 +1,24 @@ +package com.genersoft.iot.vmp.conf.webLog; + +import ch.qos.logback.classic.encoder.PatternLayoutEncoder; +import ch.qos.logback.classic.spi.ILoggingEvent; +import ch.qos.logback.core.AppenderBase; +import lombok.Data; +import lombok.EqualsAndHashCode; + +import java.nio.charset.StandardCharsets; + +@Data +@EqualsAndHashCode(callSuper = true) +public class WebSocketAppender extends AppenderBase { + + private PatternLayoutEncoder encoder; + + @Override + protected void append(ILoggingEvent loggingEvent) { + byte[] data = this.encoder.encode(loggingEvent); + // Push to client. +// LogChannel.push(DateUtil.timestampMsTo_yyyy_MM_dd_HH_mm_ss(loggingEvent.getTimeStamp()) + " " + loggingEvent.getFormattedMessage()); + LogChannel.push(new String(data, StandardCharsets.UTF_8)); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/conf/websocket/WebSocketConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/websocket/WebSocketConfig.java new file mode 100644 index 000000000..0ef52675d --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/conf/websocket/WebSocketConfig.java @@ -0,0 +1,19 @@ +package com.genersoft.iot.vmp.conf.websocket; + +import com.genersoft.iot.vmp.conf.webLog.LogChannel; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +import org.springframework.web.socket.server.standard.ServerEndpointExporter; + +@Configuration +public class WebSocketConfig { + + @Bean + public ServerEndpointExporter serverEndpointExporter(){ + ServerEndpointExporter endpointExporter = new ServerEndpointExporter(); + + endpointExporter.setAnnotatedEndpointClasses(LogChannel.class); + + return endpointExporter; + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java index 4666b7070..2c37a1de5 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/CatalogData.java @@ -1,9 +1,14 @@ package com.genersoft.iot.vmp.gb28181.bean; import lombok.Data; +import org.jetbrains.annotations.NotNull; import java.time.Instant; +import java.util.HashSet; import java.util.List; +import java.util.Set; +import java.util.concurrent.Delayed; +import java.util.concurrent.TimeUnit; /** * @author lin @@ -15,15 +20,16 @@ public class CatalogData { */ private int sn; private int total; - private List channelList; - private List regionListList; - private List groupListListList; - private Instant lastTime; + private Instant time; private Device device; private String errorMsg; + private Set redisKeysForChannel = new HashSet<>(); + private Set redisKeysForRegion = new HashSet<>(); + private Set redisKeysForGroup = new HashSet<>(); public enum CatalogDataStatus{ ready, runIng, end } private CatalogDataStatus status; + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java index 371134160..22d3dd2ae 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/DeviceAlarm.java @@ -1,187 +1,269 @@ package com.genersoft.iot.vmp.gb28181.bean; import io.swagger.v3.oas.annotations.media.Schema; +import lombok.Data; + +import java.util.HashSet; +import java.util.Set; /** * @author lin */ @Schema(description = "报警信息") +@Data public class DeviceAlarm { - /** - * 数据库id - */ - @Schema(description = "数据库id") - private String id; + @Schema(description = "数据库id") + private String id; - /** - * 设备Id - */ - @Schema(description = "设备的国标编号") - private String deviceId; + @Schema(description = "设备的国标编号") + private String deviceId; - /** - * 通道Id - */ - @Schema(description = "通道的国标编号") - private String channelId; + @Schema(description = "设备名称") + private String deviceName; - /** - * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情 - */ - @Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情") - private String alarmPriority; + /** + * 通道Id + */ + @Schema(description = "通道的国标编号") + private String channelId; - /** - * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, - * 7其他报警;可以为直接组合如12为电话报警或 设备报警- - */ - @Schema(description = "报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,\n" + - "\t * 7其他报警;可以为直接组合如12为电话报警或设备报警") - private String alarmMethod; + /** + * 报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情 + */ + @Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情") + private String alarmPriority; - /** - * 报警时间 - */ - @Schema(description = "报警时间") - private String alarmTime; + @Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情") + private String alarmPriorityDescription; - /** - * 报警内容描述 - */ - @Schema(description = "报警内容描述") - private String alarmDescription; + public String getAlarmPriorityDescription() { + switch (alarmPriority) { + case "1": + return "一级警情"; + case "2": + return "二级警情"; + case "3": + return "三级警情"; + case "4": + return "四级警情"; + default: + return alarmPriority; + } + } - /** - * 经度 - */ - @Schema(description = "经度") - private double longitude; - - /** - * 纬度 - */ - @Schema(description = "纬度") - private double latitude; - - /** - * 报警类型, - * 报警方式为2时,不携带 AlarmType为默认的报警设备报警, - * 携带 AlarmType取值及对应报警类型如下: - * 1-视频丢失报警; - * 2-设备防拆报警; - * 3-存储设备磁盘满报警; - * 4-设备高温报警; - * 5-设备低温报警。 - * 报警方式为5时,取值如下: - * 1-人工视频报警; - * 2-运动目标检测报警; - * 3-遗留物检测报警; - * 4-物体移除检测报警; - * 5-绊线检测报警; - * 6-入侵检测报警; - * 7-逆行检测报警; - * 8-徘徊检测报警; - * 9-流量统计报警; - * 10-密度检测报警; - * 11-视频异常检测报警; - * 12-快速移动报警。 - * 报警方式为6时,取值下: - * 1-存储设备磁盘故障报警; - * 2-存储设备风扇故障报警。 - */ - @Schema(description = "报警类型") - private String alarmType; - - @Schema(description = "创建时间") - private String createTime; + /** + * 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警, + * 7其他报警;可以为直接组合如12为电话报警或 设备报警- + */ + @Schema(description = "报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,\n" + + "\t * 7其他报警;可以为直接组合如12为电话报警或设备报警") + private String alarmMethod; - public String getId() { - return id; + private String alarmMethodDescription; + + public String getAlarmMethodDescription() { + StringBuilder stringBuilder = new StringBuilder(); + char[] charArray = alarmMethod.toCharArray(); + for (char c : charArray) { + switch (c) { + case '1': + stringBuilder.append("-电话报警"); + break; + case '2': + stringBuilder.append("-设备报警"); + break; + case '3': + stringBuilder.append("-短信报警"); + break; + case '4': + stringBuilder.append("-GPS报警"); + break; + case '5': + stringBuilder.append("-视频报警"); + break; + case '6': + stringBuilder.append("-设备故障报警"); + break; + case '7': + stringBuilder.append("-其他报警"); + break; + } + } + stringBuilder.delete(0, 1); + return stringBuilder.toString(); + } + + /** + * 报警时间 + */ + @Schema(description = "报警时间") + private String alarmTime; + + /** + * 报警内容描述 + */ + @Schema(description = "报警内容描述") + private String alarmDescription; + + /** + * 经度 + */ + @Schema(description = "经度") + private double longitude; + + /** + * 纬度 + */ + @Schema(description = "纬度") + private double latitude; + + /** + * 报警类型, + * 报警方式为2时,不携带 AlarmType为默认的报警设备报警, + * 携带 AlarmType取值及对应报警类型如下: + * 1-视频丢失报警; + * 2-设备防拆报警; + * 3-存储设备磁盘满报警; + * 4-设备高温报警; + * 5-设备低温报警。 + * 报警方式为5时,取值如下: + * 1-人工视频报警; + * 2-运动目标检测报警; + * 3-遗留物检测报警; + * 4-物体移除检测报警; + * 5-绊线检测报警; + * 6-入侵检测报警; + * 7-逆行检测报警; + * 8-徘徊检测报警; + * 9-流量统计报警; + * 10-密度检测报警; + * 11-视频异常检测报警; + * 12-快速移动报警。 + * 报警方式为6时,取值下: + * 1-存储设备磁盘故障报警; + * 2-存储设备风扇故障报警。 + */ + @Schema(description = "报警类型") + private String alarmType; + + public String getAlarmTypeDescription() { + if (alarmType == null) { + return ""; + } + char[] charArray = alarmMethod.toCharArray(); + Set alarmMethodSet = new HashSet<>(); + for (char c : charArray) { + alarmMethodSet.add(Character.toString(c)); + } + String result = alarmType; + if (alarmMethodSet.contains("2")) { + switch (alarmType) { + case "1": + result = "视频丢失报警"; + break; + case "2": + result = "设备防拆报警"; + break; + case "3": + result = "存储设备磁盘满报警"; + break; + case "4": + result = "设备高温报警"; + break; + case "5": + result = "设备低温报警"; + break; + } + } + if (alarmMethodSet.contains("5")) { + switch (alarmType) { + case "1": + result = "人工视频报警"; + break; + case "2": + result = "运动目标检测报警"; + break; + case "3": + result = "遗留物检测报警"; + break; + case "4": + result = "物体移除检测报警"; + break; + case "5": + result = "绊线检测报警"; + break; + case "6": + result = "入侵检测报警"; + break; + case "7": + result = "逆行检测报警"; + break; + case "8": + result = "徘徊检测报警"; + break; + case "9": + result = "流量统计报警"; + break; + case "10": + result = "密度检测报警"; + break; + case "11": + result = "视频异常检测报警"; + break; + case "12": + result = "快速移动报警"; + break; + } + } + if (alarmMethodSet.contains("6")) { + switch (alarmType) { + case "1": + result = "人工视频报警"; + break; + case "2": + result = "运动目标检测报警"; + break; + case "3": + result = "遗留物检测报警"; + break; + case "4": + result = "物体移除检测报警"; + break; + case "5": + result = "绊线检测报警"; + break; + case "6": + result = "入侵检测报警"; + break; + case "7": + result = "逆行检测报警"; + break; + case "8": + result = "徘徊检测报警"; + break; + case "9": + result = "流量统计报警"; + break; + case "10": + result = "密度检测报警"; + break; + case "11": + result = "视频异常检测报警"; + break; + case "12": + result = "快速移动报警"; + break; + } + } + return result; } - public void setId(String id) { - this.id = id; - } + @Schema(description = "报警类型描述") + private String alarmTypeDescription; - public String getDeviceId() { - return deviceId; - } + @Schema(description = "创建时间") + private String createTime; - public void setDeviceId(String deviceId) { - this.deviceId = deviceId; - } - - public String getAlarmPriority() { - return alarmPriority; - } - - public void setAlarmPriority(String alarmPriority) { - this.alarmPriority = alarmPriority; - } - - public String getAlarmMethod() { - return alarmMethod; - } - - public void setAlarmMethod(String alarmMethod) { - this.alarmMethod = alarmMethod; - } - - public String getAlarmTime() { - return alarmTime; - } - - public void setAlarmTime(String alarmTime) { - this.alarmTime = alarmTime; - } - - public String getAlarmDescription() { - return alarmDescription; - } - - public void setAlarmDescription(String alarmDescription) { - this.alarmDescription = alarmDescription; - } - - public double getLongitude() { - return longitude; - } - - public void setLongitude(double longitude) { - this.longitude = longitude; - } - - public double getLatitude() { - return latitude; - } - - public void setLatitude(double latitude) { - this.latitude = latitude; - } - - public String getAlarmType() { - return alarmType; - } - - public void setAlarmType(String alarmType) { - this.alarmType = alarmType; - } - - public String getChannelId() { - return channelId; - } - - public void setChannelId(String channelId) { - this.channelId = channelId; - } - - public String getCreateTime() { - return createTime; - } - - public void setCreateTime(String createTime) { - this.createTime = createTime; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GroupTree.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GroupTree.java index 8ac1278e1..1f0f3658e 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GroupTree.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/GroupTree.java @@ -19,4 +19,7 @@ public class GroupTree extends Group{ @Schema(description = "类型, 行政区划:0 摄像头: 1") private int type; + @Schema(description = "在线状态") + private String status; + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RegionTree.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RegionTree.java index b771a603c..125bd3da7 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RegionTree.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RegionTree.java @@ -18,4 +18,7 @@ public class RegionTree extends Region { @Schema(description = "类型, 行政区划:0 摄像头: 1") private int type; + + @Schema(description = "在线状态") + private String status; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java index 398b107dc..2c09df4bc 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/CommonChannelController.java @@ -98,22 +98,40 @@ public class CommonChannelController { return channel; } - @Operation(summary = "获取通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Operation(summary = "获取关联行政区划通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) @Parameter(name = "page", description = "当前页", required = true) @Parameter(name = "count", description = "每页查询数量", required = true) @Parameter(name = "query", description = "查询内容") @Parameter(name = "online", description = "是否在线") - @Parameter(name = "hasCivilCode", description = "是否分配行政区划") - @GetMapping("/list") - public PageInfo queryList(int page, int count, + @Parameter(name = "civilCode", description = "行政区划") + @GetMapping("/civilcode/list") + public PageInfo queryListByCivilCode(int page, int count, @RequestParam(required = false) String query, @RequestParam(required = false) Boolean online, - @RequestParam(required = false) Boolean hasCivilCode, - @RequestParam(required = false) Boolean hasGroup){ + @RequestParam(required = false) Integer channelType, + @RequestParam(required = false) String civilCode){ if (ObjectUtils.isEmpty(query)){ query = null; } - return channelService.queryList(page, count, query, online, hasCivilCode, hasGroup); + return channelService.queryListByCivilCode(page, count, query, online, channelType, civilCode); + } + + @Operation(summary = "获取关联业务分组通道列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "page", description = "当前页", required = true) + @Parameter(name = "count", description = "每页查询数量", required = true) + @Parameter(name = "query", description = "查询内容") + @Parameter(name = "online", description = "是否在线") + @Parameter(name = "groupDeviceId", description = "业务分组下的父节点ID") + @GetMapping("/parent/list") + public PageInfo queryListByParentId(int page, int count, + @RequestParam(required = false) String query, + @RequestParam(required = false) Boolean online, + @RequestParam(required = false) Integer channelType, + @RequestParam(required = false) String groupDeviceId){ + if (ObjectUtils.isEmpty(query)){ + query = null; + } + return channelService.queryListByParentId(page, count, query, online, channelType, groupDeviceId); } @Operation(summary = "通道设置行政区划", security = @SecurityRequirement(name = JwtUtils.HEADER)) diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceControl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceControl.java index 06d5fa85f..55017deb7 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceControl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceControl.java @@ -16,6 +16,7 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; @@ -80,22 +81,22 @@ public class DeviceControl { @Parameter(name = "channelId", description = "通道国标编号", required = true) @Parameter(name = "recordCmdStr", description = "命令, 可选值:Record(手动录像),StopRecord(停止手动录像)", required = true) @GetMapping("/record/{deviceId}/{recordCmdStr}") - public DeferredResult> recordApi(@PathVariable String deviceId, - @PathVariable String recordCmdStr, String channelId) { + public DeferredResult>> recordApi(@PathVariable String deviceId, + @PathVariable String recordCmdStr, String channelId) { if (log.isDebugEnabled()) { log.debug("开始/停止录像API调用"); } Device device = deviceService.getDeviceByDeviceId(deviceId); String uuid = UUID.randomUUID().toString(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId; - DeferredResult> result = new DeferredResult>(3 * 1000L); + DeferredResult>> result = new DeferredResult<>(3 * 1000L); result.onTimeout(() -> { log.warn(String.format("开始/停止录像操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setKey(key); msg.setId(uuid); - msg.setData("Timeout. Device did not response to this command."); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答")); resultHolder.invokeAllResult(msg); }); if (resultHolder.exist(key, null)){ @@ -107,7 +108,7 @@ public class DeviceControl { RequestMessage msg = new RequestMessage(); msg.setId(uuid); msg.setKey(key); - msg.setData(String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg)); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), String.format("开始/停止录像操作失败,错误码: %s, %s", event.statusCode, event.msg))); resultHolder.invokeAllResult(msg); },null); } catch (InvalidArgumentException | SipException | ParseException e) { @@ -128,7 +129,7 @@ public class DeviceControl { @Parameter(name = "deviceId", description = "设备国标编号", required = true) @Parameter(name = "guardCmdStr", description = "命令, 可选值:SetGuard(布防),ResetGuard(撤防)", required = true) @GetMapping("/guard/{deviceId}/{guardCmdStr}") - public DeferredResult guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) { + public DeferredResult> guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) { if (log.isDebugEnabled()) { log.debug("布防/撤防API调用"); } @@ -140,14 +141,14 @@ public class DeviceControl { RequestMessage msg = new RequestMessage(); msg.setId(uuid); msg.setKey(key); - msg.setData(String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg)); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), String.format("布防/撤防操作失败,错误码: %s, %s", event.statusCode, event.msg))); resultHolder.invokeResult(msg); },null); } catch (InvalidArgumentException | SipException | ParseException e) { log.error("[命令发送失败] 布防/撤防操作: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送: " + e.getMessage()); } - DeferredResult result = new DeferredResult<>(3 * 1000L); + DeferredResult> result = new DeferredResult<>(3 * 1000L); resultHolder.put(key, uuid, result); result.onTimeout(() -> { log.warn(String.format("布防/撤防操作超时, 设备未返回应答指令")); @@ -155,7 +156,7 @@ public class DeviceControl { RequestMessage msg = new RequestMessage(); msg.setKey(key); msg.setId(uuid); - msg.setData("Timeout. Device did not response to this command."); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答")); resultHolder.invokeResult(msg); }); @@ -175,7 +176,7 @@ public class DeviceControl { @Parameter(name = "alarmMethod", description = "报警方式") @Parameter(name = "alarmType", description = "报警类型") @GetMapping("/reset_alarm/{deviceId}") - public DeferredResult> resetAlarmApi(@PathVariable String deviceId, String channelId, + public DeferredResult>> resetAlarmApi(@PathVariable String deviceId, String channelId, @RequestParam(required = false) String alarmMethod, @RequestParam(required = false) String alarmType) { if (log.isDebugEnabled()) { @@ -189,21 +190,21 @@ public class DeviceControl { RequestMessage msg = new RequestMessage(); msg.setId(uuid); msg.setKey(key); - msg.setData(String.format("报警复位操作失败,错误码: %s, %s", event.statusCode, event.msg)); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), String.format("操作失败,错误码: %s, %s", event.statusCode, event.msg))); resultHolder.invokeResult(msg); },null); } catch (InvalidArgumentException | SipException | ParseException e) { log.error("[命令发送失败] 报警复位: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } - DeferredResult> result = new DeferredResult>(3 * 1000L); + DeferredResult>> result = new DeferredResult<>(3 * 1000L); result.onTimeout(() -> { log.warn(String.format("报警复位操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); msg.setKey(key); - msg.setData("Timeout. Device did not response to this command."); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答")); resultHolder.invokeResult(msg); }); resultHolder.put(key, uuid, result); @@ -255,7 +256,7 @@ public class DeviceControl { @Parameter(name = "presetIndex", description = "调用预置位编号") @Parameter(name = "resetTime", description = "自动归位时间间隔 单位:秒") @GetMapping("/home_position") - public DeferredResult homePositionApi(String deviceId, String channelId, Boolean enabled, + public DeferredResult> homePositionApi(String deviceId, String channelId, Boolean enabled, @RequestParam(required = false) Integer resetTime, @RequestParam(required = false) Integer presetIndex) { if (log.isDebugEnabled()) { @@ -269,25 +270,21 @@ public class DeviceControl { RequestMessage msg = new RequestMessage(); msg.setId(uuid); msg.setKey(key); - msg.setData(String.format("看守位控制操作失败,错误码: %s, %s", event.statusCode, event.msg)); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), String.format("操作失败,错误码: %s, %s", event.statusCode, event.msg))); resultHolder.invokeResult(msg); },null); } catch (InvalidArgumentException | SipException | ParseException e) { log.error("[命令发送失败] 看守位控制: {}", e.getMessage()); throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } - DeferredResult result = new DeferredResult<>(3 * 1000L); + DeferredResult> result = new DeferredResult<>(3 * 1000L); result.onTimeout(() -> { log.warn(String.format("看守位控制操作超时, 设备未返回应答指令")); // 释放rtpserver RequestMessage msg = new RequestMessage(); msg.setId(uuid); msg.setKey(key); - JSONObject json = new JSONObject(); - json.put("DeviceID", deviceId); - json.put("Status", "Timeout"); - json.put("Description", "看守位控制操作超时, 设备未返回应答指令"); - msg.setData(json); //("看守位控制操作超时, 设备未返回应答指令"); + msg.setData(WVPResult.fail(ErrorCode.ERROR100.getCode(), "操作超时, 设备未应答")); //("看守位控制操作超时, 设备未返回应答指令"); resultHolder.invokeResult(msg); }); resultHolder.put(key, uuid, result); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceQuery.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceQuery.java index c3ba87a48..f6febd83a 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceQuery.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/DeviceQuery.java @@ -243,8 +243,6 @@ public class DeviceQuery { } @Operation(summary = "修改通道的码流类型", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "deviceId", description = "设备国标编号", required = true) - @Parameter(name = "channel", description = "通道信息", required = true) @PostMapping("/channel/stream/identification/update/") public void updateChannelStreamIdentification(DeviceChannel channel){ deviceChannelService.updateChannelStreamIdentification(channel); @@ -298,13 +296,10 @@ public class DeviceQuery { @Parameter(name = "device", description = "设备", required = true) @PostMapping("/device/update/") public void updateDevice(Device device){ - - if (device != null && device.getDeviceId() != null) { - if (device.getSubscribeCycleForMobilePosition() > 0 && device.getMobilePositionSubmissionInterval() <= 0) { - device.setMobilePositionSubmissionInterval(5); - } - deviceService.updateCustomDevice(device); + if (device == null || device.getDeviceId() == null || device.getId() <= 0) { + throw new ControllerException(ErrorCode.ERROR400); } + deviceService.updateCustomDevice(device); } /** @@ -469,7 +464,7 @@ public class DeviceQuery { in.close(); outputStream.close(); } catch (IOException e) { - resp.setStatus(HttpServletResponse.SC_NOT_FOUND); + resp.setStatus(HttpServletResponse.SC_NO_CONTENT); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GroupController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GroupController.java index ef30624c9..1284477af 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GroupController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/GroupController.java @@ -40,12 +40,13 @@ public class GroupController { @GetMapping("/tree/list") public List queryForTree( @RequestParam(required = false) String query, - @RequestParam(required = false) Integer parent + @RequestParam(required = false) Integer parent, + @RequestParam(required = false) Boolean hasChannel ){ if (ObjectUtils.isEmpty(query)) { query = null; } - return groupService.queryForTree(query, parent); + return groupService.queryForTree(query, parent, hasChannel); } @Operation(summary = "更新分组") @@ -68,6 +69,14 @@ public class GroupController { } } + @Operation(summary = "获取所属的行政区划下的行政区划") + @Parameter(name = "deviceId", description = "当前的行政区划", required = false) + @ResponseBody + @GetMapping("/path") + public List getPath(String deviceId, String businessGroup){ + return groupService.getPath(deviceId, businessGroup); + } + // @Operation(summary = "根据分组Id查询分组") // @Parameter(name = "groupDeviceId", description = "分组节点编号", required = true) // @ResponseBody diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlayController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlayController.java index 8c53ca629..e7a8b1c87 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlayController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/PlayController.java @@ -211,7 +211,7 @@ public class PlayController { if (device == null) { throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到设备: " + deviceId); } - DeviceChannel channel = deviceChannelService.getOneForSource(device.getId(), channelId); + DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId); if (channel == null) { throw new ControllerException(ErrorCode.ERROR400.getCode(), "未找到通道: " + channelId); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/RegionController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/RegionController.java index 7da244893..3fd7f79a3 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/RegionController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/RegionController.java @@ -57,12 +57,13 @@ public class RegionController { @GetMapping("/tree/list") public List queryForTree( @RequestParam(required = false) String query, - @RequestParam(required = false) Integer parent + @RequestParam(required = false) Integer parent, + @RequestParam(required = false) Boolean hasChannel ){ if (ObjectUtils.isEmpty(query)) { query = null; } - return regionService.queryForTree(query, parent); + return regionService.queryForTree(query, parent, hasChannel); } @Operation(summary = "更新区域") @@ -109,6 +110,14 @@ public class RegionController { return regionService.getAllChild(parent); } + @Operation(summary = "获取所属的行政区划下的行政区划") + @Parameter(name = "deviceId", description = "当前的行政区划", required = false) + @ResponseBody + @GetMapping("/path") + public List getPath(String deviceId){ + return regionService.getPath(deviceId); + } + @Operation(summary = "从通道中同步行政区划") @ResponseBody @GetMapping("/sync") diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/SseController.java b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/SseController.java index 2ca1a4dba..08cb88579 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/controller/SseController.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/controller/SseController.java @@ -1,16 +1,16 @@ package com.genersoft.iot.vmp.gb28181.controller; -import com.genersoft.iot.vmp.gb28181.event.alarm.AlarmEventListener; +import com.genersoft.iot.vmp.gb28181.session.SseSessionManager; import io.swagger.v3.oas.annotations.tags.Tag; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RequestParam; import org.springframework.web.bind.annotation.RestController; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; import javax.annotation.Resource; import javax.servlet.http.HttpServletResponse; import java.io.IOException; -import java.io.PrintWriter; /** @@ -26,30 +26,17 @@ import java.io.PrintWriter; public class SseController { @Resource - private AlarmEventListener alarmEventListener; + private SseSessionManager sseSessionManager; /** * SSE 推送. * - * @param response 响应 * @param browserId 浏览器ID - * @throws IOException IOEXCEPTION - * @author xiaoQQya - * @since 2023/11/06 */ @GetMapping("/emit") - public void emit(HttpServletResponse response, @RequestParam String browserId) throws IOException, InterruptedException { - response.setContentType("text/event-stream"); - response.setCharacterEncoding("utf-8"); - - PrintWriter writer = response.getWriter(); - alarmEventListener.addSseEmitter(browserId, writer); - - while (!writer.checkError()) { - Thread.sleep(1000); - writer.write(":keep alive\n\n"); - writer.flush(); - } - alarmEventListener.removeSseEmitter(browserId, writer); + public SseEmitter emit(HttpServletResponse response, @RequestParam String browserId) throws IOException, InterruptedException { +// response.setContentType("text/event-stream"); +// response.setCharacterEncoding("utf-8"); + return sseSessionManager.conect(browserId); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java index 52213d641..caa8fa08b 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/CommonGBChannelMapper.java @@ -255,10 +255,17 @@ public interface CommonGBChannelMapper { @SelectProvider(type = ChannelProvider.class, method = "queryByStreamProxyId") CommonGBChannel queryByStreamProxyId(@Param("streamProxyId") Integer streamProxyId); - @SelectProvider(type = ChannelProvider.class, method = "queryList") - List queryList(@Param("query") String query, @Param("online") Boolean online, - @Param("hasCivilCode") Boolean hasCivilCode, - @Param("hasGroup") Boolean hasGroup); + @SelectProvider(type = ChannelProvider.class, method = "queryListByCivilCode") + List queryListByCivilCode(@Param("query") String query, @Param("online") Boolean online, + @Param("channelType") Integer channelType, @Param("civilCode") String civilCode); + + + + @SelectProvider(type = ChannelProvider.class, method = "queryListByParentId") + List queryListByParentId(@Param("query") String query, @Param("online") Boolean online, + @Param("channelType") Integer channelType, @Param("groupDeviceId") String groupDeviceId); + + @Select(""}) + void updateGpsByDeviceIdForStreamPush(List channels); + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java index e5c65c416..ea4b97174 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceChannelMapper.java @@ -5,11 +5,13 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelReduce; import com.genersoft.iot.vmp.gb28181.dao.provider.DeviceChannelProvider; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; +import com.genersoft.iot.vmp.streamPush.bean.StreamPush; import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend; import org.apache.ibatis.annotations.*; import org.springframework.stereotype.Repository; import java.util.List; +import java.util.Map; /** * 用于存储设备通道信息 @@ -85,7 +87,10 @@ public interface DeviceChannelMapper { int update(DeviceChannel channel); @SelectProvider(type = DeviceChannelProvider.class, method = "queryChannels") - List queryChannels(@Param("deviceDbId") int deviceDbId, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("channelIds") List channelIds); + List queryChannels(@Param("deviceDbId") int deviceDbId, @Param("civilCode") String civilCode, + @Param("businessGroupId") String businessGroupId, @Param("parentChannelId") String parentChannelId, + @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, + @Param("online") Boolean online, @Param("channelIds") List channelIds); @SelectProvider(type = DeviceChannelProvider.class, method = "queryChannelsByDeviceDbId") List queryChannelsByDeviceDbId(@Param("deviceDbId") int deviceDbId); @@ -148,9 +153,6 @@ public interface DeviceChannelMapper { " "}) List queryChannelsWithDeviceInfo(@Param("deviceId") String deviceId, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("channelIds") List channelIds); - @Update(value = {"UPDATE wvp_device_channel SET stream_id=null WHERE device_db_id=#{deviceId} AND device_id=#{channelId}"}) - void stopPlay(@Param("deviceId") int deviceId, @Param("channelId") String channelId); - @Update(value = {"UPDATE wvp_device_channel SET stream_id=#{streamId} WHERE id=#{channelId}"}) void startPlay(@Param("channelId") Integer channelId, @Param("streamId") String streamId); @@ -206,7 +208,7 @@ public interface DeviceChannelMapper { int batchAdd(@Param("addChannels") List addChannels); - @Update(value = {"UPDATE wvp_device_channel SET status='OFF' WHERE id=#{id}"}) + @Update(value = {"UPDATE wvp_device_channel SET status='ON' WHERE id=#{id}"}) void online(@Param("id") int id); @Update({""}) void updateChannelForNotify(DeviceChannel channel); + + + @Select(value = {" "}) + DeviceChannel getOneBySourceChannelId(int deviceDbId, String channelId); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java index cca4d84ff..3df8b8878 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/DeviceMapper.java @@ -2,9 +2,7 @@ package com.genersoft.iot.vmp.gb28181.dao; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; -import com.github.pagehelper.PageInfo; import org.apache.ibatis.annotations.*; -import org.apache.ibatis.annotations.Param; import org.springframework.stereotype.Repository; import java.util.List; @@ -59,6 +57,7 @@ public interface DeviceMapper { "firmware, " + "transport," + "stream_mode," + + "media_server_id," + "ip," + "sdp_ip," + "local_ip," + @@ -88,6 +87,7 @@ public interface DeviceMapper { "#{firmware}," + "#{transport}," + "#{streamMode}," + + "#{mediaServerId}," + "#{ip}," + "#{sdpIp}," + "#{localIp}," + @@ -246,24 +246,12 @@ public interface DeviceMapper { @Update(value = {" "}) void updateCustom(Device device); @@ -280,6 +268,7 @@ public interface DeviceMapper { "broadcast_push_after_ack,"+ "geo_coord_sys,"+ "on_line,"+ + "stream_mode," + "media_server_id"+ ") VALUES (" + "#{deviceId}," + @@ -294,6 +283,7 @@ public interface DeviceMapper { "#{broadcastPushAfterAck}," + "#{geoCoordSys}," + "#{onLine}," + + "#{streamMode}," + "#{mediaServerId}" + ")") void addCustomDevice(Device device); @@ -340,7 +330,7 @@ public interface DeviceMapper { " FROM wvp_device de" + " where 1 = 1 "+ " AND de.on_line=${status}"+ - " AND (coalesce(custom_name, name) LIKE '%${query}%' OR device_id LIKE '%${query}%') " + + " AND (coalesce(custom_name, name) LIKE '%${query}%' OR device_id LIKE '%${query}%' OR ip LIKE '%${query}%') " + " order by create_time desc "+ " ") List getDeviceList(@Param("query") String query, @Param("status") Boolean status); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/GroupMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/GroupMapper.java index 5fb644680..1c89a4689 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/GroupMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/GroupMapper.java @@ -69,7 +69,8 @@ public interface GroupMapper { " * , " + " concat('group', id) as tree_id," + " 0 as type," + - " false as is_leaf" + + " false as is_leaf," + + " 'ON' as status" + " from wvp_common_group " + " where 1=1 " + " and parent_id = #{parentId} " + @@ -153,7 +154,7 @@ public interface GroupMapper { " SELECT " + " wcg.device_id as gb_device_id," + " wcg.name as gb_name," + - " wcg.business_group as gb_business_group," + + " wcg.business_group as gb_business_group_id," + " 1 as gb_parental," + " wcg.parent_device_id as gb_parent_id" + " from wvp_common_group wcg" + diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformChannelMapper.java index 3311a249c..d23eec4ba 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformChannelMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/PlatformChannelMapper.java @@ -540,4 +540,12 @@ public interface PlatformChannelMapper { " ") Set queryShareGroup(@Param("platformId") Integer platformId); + @Select(" ") + Set queryShareRegion(Integer id); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/RegionMapper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/RegionMapper.java index 42db7bbf9..9021916a6 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/RegionMapper.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/RegionMapper.java @@ -1,7 +1,6 @@ package com.genersoft.iot.vmp.gb28181.dao; import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; -import com.genersoft.iot.vmp.gb28181.bean.Group; import com.genersoft.iot.vmp.gb28181.bean.Region; import com.genersoft.iot.vmp.gb28181.bean.RegionTree; import org.apache.ibatis.annotations.*; @@ -74,6 +73,7 @@ public interface RegionMapper { " *, " + " concat('region', id) as tree_id," + " 0 as type," + + " 'ON' as status," + " false as is_leaf" + " from wvp_common_region " + " where " + diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/ChannelProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/ChannelProvider.java index d4f5ce73f..2fe3dc5ef 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/ChannelProvider.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/ChannelProvider.java @@ -117,7 +117,7 @@ public class ChannelProvider { } - public String queryList(Map params ){ + public String queryListByCivilCode(Map params ){ StringBuilder sqlBuild = new StringBuilder(); sqlBuild.append(BASE_SQL); sqlBuild.append(" where channel_type = 0 "); @@ -132,18 +132,53 @@ public class ChannelProvider { if (params.get("online") != null && !(Boolean)params.get("online")) { sqlBuild.append(" AND coalesce(gb_status, status) = 'OFF'"); } - if (params.get("hasCivilCode") != null && (Boolean)params.get("hasCivilCode")) { - sqlBuild.append(" AND coalesce(gb_civil_code, civil_code) is not null"); - } - if (params.get("hasCivilCode") != null && !(Boolean)params.get("hasCivilCode")) { + if (params.get("civilCode") != null) { + sqlBuild.append(" AND coalesce(gb_civil_code, civil_code) = #{civilCode}"); + }else { sqlBuild.append(" AND coalesce(gb_civil_code, civil_code) is null"); } - if (params.get("hasGroup") != null && (Boolean)params.get("hasGroup")) { - sqlBuild.append(" AND coalesce(gb_parent_id, parent_id) is not null"); + if (params.get("channelType") != null) { + if ((Integer)params.get("channelType") == 0) { + sqlBuild.append(" AND device_db_id is not null"); + }else if ((Integer)params.get("channelType") == 1) { + sqlBuild.append(" AND stream_push_id is not null"); + }else if ((Integer)params.get("channelType") == 2) { + sqlBuild.append(" AND stream_proxy_id is not null"); + } } - if (params.get("hasGroup") != null && !(Boolean)params.get("hasGroup")) { + return sqlBuild.toString(); + } + + public String queryListByParentId(Map params ){ + StringBuilder sqlBuild = new StringBuilder(); + sqlBuild.append(BASE_SQL); + sqlBuild.append(" where channel_type = 0 "); + if (params.get("query") != null) { + sqlBuild.append(" AND (coalesce(gb_device_id, device_id) LIKE concat('%',#{query},'%')" + + " OR coalesce(gb_name, name) LIKE concat('%',#{query},'%') )") + ; + } + if (params.get("online") != null && (Boolean)params.get("online")) { + sqlBuild.append(" AND coalesce(gb_status, status) = 'ON'"); + } + if (params.get("online") != null && !(Boolean)params.get("online")) { + sqlBuild.append(" AND coalesce(gb_status, status) = 'OFF'"); + } + if (params.get("groupDeviceId") != null) { + sqlBuild.append(" AND coalesce(gb_parent_id, parent_id) = #{groupDeviceId}"); + }else { sqlBuild.append(" AND coalesce(gb_parent_id, parent_id) is null"); } + + if (params.get("channelType") != null) { + if ((Integer)params.get("channelType") == 0) { + sqlBuild.append(" AND device_db_id is not null"); + }else if ((Integer)params.get("channelType") == 1) { + sqlBuild.append(" AND stream_push_id is not null"); + }else if ((Integer)params.get("channelType") == 2) { + sqlBuild.append(" AND stream_proxy_id is not null"); + } + } return sqlBuild.toString(); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/DeviceChannelProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/DeviceChannelProvider.java index e21b7729d..dc29280e2 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/DeviceChannelProvider.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/dao/provider/DeviceChannelProvider.java @@ -1,5 +1,7 @@ package com.genersoft.iot.vmp.gb28181.dao.provider; +import org.springframework.util.ObjectUtils; + import java.util.List; import java.util.Map; @@ -58,14 +60,20 @@ public class DeviceChannelProvider { public String queryChannels(Map params ){ StringBuilder sqlBuild = new StringBuilder(); sqlBuild.append(getBaseSelectSql()); - sqlBuild.append(" where dc.device_db_id = #{deviceDbId}"); - if (params.get("query") != null) { - sqlBuild.append(" AND coalesce(dc.gb_device_id, dc.device_id) LIKE concat('%',#{query},'%')" + - " OR coalesce(dc.gb_name, dc.name) LIKE concat('%',#{query},'%')") - ; + sqlBuild.append(" where dc.device_db_id = #{deviceDbId} "); + if (params.get("businessGroupId") != null ) { + sqlBuild.append(" AND coalesce(dc.gb_business_group_id, dc.business_group_id)=#{businessGroupId} AND coalesce(dc.gb_parent_id, dc.parent_id) is null"); + }else if (params.get("parentChannelId") != null ) { + sqlBuild.append(" AND coalesce(dc.gb_parent_id, dc.parent_id)=#{parentChannelId}"); } - if (params.get("parentChannelId") != null ) { - sqlBuild.append(" AND (dc.parent_id=#{parentChannelId} OR coalesce(dc.gb_civil_code, dc.civil_code) = #{parentChannelId})"); + if (params.get("civilCode") != null ) { + sqlBuild.append(" AND (coalesce(dc.gb_civil_code, dc.civil_code) = #{civilCode} " + + "OR (LENGTH(coalesce(dc.gb_device_id, dc.device_id))=LENGTH(#{civilCode}) + 2) AND coalesce(dc.gb_device_id, dc.device_id) LIKE concat(#{civilCode},'%'))"); + } + if (params.get("query") != null && !ObjectUtils.isEmpty(params.get("query"))) { + sqlBuild.append(" AND (coalesce(dc.gb_device_id, dc.device_id) LIKE concat('%',#{query},'%')" + + " OR coalesce(dc.gb_name, dc.name) LIKE concat('%',#{query},'%'))") + ; } if (params.get("online") != null && (Boolean)params.get("online")) { sqlBuild.append(" AND coalesce(gb_status, status) = 'ON'"); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java index 8355f2a99..7d4c876a9 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/EventPublisher.java @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.event.device.RequestTimeoutEvent; import com.genersoft.iot.vmp.gb28181.event.record.RecordEndEvent; import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.gb28181.event.subscribe.mobilePosition.MobilePositionEvent; +import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent; import org.springframework.beans.factory.annotation.Autowired; @@ -39,15 +40,15 @@ public class EventPublisher { applicationEventPublisher.publishEvent(alarmEvent); } - public void mediaServerOfflineEventPublish(String mediaServerId){ + public void mediaServerOfflineEventPublish(MediaServer mediaServer){ MediaServerOfflineEvent outEvent = new MediaServerOfflineEvent(this); - outEvent.setMediaServerId(mediaServerId); + outEvent.setMediaServer(mediaServer); applicationEventPublisher.publishEvent(outEvent); } - public void mediaServerOnlineEventPublish(String mediaServerId) { + public void mediaServerOnlineEventPublish(MediaServer mediaServer) { MediaServerOnlineEvent outEvent = new MediaServerOnlineEvent(this); - outEvent.setMediaServerId(mediaServerId); + outEvent.setMediaServer(mediaServer); applicationEventPublisher.publishEvent(outEvent); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java index 455be8df0..a2034c726 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/SipSubscribe.java @@ -29,7 +29,7 @@ public class SipSubscribe { private final DelayQueue delayQueue = new DelayQueue<>(); - @Scheduled(fixedRate = 200) //每200毫秒执行 + @Scheduled(fixedDelay = 200) //每200毫秒执行 public void execute(){ if (delayQueue.isEmpty()) { return; @@ -180,4 +180,8 @@ public class SipSubscribe { public boolean isEmpty(){ return subscribes.isEmpty(); } + + public Integer size() { + return subscribes.size(); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java index 317683d61..a5ffc1a1e 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/alarm/AlarmEventListener.java @@ -1,14 +1,12 @@ package com.genersoft.iot.vmp.gb28181.event.alarm; +import com.genersoft.iot.vmp.gb28181.session.SseSessionManager; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.context.ApplicationListener; import org.springframework.stereotype.Component; -import java.io.PrintWriter; -import java.util.Iterator; -import java.util.Map; -import java.util.concurrent.ConcurrentHashMap; +import javax.annotation.Resource; /** * 报警事件监听器. @@ -21,48 +19,14 @@ import java.util.concurrent.ConcurrentHashMap; @Component public class AlarmEventListener implements ApplicationListener { - private static final Map SSE_CACHE = new ConcurrentHashMap<>(); - - public void addSseEmitter(String browserId, PrintWriter writer) { - SSE_CACHE.put(browserId, writer); - log.info("SSE 在线数量: {}", SSE_CACHE.size()); - } - - public void removeSseEmitter(String browserId, PrintWriter writer) { - SSE_CACHE.remove(browserId, writer); - log.info("SSE 在线数量: {}", SSE_CACHE.size()); - } + @Resource + private SseSessionManager sseSessionManager; @Override public void onApplicationEvent(@NotNull AlarmEvent event) { if (log.isDebugEnabled()) { log.debug("设备报警事件触发, deviceId: {}, {}", event.getAlarmInfo().getDeviceId(), event.getAlarmInfo().getAlarmDescription()); } - - String msg = "设备编号: " + event.getAlarmInfo().getDeviceId() + "" - + "
通道编号: " + event.getAlarmInfo().getChannelId() + "" - + "
报警描述: " + event.getAlarmInfo().getAlarmDescription() + "" - + "
报警时间: " + event.getAlarmInfo().getAlarmTime() + ""; - - for (Iterator> it = SSE_CACHE.entrySet().iterator(); it.hasNext(); ) { - Map.Entry response = it.next(); - log.info("推送到 SSE 连接, 浏览器 ID: {}", response.getKey()); - try { - PrintWriter writer = response.getValue(); - - if (writer.checkError()) { - it.remove(); - continue; - } - - String sseMsg = "event:message\n" + - "data:" + msg + "\n" + - "\n"; - writer.write(sseMsg); - writer.flush(); - } catch (Exception e) { - it.remove(); - } - } + sseSessionManager.sendForAll("message", event.getAlarmInfo()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/sip/SipEvent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/sip/SipEvent.java index 45b2626f0..e6af35caf 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/sip/SipEvent.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/sip/SipEvent.java @@ -23,7 +23,7 @@ public class SipEvent implements Delayed { private SipSubscribe.Event errorEvent; /** - * 超时时间 + * 超时时间(单位: 毫秒) */ private long delay; @@ -38,7 +38,7 @@ public class SipEvent implements Delayed { @Override public long getDelay(@NotNull TimeUnit unit) { - return unit.convert(delay - System.currentTimeMillis(),TimeUnit.MILLISECONDS); + return unit.convert(delay, TimeUnit.MILLISECONDS); } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceChannelService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceChannelService.java index 8b5a8abbd..81d9f9aff 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceChannelService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IDeviceChannelService.java @@ -119,4 +119,7 @@ public interface IDeviceChannelService { void updateChannelForNotify(DeviceChannel channel); DeviceChannel getOneForSource(int deviceDbId, String channelId); + + DeviceChannel getOneBySourceId(int deviceDbId, String channelId); + } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java index ac13ffa09..2d8f95315 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGbChannelService.java @@ -41,7 +41,9 @@ public interface IGbChannelService { void reset(int id); - PageInfo queryList(int page, int count, String query, Boolean online, Boolean hasCivilCode, Boolean hasGroup); + PageInfo queryListByCivilCode(int page, int count, String query, Boolean online, Integer channelType, String civilCode); + + PageInfo queryListByParentId(int page, int count, String query, Boolean online, Integer channelType, String groupDeviceId); void removeCivilCode(List allChildren); @@ -81,4 +83,5 @@ public interface IGbChannelService { List queryListByStreamPushList(List streamPushList); + void updateGpsByDeviceIdForStreamPush(List channels); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGroupService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGroupService.java index a3265bd6d..f1ff825c6 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGroupService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IGroupService.java @@ -14,11 +14,13 @@ public interface IGroupService { Group queryGroupByDeviceId(String regionDeviceId); - List queryForTree(String query, Integer parent); + List queryForTree(String query, Integer parent, Boolean hasChannel); void syncFromChannel(); boolean delete(int id); boolean batchAdd(List groupList); + + List getPath(String deviceId, String businessGroup); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IInviteStreamService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IInviteStreamService.java index 9903d59bc..e8e4d3c62 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IInviteStreamService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IInviteStreamService.java @@ -39,7 +39,7 @@ public interface IInviteStreamService { */ void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, Integer channelId); - List getAllInviteInfo(InviteSessionType type, Integer channelId, String stream); + List getAllInviteInfo(); /** * 获取点播的状态信息 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformChannelService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformChannelService.java index c862b6653..e03e0907e 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformChannelService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlatformChannelService.java @@ -1,9 +1,6 @@ package com.genersoft.iot.vmp.gb28181.service; -import com.genersoft.iot.vmp.gb28181.bean.CommonGBChannel; -import com.genersoft.iot.vmp.gb28181.bean.Group; -import com.genersoft.iot.vmp.gb28181.bean.Platform; -import com.genersoft.iot.vmp.gb28181.bean.PlatformChannel; +import com.genersoft.iot.vmp.gb28181.bean.*; import com.github.pagehelper.PageInfo; import java.util.List; @@ -45,4 +42,8 @@ public interface IPlatformChannelService { List queryPlatFormListByChannelDeviceId(Integer channelId, List platforms); CommonGBChannel queryChannelByPlatformIdAndChannelId(Integer platformId, Integer channelId); + + void checkRegionAdd(List channelList); + + void checkRegionRemove(List channelList, List regionList); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java index 7f8bc0142..aa2ca3ab6 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IPlayService.java @@ -30,13 +30,13 @@ public interface IPlayService { MediaServer getNewMediaServerItem(Device device); void playBack(Device device, DeviceChannel channel, String startTime, String endTime, ErrorCallback callback); - void zlmServerOffline(String mediaServerId); + void zlmServerOffline(MediaServer mediaServer); void download(Device device, DeviceChannel channel, String startTime, String endTime, int downloadSpeed, ErrorCallback callback); StreamInfo getDownLoadInfo(Device device, DeviceChannel channel, String stream); - void zlmServerOnline(String mediaServerId); + void zlmServerOnline(MediaServer mediaServer); AudioBroadcastResult audioBroadcast(Device device, DeviceChannel deviceChannel, Boolean broadcastMode); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IRegionService.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IRegionService.java index 3fe220748..0bd8700db 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/IRegionService.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/IRegionService.java @@ -27,11 +27,13 @@ public interface IRegionService { Region queryRegionByDeviceId(String regionDeviceId); - List queryForTree(String query, Integer parent); + List queryForTree(String query, Integer parent, Boolean hasChannel); void syncFromChannel(); boolean delete(int id); boolean batchAdd(List regionList); + + List getPath(String deviceId); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java index 9f0d67278..198d65d10 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java @@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; +import com.genersoft.iot.vmp.gb28181.bean.GbCode; import com.genersoft.iot.vmp.gb28181.bean.MobilePosition; import com.genersoft.iot.vmp.gb28181.controller.bean.ChannelReduce; import com.genersoft.iot.vmp.gb28181.dao.DeviceChannelMapper; @@ -36,7 +37,6 @@ import org.springframework.util.CollectionUtils; import org.springframework.util.ObjectUtils; import java.util.*; -import java.util.concurrent.ConcurrentHashMap; /** * @author lin @@ -272,6 +272,11 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { return channelMapper.getOneByDeviceIdForSource(deviceDbId, channelId); } + @Override + public DeviceChannel getOneBySourceId(int deviceDbId, String channelId) { + return channelMapper.getOneBySourceChannelId(deviceDbId, channelId); + } + @Override @Transactional public synchronized void batchUpdateChannelForNotify(List channels) { @@ -326,7 +331,6 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { @Override public void updateChannelStreamIdentification(DeviceChannel channel) { - Assert.isTrue(channel.getId() > 0, "通道ID必须存在"); Assert.hasLength(channel.getStreamIdentification(), "码流标识必须存在"); if (ObjectUtils.isEmpty(channel.getStreamIdentification())) { log.info("[重置通道码流类型] 设备: {}, 码流: {}", channel.getDeviceId(), channel.getStreamIdentification()); @@ -466,7 +470,7 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { return false; } List allChannels = channelMapper.queryAllChannelsForRefresh(deviceDbId); - Map allChannelMap = new ConcurrentHashMap<>(); + Map allChannelMap = new HashMap<>(); if (!allChannels.isEmpty()) { for (DeviceChannel deviceChannel : allChannels) { allChannelMap.put(deviceChannel.getDeviceDbId() + deviceChannel.getDeviceId(), deviceChannel); @@ -481,15 +485,8 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { StringBuilder stringBuilder = new StringBuilder(); Map subContMap = new HashMap<>(); - // 数据去重 - Set gbIdSet = new HashSet<>(); for (DeviceChannel deviceChannel : deviceChannelList) { - if (gbIdSet.contains(deviceDbId + deviceChannel.getDeviceId())) { - stringBuilder.append(deviceChannel.getDeviceId()).append(","); - continue; - } - gbIdSet.add(deviceDbId + deviceChannel.getDeviceId()); - DeviceChannel channelInDb = allChannelMap.get(deviceDbId + deviceChannel.getDeviceId()); + DeviceChannel channelInDb = allChannelMap.get(deviceChannel.getDeviceDbId() + deviceChannel.getDeviceId()); if (channelInDb != null) { deviceChannel.setStreamId(channelInDb.getStreamId()); deviceChannel.setHasAudio(channelInDb.isHasAudio()); @@ -540,7 +537,7 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { log.info("通道重设,数据为空={}" , deviceChannelList); return false; } - int limitCount = 50; + int limitCount = 500; if (!addChannels.isEmpty()) { if (addChannels.size() > limitCount) { for (int i = 0; i < addChannels.size(); i += limitCount) { @@ -599,7 +596,20 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { @Override public PageInfo getSubChannels(int deviceDbId, String channelId, String query, Boolean channelType, Boolean online, int page, int count) { PageHelper.startPage(page, count); - List all = channelMapper.queryChannels(deviceDbId, channelId, query, channelType, online,null); + String civilCode = null; + String parentId = null; + String businessGroupId = null; + if (channelId.length() <= 8) { + civilCode = channelId; + }else { + GbCode decode = GbCode.decode(channelId); + if (Integer.parseInt(decode.getTypeCode()) == 215) { + businessGroupId = channelId; + }else { + parentId = channelId; + } + } + List all = channelMapper.queryChannels(deviceDbId, civilCode, businessGroupId, parentId, query, channelType, online,null); return new PageInfo<>(all); } @@ -616,7 +626,7 @@ public class DeviceChannelServiceImpl implements IDeviceChannelService { } // 获取到所有正在播放的流 PageHelper.startPage(page, count); - List all = channelMapper.queryChannels(device.getId(), null, query, hasSubChannel, online,null); + List all = channelMapper.queryChannels(device.getId(), null,null, null, query, hasSubChannel, online,null); return new PageInfo<>(all); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java index cbef7b5fc..f731ce70b 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceServiceImpl.java @@ -168,15 +168,13 @@ public class DeviceServiceImpl implements IDeviceService { } }else { - if (deviceChannelMapper.queryChannelsByDeviceDbId(device.getId()).isEmpty()) { - log.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId()); - sync(device); - } - deviceMapper.update(device); redisCatchStorage.updateDevice(device); } - + if (deviceChannelMapper.queryChannelsByDeviceDbId(device.getId()).isEmpty()) { + log.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId()); + sync(device); + } } // 刷新过期任务 @@ -337,12 +335,12 @@ public class DeviceServiceImpl implements IDeviceService { try { sipCommander.catalogQuery(device, sn, event -> { String errorMsg = String.format("同步通道失败,错误码: %s, %s", event.statusCode, event.msg); - catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); + catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), sn, errorMsg); }); } catch (SipException | InvalidArgumentException | ParseException e) { log.error("[同步通道], 信令发送失败:{}", e.getMessage() ); String errorMsg = String.format("同步通道失败,信令发送失败: %s", e.getMessage()); - catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), errorMsg); + catalogResponseMessageHandler.setChannelSyncEnd(device.getDeviceId(), sn, errorMsg); } } @@ -415,39 +413,19 @@ public class DeviceServiceImpl implements IDeviceService { device.setOnLine(false); device.setCreateTime(DateUtil.getNow()); device.setUpdateTime(DateUtil.getNow()); + if(device.getStreamMode() == null) { + device.setStreamMode("UDP"); + } deviceMapper.addCustomDevice(device); } @Override public void updateCustomDevice(Device device) { - Device deviceInStore = deviceMapper.getDeviceByDeviceId(device.getDeviceId()); + Device deviceInStore = deviceMapper.query(device.getId()); if (deviceInStore == null) { log.warn("更新设备时未找到设备信息"); return; } - - if (!ObjectUtils.isEmpty(device.getName())) { - deviceInStore.setName(device.getName()); - } - if (!ObjectUtils.isEmpty(device.getCharset())) { - deviceInStore.setCharset(device.getCharset()); - } - if (!ObjectUtils.isEmpty(device.getMediaServerId())) { - deviceInStore.setMediaServerId(device.getMediaServerId()); - } - if (!ObjectUtils.isEmpty(device.getCharset())) { - deviceInStore.setCharset(device.getCharset()); - } - if (!ObjectUtils.isEmpty(device.getSdpIp())) { - deviceInStore.setSdpIp(device.getSdpIp()); - } - if (!ObjectUtils.isEmpty(device.getPassword())) { - deviceInStore.setPassword(device.getPassword()); - } - if (!ObjectUtils.isEmpty(device.getStreamMode())) { - deviceInStore.setStreamMode(device.getStreamMode()); - } - deviceInStore.setBroadcastPushAfterAck(device.isBroadcastPushAfterAck()); // 目录订阅相关的信息 if (deviceInStore.getSubscribeCycleForCatalog() != device.getSubscribeCycleForCatalog()) { if (device.getSubscribeCycleForCatalog() > 0) { @@ -512,13 +490,9 @@ public class DeviceServiceImpl implements IDeviceService { if (device.getCharset() == null) { deviceInStore.setCharset("GB2312"); } - //SSRC校验 - deviceInStore.setSsrcCheck(device.isSsrcCheck()); - //作为消息通道 - deviceInStore.setAsMessageChannel(device.isAsMessageChannel()); - deviceMapper.updateCustom(deviceInStore); - redisCatchStorage.updateDevice(deviceInStore); + deviceMapper.updateCustom(device); + redisCatchStorage.updateDevice(device); } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java index 62be902bd..38e3bd125 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GbChannelServiceImpl.java @@ -156,7 +156,7 @@ public class GbChannelServiceImpl implements IGbChannelService { } List onlineChannelList = commonGBChannelMapper.queryInListByStatus(commonGBChannelList, "ON"); if (onlineChannelList.isEmpty()) { - log.warn("[多个通道离线] 更新失败, 参数内通道已经离线, 无需更新"); + log.info("[多个通道离线] 更新失败, 参数内通道已经离线, 无需更新"); return 0; } int limitCount = 1000; @@ -388,10 +388,16 @@ public class GbChannelServiceImpl implements IGbChannelService { } @Override - public PageInfo queryList(int page, int count, String query, Boolean online, Boolean hasCivilCode, - Boolean hasGroup) { + public PageInfo queryListByCivilCode(int page, int count, String query, Boolean online, Integer channelType, String civilCode) { PageHelper.startPage(page, count); - List all = commonGBChannelMapper.queryList(query, online, hasCivilCode, hasGroup); + List all = commonGBChannelMapper.queryListByCivilCode(query, online, channelType, civilCode); + return new PageInfo<>(all); + } + + @Override + public PageInfo queryListByParentId(int page, int count, String query, Boolean online, Integer channelType, String groupDeviceId) { + PageHelper.startPage(page, count); + List all = commonGBChannelMapper.queryListByParentId(query, online, channelType, groupDeviceId); return new PageInfo<>(all); } @@ -414,6 +420,7 @@ public class GbChannelServiceImpl implements IGbChannelService { int result = commonGBChannelMapper.updateRegion(civilCode, channelList); // 发送通知 if (result > 0) { + platformChannelService.checkRegionAdd(channelList); try { // 发送catalog eventPublisher.catalogEventPublish(null, channelList, CatalogEvent.UPDATE); @@ -441,6 +448,14 @@ public class GbChannelServiceImpl implements IGbChannelService { throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); } int result = commonGBChannelMapper.removeCivilCodeByChannels(channelList); + Region region = regionMapper.queryByDeviceId(civilCode); + if (region == null) { + platformChannelService.checkRegionRemove(channelList, null); + }else { + List regionList = new ArrayList<>(); + regionList.add(region); + platformChannelService.checkRegionRemove(channelList, regionList); + } // TODO 发送通知 // if (result > 0) { // try { @@ -459,6 +474,8 @@ public class GbChannelServiceImpl implements IGbChannelService { throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); } int result = commonGBChannelMapper.removeCivilCodeByChannels(channelList); + + platformChannelService.checkRegionRemove(channelList, null); // TODO 发送通知 // if (result > 0) { // try { @@ -498,6 +515,7 @@ public class GbChannelServiceImpl implements IGbChannelService { throw new ControllerException(ErrorCode.ERROR100.getCode(), "所有通道Id不存在"); } int result = commonGBChannelMapper.removeCivilCodeByChannels(channelList); + platformChannelService.checkRegionRemove(channelList, null); } @Override @@ -681,4 +699,9 @@ public class GbChannelServiceImpl implements IGbChannelService { public List queryListByStreamPushList(List streamPushList) { return commonGBChannelMapper.queryListByStreamPushList(streamPushList); } + + @Override + public void updateGpsByDeviceIdForStreamPush(List channels) { + commonGBChannelMapper.updateGpsByDeviceIdForStreamPush(channels); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GroupServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GroupServiceImpl.java index ebb631cbf..3e79f7c4a 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GroupServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/GroupServiceImpl.java @@ -1,5 +1,6 @@ package com.genersoft.iot.vmp.gb28181.service.impl; +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.dao.CommonGBChannelMapper; import com.genersoft.iot.vmp.gb28181.dao.GroupMapper; @@ -8,6 +9,7 @@ import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.gb28181.service.IGbChannelService; import com.genersoft.iot.vmp.gb28181.service.IGroupService; import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; @@ -15,10 +17,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.util.Assert; import org.springframework.util.ObjectUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; /** * 区域管理类 @@ -154,7 +153,7 @@ public class GroupServiceImpl implements IGroupService { } @Override - public List queryForTree(String query, Integer parentId) { + public List queryForTree(String query, Integer parentId, Boolean hasChannel) { List groupTrees = groupManager.queryForTree(query, parentId); if (parentId == null) { @@ -162,7 +161,7 @@ public class GroupServiceImpl implements IGroupService { } // 查询含有的通道 Group parentGroup = groupManager.queryOne(parentId); - if (parentGroup != null ) { + if (parentGroup != null && hasChannel != null && hasChannel) { List groupTreesForChannel = commonGBChannelMapper.queryForGroupTreeByParentId(query, parentGroup.getDeviceId()); if (!ObjectUtils.isEmpty(groupTreesForChannel)) { groupTrees.addAll(groupTreesForChannel); @@ -247,4 +246,37 @@ public class GroupServiceImpl implements IGroupService { return true; } + + @Override + public List getPath(String deviceId, String businessGroup) { + Group businessGroupInDb = groupManager.queryBusinessGroup(businessGroup); + if (businessGroupInDb == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "业务分组不存在"); + } + List groupList = new LinkedList<>(); + groupList.add(businessGroupInDb); + Group group = groupManager.queryOneByDeviceId(deviceId, businessGroup); + if (group == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "虚拟组织不存在"); + } + groupList.add(group); + List allParent = getAllParent(group); + groupList.addAll(allParent); + return groupList; + } + + private List getAllParent(Group group) { + if (group.getParentId() == null || group.getBusinessGroup() == null) { + return new ArrayList<>(); + } + + List groupList = new ArrayList<>(); + Group parent = groupManager.queryOneByDeviceId(group.getParentDeviceId(), group.getBusinessGroup()); + if (parent == null) { + return groupList; + } + List allParent = getAllParent(parent); + allParent.add(parent); + return allParent; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/InviteStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/InviteStreamServiceImpl.java index 3ca087065..4338bbfe5 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/InviteStreamServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/InviteStreamServiceImpl.java @@ -10,12 +10,14 @@ import com.genersoft.iot.vmp.gb28181.dao.DeviceMapper; import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; import com.genersoft.iot.vmp.service.bean.ErrorCallback; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.context.event.EventListener; +import org.springframework.data.redis.core.Cursor; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; import org.springframework.scheduling.annotation.Async; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; import java.util.ArrayList; @@ -23,7 +25,6 @@ import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.CopyOnWriteArrayList; -import java.util.concurrent.TimeUnit; @Slf4j @Service @@ -61,11 +62,12 @@ public class InviteStreamServiceImpl implements IInviteStreamService { } } } + @Override public void updateInviteInfo(InviteInfo inviteInfo) { if (InviteSessionStatus.ready == inviteInfo.getStatus()) { updateInviteInfo(inviteInfo, Long.valueOf(userSetting.getPlayTimeout()) * 2); - }else { + } else { updateInviteInfo(inviteInfo, null); } } @@ -79,10 +81,8 @@ public class InviteStreamServiceImpl implements IInviteStreamService { InviteInfo inviteInfoForUpdate; if (InviteSessionStatus.ready == inviteInfo.getStatus()) { - if (inviteInfo.getDeviceId() == null - || inviteInfo.getChannelId() == null - || inviteInfo.getType() == null - || inviteInfo.getStream() == null + if (inviteInfo.getDeviceId() == null || inviteInfo.getChannelId() == null + || inviteInfo.getType() == null || inviteInfo.getStream() == null ) { return; } @@ -116,17 +116,17 @@ public class InviteStreamServiceImpl implements IInviteStreamService { inviteInfoForUpdate = inviteInfoInRedis; } - String key = VideoManagerConstants.INVITE_PREFIX + - ":" + inviteInfoForUpdate.getType() + - ":" + inviteInfoForUpdate.getDeviceId() + - ":" + inviteInfoForUpdate.getChannelId() + - ":" + inviteInfoForUpdate.getStream()+ - ":" + inviteInfoForUpdate.getSsrcInfo().getSsrc(); - if (time != null && time > 0) { - redisTemplate.opsForValue().set(key, inviteInfoForUpdate, time, TimeUnit.SECONDS); - }else { - redisTemplate.opsForValue().set(key, inviteInfoForUpdate); + if (inviteInfoForUpdate.getCreateTime() == null) { + inviteInfoForUpdate.setCreateTime(System.currentTimeMillis()); } + String key = VideoManagerConstants.INVITE_PREFIX; + String objectKey = inviteInfoForUpdate.getType() + + ":" + inviteInfoForUpdate.getChannelId() + + ":" + inviteInfoForUpdate.getStream(); + if (time != null && time > 0) { + inviteInfoForUpdate.setExpirationTime(time); + } + redisTemplate.opsForHash().put(key, objectKey, inviteInfoForUpdate); } @Override @@ -137,59 +137,54 @@ public class InviteStreamServiceImpl implements IInviteStreamService { return null; } removeInviteInfo(inviteInfoInDb); - String key = VideoManagerConstants.INVITE_PREFIX + - ":" + inviteInfo.getType() + - ":" + inviteInfo.getDeviceId() + + String key = VideoManagerConstants.INVITE_PREFIX; + String objectKey = inviteInfo.getType() + ":" + inviteInfo.getChannelId() + - ":" + stream + - ":" + inviteInfo.getSsrcInfo().getSsrc(); + ":" + stream; inviteInfoInDb.setStream(stream); if (inviteInfoInDb.getSsrcInfo() != null) { inviteInfoInDb.getSsrcInfo().setStream(stream); } if (InviteSessionStatus.ready == inviteInfo.getStatus()) { - redisTemplate.opsForValue().set(key, inviteInfoInDb, userSetting.getPlayTimeout() * 2, TimeUnit.SECONDS); - }else { - redisTemplate.opsForValue().set(key, inviteInfoInDb); + inviteInfoInDb.setExpirationTime((long) (userSetting.getPlayTimeout() * 2)); } - + if (inviteInfoInDb.getCreateTime() == null) { + inviteInfoInDb.setCreateTime(System.currentTimeMillis()); + } + redisTemplate.opsForHash().put(key, objectKey, inviteInfoInDb); return inviteInfoInDb; } @Override public InviteInfo getInviteInfo(InviteSessionType type, Integer channelId, String stream) { - String key = VideoManagerConstants.INVITE_PREFIX + - ":" + (type != null ? type : "*") + - ":*" + + String key = VideoManagerConstants.INVITE_PREFIX; + String keyPattern = (type != null ? type : "*") + ":" + (channelId != null ? channelId : "*") + - ":" + (stream != null ? stream : "*") - + ":*"; - List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.isEmpty()) { - return null; - } - if (scanResult.size() != 1) { - log.warn("[获取InviteInfo] 发现 key: {}存在多条", key); - } + ":" + (stream != null ? stream : "*"); + ScanOptions options = ScanOptions.scanOptions().match(keyPattern).count(20).build(); + try (Cursor> cursor = redisTemplate.opsForHash().scan(key, options)) { + if (cursor.hasNext()) { + InviteInfo inviteInfo = (InviteInfo) cursor.next().getValue(); + cursor.close(); + return inviteInfo; - return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); + } + } catch (Exception e) { + log.error("[Redis-InviteInfo] 查询异常: ", e); + } + return null; } @Override - public List getAllInviteInfo(InviteSessionType type, Integer channelId, String stream) { - String key = VideoManagerConstants.INVITE_PREFIX + - ":" + (type != null ? type : "*") + - ":*" + - ":" + (channelId != null ? channelId : "*") + - ":" + (stream != null ? stream : "*") - + ":*"; - List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.isEmpty()) { - return new ArrayList<>(); - } + public List getAllInviteInfo() { List result = new ArrayList<>(); - for (Object keyObj : scanResult) { - result.add((InviteInfo) redisTemplate.opsForValue().get(keyObj)); + String key = VideoManagerConstants.INVITE_PREFIX; + List values = redisTemplate.opsForHash().values(key); + if(values.isEmpty()) { + return result; + } + for (Object value : values) { + result.add((InviteInfo)value); } return result; } @@ -205,24 +200,18 @@ public class InviteStreamServiceImpl implements IInviteStreamService { } @Override - public void removeInviteInfo(InviteSessionType type, Integer channelId, String stream) { - String scanKey = VideoManagerConstants.INVITE_PREFIX + - ":" + (type != null ? type : "*") + - ":*" + - ":" + (channelId != null ? channelId : "*") + - ":" + (stream != null ? stream : "*") + - ":*"; - List scanResult = RedisUtil.scan(redisTemplate, scanKey); - if (!scanResult.isEmpty()) { - for (Object keyObj : scanResult) { - String key = (String) keyObj; - InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(key); - if (inviteInfo == null) { - continue; - } - redisTemplate.delete(key); - inviteErrorCallbackMap.remove(buildKey(type,channelId, inviteInfo.getStream())); - } + public void removeInviteInfo(InviteSessionType type, Integer channelId, String stream) { + String key = VideoManagerConstants.INVITE_PREFIX; + if (type == null && channelId == null && stream == null) { + redisTemplate.opsForHash().delete(key); + return; + } + InviteInfo inviteInfo = getInviteInfo(type, channelId, stream); + if (inviteInfo != null) { + String objectKey = inviteInfo.getType() + + ":" + inviteInfo.getChannelId() + + ":" + inviteInfo.getStream(); + redisTemplate.opsForHash().delete(key, objectKey); } } @@ -237,14 +226,14 @@ public class InviteStreamServiceImpl implements IInviteStreamService { } @Override - public void once(InviteSessionType type, Integer channelId, String stream, ErrorCallback callback) { + public void once(InviteSessionType type, Integer channelId, String stream, ErrorCallback callback) { String key = buildKey(type, channelId, stream); List> callbacks = inviteErrorCallbackMap.computeIfAbsent(key, k -> new CopyOnWriteArrayList<>()); callbacks.add(callback); } - private String buildKey(InviteSessionType type, Integer channelId, String stream) { + private String buildKey(InviteSessionType type, Integer channelId, String stream) { String key = type + ":" + channelId; // 如果ssrc未null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite if (stream != null) { @@ -256,7 +245,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @Override public void clearInviteInfo(String deviceId) { - List inviteInfoList = getAllInviteInfo(null, null, null); + List inviteInfoList = getAllInviteInfo(); for (InviteInfo inviteInfo : inviteInfoList) { if (inviteInfo.getDeviceId().equals(deviceId)) { removeInviteInfo(inviteInfo); @@ -267,23 +256,21 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @Override public int getStreamInfoCount(String mediaServerId) { int count = 0; - String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*:*"; - List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.isEmpty()) { - return 0; - }else { - for (Object keyObj : scanResult) { - String keyStr = (String) keyObj; - InviteInfo inviteInfo = (InviteInfo) redisTemplate.opsForValue().get(keyStr); - if (inviteInfo != null - && inviteInfo.getStreamInfo() != null - && inviteInfo.getStreamInfo().getMediaServer() != null - && inviteInfo.getStreamInfo().getMediaServer().getId().equals(mediaServerId)) { - if (inviteInfo.getType().equals(InviteSessionType.DOWNLOAD) && inviteInfo.getStreamInfo().getProgress() == 1) { - continue; - } - count++; + String key = VideoManagerConstants.INVITE_PREFIX; + List values = redisTemplate.opsForHash().values(key); + if (values.isEmpty()) { + return count; + } + for (Object value : values) { + InviteInfo inviteInfo = (InviteInfo)value; + if (inviteInfo != null + && inviteInfo.getStreamInfo() != null + && inviteInfo.getStreamInfo().getMediaServer() != null + && inviteInfo.getStreamInfo().getMediaServer().getId().equals(mediaServerId)) { + if (inviteInfo.getType().equals(InviteSessionType.DOWNLOAD) && inviteInfo.getStreamInfo().getProgress() == 1) { + continue; } + count++; } } return count; @@ -307,7 +294,6 @@ public class InviteStreamServiceImpl implements IInviteStreamService { private String buildSubStreamKey(InviteSessionType type, Integer channelId, String stream) { String key = type + ":" + channelId; - // 如果ssrc为null那么可以实现一个通道只能一次操作,ssrc不为null则可以支持一个通道多次invite if (stream != null) { key += (":" + stream); } @@ -316,13 +302,16 @@ public class InviteStreamServiceImpl implements IInviteStreamService { @Override public InviteInfo getInviteInfoBySSRC(String ssrc) { - String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*:" + ssrc; - List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.size() != 1) { + List inviteInfoList = getAllInviteInfo(); + if (inviteInfoList.isEmpty()) { return null; } - - return (InviteInfo) redisTemplate.opsForValue().get(scanResult.get(0)); + for (InviteInfo inviteInfo : inviteInfoList) { + if (inviteInfo.getSsrcInfo() != null && ssrc.equals(inviteInfo.getSsrcInfo().getSsrc())) { + return inviteInfo; + } + } + return null; } @Override @@ -332,16 +321,36 @@ public class InviteStreamServiceImpl implements IInviteStreamService { return null; } removeInviteInfo(inviteInfoInDb); - String key = VideoManagerConstants.INVITE_PREFIX + - ":" + inviteInfo.getType() + - ":" + inviteInfo.getDeviceId() + + String key = VideoManagerConstants.INVITE_PREFIX; + String objectKey = inviteInfo.getType() + ":" + inviteInfo.getChannelId() + - ":" + inviteInfo.getStream() + - ":" + ssrc; + ":" + inviteInfo.getStream(); if (inviteInfoInDb.getSsrcInfo() != null) { inviteInfoInDb.getSsrcInfo().setSsrc(ssrc); } - redisTemplate.opsForValue().set(key, inviteInfoInDb); + redisTemplate.opsForHash().put(key, objectKey, inviteInfoInDb); return inviteInfoInDb; } + + @Scheduled(fixedRate = 10000) //定时检测,清理错误的redis数据,防止因为错误数据导致的点播不可用 + public void execute(){ + String key = VideoManagerConstants.INVITE_PREFIX; + if(redisTemplate.opsForHash().size(key) == 0) { + return; + } + List values = redisTemplate.opsForHash().values(key); + for (Object value : values) { + InviteInfo inviteInfo = (InviteInfo)value; + if (inviteInfo.getStreamInfo() != null) { + continue; + } + if (inviteInfo.getCreateTime() == null || inviteInfo.getExpirationTime() == 0) { + removeInviteInfo(inviteInfo); + } + long time = inviteInfo.getCreateTime() + inviteInfo.getExpirationTime(); + if (System.currentTimeMillis() > time) { + removeInviteInfo(inviteInfo); + } + } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformChannelServiceImpl.java index 54b43ae64..71ca0988e 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformChannelServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformChannelServiceImpl.java @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent; import com.genersoft.iot.vmp.gb28181.service.IPlatformChannelService; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; +import com.genersoft.iot.vmp.jt1078.proc.request.Re; import com.github.pagehelper.PageHelper; import com.github.pagehelper.PageInfo; import lombok.extern.slf4j.Slf4j; @@ -51,8 +52,6 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService { private ISIPCommanderForPlatform sipCommanderFroPlatform; - - @Override public PageInfo queryChannelList(int page, int count, String query, Integer channelType, Boolean online, Integer platformId, Boolean hasShare) { PageHelper.startPage(page, count); @@ -93,8 +92,6 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService { return regionMapper.queryNotShareRegionForPlatformByRegionList(allRegion, platformId); } - - /** * 移除空的共享,并返回移除的分组 */ @@ -473,6 +470,44 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService { } } + @Override + @Transactional + public void checkRegionRemove(List channelList, List regionList) { + List channelIds = new ArrayList<>(); + channelList.stream().forEach(commonGBChannel -> { + channelIds.add(commonGBChannel.getGbId()); + }); + // 获取关联这些通道的平台 + List platformList = platformChannelMapper.queryPlatFormListByChannelList(channelIds); + if (platformList.isEmpty()) { + return; + } + for (Platform platform : platformList) { + Set regionSet; + if (regionList == null || regionList.isEmpty()) { + regionSet = platformChannelMapper.queryShareRegion(platform.getId()); + }else { + regionSet = new HashSet<>(regionList); + } + // 清理空的分组并发送消息 + Set deleteRegion = deleteEmptyRegion(regionSet, platform.getId()); + + List channelListForEvent = new ArrayList<>(); + if (!deleteRegion.isEmpty()) { + for (Region region : deleteRegion) { + channelListForEvent.add(0, CommonGBChannel.build(region)); + } + } + // 发送消息 + try { + // 发送catalog + eventPublisher.catalogEventPublish(platform.getId(), channelListForEvent, CatalogEvent.DEL); + } catch (Exception e) { + log.warn("[移除关联通道] 发送失败,数量:{}", channelList.size(), e); + } + } + } + @Override @Transactional public void checkGroupAdd(List channelList) { @@ -505,6 +540,36 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService { } } + @Override + public void checkRegionAdd(List channelList) { + List channelIds = new ArrayList<>(); + channelList.stream().forEach(commonGBChannel -> { + channelIds.add(commonGBChannel.getGbId()); + }); + List platformList = platformChannelMapper.queryPlatFormListByChannelList(channelIds); + if (platformList.isEmpty()) { + return; + } + for (Platform platform : platformList) { + + Set addRegion = getRegionNotShareByChannelList(channelList, platform.getId()); + List channelListForEvent = new ArrayList<>(); + if (!addRegion.isEmpty()) { + for (Region region : addRegion) { + channelListForEvent.add(0, CommonGBChannel.build(region)); + } + platformChannelMapper.addPlatformRegion(new ArrayList<>(addRegion), platform.getId()); + // 发送消息 + try { + // 发送catalog + eventPublisher.catalogEventPublish(platform.getId(), channelListForEvent, CatalogEvent.ADD); + } catch (Exception e) { + log.warn("[移除关联通道] 发送失败,数量:{}", channelList.size(), e); + } + } + } + } + @Override public List queryPlatFormListByChannelDeviceId(Integer channelId, List platforms) { return platformChannelMapper.queryPlatFormListForGBWithGBId(channelId, platforms); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java index 101712bb9..19a5e56fd 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlatformServiceImpl.java @@ -246,32 +246,6 @@ public class PlatformServiceImpl implements IPlatformService { return false; } - private void unregister(Platform platform) { - // 停止心跳定时 - final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + platform.getServerGBId(); - dynamicTask.stop(keepaliveTaskKey); - // 停止注册定时 - final String registerTaskKey = REGISTER_KEY_PREFIX + platform.getServerGBId(); - dynamicTask.stop(registerTaskKey); - - PlatformCatch platformCatchOld = redisCatchStorage.queryPlatformCatchInfo(platform.getServerGBId()); - // 注销旧的 - try { - if (platform.isStatus()) { - commanderForPlatform.unregister(platform, platformCatchOld.getSipTransactionInfo(), null, eventResult -> { - log.info("[国标级联] 注销命令发送成功,平台:{}", platform.getServerGBId()); - }); - } - } catch (InvalidArgumentException | ParseException | SipException e) { - log.error("[命令发送失败] 国标级联 注销: {}", e.getMessage()); - } - } - - private void register(Platform platform) { - - } - - @Override public void online(Platform platform, SipTransactionInfo sipTransactionInfo) { log.info("[国标级联]:{}, 平台上线", platform.getServerGBId()); @@ -335,7 +309,7 @@ public class PlatformServiceImpl implements IPlatformService { platformCatchForNow.setKeepAliveReply(0); redisCatchStorage.updatePlatformCatchInfo(platformCatchForNow); } - log.info("[发送心跳] 国标级联 发送心跳, code: {}, msg: {}", eventResult.statusCode, eventResult.msg); + log.info("[国标级联] 发送心跳,平台{}({}), code: {}, msg: {}", platform.getName(), platform.getServerGBId(), eventResult.statusCode, eventResult.msg); }); } catch (SipException | InvalidArgumentException | ParseException e) { log.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage()); @@ -512,7 +486,7 @@ public class PlatformServiceImpl implements IPlatformService { log.info("[国标级联] 语音喊话未找到可用的zlm. platform: {}", platform.getServerGBId()); return; } - InviteInfo inviteInfoForOld = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getGbId()); + InviteInfo inviteInfoForOld = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.BROADCAST, channel.getGbId()); if (inviteInfoForOld != null && inviteInfoForOld.getStreamInfo() != null) { // 如果zlm不存在这个流,则删除数据即可 @@ -562,7 +536,7 @@ public class PlatformServiceImpl implements IPlatformService { platform.getServerGBId(), channel.getGbDeviceId(), ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), ssrcInfo.getSsrc(), ssrcCheck); // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(platform.getServerGBId(), channel.getGbId(), ssrcInfo.getStream(), ssrcInfo, + InviteInfo inviteInfo = InviteInfo.getInviteInfo(platform.getServerGBId(), channel.getGbId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort(), userSetting.getBroadcastForPlatform(), InviteSessionType.BROADCAST, InviteSessionStatus.ready); inviteStreamService.updateInviteInfo(inviteInfo); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java index 04fdd0b97..fcc5db8fe 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/PlayServiceImpl.java @@ -138,7 +138,7 @@ public class PlayServiceImpl implements IPlayService { String deviceId = streamArray[0]; String channelId = streamArray[1]; Device device = deviceService.getDeviceByDeviceId(deviceId); - DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId); + DeviceChannel channel = deviceChannelService.getOneForSource(deviceId, channelId); if (device == null) { log.info("[语音对讲/喊话] 未找到设备:{}", deviceId); return; @@ -218,7 +218,7 @@ public class PlayServiceImpl implements IPlayService { log.info("[语音对讲/喊话] 未找到设备:{}", deviceId); return; } - DeviceChannel channel = deviceChannelService.getOne(deviceId, channelId); + DeviceChannel channel = deviceChannelService.getOneForSource(deviceId, channelId); if (channel == null) { log.info("[语音对讲/喊话] 未找到通道:{}", channelId); return; @@ -267,7 +267,7 @@ public class PlayServiceImpl implements IPlayService { } if (s.length == 2) { log.info("[ZLM HOOK] 预览流未找到, 发起自动点播:{}->{}->{}/{}", event.getMediaServer().getId(), event.getSchema(), event.getApp(), event.getStream()); - play(event.getMediaServer(), deviceId, channelId, null, null); + play(event.getMediaServer(), deviceId, channelId, null, (code, msg, data) -> {}); } else if (s.length == 4) { // 此时为录像回放, 录像回放格式为> 设备ID_通道ID_开始时间_结束时间 String startTimeStr = s[2]; @@ -283,7 +283,7 @@ public class PlayServiceImpl implements IPlayService { startTime, endTime ); - playBack(event.getMediaServer(), device, deviceChannel, startTime, endTime, null); + playBack(event.getMediaServer(), device, deviceChannel, startTime, endTime, (code, msg, data) -> {}); } } @@ -304,44 +304,6 @@ public class PlayServiceImpl implements IPlayService { log.warn("[点播] 未找到通道 deviceId: {},channelId:{}", deviceId, channelId); throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到通道"); } - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); - if (inviteInfo != null ) { - if (inviteInfo.getStreamInfo() == null) { - // 释放生成的ssrc,使用上一次申请的 - ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); - // 点播发起了但是尚未成功, 仅注册回调等待结果即可 - inviteStreamService.once(InviteSessionType.PLAY, channel.getId(), null, callback); - log.info("[点播开始] 已经请求中,等待结果, deviceId: {}, channelId: {}", device.getDeviceId(), channelId); - return inviteInfo.getSsrcInfo(); - }else { - StreamInfo streamInfo = inviteInfo.getStreamInfo(); - String streamId = streamInfo.getStream(); - if (streamId == null) { - callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null); - inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, - InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), - "点播失败, redis缓存streamId等于null", - null); - return inviteInfo.getSsrcInfo(); - } - MediaServer mediaInfo = streamInfo.getMediaServer(); - Boolean ready = mediaServerService.isStreamReady(mediaInfo, "rtp", streamId); - if (ready != null && ready) { - callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); - inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, - InviteErrorCode.SUCCESS.getCode(), - InviteErrorCode.SUCCESS.getMsg(), - streamInfo); - log.info("[点播已存在] 直接返回, deviceId: {}, channelId: {}", device.getDeviceId(), channelId); - return inviteInfo.getSsrcInfo(); - }else { - // 点播发起了但是尚未成功, 仅注册回调等待结果即可 - inviteStreamService.once(InviteSessionType.PLAY, channel.getId(), null, callback); - deviceChannelService.stopPlay(channel.getId()); - inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); - } - } - } return play(mediaServerItem, device, channel, ssrc, callback); } @@ -356,6 +318,48 @@ public class PlayServiceImpl implements IPlayService { } return null; } + + InviteInfo inviteInfoInCatch = inviteStreamService.getInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + if (inviteInfoInCatch != null ) { + if (inviteInfoInCatch.getStreamInfo() == null) { + // 释放生成的ssrc,使用上一次申请的 + ssrcFactory.releaseSsrc(mediaServerItem.getId(), ssrc); + // 点播发起了但是尚未成功, 仅注册回调等待结果即可 + inviteStreamService.once(InviteSessionType.PLAY, channel.getId(), null, callback); + log.info("[点播开始] 已经请求中,等待结果, deviceId: {}, channelId: {}", device.getDeviceId(), channel.getDeviceId()); + return inviteInfoInCatch.getSsrcInfo(); + }else { + StreamInfo streamInfo = inviteInfoInCatch.getStreamInfo(); + String streamId = streamInfo.getStream(); + if (streamId == null) { + callback.run(InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), "点播失败, redis缓存streamId等于null", null); + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.ERROR_FOR_CATCH_DATA.getCode(), + "点播失败, redis缓存streamId等于null", + null); + return inviteInfoInCatch.getSsrcInfo(); + } + MediaServer mediaInfo = streamInfo.getMediaServer(); + Boolean ready = mediaServerService.isStreamReady(mediaInfo, "rtp", streamId); + if (ready != null && ready) { + if(callback != null) { + callback.run(InviteErrorCode.SUCCESS.getCode(), InviteErrorCode.SUCCESS.getMsg(), streamInfo); + } + inviteStreamService.call(InviteSessionType.PLAY, channel.getId(), null, + InviteErrorCode.SUCCESS.getCode(), + InviteErrorCode.SUCCESS.getMsg(), + streamInfo); + log.info("[点播已存在] 直接返回, deviceId: {}, channelId: {}", device.getDeviceId(), channel.getDeviceId()); + return inviteInfoInCatch.getSsrcInfo(); + }else { + // 点播发起了但是尚未成功, 仅注册回调等待结果即可 + inviteStreamService.once(InviteSessionType.PLAY, channel.getId(), null, callback); + deviceChannelService.stopPlay(channel.getId()); + inviteStreamService.removeInviteInfoByDeviceAndChannel(InviteSessionType.PLAY, channel.getId()); + } + } + } + String streamId = String.format("%s_%s", device.getDeviceId(), channel.getDeviceId()); int tcpMode = device.getStreamMode().equals("TCP-ACTIVE")? 2: (device.getStreamMode().equals("TCP-PASSIVE")? 1:0); RTPServerParam rtpServerParam = new RTPServerParam(); @@ -426,7 +430,7 @@ public class PlayServiceImpl implements IPlayService { device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAY, InviteSessionStatus.ready); inviteStreamService.updateInviteInfo(inviteInfo); @@ -800,7 +804,7 @@ public class PlayServiceImpl implements IPlayService { device.getDeviceId(), channel.getGbDeviceId(), startTime, endTime, ssrcInfo.getPort(), device.getStreamMode(), ssrcInfo.getSsrc(), device.isSsrcCheck()); // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.PLAYBACK, InviteSessionStatus.ready); inviteStreamService.updateInviteInfo(inviteInfo); @@ -1006,7 +1010,7 @@ public class PlayServiceImpl implements IPlayService { device.isSsrcCheck()); // 初始化redis中的invite消息状态 - InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, + InviteInfo inviteInfo = InviteInfo.getInviteInfo(device.getDeviceId(), channel.getId(), ssrcInfo.getStream(), ssrcInfo, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort(), device.getStreamMode(), InviteSessionType.DOWNLOAD, InviteSessionStatus.ready); @@ -1155,12 +1159,12 @@ public class PlayServiceImpl implements IPlayService { @Override - public void zlmServerOffline(String mediaServerId) { + public void zlmServerOffline(MediaServer mediaServer) { // 处理正在向上推流的上级平台 List sendRtpInfos = sendRtpServerService.queryAll(); if (!sendRtpInfos.isEmpty()) { for (SendRtpInfo sendRtpInfo : sendRtpInfos) { - if (sendRtpInfo.getMediaServerId().equals(mediaServerId) && sendRtpInfo.isSendToPlatform()) { + if (sendRtpInfo.getMediaServerId().equals(mediaServer.getId()) && sendRtpInfo.isSendToPlatform()) { Platform platform = platformService.queryPlatformByServerGBId(sendRtpInfo.getTargetId()); CommonGBChannel channel = channelService.getOne(sendRtpInfo.getChannelId()); try { @@ -1175,7 +1179,7 @@ public class PlayServiceImpl implements IPlayService { List allSsrc = sessionManager.getAll(); if (allSsrc.size() > 0) { for (SsrcTransaction ssrcTransaction : allSsrc) { - if (ssrcTransaction.getMediaServerId().equals(mediaServerId)) { + if (ssrcTransaction.getMediaServerId().equals(mediaServer.getId())) { Device device = deviceService.getDeviceByDeviceId(ssrcTransaction.getDeviceId()); if (device == null) { continue; @@ -1218,36 +1222,30 @@ public class PlayServiceImpl implements IPlayService { } @Override - public boolean audioBroadcastCmd(Device device, DeviceChannel channel, MediaServer mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException { + public boolean audioBroadcastCmd(Device device, DeviceChannel deviceChannel, MediaServer mediaServerItem, String app, String stream, int timeout, boolean isFromPlatform, AudioBroadcastEvent event) throws InvalidArgumentException, ParseException, SipException { Assert.notNull(device, "设备不存在"); - Assert.notNull(channel, "通道不存在"); - log.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), channel.getDeviceId()); - DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channel.getDeviceId()); - if (deviceChannel == null) { - log.warn("开启语音广播的时候未找到通道: {}", channel.getDeviceId()); - event.call("开启语音广播的时候未找到通道"); - return false; - } + Assert.notNull(deviceChannel, "通道不存在"); + log.info("[语音喊话] device: {}, channel: {}", device.getDeviceId(), deviceChannel.getDeviceId()); // 查询通道使用状态 if (audioBroadcastManager.exit(deviceChannel.getId())) { - SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(channel.getId(), device.getDeviceId()); + SendRtpInfo sendRtpInfo = sendRtpServerService.queryByChannelId(deviceChannel.getId(), device.getDeviceId()); if (sendRtpInfo != null && sendRtpInfo.isOnlyAudio()) { // 查询流是否存在,不存在则认为是异常状态 Boolean streamReady = mediaServerService.isStreamReady(mediaServerItem, sendRtpInfo.getApp(), sendRtpInfo.getStream()); if (streamReady) { - log.warn("语音广播已经开启: {}", channel.getDeviceId()); + log.warn("语音广播已经开启: {}", deviceChannel.getDeviceId()); event.call("语音广播已经开启"); return false; } else { - stopAudioBroadcast(device, channel); + stopAudioBroadcast(device, deviceChannel); } } } // 发送通知 - cmder.audioBroadcastCmd(device, channel.getDeviceId(), eventResultForOk -> { + cmder.audioBroadcastCmd(device, deviceChannel.getDeviceId(), eventResultForOk -> { // 发送成功 - AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), channel.getId(), mediaServerItem, app, stream, event, AudioBroadcastCatchStatus.Ready, isFromPlatform); + AudioBroadcastCatch audioBroadcastCatch = new AudioBroadcastCatch(device.getDeviceId(), deviceChannel.getId(), mediaServerItem, app, stream, event, AudioBroadcastCatchStatus.Ready, isFromPlatform); audioBroadcastManager.update(audioBroadcastCatch); // 等待invite消息, 超时则结束 String key = VideoManagerConstants.BROADCAST_WAITE_INVITE + device.getDeviceId(); @@ -1255,14 +1253,14 @@ public class PlayServiceImpl implements IPlayService { key += audioBroadcastCatch.getChannelId(); } dynamicTask.startDelay(key, ()->{ - log.info("[语音广播]等待invite消息超时:{}/{}", device.getDeviceId(), channel.getDeviceId()); - stopAudioBroadcast(device, channel); + log.info("[语音广播]等待invite消息超时:{}/{}", device.getDeviceId(), deviceChannel.getDeviceId()); + stopAudioBroadcast(device, deviceChannel); }, 10*1000); }, eventResultForError -> { // 发送失败 - log.error("语音广播发送失败: {}:{}", channel.getDeviceId(), eventResultForError.msg); + log.error("语音广播发送失败: {}:{}", deviceChannel.getDeviceId(), eventResultForError.msg); event.call("语音广播发送失败"); - stopAudioBroadcast(device, channel); + stopAudioBroadcast(device, deviceChannel); }); return true; } @@ -1318,7 +1316,22 @@ public class PlayServiceImpl implements IPlayService { } @Override - public void zlmServerOnline(String mediaServerId) { + public void zlmServerOnline(MediaServer mediaServer) { + // 获取 + List inviteInfoList = inviteStreamService.getAllInviteInfo(); + if (inviteInfoList.isEmpty()) { + return; + } + + List rtpServerList = mediaServerService.listRtpServer(mediaServer); + if (rtpServerList.isEmpty()) { + return; + } + for (InviteInfo inviteInfo : inviteInfoList) { + if (!rtpServerList.contains(inviteInfo.getStream())){ + inviteStreamService.removeInviteInfo(inviteInfo); + } + } } @Override @@ -1570,10 +1583,14 @@ public class PlayServiceImpl implements IPlayService { @Override public void stop(InviteSessionType type, Device device, DeviceChannel channel, String stream) { - InviteInfo inviteInfo = inviteStreamService.getInviteInfoByStream(type, stream); + InviteInfo inviteInfo = inviteStreamService.getInviteInfo(type, channel.getId(), stream); if (inviteInfo == null) { - throw new ControllerException(ErrorCode.ERROR100.getCode(), "点播未找到"); + if (type == InviteSessionType.PLAY) { + deviceChannelService.stopPlay(channel.getId()); + } + return; } + inviteStreamService.removeInviteInfo(inviteInfo); if (InviteSessionStatus.ok == inviteInfo.getStatus()) { try { log.info("[停止点播/回放/下载] {}/{}", device.getDeviceId(), channel.getDeviceId()); @@ -1583,7 +1600,7 @@ public class PlayServiceImpl implements IPlayService { throw new ControllerException(ErrorCode.ERROR100.getCode(), "命令发送失败: " + e.getMessage()); } } - inviteStreamService.removeInviteInfoByDeviceAndChannel(inviteInfo.getType(), channel.getId()); + if (inviteInfo.getType() == InviteSessionType.PLAY) { deviceChannelService.stopPlay(channel.getId()); } @@ -1605,6 +1622,7 @@ public class PlayServiceImpl implements IPlayService { log.warn("[停止点播] 发现设备不存在"); return; } + inviteStreamService.removeInviteInfo(inviteInfo); if (InviteSessionStatus.ok == inviteInfo.getStatus()) { try { log.info("[停止点播/回放/下载] {}/{}", device.getDeviceId(), channel.getDeviceId()); @@ -1613,7 +1631,7 @@ public class PlayServiceImpl implements IPlayService { log.warn("[命令发送失败] 停止点播/回放/下载, 发送BYE: {}", e.getMessage()); } } - inviteStreamService.removeInviteInfoByDeviceAndChannel(inviteInfo.getType(), channel.getId()); + if (inviteInfo.getType() == InviteSessionType.PLAY) { deviceChannelService.stopPlay(channel.getId()); } @@ -1634,7 +1652,7 @@ public class PlayServiceImpl implements IPlayService { log.warn("[点播] 未找到可用媒体节点"); throw new PlayException(Response.SERVER_INTERNAL_ERROR, "server internal error"); } - DeviceChannel deviceChannel = deviceChannelService.getOneById(channel.getGbId()); + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); play(mediaServer, device, deviceChannel, null, callback); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/RegionServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/RegionServiceImpl.java index 9fb2fdc80..fc4c016be 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/RegionServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/RegionServiceImpl.java @@ -139,9 +139,9 @@ public class RegionServiceImpl implements IRegionService { } @Override - public List queryForTree(String query, Integer parent) { + public List queryForTree(String query, Integer parent, Boolean hasChannel) { List regionList = regionMapper.queryForTree(query, parent); - if (parent != null) { + if (parent != null && hasChannel != null && hasChannel) { Region parentRegion = regionMapper.queryOne(parent); if (parentRegion != null) { List channelList = commonGBChannelMapper.queryForRegionTreeByCivilCode(query, parentRegion.getDeviceId()); @@ -224,4 +224,32 @@ public class RegionServiceImpl implements IRegionService { return true; } + + @Override + public List getPath(String deviceId) { + Region region = regionMapper.queryByDeviceId(deviceId); + if (region == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "行政区划不存在"); + } + List allParent = getAllParent(region); + allParent.add(region); + return allParent; + } + + + private List getAllParent(Region region) { + if (region.getParentId() == null) { + return new ArrayList<>(); + } + + List regionList = new LinkedList<>(); + Region parent = regionMapper.queryByDeviceId(region.getParentDeviceId()); + if (parent == null) { + return regionList; + } + regionList.add(parent); + List allParent = getAllParent(parent); + regionList.addAll(allParent); + return regionList; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java deleted file mode 100755 index baa9e2f21..000000000 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataCatch.java +++ /dev/null @@ -1,181 +0,0 @@ -package com.genersoft.iot.vmp.gb28181.session; - -import com.genersoft.iot.vmp.gb28181.bean.*; -import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.stereotype.Component; - -import java.time.Instant; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.TimeUnit; - -@Component -public class CatalogDataCatch { - - public static Map data = new ConcurrentHashMap<>(); - - @Autowired - private IDeviceChannelService deviceChannelService; - - public void addReady(Device device, int sn ) { - CatalogData catalogData = data.get(device.getDeviceId()); - if (catalogData == null || catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { - catalogData = new CatalogData(); - catalogData.setChannelList(Collections.synchronizedList(new ArrayList<>())); - catalogData.setDevice(device); - catalogData.setSn(sn); - catalogData.setStatus(CatalogData.CatalogDataStatus.ready); - catalogData.setLastTime(Instant.now()); - data.put(device.getDeviceId(), catalogData); - } - } - - public void put(String deviceId, int sn, int total, Device device, List deviceChannelList, - List regionList, List groupList) { - CatalogData catalogData = data.get(deviceId); - if (catalogData == null) { - catalogData = new CatalogData(); - catalogData.setSn(sn); - catalogData.setTotal(total); - catalogData.setDevice(device); - catalogData.setChannelList(deviceChannelList); - catalogData.setRegionListList(regionList); - catalogData.setGroupListListList(groupList); - catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); - catalogData.setLastTime(Instant.now()); - data.put(deviceId, catalogData); - }else { - // 同一个设备的通道同步请求只考虑一个,其他的直接忽略 - if (catalogData.getSn() != sn) { - return; - } - catalogData.setTotal(total); - catalogData.setDevice(device); - catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); - - if (deviceChannelList != null && !deviceChannelList.isEmpty()) { - if (catalogData.getChannelList() != null) { - catalogData.getChannelList().addAll(deviceChannelList); - } - } - if (regionList != null && !regionList.isEmpty()) { - if (catalogData.getRegionListList() != null) { - catalogData.getRegionListList().addAll(regionList); - }else { - catalogData.setRegionListList(regionList); - } - } - if (groupList != null && !groupList.isEmpty()) { - if (catalogData.getGroupListListList() != null) { - catalogData.getGroupListListList().addAll(groupList); - }else { - catalogData.setGroupListListList(groupList); - } - } - catalogData.setLastTime(Instant.now()); - } - } - - public List getDeviceChannelList(String deviceId) { - CatalogData catalogData = data.get(deviceId); - if (catalogData == null) { - return null; - } - return catalogData.getChannelList(); - } - - public List getRegionList(String deviceId) { - CatalogData catalogData = data.get(deviceId); - if (catalogData == null) { - return null; - } - return catalogData.getRegionListList(); - } - - public List getGroupList(String deviceId) { - CatalogData catalogData = data.get(deviceId); - if (catalogData == null) { - return null; - } - return catalogData.getGroupListListList(); - } - - public int getTotal(String deviceId) { - CatalogData catalogData = data.get(deviceId); - if (catalogData == null) { - return 0; - } - return catalogData.getTotal(); - } - - public SyncStatus getSyncStatus(String deviceId) { - CatalogData catalogData = data.get(deviceId); - if (catalogData == null) { - return null; - } - SyncStatus syncStatus = new SyncStatus(); - syncStatus.setCurrent(catalogData.getChannelList().size()); - syncStatus.setTotal(catalogData.getTotal()); - syncStatus.setErrorMsg(catalogData.getErrorMsg()); - if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { - syncStatus.setSyncIng(false); - }else { - syncStatus.setSyncIng(true); - } - return syncStatus; - } - - public boolean isSyncRunning(String deviceId) { - CatalogData catalogData = data.get(deviceId); - if (catalogData == null) { - return false; - } - return !catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end); - } - - @Scheduled(fixedRate = 5 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 - private void timerTask(){ - Set keys = data.keySet(); - - Instant instantBefore5S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(5)); - Instant instantBefore30S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(30)); - - for (String deviceId : keys) { - CatalogData catalogData = data.get(deviceId); - if ( catalogData.getLastTime().isBefore(instantBefore5S)) { - // 超过五秒收不到消息任务超时, 只更新这一部分数据, 收到数据与声明的总数一致,则重置通道数据,数据不全则只对收到的数据做更新操作 - if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) { - if (catalogData.getTotal() == catalogData.getChannelList().size()) { - deviceChannelService.resetChannels(catalogData.getDevice().getId(), catalogData.getChannelList()); - }else { - deviceChannelService.updateChannels(catalogData.getDevice(), catalogData.getChannelList()); - } - String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + catalogData.getChannelList().size() + "条"; - catalogData.setErrorMsg(errorMsg); - if (catalogData.getTotal() != catalogData.getChannelList().size()) { - - } - }else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) { - String errorMsg = "同步失败,等待回复超时"; - catalogData.setErrorMsg(errorMsg); - } - catalogData.setStatus(CatalogData.CatalogDataStatus.end); - } - if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end) && catalogData.getLastTime().isBefore(instantBefore30S)) { // 超过三十秒,如果标记为end则删除 - data.remove(deviceId); - } - } - } - - - public void setChannelSyncEnd(String deviceId, String errorMsg) { - CatalogData catalogData = data.get(deviceId); - if (catalogData == null) { - return; - } - catalogData.setStatus(CatalogData.CatalogDataStatus.end); - catalogData.setErrorMsg(errorMsg); - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataManager.java new file mode 100755 index 000000000..d53ffaeea --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataManager.java @@ -0,0 +1,290 @@ +package com.genersoft.iot.vmp.gb28181.session; + +import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IGroupService; +import com.genersoft.iot.vmp.gb28181.service.IRegionService; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.CommandLineRunner; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; + +import java.time.Instant; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.TimeUnit; + +@Slf4j +@Component +public class CatalogDataManager implements CommandLineRunner { + + @Autowired + private IDeviceChannelService deviceChannelService; + + @Autowired + private IRegionService regionService; + + @Autowired + private IGroupService groupService; + + @Autowired + private RedisTemplate redisTemplate; + + private final Map dataMap = new ConcurrentHashMap<>(); + + private final String key = "VMP_CATALOG_DATA"; + + public String buildMapKey(String deviceId, int sn ) { + return deviceId + "_" + sn; + } + + public void addReady(Device device, int sn ) { + CatalogData catalogData = dataMap.get(buildMapKey(device.getDeviceId(),sn)); + if (catalogData != null) { + Set redisKeysForChannel = catalogData.getRedisKeysForChannel(); + if (redisKeysForChannel != null && !redisKeysForChannel.isEmpty()) { + for (String deleteKey : redisKeysForChannel) { + redisTemplate.opsForHash().delete(key, deleteKey); + } + } + Set redisKeysForRegion = catalogData.getRedisKeysForRegion(); + if (redisKeysForRegion != null && !redisKeysForRegion.isEmpty()) { + for (String deleteKey : redisKeysForRegion) { + redisTemplate.opsForHash().delete(key, deleteKey); + } + } + Set redisKeysForGroup = catalogData.getRedisKeysForGroup(); + if (redisKeysForGroup != null && !redisKeysForGroup.isEmpty()) { + for (String deleteKey : redisKeysForGroup) { + redisTemplate.opsForHash().delete(key, deleteKey); + } + } + dataMap.remove(buildMapKey(device.getDeviceId(),sn)); + } + catalogData = new CatalogData(); + catalogData.setDevice(device); + catalogData.setSn(sn); + catalogData.setStatus(CatalogData.CatalogDataStatus.ready); + catalogData.setTime(Instant.now()); + dataMap.put(buildMapKey(device.getDeviceId(),sn), catalogData); + } + + public void put(String deviceId, int sn, int total, Device device, List deviceChannelList, + List regionList, List groupList) { + CatalogData catalogData = dataMap.get(buildMapKey(device.getDeviceId(),sn)); + if (catalogData == null ) { + log.warn("[缓存-Catalog] 未找到缓存对象,可能已经结束"); + return; + } + catalogData.setStatus(CatalogData.CatalogDataStatus.runIng); + catalogData.setTotal(total); + catalogData.setTime(Instant.now()); + + if (deviceChannelList != null && !deviceChannelList.isEmpty()) { + for (DeviceChannel deviceChannel : deviceChannelList) { + String keyForChannel = "CHANNEL:" + deviceId + ":" + deviceChannel.getDeviceId() + ":" + sn; + redisTemplate.opsForHash().put(key, keyForChannel, deviceChannel); + catalogData.getRedisKeysForChannel().add(keyForChannel); + } + } + + if (regionList != null && !regionList.isEmpty()) { + for (Region region : regionList) { + String keyForRegion = "REGION:" + deviceId + ":" + region.getDeviceId() + ":" + sn; + redisTemplate.opsForHash().put(key, keyForRegion, region); + catalogData.getRedisKeysForRegion().add(keyForRegion); + } + } + + if (groupList != null && !groupList.isEmpty()) { + for (Group group : groupList) { + String keyForGroup = "GROUP:" + deviceId + ":" + group.getDeviceId() + ":" + sn; + redisTemplate.opsForHash().put(key, keyForGroup, group); + catalogData.getRedisKeysForGroup().add(keyForGroup); + } + } + } + + public List getDeviceChannelList(String deviceId, int sn) { + List result = new ArrayList<>(); + CatalogData catalogData = dataMap.get(buildMapKey(deviceId,sn)); + if (catalogData == null ) { + log.warn("[Redis-Catalog] 未找到缓存对象,可能已经结束"); + return result; + } + for (String objectKey : catalogData.getRedisKeysForChannel()) { + DeviceChannel deviceChannel = (DeviceChannel) redisTemplate.opsForHash().get(key, objectKey); + if (deviceChannel != null) { + result.add(deviceChannel); + } + } + return result; + } + + public List getRegionList(String deviceId, int sn) { + List result = new ArrayList<>(); + CatalogData catalogData = dataMap.get(buildMapKey(deviceId,sn)); + if (catalogData == null ) { + log.warn("[Redis-Catalog] 未找到缓存对象,可能已经结束"); + return result; + } + for (String objectKey : catalogData.getRedisKeysForRegion()) { + Region region = (Region) redisTemplate.opsForHash().get(key, objectKey); + if (region != null) { + result.add(region); + } + } + return result; + } + + public List getGroupList(String deviceId, int sn) { + List result = new ArrayList<>(); + CatalogData catalogData = dataMap.get(buildMapKey(deviceId,sn)); + if (catalogData == null ) { + log.warn("[Redis-Catalog] 未找到缓存对象,可能已经结束"); + return result; + } + for (String objectKey : catalogData.getRedisKeysForGroup()) { + Group group = (Group) redisTemplate.opsForHash().get(key, objectKey); + if (group != null) { + result.add(group); + } + } + return result; + } + + public SyncStatus getSyncStatus(String deviceId) { + if (dataMap.isEmpty()) { + return null; + } + Set keySet = dataMap.keySet(); + for (String key : keySet) { + CatalogData catalogData = dataMap.get(key); + if (catalogData != null && deviceId.equals(catalogData.getDevice().getDeviceId())) { + SyncStatus syncStatus = new SyncStatus(); + syncStatus.setCurrent(catalogData.getRedisKeysForChannel().size()); + syncStatus.setTotal(catalogData.getTotal()); + syncStatus.setErrorMsg(catalogData.getErrorMsg()); + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end)) { + syncStatus.setSyncIng(false); + }else { + syncStatus.setSyncIng(true); + } + return syncStatus; + } + } + return null; + } + + public boolean isSyncRunning(String deviceId) { + if (dataMap.isEmpty()) { + return false; + } + Set keySet = dataMap.keySet(); + for (String key : keySet) { + CatalogData catalogData = dataMap.get(key); + if (catalogData != null && deviceId.equals(catalogData.getDevice().getDeviceId())) { + return !catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end); + } + } + return false; + } + + @Override + public void run(String... args) throws Exception { + // 启动时清理旧的数据 + redisTemplate.delete(key); + } + + @Scheduled(fixedDelay = 5 * 1000) //每5秒执行一次, 发现数据5秒未更新则移除数据并认为数据接收超时 + private void timerTask(){ + if (dataMap.isEmpty()) { + return; + } + Set keys = dataMap.keySet(); + + Instant instantBefore5S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(5)); + Instant instantBefore30S = Instant.now().minusMillis(TimeUnit.SECONDS.toMillis(30)); + for (String dataKey : keys) { + CatalogData catalogData = dataMap.get(dataKey); + if ( catalogData.getTime().isBefore(instantBefore5S)) { + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.runIng)) { + String deviceId = catalogData.getDevice().getDeviceId(); + int sn = catalogData.getSn(); + List deviceChannelList = getDeviceChannelList(deviceId, sn); + if (catalogData.getTotal() == deviceChannelList.size()) { + deviceChannelService.resetChannels(catalogData.getDevice().getId(), deviceChannelList); + }else { + deviceChannelService.updateChannels(catalogData.getDevice(), deviceChannelList); + } + List regionList = getRegionList(deviceId, sn); + if ( regionList!= null && !regionList.isEmpty()) { + regionService.batchAdd(regionList); + } + List groupList = getGroupList(deviceId, sn); + if (groupList != null && !groupList.isEmpty()) { + groupService.batchAdd(groupList); + } + String errorMsg = "更新成功,共" + catalogData.getTotal() + "条,已更新" + deviceChannelList.size() + "条"; + catalogData.setErrorMsg(errorMsg); + }else if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.ready)) { + String errorMsg = "同步失败,等待回复超时"; + catalogData.setErrorMsg(errorMsg); + } + } + if (catalogData.getStatus().equals(CatalogData.CatalogDataStatus.end) && catalogData.getTime().isBefore(instantBefore30S)) { // 超过三十秒,如果标记为end则删除 + dataMap.remove(dataKey); + Set redisKeysForChannel = catalogData.getRedisKeysForChannel(); + if (redisKeysForChannel != null && !redisKeysForChannel.isEmpty()) { + for (String deleteKey : redisKeysForChannel) { + redisTemplate.opsForHash().delete(key, deleteKey); + } + } + Set redisKeysForRegion = catalogData.getRedisKeysForRegion(); + if (redisKeysForRegion != null && !redisKeysForRegion.isEmpty()) { + for (String deleteKey : redisKeysForRegion) { + redisTemplate.opsForHash().delete(key, deleteKey); + } + } + Set redisKeysForGroup = catalogData.getRedisKeysForGroup(); + if (redisKeysForGroup != null && !redisKeysForGroup.isEmpty()) { + for (String deleteKey : redisKeysForGroup) { + redisTemplate.opsForHash().delete(key, deleteKey); + } + } + } + } + } + + + public void setChannelSyncEnd(String deviceId, int sn, String errorMsg) { + CatalogData catalogData = dataMap.get(buildMapKey(deviceId,sn)); + if (catalogData == null) { + return; + } + catalogData.setStatus(CatalogData.CatalogDataStatus.end); + catalogData.setErrorMsg(errorMsg); + catalogData.setTime(Instant.now()); + } + + public int size(String deviceId, int sn) { + CatalogData catalogData = dataMap.get(buildMapKey(deviceId,sn)); + if (catalogData == null) { + return 0; + } + return catalogData.getRedisKeysForChannel().size(); + } + + public int sumNum(String deviceId, int sn) { + CatalogData catalogData = dataMap.get(buildMapKey(deviceId,sn)); + if (catalogData == null) { + return 0; + } + return catalogData.getTotal(); + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java index 202c7fb64..3a3bdaef3 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SipInviteSessionManager.java @@ -3,7 +3,6 @@ package com.genersoft.iot.vmp.gb28181.session; import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; @@ -27,39 +26,34 @@ public class SipInviteSessionManager { * 添加一个点播/回放的事务信息 */ public void put(SsrcTransaction ssrcTransaction){ - redisTemplate.opsForValue().set(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() - + ":" + ssrcTransaction.getStream(), ssrcTransaction); + redisTemplate.opsForHash().put(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() + , ssrcTransaction.getStream(), ssrcTransaction); - redisTemplate.opsForValue().set(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() - + ":" + ssrcTransaction.getCallId(), ssrcTransaction); + redisTemplate.opsForHash().put(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + , ssrcTransaction.getCallId(), ssrcTransaction); } public SsrcTransaction getSsrcTransactionByStream(String stream){ - String key = VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() + ":" + stream; - return (SsrcTransaction)redisTemplate.opsForValue().get(key); + String key = VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId(); + return (SsrcTransaction)redisTemplate.opsForHash().get(key, stream); } public SsrcTransaction getSsrcTransactionByCallId(String callId){ - String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":" + callId; - return (SsrcTransaction)redisTemplate.opsForValue().get(key); + String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(); + return (SsrcTransaction)redisTemplate.opsForHash().get(key, callId); } public List getSsrcTransactionByDeviceId(String deviceId){ - String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":*"; - - List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.isEmpty()) { - return new ArrayList<>(); - } + String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(); + List values = redisTemplate.opsForHash().values(key); List result = new ArrayList<>(); - for (Object keyObj : scanResult) { - SsrcTransaction ssrcTransaction = (SsrcTransaction)redisTemplate.opsForValue().get(keyObj); - if (ssrcTransaction != null && ssrcTransaction.getDeviceId().equals(deviceId)) { + for (Object value : values) { + SsrcTransaction ssrcTransaction = (SsrcTransaction) value; + if (ssrcTransaction != null && deviceId.equals(ssrcTransaction.getDeviceId())) { result.add(ssrcTransaction); } } return result; - } public void removeByStream(String stream) { @@ -67,9 +61,9 @@ public class SipInviteSessionManager { if (ssrcTransaction == null ) { return; } - redisTemplate.delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() + ":" + stream); + redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId(), stream); if (ssrcTransaction.getCallId() != null) { - redisTemplate.delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":" + ssrcTransaction.getCallId()); + redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(), ssrcTransaction.getCallId()); } } @@ -78,23 +72,18 @@ public class SipInviteSessionManager { if (ssrcTransaction == null ) { return; } - redisTemplate.delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":" + callId); + redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(), callId); if (ssrcTransaction.getStream() != null) { - redisTemplate.delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId() + ":" + ssrcTransaction.getStream()); + redisTemplate.opsForHash().delete(VideoManagerConstants.SIP_INVITE_SESSION_STREAM + userSetting.getServerId(), ssrcTransaction.getStream()); } } public List getAll() { - String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":*"; - - List scanResult = RedisUtil.scan(redisTemplate, key); - if (scanResult.isEmpty()) { - return new ArrayList<>(); - } + String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId(); + List values = redisTemplate.opsForHash().values(key); List result = new ArrayList<>(); - for (Object keyObj : scanResult) { - SsrcTransaction ssrcTransaction = (SsrcTransaction)redisTemplate.opsForValue().get(keyObj); - result.add(ssrcTransaction); + for (Object value : values) { + result.add((SsrcTransaction) value); } return result; } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/session/SseSessionManager.java b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SseSessionManager.java new file mode 100644 index 000000000..6c88f7df2 --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/session/SseSessionManager.java @@ -0,0 +1,72 @@ +package com.genersoft.iot.vmp.gb28181.session; + +import com.genersoft.iot.vmp.conf.DynamicTask; +import lombok.extern.slf4j.Slf4j; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Component; +import org.springframework.web.servlet.mvc.method.annotation.SseEmitter; + +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +@Component +@Slf4j +public class SseSessionManager { + + private static final Map sseSessionMap = new ConcurrentHashMap<>(); + + @Autowired + private DynamicTask dynamicTask; + + public SseEmitter conect(String browserId){ + SseEmitter sseEmitter = new SseEmitter(0L); + sseEmitter.onError((err)-> { + log.error("[SSE推送] 连接错误, 浏览器 ID: {}, {}", browserId, err.getMessage()); + sseSessionMap.remove(browserId); + sseEmitter.completeWithError(err); + }); + +// sseEmitter.onTimeout(() -> { +// log.info("[SSE推送] 连接超时, 浏览器 ID: {}", browserId); +// sseSessionMap.remove(browserId); +// sseEmitter.complete(); +// dynamicTask.stop(key); +// }); + + sseEmitter.onCompletion(() -> { + log.info("[SSE推送] 连接结束, 浏览器 ID: {}", browserId); + sseSessionMap.remove(browserId); + }); + + sseSessionMap.put(browserId, sseEmitter); + + log.info("[SSE推送] 连接已建立, 浏览器 ID: {}, 当前在线数: {}", browserId, sseSessionMap.size()); + return sseEmitter; + } + + @Scheduled(fixedRate = 1000) //每1秒执行一次 + public void execute(){ + if (sseSessionMap.isEmpty()){ + return; + } + sendForAll("keepalive", "alive"); + } + + + public void sendForAll(String event, Object data) { + for (String browserId : sseSessionMap.keySet()) { + SseEmitter sseEmitter = sseSessionMap.get(browserId); + if (sseEmitter == null) { + continue; + }; + try { + sseEmitter.send(SseEmitter.event().name(event).data(data)); + } catch (Exception e) { + log.error("[SSE推送] 发送失败: {}", e.getMessage()); + sseSessionMap.remove(browserId); + sseEmitter.completeWithError(e); + } + } + } +} diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java index 5c2037578..efc1d91d3 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPProcessorObserver.java @@ -97,10 +97,12 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { CallIdHeader callIdHeader = response.getCallIdHeader(); if (callIdHeader != null) { SipEvent sipEvent = sipSubscribe.getSubscribe(callIdHeader.getCallId()); - if (sipEvent != null && sipEvent.getOkEvent() != null) { - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult<>(responseEvent); + if (sipEvent != null) { + if (sipEvent.getOkEvent() != null) { + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult<>(responseEvent); + sipEvent.getOkEvent().response(eventResult); + } sipSubscribe.removeSubscribe(callIdHeader.getCallId()); - sipEvent.getOkEvent().response(eventResult); } } } @@ -118,10 +120,12 @@ public class SIPProcessorObserver implements ISIPProcessorObserver { CallIdHeader callIdHeader = (CallIdHeader)responseEvent.getResponse().getHeader(CallIdHeader.NAME); if (callIdHeader != null) { SipEvent sipEvent = sipSubscribe.getSubscribe(callIdHeader.getCallId()); - if (sipEvent != null && sipEvent.getErrorEvent() != null) { - SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult(responseEvent); + if (sipEvent != null ) { + if (sipEvent.getErrorEvent() != null) { + SipSubscribe.EventResult eventResult = new SipSubscribe.EventResult<>(responseEvent); + sipEvent.getErrorEvent().response(eventResult); + } sipSubscribe.removeSubscribe(callIdHeader.getCallId()); - sipEvent.getErrorEvent().response(eventResult); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java index d894bb6f1..fcda7a720 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/SIPSender.java @@ -72,12 +72,12 @@ public class SIPSender { if (okEvent != null || errorEvent != null) { CallIdHeader callIdHeader = (CallIdHeader) message.getHeader(CallIdHeader.NAME); SipEvent sipEvent = SipEvent.getInstance(callIdHeader.getCallId(), eventResult -> { - sipSubscribe.removeSubscribe(eventResult.callId); + sipSubscribe.removeSubscribe(callIdHeader.getCallId()); if(okEvent != null) { okEvent.response(eventResult); } }, (eventResult -> { - sipSubscribe.removeSubscribe(eventResult.callId); + sipSubscribe.removeSubscribe(callIdHeader.getCallId()); if (errorEvent != null) { errorEvent.response(eventResult); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java index 628bdcd82..b09d47cce 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java @@ -345,7 +345,7 @@ public class SIPRequestHeaderPlarformProvider { Address concatAddress = SipFactory.getInstance().createAddressFactory().createAddress(SipFactory.getInstance().createAddressFactory().createSipURI(sipConfig.getId(),localHostAddress)); request.addHeader(SipFactory.getInstance().createHeaderFactory().createContactHeader(concatAddress)); // Subject - SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", channelId, ssrc, sipConfig.getId(), 0)); + SubjectHeader subjectHeader = SipFactory.getInstance().createHeaderFactory().createSubjectHeader(String.format("%s:%s,%s:%s", sipConfig.getId(), ssrc, channelId, 0)); request.addHeader(subjectHeader); ContentTypeHeader contentTypeHeader = SipFactory.getInstance().createHeaderFactory().createContentTypeHeader("APPLICATION", "SDP"); request.setContent(content, contentTypeHeader); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java index 8cb38a103..9a77d7b8c 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java @@ -281,7 +281,7 @@ public class SIPCommander implements ISIPCommander { content.append("c=IN IP4 " + sdpIp + "\r\n"); content.append("t=0 0\r\n"); - if (userSetting.isSeniorSdp()) { + if (userSetting.getSeniorSdp()) { if ("TCP-PASSIVE".equalsIgnoreCase(device.getStreamMode())) { content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); } else if ("TCP-ACTIVE".equalsIgnoreCase(device.getStreamMode())) { @@ -383,7 +383,7 @@ public class SIPCommander implements ISIPCommander { String streamMode = device.getStreamMode(); - if (userSetting.isSeniorSdp()) { + if (userSetting.getSeniorSdp()) { if ("TCP-PASSIVE".equalsIgnoreCase(streamMode)) { content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); } else if ("TCP-ACTIVE".equalsIgnoreCase(streamMode)) { @@ -472,7 +472,7 @@ public class SIPCommander implements ISIPCommander { String streamMode = device.getStreamMode().toUpperCase(); - if (userSetting.isSeniorSdp()) { + if (userSetting.getSeniorSdp()) { if ("TCP-PASSIVE".equals(streamMode)) { content.append("m=video " + ssrcInfo.getPort() + " TCP/RTP/AVP 96 126 125 99 34 98 97\r\n"); } else if ("TCP-ACTIVE".equals(streamMode)) { diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java index 87821ab08..918a29fa3 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderForPlatform.java @@ -222,7 +222,7 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform { } private void sendCatalogResponse(List channels, Platform parentPlatform, String sn, String fromTag, int index, boolean sendAfterResponse) throws SipException, InvalidArgumentException, ParseException { - if (index >= channels.size()) { + if (index > channels.size()) { return; } List deviceChannels; @@ -231,6 +231,9 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform { }else { deviceChannels = channels.subList(index, channels.size()); } + if(deviceChannels.isEmpty()) { + return; + } String catalogXml = getCatalogXml(deviceChannels, sn, parentPlatform, channels.size()); // callid CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport()); @@ -280,7 +283,7 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform { } catch (SipException | InvalidArgumentException | ParseException e) { log.error("[命令发送失败] 国标级联 目录查询回复: {}", e.getMessage()); } - }, 30); + }, 100); } } @@ -734,6 +737,8 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform { } content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc + // f字段:f= v/编码格式/分辨率/帧率/码率类型/码率大小a/编码格式/码率大小/采样率 + content.append("f=v/////a/1/8/1\r\n"); CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(platform.getDeviceIp()), platform.getTransport()); Request request = headerProviderPlatformProvider.createInviteRequest(platform, channel.getGbDeviceId(), diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java index 990e788b9..cd72c8d8a 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java @@ -12,6 +12,7 @@ import org.dom4j.DocumentException; import org.dom4j.Element; import org.dom4j.io.SAXReader; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.util.ObjectUtils; import javax.sip.*; import javax.sip.address.Address; @@ -170,10 +171,17 @@ public abstract class SIPRequestProcessorParent { } public Element getRootElement(RequestEvent evt, String charset) throws DocumentException { + byte[] rawContent = evt.getRequest().getRawContent(); + if (evt.getRequest().getContentLength().getContentLength() == 0 + || rawContent == null + || rawContent.length == 0 + || ObjectUtils.isEmpty(new String(rawContent))) { + return null; + } + if (charset == null) { charset = "gb2312"; } - Request request = evt.getRequest(); SAXReader reader = new SAXReader(); reader.setEncoding(charset); // 对海康出现的未转义字符做处理。 @@ -182,10 +190,6 @@ public abstract class SIPRequestProcessorParent { char despChar = '&'; byte destBye = (byte) despChar; List result = new ArrayList<>(); - byte[] rawContent = request.getRawContent(); - if (rawContent == null) { - return null; - } for (int i = 0; i < rawContent.length; i++) { if (rawContent[i] == destBye) { boolean resul = false; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java index 004b1ec82..ed63b9ad4 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/AckRequestProcessor.java @@ -99,7 +99,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In } // tcp主动时,此时是级联下级平台,在回复200ok时,本地已经请求zlm开启监听,跳过下面步骤 if (sendRtpItem.isTcpActive()) { - log.info("收到ACK,rtp/{} TCP主动方式后续处理", sendRtpItem.getStream()); + log.info("收到ACK,rtp/{} TCP主动方式等收到上级连接后开始发流", sendRtpItem.getStream()); return; } MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId()); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java index 6d905f18f..c51b570a0 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/ByeRequestProcessor.java @@ -184,22 +184,22 @@ public class ByeRequestProcessor extends SIPRequestProcessorParent implements In return; } log.info("[收到bye] 来自:{}, 通道: {}, 类型: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId(), ssrcTransaction.getType()); - + // TODO 结束点播 避免等待 if (ssrcTransaction.getPlatformId() != null ) { - Platform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getDeviceId()); + Platform platform = platformService.queryPlatformByServerGBId(ssrcTransaction.getPlatformId()); if (ssrcTransaction.getType().equals(InviteSessionType.BROADCAST)) { - log.info("[收到bye] 上级停止语音对讲,来自:{}, 通道已停止推流: {}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + log.info("[收到bye] 上级停止语音对讲,来自:{}, 通道已停止推流: {}", ssrcTransaction.getPlatformId(), ssrcTransaction.getChannelId()); CommonGBChannel channel = channelService.getOne(ssrcTransaction.getChannelId()); if (channel == null) { - log.info("[收到bye] 未找到通道,设备:{}, 通道:{}", ssrcTransaction.getDeviceId(), ssrcTransaction.getChannelId()); + log.info("[收到bye] 未找到通道,上级:{}, 通道:{}", ssrcTransaction.getPlatformId(), ssrcTransaction.getChannelId()); return; } String mediaServerId = ssrcTransaction.getMediaServerId(); platformService.stopBroadcast(platform, channel, ssrcTransaction.getStream(), false, mediaServerService.getOne(mediaServerId)); - Device device = deviceService.getDeviceByDeviceId(ssrcTransaction.getDeviceId()); - DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(ssrcTransaction.getChannelId()); + DeviceChannel deviceChannel = deviceChannelService.getOneForSourceById(channel.getGbId()); + Device device = deviceService.getDevice(channel.getGbDeviceDbId()); playService.stopAudioBroadcast(device, deviceChannel); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java index bf1e17eb7..3b6dc6a82 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java @@ -17,12 +17,9 @@ import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.bean.InviteErrorCode; -import com.genersoft.iot.vmp.service.redisMsg.RedisPushStreamResponseListener; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import gov.nist.javax.sdp.TimeDescriptionImpl; import gov.nist.javax.sdp.fields.TimeField; @@ -34,7 +31,6 @@ import org.apache.commons.lang3.ObjectUtils; import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; import org.springframework.stereotype.Component; import javax.sdp.*; @@ -44,7 +40,6 @@ import javax.sip.SipException; import javax.sip.header.CallIdHeader; import javax.sip.message.Response; import java.text.ParseException; -import java.time.Instant; import java.util.List; import java.util.Vector; @@ -82,12 +77,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @Autowired private IMediaServerService mediaServerService; - @Autowired - private RedisTemplate redisTemplate; - - @Autowired - private SSRCFactory ssrcFactory; - @Autowired private DynamicTask dynamicTask; @@ -100,15 +89,9 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements @Autowired private AudioBroadcastManager audioBroadcastManager; - @Autowired - private HookSubscribe hookSubscribe; - @Autowired private SIPProcessorObserver sipProcessorObserver; - @Autowired - private UserSetting userSetting; - @Autowired private SipConfig config; @@ -116,10 +99,10 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements private SipInviteSessionManager sessionManager; @Autowired - private SendRtpPortManager sendRtpPortManager; + private UserSetting userSetting; @Autowired - private RedisPushStreamResponseListener redisPushStreamResponseListener; + private SSRCFactory ssrcFactory; @Override @@ -161,6 +144,16 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements platform.getName(), channel.getGbName(), channel.getGbDeviceId(), inviteInfo.getIp(), inviteInfo.getPort(), inviteInfo.isTcp()?(inviteInfo.isTcpActive()?"TCP主动":"TCP被动"): "UDP", inviteInfo.getSessionName(), inviteInfo.getSsrc()); + if(!userSetting.getUseCustomSsrcForParentInvite() && ObjectUtils.isEmpty(inviteInfo.getSsrc())) { + log.warn("[上级INVITE] 点播失败, 上级为携带SSRC, 并且本级未设置使用自定义ssrc"); + // 通道存在,发100,TRYING + try { + responseAck(request, Response.BAD_REQUEST); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 上级Invite TRYING: {}", e.getMessage()); + } + return; + } // 通道存在,发100,TRYING try { responseAck(request, Response.TRYING); @@ -177,7 +170,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements } }else { // 点播成功, TODO 可以在此处检测cancel命令是否存在,存在则不发送 - + if (userSetting.getUseCustomSsrcForParentInvite()) { + // 上级平台点播时不使用上级平台指定的ssrc,使用自定义的ssrc,参考国标文档-点播外域设备媒体流SSRC处理方式 + String ssrc = "Play".equalsIgnoreCase(inviteInfo.getSessionName()) + ? ssrcFactory.getPlaySsrc(streamInfo.getMediaServer().getId()) + : ssrcFactory.getPlayBackSsrc(streamInfo.getMediaServer().getId()); + inviteInfo.setSsrc(ssrc); + } // 构建sendRTP内容 SendRtpInfo sendRtpItem = sendRtpServerService.createSendRtpInfo(streamInfo.getMediaServer(), inviteInfo.getIp(), inviteInfo.getPort(), inviteInfo.getSsrc(), platform.getServerGBId(), @@ -223,7 +222,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements sendBye(platform, inviteInfo.getCallId()); } } - } })); } @@ -287,16 +285,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements // 如果是录像回放,则会存在录像的开始时间与结束时间 Long startTime = null; Long stopTime = null; - Instant start = null; - Instant end = null; - if (sdp.getTimeDescriptions(false) != null && sdp.getTimeDescriptions(false).size() > 0) { + if (sdp.getTimeDescriptions(false) != null && !sdp.getTimeDescriptions(false).isEmpty()) { TimeDescriptionImpl timeDescription = (TimeDescriptionImpl) (sdp.getTimeDescriptions(false).get(0)); TimeField startTimeFiled = (TimeField) timeDescription.getTime(); startTime = startTimeFiled.getStartTime(); stopTime = startTimeFiled.getStopTime(); - - start = Instant.ofEpochSecond(startTime); - end = Instant.ofEpochSecond(stopTime); } // 获取支持的格式 Vector mediaDescriptions = sdp.getMediaDescriptions(true); @@ -338,13 +331,6 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements inviteInfo.setTcpActive(tcpActive != null? tcpActive: false); inviteInfo.setStartTime(startTime); inviteInfo.setStopTime(stopTime); - String username = sdp.getOrigin().getUsername(); -// String addressStr; -// if(StringUtils.isEmpty(platform.getSendStreamIp())){ -// addressStr = sdp.getConnection().getAddress(); -// }else { -// addressStr = platform.getSendStreamIp(); -// } Vector sdpMediaDescriptions = sdp.getMediaDescriptions(true); MediaDescription mediaDescription = null; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java index 00f7fd25d..86be394a8 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForCatalogProcessor.java @@ -35,7 +35,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent private final ConcurrentLinkedQueue channelList = new ConcurrentLinkedQueue<>(); - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Autowired private UserSetting userSetting; @@ -62,7 +62,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent taskQueue.offer(new HandlerCatchData(evt, null, null)); } - @Scheduled(fixedRate = 400) //每400毫秒执行一次 + @Scheduled(fixedDelay = 400) //每400毫秒执行一次 public void executeTaskQueue(){ if (taskQueue.isEmpty()) { return; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForMobilePositionProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForMobilePositionProcessor.java index c4124cad3..ed367876d 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForMobilePositionProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestForMobilePositionProcessor.java @@ -35,7 +35,7 @@ import java.util.concurrent.ConcurrentLinkedQueue; @Component public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessorParent { - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Autowired private UserSetting userSetting; @@ -61,7 +61,7 @@ public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessor taskQueue.offer(new HandlerCatchData(evt, null, null)); } - @Scheduled(fixedRate = 200) //每200毫秒执行一次 + @Scheduled(fixedDelay = 200) //每200毫秒执行一次 public void executeTaskQueue() { if (taskQueue.isEmpty()) { return; diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java index ba1cd6236..b7ed168bf 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java @@ -122,6 +122,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements } DeviceAlarm deviceAlarm = new DeviceAlarm(); deviceAlarm.setDeviceId(deviceId); + deviceAlarm.setDeviceName(device.getName()); deviceAlarm.setAlarmPriority(XmlUtil.getText(rootElement, "AlarmPriority")); deviceAlarm.setAlarmMethod(XmlUtil.getText(rootElement, "AlarmMethod")); String alarmTime = XmlUtil.getText(rootElement, "AlarmTime"); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java index 23a6e42a5..44a3eb1ed 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java @@ -7,14 +7,13 @@ import com.genersoft.iot.vmp.gb28181.bean.Device; import com.genersoft.iot.vmp.gb28181.bean.GbSipDate; import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo; +import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver; import com.genersoft.iot.vmp.gb28181.transmit.SIPSender; import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.utils.SipUtils; -import com.genersoft.iot.vmp.gb28181.service.IDeviceService; import com.genersoft.iot.vmp.utils.DateUtil; -import gov.nist.javax.sip.RequestEventExt; import gov.nist.javax.sip.address.AddressImpl; import gov.nist.javax.sip.address.SipUri; import gov.nist.javax.sip.header.SIPDateHeader; @@ -77,9 +76,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen @Override public void process(RequestEvent evt) { try { - RequestEventExt evtExt = (RequestEventExt) evt; - - SIPRequest request = (SIPRequest)evt.getRequest(); + SIPRequest request = (SIPRequest) evt.getRequest(); Response response = null; boolean passwordCorrect = false; // 注册标志 @@ -98,12 +95,12 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); String requestAddress = remoteAddressInfo.getIp() + ":" + remoteAddressInfo.getPort(); - String title = registerFlag ? "[注册请求]": "[注销请求]"; - log.info(title + "设备:{}, 开始处理: {}", deviceId, requestAddress); + String title = registerFlag ? "[注册请求]" : "[注销请求]"; + log.info(title + "设备:{}, 开始处理: {}", deviceId, requestAddress); if (device != null && device.getSipTransactionInfo() != null && request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) { - log.info(title + "设备:{}, 注册续订: {}",device.getDeviceId(), device.getDeviceId()); + log.info(title + "设备:{}, 注册续订: {}", device.getDeviceId(), device.getDeviceId()); if (registerFlag) { device.setExpires(request.getExpires().getExpires()); device.setIp(remoteAddressInfo.getIp()); @@ -118,17 +115,17 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP"); sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse); device.setRegisterTime(DateUtil.getNow()); - SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse); + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse) registerOkResponse); deviceService.online(device, sipTransactionInfo); - }else { + } else { deviceService.offline(deviceId, "主动注销"); } return; } - String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword(); + String password = (device != null && !ObjectUtils.isEmpty(device.getPassword())) ? device.getPassword() : sipConfig.getPassword(); AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME); if (authHead == null && !ObjectUtils.isEmpty(password)) { - log.info(title + " 设备:{}, 回复401: {}",deviceId, requestAddress); + log.info(title + " 设备:{}, 回复401: {}", deviceId, requestAddress); response = getMessageFactory().createResponse(Response.UNAUTHORIZED, request); new DigestServerAuthenticationHelper().generateChallenge(getHeaderFactory(), response, sipConfig.getDomain()); sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response); @@ -172,9 +169,10 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen device.setStreamMode("UDP"); device.setCharset("GB2312"); device.setGeoCoordSys("WGS84"); + device.setMediaServerId("auto"); device.setDeviceId(deviceId); device.setOnLine(false); - }else { + } else { if (ObjectUtils.isEmpty(device.getStreamMode())) { device.setStreamMode("UDP"); } @@ -207,12 +205,12 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen // 注册成功 // 保存到redis if (registerFlag) { - log.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); + log.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress); device.setRegisterTime(DateUtil.getNow()); - SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)response); + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse) response); deviceService.online(device, sipTransactionInfo); } else { - log.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress); + log.info("[注销成功] deviceId: {}->{}", deviceId, requestAddress); deviceService.offline(deviceId, "主动注销"); } } catch (SipException | NoSuchAlgorithmException | ParseException e) { @@ -222,7 +220,7 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen private Response getRegisterOkResponse(Request request) throws ParseException { // 携带授权头并且密码正确 - Response response = getMessageFactory().createResponse(Response.OK, request); + Response response = getMessageFactory().createResponse(Response.OK, request); // 添加date头 SIPDateHeader dateHeader = new SIPDateHeader(); // 使用自己修改的 diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java index 39d04c13d..acdff1ce3 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java @@ -68,6 +68,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme Element rootElement = getRootElement(evt); if (rootElement == null) { log.error("处理SUBSCRIBE请求 未获取到消息体{}", evt.getRequest()); + responseAck(request, Response.BAD_REQUEST); return; } String cmd = XmlUtil.getText(rootElement, "CmdType"); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java index 293f3e374..999a5e944 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java @@ -20,8 +20,7 @@ import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; @@ -30,6 +29,8 @@ import javax.sip.RequestEvent; import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; import static com.genersoft.iot.vmp.gb28181.utils.XmlUtil.getText; @@ -64,12 +65,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @Autowired private IDeviceChannelService deviceChannelService; - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; - + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Override public void afterPropertiesSet() throws Exception { @@ -78,118 +74,136 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme @Override public void handForDevice(RequestEvent evt, Device device, Element rootElement) { - boolean isEmpty = taskQueue.isEmpty(); - taskQueue.offer(new SipMsgInfo(evt, device, rootElement)); - // 回复200 OK - try { - responseAck((SIPRequest) evt.getRequest(), Response.OK); - } catch (SipException | InvalidArgumentException | ParseException e) { - log.error("[命令发送失败] 报警通知回复: {}", e.getMessage()); + if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) { + log.error("[Alarm] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue()); + return; } - if (isEmpty) { - taskExecutor.execute(() -> { - if (log.isDebugEnabled()) { - log.info("[处理报警通知]待处理数量:{}", taskQueue.size() ); + taskQueue.offer(new SipMsgInfo(evt, device, rootElement)); + } + + @Scheduled(fixedDelay = 200) + public void executeTaskQueue() { + if (taskQueue.isEmpty()) { + return; + } + List handlerCatchDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + SipMsgInfo poll = taskQueue.poll(); + if (poll != null) { + handlerCatchDataList.add(poll); + } + } + if (handlerCatchDataList.isEmpty()) { + return; + } + for (SipMsgInfo sipMsgInfo : handlerCatchDataList) { + if (sipMsgInfo == null) { + continue; + } + RequestEvent evt = sipMsgInfo.getEvt(); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 报警通知回复: {}", e.getMessage()); + } + try { + Device device = sipMsgInfo.getDevice(); + Element deviceIdElement = sipMsgInfo.getRootElement().element("DeviceID"); + String channelId = deviceIdElement.getText(); + + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setCreateTime(DateUtil.getNow()); + deviceAlarm.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); + deviceAlarm.setDeviceName(sipMsgInfo.getDevice().getName()); + deviceAlarm.setChannelId(channelId); + deviceAlarm.setAlarmPriority(getText(sipMsgInfo.getRootElement(), "AlarmPriority")); + deviceAlarm.setAlarmMethod(getText(sipMsgInfo.getRootElement(), "AlarmMethod")); + String alarmTime = XmlUtil.getText(sipMsgInfo.getRootElement(), "AlarmTime"); + if (alarmTime == null) { + continue; + } + deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); + String alarmDescription = getText(sipMsgInfo.getRootElement(), "AlarmDescription"); + if (alarmDescription == null) { + deviceAlarm.setAlarmDescription(""); + } else { + deviceAlarm.setAlarmDescription(alarmDescription); + } + String longitude = getText(sipMsgInfo.getRootElement(), "Longitude"); + if (longitude != null && NumericUtil.isDouble(longitude)) { + deviceAlarm.setLongitude(Double.parseDouble(longitude)); + } else { + deviceAlarm.setLongitude(0.00); + } + String latitude = getText(sipMsgInfo.getRootElement(), "Latitude"); + if (latitude != null && NumericUtil.isDouble(latitude)) { + deviceAlarm.setLatitude(Double.parseDouble(latitude)); + } else { + deviceAlarm.setLatitude(0.00); } - while (!taskQueue.isEmpty()) { - try { - SipMsgInfo sipMsgInfo = taskQueue.poll(); - Element deviceIdElement = sipMsgInfo.getRootElement().element("DeviceID"); - String channelId = deviceIdElement.getText().toString(); + if (!ObjectUtils.isEmpty(deviceAlarm.getAlarmMethod()) && deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.GPS.getVal() + "")) { + DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId); + if (deviceChannel == null) { + log.warn("[解析报警消息] 未找到通道:{}/{}", device.getDeviceId(), channelId); + } else { + MobilePosition mobilePosition = new MobilePosition(); + mobilePosition.setCreateTime(DateUtil.getNow()); + mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); + mobilePosition.setChannelId(deviceChannel.getId()); + mobilePosition.setTime(deviceAlarm.getAlarmTime()); + mobilePosition.setLongitude(deviceAlarm.getLongitude()); + mobilePosition.setLatitude(deviceAlarm.getLatitude()); + mobilePosition.setReportSource("GPS Alarm"); - DeviceAlarm deviceAlarm = new DeviceAlarm(); - deviceAlarm.setCreateTime(DateUtil.getNow()); - deviceAlarm.setDeviceId(sipMsgInfo.getDevice().getDeviceId()); - deviceAlarm.setChannelId(channelId); - deviceAlarm.setAlarmPriority(getText(sipMsgInfo.getRootElement(), "AlarmPriority")); - deviceAlarm.setAlarmMethod(getText(sipMsgInfo.getRootElement(), "AlarmMethod")); - String alarmTime = XmlUtil.getText(sipMsgInfo.getRootElement(), "AlarmTime"); - if (alarmTime == null) { - continue; - } - deviceAlarm.setAlarmTime(DateUtil.ISO8601Toyyyy_MM_dd_HH_mm_ss(alarmTime)); - String alarmDescription = getText(sipMsgInfo.getRootElement(), "AlarmDescription"); - if (alarmDescription == null) { - deviceAlarm.setAlarmDescription(""); - } else { - deviceAlarm.setAlarmDescription(alarmDescription); - } - String longitude = getText(sipMsgInfo.getRootElement(), "Longitude"); - if (longitude != null && NumericUtil.isDouble(longitude)) { - deviceAlarm.setLongitude(Double.parseDouble(longitude)); - } else { - deviceAlarm.setLongitude(0.00); - } - String latitude = getText(sipMsgInfo.getRootElement(), "Latitude"); - if (latitude != null && NumericUtil.isDouble(latitude)) { - deviceAlarm.setLatitude(Double.parseDouble(latitude)); - } else { - deviceAlarm.setLatitude(0.00); - } + // 更新device channel 的经纬度 + deviceChannel.setLongitude(mobilePosition.getLongitude()); + deviceChannel.setLatitude(mobilePosition.getLatitude()); + deviceChannel.setGpsTime(mobilePosition.getTime()); - if (!ObjectUtils.isEmpty(deviceAlarm.getAlarmMethod()) && deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.GPS.getVal() + "")) { - DeviceChannel deviceChannel = deviceChannelService.getOne(device.getDeviceId(), channelId); - if (deviceChannel == null) { - log.warn("[解析报警消息] 未找到通道:{}/{}", device.getDeviceId(), channelId); - } else { - MobilePosition mobilePosition = new MobilePosition(); - mobilePosition.setCreateTime(DateUtil.getNow()); - mobilePosition.setDeviceId(deviceAlarm.getDeviceId()); - mobilePosition.setChannelId(deviceChannel.getId()); - mobilePosition.setTime(deviceAlarm.getAlarmTime()); - mobilePosition.setLongitude(deviceAlarm.getLongitude()); - mobilePosition.setLatitude(deviceAlarm.getLatitude()); - mobilePosition.setReportSource("GPS Alarm"); - - // 更新device channel 的经纬度 - deviceChannel.setLongitude(mobilePosition.getLongitude()); - deviceChannel.setLatitude(mobilePosition.getLatitude()); - deviceChannel.setGpsTime(mobilePosition.getTime()); - - deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition); - } - } - if (!ObjectUtils.isEmpty(deviceAlarm.getDeviceId())) { - if (deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.Video.getVal() + "")) { - deviceAlarm.setAlarmType(getText(sipMsgInfo.getRootElement().element("Info"), "AlarmType")); - } - } - if (log.isDebugEnabled()) { - log.debug("[收到报警通知]设备:{}, 内容:{}", device.getDeviceId(), JSON.toJSONString(deviceAlarm)); - } - // 作者自用判断,其他小伙伴需要此消息可以自行修改,但是不要提在pr里 - if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) { - // 发送给平台的报警信息。 发送redis通知 - log.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm)); - AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); - if (deviceAlarm.getAlarmMethod() != null) { - alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); - } - alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); - if (deviceAlarm.getAlarmType() != null) { - alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType())); - } - alarmChannelMessage.setGbId(channelId); - redisCatchStorage.sendAlarmMsg(alarmChannelMessage); - continue; - } - - log.debug("存储报警信息、报警分类"); - // 存储报警信息、报警分类 - if (sipConfig.isAlarm()) { - deviceAlarmService.add(deviceAlarm); - } - - if (redisCatchStorage.deviceIsOnline(sipMsgInfo.getDevice().getDeviceId())) { - publisher.deviceAlarmEventPublish(deviceAlarm); - } - }catch (Exception e) { - log.error("未处理的异常 ", e); - log.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest()); + deviceChannelService.updateChannelGPS(device, deviceChannel, mobilePosition); } } - }); + if (!ObjectUtils.isEmpty(deviceAlarm.getDeviceId())) { + if (deviceAlarm.getAlarmMethod().contains(DeviceAlarmMethod.Video.getVal() + "")) { + deviceAlarm.setAlarmType(getText(sipMsgInfo.getRootElement().element("Info"), "AlarmType")); + } + } + if (log.isDebugEnabled()) { + log.debug("[收到报警通知]设备:{}, 内容:{}", device.getDeviceId(), JSON.toJSONString(deviceAlarm)); + } + // 作者自用判断,其他小伙伴需要此消息可以自行修改,但是不要提在pr里 + if (DeviceAlarmMethod.Other.getVal() == Integer.parseInt(deviceAlarm.getAlarmMethod())) { + // 发送给平台的报警信息。 发送redis通知 + log.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm)); + AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage(); + if (deviceAlarm.getAlarmMethod() != null) { + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod())); + } + alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription()); + if (deviceAlarm.getAlarmType() != null) { + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType())); + } + alarmChannelMessage.setGbId(channelId); + redisCatchStorage.sendAlarmMsg(alarmChannelMessage); + continue; + } + + log.debug("存储报警信息、报警分类"); + // 存储报警信息、报警分类 + if (sipConfig.isAlarm()) { + deviceAlarmService.add(deviceAlarm); + } + + if (redisCatchStorage.deviceIsOnline(sipMsgInfo.getDevice().getDeviceId())) { + publisher.deviceAlarmEventPublish(deviceAlarm); + } + } catch (Exception e) { + log.error("未处理的异常 ", e); + log.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}", e.getMessage(), evt.getRequest()); + } } } @@ -203,12 +217,13 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme log.error("[命令发送失败] 国标级联 报警通知回复: {}", e.getMessage()); } Element deviceIdElement = rootElement.element("DeviceID"); - String channelId = deviceIdElement.getText().toString(); + String channelId = deviceIdElement.getText(); DeviceAlarm deviceAlarm = new DeviceAlarm(); deviceAlarm.setCreateTime(DateUtil.getNow()); deviceAlarm.setDeviceId(parentPlatform.getServerGBId()); + deviceAlarm.setDeviceName(parentPlatform.getName()); deviceAlarm.setChannelId(channelId); deviceAlarm.setAlarmPriority(getText(rootElement, "AlarmPriority")); deviceAlarm.setAlarmMethod(getText(rootElement, "AlarmMethod")); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java index f278ef308..cccce02d0 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/BroadcastNotifyMessageHandler.java @@ -123,7 +123,6 @@ public class BroadcastNotifyMessageHandler extends SIPRequestProcessorParent imp commanderForPlatform.broadcastResultCmd(platform, channel, sn, true, eventResult->{ log.info("[国标级联] 语音喊话 回复失败 platform: {}, 错误:{}/{}", platform.getServerGBId(), eventResult.statusCode, eventResult.msg); }, eventResult->{ - // 消息发送成功, 向上级发送invite,获取推流 try { platformService.broadcastInvite(platform, channel, mediaServerForMinimumLoad, (hookData)->{ diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java index aa0e05e78..3232cae2e 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java @@ -3,9 +3,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.Device; -import com.genersoft.iot.vmp.gb28181.bean.Platform; -import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo; +import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.notify.NotifyMessageHandler; @@ -18,6 +16,7 @@ import org.apache.commons.lang3.ObjectUtils; import org.dom4j.Element; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.sip.InvalidArgumentException; @@ -25,6 +24,9 @@ import javax.sip.RequestEvent; import javax.sip.SipException; import javax.sip.message.Response; import java.text.ParseException; +import java.util.ArrayList; +import java.util.List; +import java.util.concurrent.ConcurrentLinkedQueue; /** * 状态信息(心跳)报送 @@ -36,6 +38,8 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp private final static String cmdType = "Keepalive"; + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); + @Autowired private NotifyMessageHandler notifyMessageHandler; @@ -54,68 +58,89 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp } @Override - public void handForDevice(RequestEvent evt, Device device, Element element) { - if (device == null) { - // 未注册的设备不做处理 + public void handForDevice(RequestEvent evt, Device device, Element rootElement) { + if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) { + log.error("[心跳] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue()); return; } - SIPRequest request = (SIPRequest) evt.getRequest(); - log.debug("[收到心跳] device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); - if (userSetting.getGbDeviceOnline() == 0 && !device.isOnLine()) { - log.warn("[收到心跳] 设备离线,心跳不进行回复, device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); + taskQueue.offer(new SipMsgInfo(evt, device, rootElement)); + } + + @Scheduled(fixedDelay = 100) + public void executeTaskQueue() { + if (taskQueue.isEmpty()) { return; } - // 回复200 OK - try { - responseAck(request, Response.OK); - } catch (SipException | InvalidArgumentException | ParseException e) { - log.error("[命令发送失败] 心跳回复: {}", e.getMessage()); + List handlerCatchDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + SipMsgInfo poll = taskQueue.poll(); + if (poll != null) { + handlerCatchDataList.add(poll); + } } - if (!ObjectUtils.isEmpty(device.getKeepaliveTime()) && DateUtil.getDifferenceForNow(device.getKeepaliveTime()) <= 3000L) { - log.info("[收到心跳] 心跳发送过于频繁,已忽略 device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); + if (handlerCatchDataList.isEmpty()) { return; } - - RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); - if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort() || !request.getRemoteAddress().getHostAddress().equals(device.getLocalIp())) { - log.info("[收到心跳] 设备{}地址变化, 远程地址为: {}:{}", device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort()); - device.setPort(remoteAddressInfo.getPort()); - device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); - device.setIp(remoteAddressInfo.getIp()); - device.setLocalIp(request.getRemoteAddress().getHostAddress()); - // 设备地址变化会引起目录订阅任务失效,需要重新添加 - if (device.getSubscribeCycleForCatalog() > 0) { - deviceService.removeCatalogSubscribe(device, result->{ - deviceService.addCatalogSubscribe(device); - }); + for (SipMsgInfo sipMsgInfo : handlerCatchDataList) { + if (sipMsgInfo == null) { + continue; } - } - if (device.getKeepaliveTime() == null) { - device.setKeepaliveIntervalTime(60); - }else { - long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime()); - if (System.currentTimeMillis()/1000-lastTime > 10) { - device.setKeepaliveIntervalTime(Long.valueOf(System.currentTimeMillis()/1000-lastTime).intValue()); + RequestEvent evt = sipMsgInfo.getEvt(); + // 回复200 OK + try { + responseAck((SIPRequest) evt.getRequest(), Response.OK); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 心跳回复: {}", e.getMessage()); } - } - - device.setKeepaliveTime(DateUtil.getNow()); - - if (device.isOnLine()) { - deviceService.updateDevice(device); - }else { - if (userSetting.getGbDeviceOnline() == 1) { - // 对于已经离线的设备判断他的注册是否已经过期 - device.setOnLine(true); - device.setRegisterTime(DateUtil.getNow()); - deviceService.online(device, null); + Device device = sipMsgInfo.getDevice(); + SIPRequest request = (SIPRequest) evt.getRequest(); + if (!ObjectUtils.isEmpty(device.getKeepaliveTime()) && DateUtil.getDifferenceForNow(device.getKeepaliveTime()) <= 3000L) { + log.info("[收到心跳] 心跳发送过于频繁,已忽略 device: {}, callId: {}", device.getDeviceId(), request.getCallIdHeader().getCallId()); + return; } - } - // 刷新过期任务 - String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); - // 如果三次心跳失败,则设置设备离线 - dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime()*1000*3); + RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request, userSetting.getSipUseSourceIpAsRemoteAddress()); + if (!device.getIp().equalsIgnoreCase(remoteAddressInfo.getIp()) || device.getPort() != remoteAddressInfo.getPort()) { + log.info("[收到心跳] 地址变化, {}({}), {}:{}->{}", device.getName(), device.getDeviceId(), remoteAddressInfo.getIp(), remoteAddressInfo.getPort(), request.getLocalAddress().getHostAddress()); + device.setPort(remoteAddressInfo.getPort()); + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort()))); + device.setIp(remoteAddressInfo.getIp()); + device.setLocalIp(request.getLocalAddress().getHostAddress()); + // 设备地址变化会引起目录订阅任务失效,需要重新添加 + if (device.getSubscribeCycleForCatalog() > 0) { + deviceService.removeCatalogSubscribe(device, result -> { + deviceService.addCatalogSubscribe(device); + }); + } + } + if (device.getKeepaliveTime() == null) { + device.setKeepaliveIntervalTime(60); + } else { + long lastTime = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestamp(device.getKeepaliveTime()); + if (System.currentTimeMillis() / 1000 - lastTime > 10) { + device.setKeepaliveIntervalTime(Long.valueOf(System.currentTimeMillis() / 1000 - lastTime).intValue()); + } + } + + device.setKeepaliveTime(DateUtil.getNow()); + + if (device.isOnLine()) { + deviceService.updateDevice(device); + } else { + if (userSetting.getGbDeviceOnline() == 1) { + // 对于已经离线的设备判断他的注册是否已经过期 + device.setOnLine(true); + device.setRegisterTime(DateUtil.getNow()); + deviceService.online(device, null); + } + } + // 刷新过期任务 + String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId(); + // 如果三次心跳失败,则设置设备离线 + dynamicTask.startDelay(registerExpireTaskKey, () -> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime() * 1000 * 3); + + } } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java index f0741c49d..977482224 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/BroadcastResponseMessageHandler.java @@ -1,13 +1,12 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd; -import com.genersoft.iot.vmp.conf.DynamicTask; import com.genersoft.iot.vmp.gb28181.bean.*; import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IPlayService; import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; -import com.genersoft.iot.vmp.gb28181.service.IPlayService; import gov.nist.javax.sip.message.SIPRequest; import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; @@ -54,7 +53,7 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i String channelId = getText(rootElement, "DeviceID"); DeviceChannel channel = null; if (!channelId.equals(device.getDeviceId())) { - channel = deviceChannelService.getOne(device.getDeviceId(), channelId); + channel = deviceChannelService.getOneBySourceId(device.getId(), channelId); }else { channel = deviceChannelService.getBroadcastChannel(device.getId()); } @@ -66,7 +65,7 @@ public class BroadcastResponseMessageHandler extends SIPRequestProcessorParent i } if (!audioBroadcastManager.exit(channel.getId())) { // 回复410 - responseAck((SIPRequest) evt.getRequest(), Response.GONE); + responseAck((SIPRequest) evt.getRequest(), Response.BUSY_HERE); return; } String result = getText(rootElement, "Result"); diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java index 9fc2d99fc..0dc38a4ac 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java @@ -2,21 +2,20 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.respon import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.gb28181.bean.*; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.service.IGroupService; import com.genersoft.iot.vmp.gb28181.service.IRegionService; -import com.genersoft.iot.vmp.gb28181.session.CatalogDataCatch; +import com.genersoft.iot.vmp.gb28181.session.CatalogDataManager; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; -import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import gov.nist.javax.sip.message.SIPRequest; import lombok.extern.slf4j.Slf4j; import org.dom4j.DocumentException; import org.dom4j.Element; import org.springframework.beans.factory.InitializingBean; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -29,7 +28,6 @@ import java.util.ArrayList; import java.util.Iterator; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicBoolean; /** * 目录查询的回复 @@ -55,15 +53,10 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp private IGroupService groupService; @Autowired - private CatalogDataCatch catalogDataCatch; - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; + private CatalogDataManager catalogDataCatch; @Autowired private SipConfig sipConfig; - private AtomicBoolean processing = new AtomicBoolean(false); @Override public void afterPropertiesSet() throws Exception { @@ -71,7 +64,6 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp } @Override - @Transactional public void handForDevice(RequestEvent evt, Device device, Element element) { taskQueue.offer(new HandlerCatchData(evt, device, element)); // 回复200 OK @@ -80,116 +72,134 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp } catch (SipException | InvalidArgumentException | ParseException e) { log.error("[命令发送失败] 目录查询回复: {}", e.getMessage()); } - // 已经开启消息处理则跳过 - if (processing.compareAndSet(false, true)) { - taskExecutor.execute(() -> { - while (!taskQueue.isEmpty()) { - // 全局异常捕获,保证下一条可以得到处理 - try { - HandlerCatchData take = taskQueue.poll(); - Element rootElement = null; - try { - rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset()); - } catch (DocumentException e) { - log.error("[xml解析] 失败: ", e); - continue; - } - if (rootElement == null) { - log.warn("[ 收到通道 ] content cannot be null, {}", evt.getRequest()); - continue; - } - Element deviceListElement = rootElement.element("DeviceList"); - Element sumNumElement = rootElement.element("SumNum"); - Element snElement = rootElement.element("SN"); - int sumNum = Integer.parseInt(sumNumElement.getText()); + } - if (sumNum == 0) { - log.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId()); - // 数据已经完整接收 - deviceChannelService.cleanChannelsForDevice(take.getDevice().getId()); - catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); - } else { - Iterator deviceListIterator = deviceListElement.elementIterator(); - if (deviceListIterator != null) { - List channelList = new ArrayList<>(); - List regionList = new ArrayList<>(); - List groupList = new ArrayList<>(); - // 遍历DeviceList - while (deviceListIterator.hasNext()) { - Element itemDevice = deviceListIterator.next(); - Element channelDeviceElement = itemDevice.element("DeviceID"); - if (channelDeviceElement == null) { - continue; - } - DeviceChannel channel = DeviceChannel.decode(itemDevice); - if (channel.getDeviceId() == null) { - log.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); - continue; - } - channel.setDeviceDbId(device.getId()); - if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) { - channel.setParentId(null); - } - // 解析通道类型 - if (channel.getDeviceId().length() <= 8) { - // 行政区划 - Region region = Region.getInstance(channel); - regionList.add(region); - channel.setChannelType(1); - }else if (channel.getDeviceId().length() == 20){ - // 业务分组/虚拟组织 - Group group = Group.getInstance(channel); - if (group != null) { - channel.setChannelType(2); - groupList.add(group); - } - } - channelList.add(channel); - } - int sn = Integer.parseInt(snElement.getText()); - catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), - channelList, regionList, groupList); - log.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId()) == null ? 0 : catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId()).size(), sumNum); - if (catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId()).size() == sumNum) { - // 数据已经完整接收, 此时可能存在某个设备离线变上线的情况,但是考虑到性能,此处不做处理, - // 目前支持设备通道上线通知时和设备上线时向上级通知 - boolean resetChannelsResult = saveData(device); - if (!resetChannelsResult) { - String errorMsg = "接收成功,写入失败,共" + sumNum + "条,已接收" + catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId()).size() + "条"; - catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), errorMsg); - } else { - catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null); - } + @Scheduled(fixedDelay = 50) + @Transactional + public void executeTaskQueue(){ + if (taskQueue.isEmpty()) { + return; + } + List handlerCatchDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + HandlerCatchData poll = taskQueue.poll(); + if (poll != null) { + handlerCatchDataList.add(poll); + } + } + if (handlerCatchDataList.isEmpty()) { + return; + } + for (HandlerCatchData take : handlerCatchDataList) { + if (take == null) { + continue; + } + RequestEvent evt = take.getEvt(); + int sn = 0; + // 全局异常捕获,保证下一条可以得到处理 + try { + Element rootElement = null; + try { + rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset()); + } catch (DocumentException e) { + log.error("[xml解析] 失败: ", e); + continue; + } + if (rootElement == null) { + log.warn("[ 收到通道 ] content cannot be null, {}", evt.getRequest()); + continue; + } + Element deviceListElement = rootElement.element("DeviceList"); + Element sumNumElement = rootElement.element("SumNum"); + Element snElement = rootElement.element("SN"); + int sumNum = Integer.parseInt(sumNumElement.getText()); + + if (sumNum == 0) { + log.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId()); + // 数据已经完整接收 + deviceChannelService.cleanChannelsForDevice(take.getDevice().getId()); + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), sn, null); + } else { + Iterator deviceListIterator = deviceListElement.elementIterator(); + if (deviceListIterator != null) { + List channelList = new ArrayList<>(); + List regionList = new ArrayList<>(); + List groupList = new ArrayList<>(); + // 遍历DeviceList + while (deviceListIterator.hasNext()) { + Element itemDevice = deviceListIterator.next(); + Element channelDeviceElement = itemDevice.element("DeviceID"); + if (channelDeviceElement == null) { + continue; + } + // 从xml解析内容到 DeviceChannel 对象 + DeviceChannel channel = DeviceChannel.decode(itemDevice); + if (channel.getDeviceId() == null) { + log.info("[收到目录订阅]:但是解析失败 {}", new String(evt.getRequest().getRawContent())); + continue; + } + channel.setDeviceDbId(take.getDevice().getId()); + if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) { + channel.setParentId(null); + } + // 解析通道类型 + if (channel.getDeviceId().length() <= 8) { + // 行政区划 + Region region = Region.getInstance(channel); + regionList.add(region); + channel.setChannelType(1); + }else if (channel.getDeviceId().length() == 20){ + // 业务分组/虚拟组织 + Group group = Group.getInstance(channel); + if (group != null) { + channel.setParental(1); + channel.setChannelType(2); + groupList.add(group); } } - + channelList.add(channel); } - } catch (Exception e) { - log.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest()); - log.error("[收到通道] 异常内容: ", e); + sn = Integer.parseInt(snElement.getText()); + catalogDataCatch.put(take.getDevice().getDeviceId(), sn, sumNum, take.getDevice(), + channelList, regionList, groupList); + log.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.size(take.getDevice().getDeviceId(), sn), sumNum); } } - processing.set(false); - }); + } catch (Exception e) { + log.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest()); + log.error("[收到通道] 异常内容: ", e); + } finally { + if (catalogDataCatch.size(take.getDevice().getDeviceId(), sn) == catalogDataCatch.sumNum(take.getDevice().getDeviceId(), sn)) { + // 数据已经完整接收, 此时可能存在某个设备离线变上线的情况,但是考虑到性能,此处不做处理, + // 目前支持设备通道上线通知时和设备上线时向上级通知 + boolean resetChannelsResult = saveData(take.getDevice(), sn); + if (!resetChannelsResult) { + String errorMsg = "接收成功,写入失败,共" + catalogDataCatch.sumNum(take.getDevice().getDeviceId(), sn) + "条,已接收" + catalogDataCatch.getDeviceChannelList(take.getDevice().getDeviceId(), sn).size() + "条"; + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), sn, errorMsg); + } else { + catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), sn, null); + } + } + } } - } @Transactional - public boolean saveData(Device device) { + public boolean saveData(Device device, int sn) { boolean result = true; - List deviceChannelList = catalogDataCatch.getDeviceChannelList(device.getDeviceId()); + List deviceChannelList = catalogDataCatch.getDeviceChannelList(device.getDeviceId(), sn); if (deviceChannelList != null && !deviceChannelList.isEmpty()) { result &= deviceChannelService.resetChannels(device.getId(), deviceChannelList); } - List regionList = catalogDataCatch.getRegionList(device.getDeviceId()); + List regionList = catalogDataCatch.getRegionList(device.getDeviceId(), sn); if ( regionList!= null && !regionList.isEmpty()) { result &= regionService.batchAdd(regionList); } - List groupList = catalogDataCatch.getGroupList(device.getDeviceId()); + List groupList = catalogDataCatch.getGroupList(device.getDeviceId(), sn); if (groupList != null && !groupList.isEmpty()) { result &= groupService.batchAdd(groupList); } @@ -202,26 +212,18 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp } public SyncStatus getChannelSyncProgress(String deviceId) { - if (catalogDataCatch.getDeviceChannelList(deviceId) == null) { - return null; - } else { - return catalogDataCatch.getSyncStatus(deviceId); - } + return catalogDataCatch.getSyncStatus(deviceId); } public boolean isSyncRunning(String deviceId) { - if (catalogDataCatch.getDeviceChannelList(deviceId) == null) { - return false; - } else { - return catalogDataCatch.isSyncRunning(deviceId); - } + return catalogDataCatch.isSyncRunning(deviceId); } public void setChannelSyncReady(Device device, int sn) { catalogDataCatch.addReady(device, sn); } - public void setChannelSyncEnd(String deviceId, String errorMsg) { - catalogDataCatch.setChannelSyncEnd(deviceId, errorMsg); + public void setChannelSyncEnd(String deviceId, int sn, String errorMsg) { + catalogDataCatch.setChannelSyncEnd(deviceId, sn, errorMsg); } } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java index ef3531a85..f3f489781 100755 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceControlResponseMessageHandler.java @@ -8,7 +8,8 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage; import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.IMessageHandler; import com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.ResponseMessageHandler; -import com.genersoft.iot.vmp.gb28181.utils.XmlUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import gov.nist.javax.sip.message.SIPRequest; import lombok.extern.slf4j.Slf4j; import org.dom4j.Element; @@ -51,14 +52,19 @@ public class DeviceControlResponseMessageHandler extends SIPRequestProcessorPare } JSONObject json = new JSONObject(); String channelId = getText(element, "DeviceID"); - XmlUtil.node2Json(element, json); - if (log.isDebugEnabled()) { - log.debug(json.toJSONString()); - } + String result = getText(element, "Result"); + RequestMessage msg = new RequestMessage(); String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + device.getDeviceId() + channelId; msg.setKey(key); - msg.setData(json); + if ("OK".equalsIgnoreCase(result)) { + msg.setData(WVPResult.success()); + }else { + msg.setData(WVPResult.fail(ErrorCode.ERROR100)); + } + if (log.isDebugEnabled()) { + log.debug(json.toJSONString()); + } deferredResultHolder.invokeAllResult(msg); } diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java index 98c8dadb3..4656670f6 100644 --- a/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/gb28181/utils/XmlUtil.java @@ -706,21 +706,26 @@ public class XmlUtil { * @return */ private static Object simpleTypeDeal(Class tClass, Object val) { - if (val == null || val.toString().equalsIgnoreCase("null")) { + try { + if (val == null || val.toString().equalsIgnoreCase("null")) { + return null; + } + if (tClass.equals(String.class)) { + return val.toString(); + } + if (tClass.equals(Integer.class)) { + return Integer.valueOf(val.toString()); + } + if (tClass.equals(Double.class)) { + return Double.valueOf(val.toString()); + + } + if (tClass.equals(Long.class)) { + return Long.valueOf(val.toString()); + } + return val; + }catch (Exception e) { return null; } - if (tClass.equals(String.class)) { - return val.toString(); - } - if (tClass.equals(Integer.class)) { - return Integer.valueOf(val.toString()); - } - if (tClass.equals(Double.class)) { - return Double.valueOf(val.toString()); - } - if (tClass.equals(Long.class)) { - return Long.valueOf(val.toString()); - } - return val; } } \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java b/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java index 1c6bda3d5..b9f7c08ef 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java +++ b/src/main/java/com/genersoft/iot/vmp/media/MediaServerConfig.java @@ -41,7 +41,7 @@ public class MediaServerConfig implements CommandLineRunner { mediaServerService.update(mediaSerItemInConfig); }else { if (defaultMediaServer != null) { - mediaServerService.delete(defaultMediaServer.getId()); + mediaServerService.delete(defaultMediaServer); } MediaServer mediaServerItem = mediaServerService.getOneFromDatabase(mediaSerItemInConfig.getId()); if (mediaServerItem == null) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java b/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java index b88fdd443..c26f3dca3 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java +++ b/src/main/java/com/genersoft/iot/vmp/media/event/hook/HookSubscribe.java @@ -82,7 +82,6 @@ public class HookSubscribe { } public void addSubscribe(Hook hook, HookSubscribe.Event event) { - System.out.println("add==" + hook.toString()); if (hook.getExpireTime() == null) { hook.setExpireTime(System.currentTimeMillis() + subscribeExpire); } diff --git a/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerEventAbstract.java b/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerEventAbstract.java index a9bf76981..8fa9953d3 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerEventAbstract.java +++ b/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerEventAbstract.java @@ -1,24 +1,23 @@ package com.genersoft.iot.vmp.media.event.mediaServer; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import lombok.Getter; +import lombok.Setter; import org.springframework.context.ApplicationEvent; + public abstract class MediaServerEventAbstract extends ApplicationEvent { private static final long serialVersionUID = 1L; - private String mediaServerId; + @Getter + @Setter + private MediaServer mediaServer; public MediaServerEventAbstract(Object source) { super(source); } - public String getMediaServerId() { - return mediaServerId; - } - - public void setMediaServerId(String mediaServerId) { - this.mediaServerId = mediaServerId; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerStatusEventListener.java b/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerStatusEventListener.java index e30ffea40..2e5a6ea08 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerStatusEventListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/event/mediaServer/MediaServerStatusEventListener.java @@ -25,16 +25,16 @@ public class MediaServerStatusEventListener { @Async("taskExecutor") @EventListener public void onApplicationEvent(MediaServerOnlineEvent event) { - log.info("[媒体节点] 上线 ID:" + event.getMediaServerId()); - playService.zlmServerOnline(event.getMediaServerId()); + log.info("[媒体节点] 上线 ID:" + event.getMediaServer().getId()); + playService.zlmServerOnline(event.getMediaServer()); } @Async("taskExecutor") @EventListener public void onApplicationEvent(MediaServerOfflineEvent event) { - log.info("[媒体节点] 离线,ID:" + event.getMediaServerId()); + log.info("[媒体节点] 离线,ID:" + event.getMediaServer().getId()); // 处理ZLM离线 - playService.zlmServerOffline(event.getMediaServerId()); + playService.zlmServerOffline(event.getMediaServer()); } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java index eb3a2cb1e..c5b9d0d6b 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaNodeServerService.java @@ -67,4 +67,7 @@ public interface IMediaNodeServerService { StreamInfo startProxy(MediaServer mediaServer, StreamProxy streamProxy); void stopProxy(MediaServer mediaServer, String streamKey); + + List listRtpServer(MediaServer mediaServer); + } diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java index 3d1f7880c..9dcbe20e9 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/IMediaServerService.java @@ -65,7 +65,7 @@ public interface IMediaServerService { boolean checkMediaRecordServer(String ip, int port); - void delete(String id); + void delete(MediaServer mediaServer); MediaServer getDefaultMediaServer(); @@ -158,4 +158,5 @@ public interface IMediaServerService { int createRTPServer(MediaServer mediaServerItem, String streamId, long ssrc, Integer port, boolean onlyAuto, boolean disableAudio, boolean reUsePort, Integer tcpMode); + List listRtpServer(MediaServer mediaServer); } diff --git a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java index 59c9e2290..be99b226a 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/media/service/impl/MediaServerServiceImpl.java @@ -16,6 +16,8 @@ import com.genersoft.iot.vmp.media.event.media.MediaArrivalEvent; import com.genersoft.iot.vmp.media.event.media.MediaDepartureEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent; +import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOfflineEvent; +import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerOnlineEvent; import com.genersoft.iot.vmp.media.service.IMediaNodeServerService; import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; @@ -26,8 +28,6 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.storager.dao.MediaServerMapper; import com.genersoft.iot.vmp.streamProxy.bean.StreamProxy; import com.genersoft.iot.vmp.utils.DateUtil; -import com.genersoft.iot.vmp.utils.JsonUtil; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import lombok.extern.slf4j.Slf4j; @@ -40,6 +40,7 @@ import org.springframework.context.event.EventListener; import org.springframework.data.redis.core.RedisTemplate; import org.springframework.scheduling.annotation.Async; import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; import org.springframework.util.ObjectUtils; import java.time.LocalDateTime; @@ -80,6 +81,7 @@ public class MediaServerServiceImpl implements IMediaServerService { @Autowired private MediaConfig mediaConfig; + /** * 流到来的处理 */ @@ -111,6 +113,26 @@ public class MediaServerServiceImpl implements IMediaServerService { String type = OriginType.values()[mediaInfo.getOriginType()].getType(); redisCatchStorage.removeStream(mediaInfo.getMediaServer().getId(), type, event.getApp(), event.getStream()); } + } + + /** + * 流媒体节点上线 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaServerOnlineEvent event) { + // 查看是否有未处理的RTP流 + + } + + /** + * 流媒体节点离线 + */ + @Async("taskExecutor") + @EventListener + @Transactional + public void onApplicationEvent(MediaServerOfflineEvent event) { } @@ -130,10 +152,10 @@ public class MediaServerServiceImpl implements IMediaServerService { ssrcFactory.initMediaServerSSRC(mediaServer.getId(), null); } // 查询redis是否存在此mediaServer - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServer.getId(); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId(); Boolean hasKey = redisTemplate.hasKey(key); - if (hasKey != null && ! hasKey) { - redisTemplate.opsForValue().set(key, mediaServer); + if (hasKey != null && !hasKey) { + redisTemplate.opsForHash().put(key, mediaServer.getId(), mediaServer); } } } @@ -195,6 +217,16 @@ public class MediaServerServiceImpl implements IMediaServerService { return rtpServerPort; } + @Override + public List listRtpServer(MediaServer mediaServer) { + IMediaNodeServerService mediaNodeServerService = nodeServerServiceMap.get(mediaServer.getType()); + if (mediaNodeServerService == null) { + log.info("[openRTPServer] 失败, mediaServer的类型: {},未找到对应的实现类", mediaServer.getType()); + return new ArrayList<>(); + } + return mediaNodeServerService.listRtpServer(mediaServer); + } + @Override public void closeRTPServer(MediaServer mediaServer, String streamId) { if (mediaServer == null) { @@ -273,6 +305,7 @@ public class MediaServerServiceImpl implements IMediaServerService { public void update(MediaServer mediaSerItem) { mediaServerMapper.update(mediaSerItem); MediaServer mediaServerInRedis = getOne(mediaSerItem.getId()); + // 获取完整数据 MediaServer mediaServerInDataBase = mediaServerMapper.queryOne(mediaSerItem.getId()); if (mediaServerInDataBase == null) { return; @@ -281,8 +314,8 @@ public class MediaServerServiceImpl implements IMediaServerService { if (mediaServerInRedis == null || !ssrcFactory.hasMediaServerSSRC(mediaServerInDataBase.getId())) { ssrcFactory.initMediaServerSSRC(mediaServerInDataBase.getId(),null); } - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerInDataBase.getId(); - redisTemplate.opsForValue().set(key, mediaServerInDataBase); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId(); + redisTemplate.opsForHash().put(key, mediaServerInDataBase.getId(), mediaServerInDataBase); if (mediaServerInDataBase.isStatus()) { resetOnlineServerItem(mediaServerInDataBase); }else { @@ -297,14 +330,14 @@ public class MediaServerServiceImpl implements IMediaServerService { @Override public List getAllOnlineList() { List result = new ArrayList<>(); - List mediaServerKeys = RedisUtil.scan(redisTemplate, String.format("%S*", VideoManagerConstants.MEDIA_SERVER_PREFIX+ userSetting.getServerId() + ":" )); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId(); String onlineKey = VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(); - for (Object mediaServerKey : mediaServerKeys) { - String key = (String) mediaServerKey; - MediaServer mediaServer = JsonUtil.redisJsonToObject(redisTemplate, key, MediaServer.class); - if (Objects.isNull(mediaServer)) { + List values = redisTemplate.opsForHash().values(key); + for (Object value : values) { + if (Objects.isNull(value)) { continue; } + MediaServer mediaServer = (MediaServer) value; // 检查状态 Double aDouble = redisTemplate.opsForZSet().score(onlineKey, mediaServer.getId()); if (aDouble != null) { @@ -350,11 +383,11 @@ public class MediaServerServiceImpl implements IMediaServerService { Set mediaServerIdSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1); List result = new ArrayList<>(); - if (mediaServerIdSet != null && mediaServerIdSet.size() > 0) { + if (mediaServerIdSet != null && !mediaServerIdSet.isEmpty()) { for (Object mediaServerId : mediaServerIdSet) { String mediaServerIdStr = (String) mediaServerId; - String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerIdStr; - result.add((MediaServer) redisTemplate.opsForValue().get(serverKey)); + String serverKey = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId(); + result.add((MediaServer) redisTemplate.opsForHash().get(serverKey, mediaServerIdStr)); } } Collections.reverse(result); @@ -371,8 +404,8 @@ public class MediaServerServiceImpl implements IMediaServerService { if (mediaServerId == null) { return null; } - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServerId; - return JsonUtil.redisJsonToObject(redisTemplate, key, MediaServer.class); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId(); + return (MediaServer) redisTemplate.opsForHash().get(key, mediaServerId); } @@ -539,14 +572,14 @@ public class MediaServerServiceImpl implements IMediaServerService { } @Override - public void delete(String id) { - mediaServerMapper.delOne(id); - redisTemplate.opsForZSet().remove(VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(), id); - String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + id; + public void delete(MediaServer mediaServer) { + mediaServerMapper.delOne(mediaServer.getId()); + redisTemplate.opsForZSet().remove(VideoManagerConstants.ONLINE_MEDIA_SERVERS_PREFIX + userSetting.getServerId(), mediaServer.getId()); + String key = VideoManagerConstants.MEDIA_SERVER_PREFIX + userSetting.getServerId() + ":" + mediaServer.getId(); redisTemplate.delete(key); // 发送节点移除通知 MediaServerDeleteEvent event = new MediaServerDeleteEvent(this); - event.setMediaServerId(id); + event.setMediaServer(mediaServer); applicationEventPublisher.publishEvent(event); } @@ -567,7 +600,7 @@ public class MediaServerServiceImpl implements IMediaServerService { for (MediaServer mediaServer : allInCatch) { // 清除数据中不存在但redis缓存数据 if (!mediaServerMap.containsKey(mediaServer.getId())) { - delete(mediaServer.getId()); + delete(mediaServer); } } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java deleted file mode 100755 index b756b9ece..000000000 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/SendRtpPortManager.java +++ /dev/null @@ -1,133 +0,0 @@ -package com.genersoft.iot.vmp.media.zlm; - -import com.genersoft.iot.vmp.common.VideoManagerConstants; -import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; -import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; -import lombok.extern.slf4j.Slf4j; -import org.apache.commons.lang3.math.NumberUtils; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.data.redis.core.RedisTemplate; -import org.springframework.data.redis.support.atomic.RedisAtomicInteger; -import org.springframework.stereotype.Component; - -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Slf4j -@Component -public class SendRtpPortManager { - - @Autowired - private UserSetting userSetting; - - @Autowired - private RedisTemplate redisTemplate; - - private final String KEY = "VM_MEDIA_SEND_RTP_PORT_"; - - public synchronized int getNextPort(MediaServer mediaServer) { - if (mediaServer == null) { - log.warn("[发送端口管理] 参数错误,mediaServer为NULL"); - return -1; - } - String sendIndexKey = KEY + userSetting.getServerId() + "_" + mediaServer.getId(); - String key = VideoManagerConstants.SEND_RTP_INFO - + userSetting.getServerId() + "_*"; - List queryResult = RedisUtil.scan(redisTemplate, key); - Map sendRtpItemMap = new HashMap<>(); - - for (Object o : queryResult) { - SendRtpInfo sendRtpItem = (SendRtpInfo) redisTemplate.opsForValue().get(o); - if (sendRtpItem != null) { - sendRtpItemMap.put(sendRtpItem.getLocalPort(), sendRtpItem); - } - } - String sendRtpPortRange = mediaServer.getSendRtpPortRange(); - int startPort; - int endPort; - if (sendRtpPortRange != null) { - String[] portArray = sendRtpPortRange.split(","); - if (portArray.length != 2 || !NumberUtils.isParsable(portArray[0]) || !NumberUtils.isParsable(portArray[1])) { - log.warn("{}发送端口配置格式错误,自动使用50000-60000作为端口范围", mediaServer.getId()); - startPort = 50000; - endPort = 60000; - }else { - if ( Integer.parseInt(portArray[1]) - Integer.parseInt(portArray[0]) < 1) { - log.warn("{}发送端口配置错误,结束端口至少比开始端口大一,自动使用50000-60000作为端口范围", mediaServer.getId()); - startPort = 50000; - endPort = 60000; - }else { - startPort = Integer.parseInt(portArray[0]); - endPort = Integer.parseInt(portArray[1]); - } - } - }else { - log.warn("{}未设置发送端口默认值,自动使用50000-60000作为端口范围", mediaServer.getId()); - startPort = 50000; - endPort = 60000; - } - if (redisTemplate == null || redisTemplate.getConnectionFactory() == null) { - log.warn("{}获取redis连接信息失败", mediaServer.getId()); - return -1; - } -// RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory()); -// return redisAtomicInteger.getAndUpdate((current)->{ -// return getPort(current, startPort, endPort, checkPort-> !sendRtpItemMap.containsKey(checkPort)); -// }); - return getSendPort(startPort, endPort, sendIndexKey, sendRtpItemMap); - } - - private synchronized int getSendPort(int startPort, int endPort, String sendIndexKey, Map sendRtpItemMap){ - // TODO 这里改为只取偶数端口 - RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory()); - if (redisAtomicInteger.get() < startPort) { - redisAtomicInteger.set(startPort); - return startPort; - }else { - int port = redisAtomicInteger.getAndIncrement(); - if (port > endPort) { - redisAtomicInteger.set(startPort); - if (sendRtpItemMap.containsKey(startPort)) { - return getSendPort(startPort, endPort, sendIndexKey, sendRtpItemMap); - }else { - return startPort; - } - } - if (sendRtpItemMap.containsKey(port)) { - return getSendPort(startPort, endPort, sendIndexKey, sendRtpItemMap); - }else { - return port; - } - } - } - - interface CheckPortCallback{ - boolean check(int port); - } - - private int getPort(int current, int start, int end, CheckPortCallback checkPortCallback) { - if (current <= 0) { - if (start%2 == 0) { - current = start; - }else { - current = start + 1; - } - }else { - current += 2; - if (current > end) { - if (start%2 == 0) { - current = start; - }else { - current = start + 1; - } - } - } - if (!checkPortCallback.check(current)) { - return getPort(current + 2, start, end, checkPortCallback); - } - return current; - } -} diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java index fc0b60ec6..114e4c720 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java @@ -209,7 +209,7 @@ public class ZLMHttpHookListener { MediaServer mediaServer = mediaServerService.getOne(param.getMediaServerId()); - if (!userSetting.isAutoApplyPlay() || mediaServer == null) { + if (!userSetting.getAutoApplyPlay() || mediaServer == null) { return HookResult.SUCCESS(); } MediaNotFoundEvent mediaNotFoundEvent = MediaNotFoundEvent.getInstance(this, param, mediaServer); diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java index 436a78918..f81cb56a9 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaNodeServerService.java @@ -20,10 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.util.ObjectUtils; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; +import java.util.*; @Slf4j @Service("zlm") @@ -136,9 +133,13 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { param.put("ssrc", ssrc); } JSONObject jsonObject = zlmresTfulUtils.stopSendRtp(mediaInfo, param); - log.info("停止发流结果: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); - return true; - + if (jsonObject.getInteger("code") != null && jsonObject.getInteger("code") == 0) { + log.info("[停止发流] 成功: 参数:{}", JSON.toJSONString(param)); + return true; + }else { + log.info("停止发流结果: {}, 参数:{}", jsonObject.getString("msg"), JSON.toJSONString(param)); + return false; + } } @Override @@ -381,7 +382,9 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { param.put("dst_url", sendRtpItem.getIp()); param.put("dst_port", sendRtpItem.getPort()); JSONObject jsonObject = zlmresTfulUtils.startSendRtp(mediaServer, param); - if (jsonObject == null || jsonObject.getInteger("code") != 0 ) { + if (jsonObject == null ) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "连接zlm失败"); + }else if (jsonObject.getInteger("code") != 0) { throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); } log.info("[推流结果]:{} ,参数: {}",jsonObject, JSONObject.toJSONString(param)); @@ -494,4 +497,22 @@ public class ZLMMediaNodeServerService implements IMediaNodeServerService { throw new ControllerException(jsonObject.getInteger("code"), jsonObject.getString("msg")); } } + + @Override + public List listRtpServer(MediaServer mediaServer) { + JSONObject jsonObject = zlmresTfulUtils.listRtpServer(mediaServer); + List result = new ArrayList<>(); + if (jsonObject == null || jsonObject.getInteger("code") != 0) { + return result; + } + JSONArray data = jsonObject.getJSONArray("data"); + if (data == null || data.isEmpty()) { + return result; + } + for (int i = 0; i < data.size(); i++) { + JSONObject dataJSONObject = data.getJSONObject(i); + result.add(dataJSONObject.getString("stream_id")); + } + return result; + } } diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManager.java similarity index 94% rename from src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java rename to src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManager.java index 6a11455ce..029e46ca8 100644 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManger.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMMediaServerStatusManager.java @@ -4,7 +4,7 @@ import com.alibaba.fastjson2.JSON; import com.alibaba.fastjson2.JSONArray; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.conf.DynamicTask; -import com.genersoft.iot.vmp.conf.UserSetting; +import com.genersoft.iot.vmp.gb28181.event.EventPublisher; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerChangeEvent; import com.genersoft.iot.vmp.media.event.mediaServer.MediaServerDeleteEvent; @@ -32,7 +32,7 @@ import java.util.concurrent.ConcurrentHashMap; */ @Slf4j @Component -public class ZLMMediaServerStatusManger { +public class ZLMMediaServerStatusManager { private final Map offlineZlmPrimaryMap = new ConcurrentHashMap<>(); private final Map offlineZlmsecondaryMap = new ConcurrentHashMap<>(); @@ -57,7 +57,7 @@ public class ZLMMediaServerStatusManger { private String serverServletContextPath; @Autowired - private UserSetting userSetting; + private EventPublisher eventPublisher; private final String type = "zlm"; @@ -112,13 +112,13 @@ public class ZLMMediaServerStatusManger { @Async("taskExecutor") @EventListener public void onApplicationEvent(MediaServerDeleteEvent event) { - if (event.getMediaServerId() == null) { + if (event.getMediaServer() == null) { return; } - log.info("[ZLM-节点被移除] ID:" + event.getMediaServerId()); - offlineZlmPrimaryMap.remove(event.getMediaServerId()); - offlineZlmsecondaryMap.remove(event.getMediaServerId()); - offlineZlmTimeMap.remove(event.getMediaServerId()); + log.info("[ZLM-节点被移除] ID:" + event.getMediaServer().getId()); + offlineZlmPrimaryMap.remove(event.getMediaServer().getId()); + offlineZlmsecondaryMap.remove(event.getMediaServer().getId()); + offlineZlmTimeMap.remove(event.getMediaServer().getId()); } @Scheduled(fixedDelay = 10*1000) //每隔10秒检查一次 @@ -129,7 +129,8 @@ public class ZLMMediaServerStatusManger { } if (!offlineZlmPrimaryMap.isEmpty()) { for (MediaServer mediaServerItem : offlineZlmPrimaryMap.values()) { - if (offlineZlmTimeMap.get(mediaServerItem.getId()) < System.currentTimeMillis() - 30*60*1000) { + if (offlineZlmTimeMap.get(mediaServerItem.getId()) != null + && offlineZlmTimeMap.get(mediaServerItem.getId()) < System.currentTimeMillis() - 30*60*1000) { offlineZlmsecondaryMap.put(mediaServerItem.getId(), mediaServerItem); offlineZlmPrimaryMap.remove(mediaServerItem.getId()); continue; @@ -186,6 +187,8 @@ public class ZLMMediaServerStatusManger { mediaServerItem.setStatus(true); mediaServerItem.setHookAliveInterval(10F); mediaServerService.update(mediaServerItem); + // 发送上线通知 + eventPublisher.mediaServerOnlineEventPublish(mediaServerItem); if(mediaServerItem.isAutoConfig()) { if (config == null) { JSONObject responseJSON = zlmresTfulUtils.getMediaServerConfig(mediaServerItem); @@ -209,7 +212,8 @@ public class ZLMMediaServerStatusManger { mediaServerItem.setStatus(false); offlineZlmPrimaryMap.put(mediaServerItem.getId(), mediaServerItem); offlineZlmTimeMap.put(mediaServerItem.getId(), System.currentTimeMillis()); - // TODO 发送离线通知 + // 发送离线通知 + eventPublisher.mediaServerOfflineEventPublish(mediaServerItem); mediaServerService.update(mediaServerItem); }, (int)(mediaServerItem.getHookAliveInterval() * 2 * 1000)); } @@ -281,6 +285,8 @@ public class ZLMMediaServerStatusManger { // 等zlm支持给每个rtpServer设置关闭音频的时候可以不设置此选项 if (mediaServerItem.isRtpEnable() && !ObjectUtils.isEmpty(mediaServerItem.getRtpPortRange())) { param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-")); + }else { + param.put("rtp_proxy.port", mediaServerItem.getRtpProxyPort()); } if (!ObjectUtils.isEmpty(mediaServerItem.getRecordPath())) { diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java index 997ffd838..4b4ba2e4e 100755 --- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java +++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerFactory.java @@ -2,7 +2,6 @@ package com.genersoft.iot.vmp.media.zlm; import com.alibaba.fastjson2.JSONObject; import com.genersoft.iot.vmp.common.CommonCallback; -import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import lombok.extern.slf4j.Slf4j; @@ -18,12 +17,6 @@ public class ZLMServerFactory { @Autowired private ZLMRESTfulUtils zlmresTfulUtils; - @Autowired - private UserSetting userSetting; - - @Autowired - private SendRtpPortManager sendRtpPortManager; - /** * 开启rtpServer diff --git a/src/main/java/com/genersoft/iot/vmp/service/ILogService.java b/src/main/java/com/genersoft/iot/vmp/service/ILogService.java old mode 100755 new mode 100644 index b7a67ede0..ef6161c08 --- a/src/main/java/com/genersoft/iot/vmp/service/ILogService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/ILogService.java @@ -1,34 +1,12 @@ package com.genersoft.iot.vmp.service; -import com.genersoft.iot.vmp.storager.dao.dto.LogDto; -import com.github.pagehelper.PageInfo; +import com.genersoft.iot.vmp.service.bean.LogFileInfo; + +import java.io.File; +import java.util.List; -/** - * 系统日志 - */ public interface ILogService { + List queryList(String query, String startTime, String endTime); - /** - * 查询日志 - * @param page 当前页 - * @param count 每页数量 - * @param query 搜索内容 - * @param type 类型 - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return 日志列表 - */ - PageInfo getAll(int page, int count, String query, String type, String startTime, String endTime); - - /** - * 添加日志 - * @param logDto 日志 - */ - void add(LogDto logDto); - - /** - * 清空 - */ - int clear(); - + File getFileByName(String fileName); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/ISendRtpServerService.java b/src/main/java/com/genersoft/iot/vmp/service/ISendRtpServerService.java index 8b05d3a4c..ca8c33d3e 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/ISendRtpServerService.java +++ b/src/main/java/com/genersoft/iot/vmp/service/ISendRtpServerService.java @@ -40,4 +40,6 @@ public interface ISendRtpServerService { List queryByChannelId(int id); void deleteByStream(String stream); + + int getNextPort(MediaServer mediaServer); } diff --git a/src/main/java/com/genersoft/iot/vmp/service/bean/LogFileInfo.java b/src/main/java/com/genersoft/iot/vmp/service/bean/LogFileInfo.java new file mode 100644 index 000000000..c73cff3ee --- /dev/null +++ b/src/main/java/com/genersoft/iot/vmp/service/bean/LogFileInfo.java @@ -0,0 +1,13 @@ +package com.genersoft.iot.vmp.service.bean; + +import lombok.Data; + +@Data +public class LogFileInfo { + + private String fileName; + private Long fileSize; + private Long startTime; + private Long endTime; + +} diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java old mode 100755 new mode 100644 index c72d6c0ab..3aad00353 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/LogServiceImpl.java @@ -1,38 +1,110 @@ package com.genersoft.iot.vmp.service.impl; -import com.baomidou.dynamic.datasource.annotation.DS; -import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.core.rolling.RollingFileAppender; +import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.service.ILogService; -import com.genersoft.iot.vmp.storager.dao.LogMapper; -import com.genersoft.iot.vmp.storager.dao.dto.LogDto; -import com.github.pagehelper.PageHelper; -import com.github.pagehelper.PageInfo; -import org.springframework.beans.factory.annotation.Autowired; +import com.genersoft.iot.vmp.service.bean.LogFileInfo; +import com.genersoft.iot.vmp.utils.DateUtil; +import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import lombok.extern.slf4j.Slf4j; +import org.apache.commons.io.input.ReversedLinesFileReader; +import org.slf4j.LoggerFactory; import org.springframework.stereotype.Service; +import java.io.*; +import java.nio.charset.Charset; +import java.util.ArrayList; import java.util.List; @Service -@DS("master") +@Slf4j public class LogServiceImpl implements ILogService { - @Autowired - private LogMapper logMapper; - @Override - public PageInfo getAll(int page, int count, String query, String type, String startTime, String endTime) { - PageHelper.startPage(page, count); - List all = logMapper.query(query, type, startTime, endTime); - return new PageInfo<>(all); + public List queryList(String query, String startTime, String endTime) { + File logFile = getLogDir(); + if (logFile == null && !logFile.exists()) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "获取日志文件目录失败"); + } + File[] files = logFile.listFiles(); + List result = new ArrayList<>(); + if (files == null || files.length == 0) { + return result; + } + + // 读取文件创建时间作为开始时间,修改时间为结束时间 + Long startTimestamp = null; + if (startTime != null) { + startTimestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(startTime); + } + Long endTimestamp = null; + if (endTime != null) { + endTimestamp = DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(endTime); + } + for (File file : files) { + LogFileInfo logFileInfo = new LogFileInfo(); + logFileInfo.setFileName(file.getName()); + logFileInfo.setFileSize(file.length()); + if (query != null && !file.getName().contains(query)) { + continue; + } + try { + Long[] fileAttributes = getFileAttributes(file); + if (fileAttributes == null) { + continue; + } + long startTimestampForFile = fileAttributes[0]; + long endTimestampForFile = fileAttributes[1]; + logFileInfo.setStartTime(startTimestampForFile); + logFileInfo.setEndTime(endTimestampForFile); + if (startTimestamp != null && startTimestamp > startTimestampForFile) { + continue; + } + if (endTimestamp != null && endTimestamp < endTimestampForFile) { + continue; + } + } catch (IOException e) { + log.error("[读取日志文件列表] 获取创建时间和修改时间失败", e); + continue; + } + result.add(logFileInfo); + + } + result.sort((o1, o2) -> o2.getStartTime().compareTo(o1.getStartTime())); + return result; + } + + private File getLogDir() { + Logger logger = (Logger) LoggerFactory.getLogger("root"); + RollingFileAppender rollingFileAppender = (RollingFileAppender) logger.getAppender("RollingFile"); + File rollingFile = new File(rollingFileAppender.getFile()); + return rollingFile.getParentFile(); + } + + Long[] getFileAttributes(File file) throws IOException { + BufferedReader bufferedReader = new BufferedReader(new FileReader(file)); + String startLine = bufferedReader.readLine(); + if (startLine== null) { + return null; + } + String startTime = startLine.substring(0, 19); + + // 最后一行的开头不一定是时间 +// String lastLine = ""; +// try (ReversedLinesFileReader reversedLinesReader = new ReversedLinesFileReader(file, Charset.defaultCharset())) { +// lastLine = reversedLinesReader.readLine(); +// } catch (Exception e) { +// log.error("file read error, msg:{}", e.getMessage(), e); +// } +// String endTime = lastLine.substring(0, 19); + return new Long[]{DateUtil.yyyy_MM_dd_HH_mm_ssToTimestampMs(startTime), file.lastModified()}; } @Override - public void add(LogDto logDto) { - logMapper.add(logDto); - } + public File getFileByName(String fileName) { + File logDir = getLogDir(); - @Override - public int clear() { - return logMapper.clear(); + return new File(logDir, fileName); } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java index 79a5d2231..1b7b43737 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java @@ -9,8 +9,8 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel; import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.gb28181.bean.SsrcTransaction; -import com.genersoft.iot.vmp.gb28181.service.*; -import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; +import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; +import com.genersoft.iot.vmp.gb28181.service.IInviteStreamService; import com.genersoft.iot.vmp.gb28181.session.SipInviteSessionManager; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander; import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; @@ -61,30 +61,12 @@ public class MediaServiceImpl implements IMediaService { @Autowired private IInviteStreamService inviteStreamService; - @Autowired - private SSRCFactory ssrcFactory; - @Autowired private IDeviceChannelService deviceChannelService; @Autowired private SipInviteSessionManager sessionManager; - @Autowired - private IPlatformService platformService; - - @Autowired - private IGbChannelService channelService; - - @Autowired - private IDeviceService deviceService; - - @Autowired - private ISIPCommanderForPlatform commanderForPlatform; - - @Autowired - private ISIPCommander commander; - @Autowired private Ijt1078Service ijt1078Service; @@ -94,6 +76,7 @@ public class MediaServiceImpl implements IMediaService { @Autowired private ISendRtpServerService sendRtpServerService; + @Override public boolean authenticatePlay(String app, String stream, String callId) { if (app == null || stream == null) { @@ -158,7 +141,7 @@ public class MediaServiceImpl implements IMediaService { if ("rtp".equals(app)) { result.setEnable_mp4(userSetting.getRecordSip()); } else { - result.setEnable_mp4(userSetting.isRecordPushLive()); + result.setEnable_mp4(userSetting.getRecordPushLive()); } // 国标流 if ("rtp".equals(app)) { diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MobilePositionServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MobilePositionServiceImpl.java index 97f26a10b..0241f69a0 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/MobilePositionServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MobilePositionServiceImpl.java @@ -99,7 +99,7 @@ public class MobilePositionServiceImpl implements IMobilePositionService { channelMapper.updateStreamGPS(gpsMsgInfoList); } - @Scheduled(fixedRate = 1000) + @Scheduled(fixedDelay = 1000) @Transactional public void executeTaskQueue() { int countLimit = 3000; diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/SendRtpServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/SendRtpServerServiceImpl.java index c4cf7a0bd..270b5403f 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/impl/SendRtpServerServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/service/impl/SendRtpServerServiceImpl.java @@ -4,19 +4,20 @@ import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.conf.UserSetting; import com.genersoft.iot.vmp.gb28181.bean.PlayException; import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; -import com.genersoft.iot.vmp.gb28181.conf.StackLoggerImpl; import com.genersoft.iot.vmp.media.bean.MediaServer; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.utils.JsonUtil; -import com.genersoft.iot.vmp.utils.redis.RedisUtil; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.lang3.math.NumberUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.support.atomic.RedisAtomicInteger; import org.springframework.stereotype.Service; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; +import java.util.Set; @Service @Slf4j @@ -25,18 +26,14 @@ public class SendRtpServerServiceImpl implements ISendRtpServerService { @Autowired private UserSetting userSetting; - @Autowired - private SendRtpPortManager sendRtpPortManager; - @Autowired private RedisTemplate redisTemplate; - @Autowired - private StackLoggerImpl stackLoggerImpl; + @Override public SendRtpInfo createSendRtpInfo(MediaServer mediaServer, String ip, Integer port, String ssrc, String requesterId, String deviceId, Integer channelId, Boolean isTcp, Boolean rtcp) { - int localPort = sendRtpPortManager.getNextPort(mediaServer); + int localPort = getNextPort(mediaServer); if (localPort == 0) { return null; } @@ -48,7 +45,7 @@ public class SendRtpServerServiceImpl implements ISendRtpServerService { public SendRtpInfo createSendRtpInfo(MediaServer mediaServer, String ip, Integer port, String ssrc, String platformId, String app, String stream, Integer channelId, Boolean tcp, Boolean rtcp){ - int localPort = sendRtpPortManager.getNextPort(mediaServer); + int localPort = getNextPort(mediaServer); if (localPort <= 0) { throw new PlayException(javax.sip.message.Response.SERVER_INTERNAL_ERROR, "server internal error"); } @@ -64,38 +61,36 @@ public class SendRtpServerServiceImpl implements ISendRtpServerService { @Override public void update(SendRtpInfo sendRtpItem) { - redisTemplate.opsForValue().set(VideoManagerConstants.SEND_RTP_INFO_CALLID + sendRtpItem.getCallId(), sendRtpItem); - redisTemplate.opsForValue().set(VideoManagerConstants.SEND_RTP_INFO_STREAM + sendRtpItem.getStream() + ":" + sendRtpItem.getTargetId(), sendRtpItem); - redisTemplate.opsForValue().set(VideoManagerConstants.SEND_RTP_INFO_CHANNEL + sendRtpItem.getChannelId() + ":" + sendRtpItem.getTargetId(), sendRtpItem); + redisTemplate.opsForHash().put(VideoManagerConstants.SEND_RTP_INFO_CALLID, sendRtpItem.getCallId(), sendRtpItem); + redisTemplate.opsForHash().put(VideoManagerConstants.SEND_RTP_INFO_STREAM + sendRtpItem.getStream(), sendRtpItem.getTargetId(), sendRtpItem); + redisTemplate.opsForHash().put(VideoManagerConstants.SEND_RTP_INFO_CHANNEL + sendRtpItem.getChannelId(), sendRtpItem.getTargetId(), sendRtpItem); } @Override public SendRtpInfo queryByChannelId(Integer channelId, String targetId) { - String key = VideoManagerConstants.SEND_RTP_INFO_CHANNEL + channelId + ":" + targetId; - return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpInfo.class); + String key = VideoManagerConstants.SEND_RTP_INFO_CHANNEL + channelId; + return JsonUtil.redisHashJsonToObject(redisTemplate, key, targetId, SendRtpInfo.class); } @Override public SendRtpInfo queryByCallId(String callId) { - String key = VideoManagerConstants.SEND_RTP_INFO_CALLID + callId; - return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpInfo.class); + String key = VideoManagerConstants.SEND_RTP_INFO_CALLID; + return (SendRtpInfo)redisTemplate.opsForHash().get(key, callId); } @Override public SendRtpInfo queryByStream(String stream, String targetId) { - String key = VideoManagerConstants.SEND_RTP_INFO_STREAM + stream + ":" + targetId; - return JsonUtil.redisJsonToObject(redisTemplate, key, SendRtpInfo.class); + String key = VideoManagerConstants.SEND_RTP_INFO_STREAM + stream; + return JsonUtil.redisHashJsonToObject(redisTemplate, key, targetId, SendRtpInfo.class); } @Override public List queryByStream(String stream) { - String key = VideoManagerConstants.SEND_RTP_INFO_STREAM + stream + ":*"; - List queryResult = RedisUtil.scan(redisTemplate, key); + String key = VideoManagerConstants.SEND_RTP_INFO_STREAM + stream; + List values = redisTemplate.opsForHash().values(key); List result= new ArrayList<>(); - - for (Object o : queryResult) { - String keyItem = (String) o; - result.add((SendRtpInfo) redisTemplate.opsForValue().get(keyItem)); + for (Object o : values) { + result.add((SendRtpInfo) o); } return result; @@ -109,9 +104,9 @@ public class SendRtpServerServiceImpl implements ISendRtpServerService { if (sendRtpInfo == null) { return; } - redisTemplate.delete(VideoManagerConstants.SEND_RTP_INFO_CALLID + sendRtpInfo.getCallId()); - redisTemplate.delete(VideoManagerConstants.SEND_RTP_INFO_STREAM + sendRtpInfo.getStream() + ":" + sendRtpInfo.getTargetId()); - redisTemplate.delete(VideoManagerConstants.SEND_RTP_INFO_CHANNEL + sendRtpInfo.getChannelId() + ":" + sendRtpInfo.getTargetId()); + redisTemplate.opsForHash().delete(VideoManagerConstants.SEND_RTP_INFO_CALLID, sendRtpInfo.getCallId()); + redisTemplate.opsForHash().delete(VideoManagerConstants.SEND_RTP_INFO_STREAM + sendRtpInfo.getStream(), sendRtpInfo.getTargetId()); + redisTemplate.opsForHash().delete(VideoManagerConstants.SEND_RTP_INFO_CHANNEL + sendRtpInfo.getChannelId(), sendRtpInfo.getTargetId()); } @Override public void deleteByCallId(String callId) { @@ -149,30 +144,23 @@ public class SendRtpServerServiceImpl implements ISendRtpServerService { @Override public List queryByChannelId(int channelId) { - String key = VideoManagerConstants.SEND_RTP_INFO_CHANNEL + channelId + ":*"; - List queryResult = RedisUtil.scan(redisTemplate, key); + String key = VideoManagerConstants.SEND_RTP_INFO_CHANNEL + channelId; + List values = redisTemplate.opsForHash().values(key); List result= new ArrayList<>(); - - for (Object o : queryResult) { - String keyItem = (String) o; - result.add((SendRtpInfo) redisTemplate.opsForValue().get(keyItem)); + for (Object o : values) { + result.add((SendRtpInfo) o); } - return result; } @Override public List queryAll() { - String key = VideoManagerConstants.SEND_RTP_INFO_CALLID - + userSetting.getServerId() + ":*"; - List queryResult = RedisUtil.scan(redisTemplate, key); + String key = VideoManagerConstants.SEND_RTP_INFO_CALLID; + List values = redisTemplate.opsForHash().values(key); List result= new ArrayList<>(); - - for (Object o : queryResult) { - String keyItem = (String) o; - result.add((SendRtpInfo) redisTemplate.opsForValue().get(keyItem)); + for (Object o : values) { + result.add((SendRtpInfo) o); } - return result; } @@ -193,4 +181,80 @@ public class SendRtpServerServiceImpl implements ISendRtpServerService { } return sendRtpInfos; } + + private Set getAllSendRtpPort() { + String key = VideoManagerConstants.SEND_RTP_INFO_CALLID; + List values = redisTemplate.opsForHash().values(key); + Set result = new HashSet<>(); + for (Object value : values) { + SendRtpInfo sendRtpInfo = (SendRtpInfo) value; + result.add(sendRtpInfo.getPort()); + } + return result; + } + + + @Override + public synchronized int getNextPort(MediaServer mediaServer) { + if (mediaServer == null) { + log.warn("[发送端口管理] 参数错误,mediaServer为NULL"); + return -1; + } + String sendIndexKey = VideoManagerConstants.SEND_RTP_PORT + userSetting.getServerId() + ":" + mediaServer.getId(); + Set sendRtpSet = getAllSendRtpPort(); + String sendRtpPortRange = mediaServer.getSendRtpPortRange(); + int startPort; + int endPort; + if (sendRtpPortRange != null) { + String[] portArray = sendRtpPortRange.split(","); + if (portArray.length != 2 || !NumberUtils.isParsable(portArray[0]) || !NumberUtils.isParsable(portArray[1])) { + log.warn("{}发送端口配置格式错误,自动使用50000-60000作为端口范围", mediaServer.getId()); + startPort = 50000; + endPort = 60000; + }else { + if ( Integer.parseInt(portArray[1]) - Integer.parseInt(portArray[0]) < 1) { + log.warn("{}发送端口配置错误,结束端口至少比开始端口大一,自动使用50000-60000作为端口范围", mediaServer.getId()); + startPort = 50000; + endPort = 60000; + }else { + startPort = Integer.parseInt(portArray[0]); + endPort = Integer.parseInt(portArray[1]); + } + } + }else { + log.warn("{}未设置发送端口默认值,自动使用50000-60000作为端口范围", mediaServer.getId()); + startPort = 50000; + endPort = 60000; + } + if (redisTemplate == null || redisTemplate.getConnectionFactory() == null) { + log.warn("{}获取redis连接信息失败", mediaServer.getId()); + return -1; + } + return getSendPort(startPort, endPort, sendIndexKey, sendRtpSet); + } + + private synchronized int getSendPort(int startPort, int endPort, String sendIndexKey, Set sendRtpPortSet){ + // TODO 这里改为只取偶数端口 + RedisAtomicInteger redisAtomicInteger = new RedisAtomicInteger(sendIndexKey , redisTemplate.getConnectionFactory()); + if (redisAtomicInteger.get() < startPort) { + redisAtomicInteger.set(startPort); + return startPort; + }else { + int port = redisAtomicInteger.getAndIncrement(); + if (port > endPort) { + redisAtomicInteger.set(startPort); + if (sendRtpPortSet.contains(startPort)) { + return getSendPort(startPort, endPort, sendIndexKey, sendRtpPortSet); + }else { + return startPort; + } + } + if (sendRtpPortSet.contains(port)) { + return getSendPort(startPort, endPort, sendIndexKey, sendRtpPortSet); + }else { + return port; + } + } + } + } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java index e31c65cb5..d4f65e138 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java @@ -15,10 +15,9 @@ import com.genersoft.iot.vmp.service.IMobilePositionService; import com.genersoft.iot.vmp.utils.DateUtil; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; @@ -26,6 +25,7 @@ import javax.sip.InvalidArgumentException; import javax.sip.SipException; import javax.validation.constraints.NotNull; import java.text.ParseException; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; @@ -55,111 +55,119 @@ public class RedisAlarmMsgListener implements MessageListener { @Autowired private IPlatformService platformService; - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Autowired private UserSetting userSetting; @Override public void onMessage(@NotNull Message message, byte[] bytes) { - log.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody())); - boolean isEmpty = taskQueue.isEmpty(); + log.info("[REDIS: ALARM]: {}", new String(message.getBody())); taskQueue.offer(message); - if (isEmpty) { -// logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize()); - taskExecutor.execute(() -> { - while (!taskQueue.isEmpty()) { - Message msg = taskQueue.poll(); - try { - AlarmChannelMessage alarmChannelMessage = JSON.parseObject(msg.getBody(), AlarmChannelMessage.class); - if (alarmChannelMessage == null) { - log.warn("[REDIS的ALARM通知]消息解析失败"); - continue; - } - String gbId = alarmChannelMessage.getGbId(); + } - DeviceAlarm deviceAlarm = new DeviceAlarm(); - deviceAlarm.setCreateTime(DateUtil.getNow()); - deviceAlarm.setChannelId(gbId); - deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription()); - deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn()); - deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType()); - deviceAlarm.setAlarmPriority("1"); - deviceAlarm.setAlarmTime(DateUtil.getNow()); - deviceAlarm.setLongitude(0); - deviceAlarm.setLatitude(0); + @Scheduled(fixedDelay = 100) + public void executeTaskQueue() { + if (taskQueue.isEmpty()) { + return; + } + List messageDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + Message msg = taskQueue.poll(); + if (msg != null) { + messageDataList.add(msg); + } + } + if (messageDataList.isEmpty()) { + return; + } + for (Message msg : messageDataList) { + try { + AlarmChannelMessage alarmChannelMessage = JSON.parseObject(msg.getBody(), AlarmChannelMessage.class); + if (alarmChannelMessage == null) { + log.warn("[REDIS的ALARM通知]消息解析失败"); + continue; + } + String gbId = alarmChannelMessage.getGbId(); - if (ObjectUtils.isEmpty(gbId)) { - if (userSetting.getSendToPlatformsWhenIdLost()) { - // 发送给所有的上级 - List parentPlatforms = platformService.queryEnablePlatformList(); - if (!parentPlatforms.isEmpty()) { - for (Platform parentPlatform : parentPlatforms) { - try { - deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); - commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); - } catch (SipException | InvalidArgumentException | ParseException e) { - log.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); - } - } - } - }else { - // 获取开启了消息推送的设备和平台 - List parentPlatforms = mobilePositionService.queryEnablePlatformListWithAsMessageChannel(); - if (parentPlatforms.size() > 0) { - for (Platform parentPlatform : parentPlatforms) { - try { - deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); - commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); - } catch (SipException | InvalidArgumentException | ParseException e) { - log.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); - } - } - } + DeviceAlarm deviceAlarm = new DeviceAlarm(); + deviceAlarm.setCreateTime(DateUtil.getNow()); + deviceAlarm.setChannelId(gbId); + deviceAlarm.setAlarmDescription(alarmChannelMessage.getAlarmDescription()); + deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn()); + deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType()); + deviceAlarm.setAlarmPriority("1"); + deviceAlarm.setAlarmTime(DateUtil.getNow()); + deviceAlarm.setLongitude(0); + deviceAlarm.setLatitude(0); - } - // 获取开启了消息推送的设备和平台 - List devices = channelService.queryDeviceWithAsMessageChannel(); - if (devices.size() > 0) { - for (Device device : devices) { - try { - deviceAlarm.setChannelId(device.getDeviceId()); - commander.sendAlarmMessage(device, deviceAlarm); - } catch (InvalidArgumentException | SipException | ParseException e) { - log.error("[命令发送失败] 发送报警: {}", e.getMessage()); - } - } - } - - }else { - Device device = deviceService.getDeviceByDeviceId(gbId); - Platform platform = platformService.queryPlatformByServerGBId(gbId); - if (device != null && platform == null) { + if (ObjectUtils.isEmpty(gbId)) { + if (userSetting.getSendToPlatformsWhenIdLost()) { + // 发送给所有的上级 + List parentPlatforms = platformService.queryEnablePlatformList(); + if (!parentPlatforms.isEmpty()) { + for (Platform parentPlatform : parentPlatforms) { try { - commander.sendAlarmMessage(device, deviceAlarm); - } catch (InvalidArgumentException | SipException | ParseException e) { - log.error("[命令发送失败] 发送报警: {}", e.getMessage()); + deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); + commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); } - }else if (device == null && platform != null){ - try { - commanderForPlatform.sendAlarmMessage(platform, deviceAlarm); - } catch (InvalidArgumentException | SipException | ParseException e) { - log.error("[命令发送失败] 发送报警: {}", e.getMessage()); - } - }else { - log.warn("无法确定" + gbId + "是平台还是设备"); } } - }catch (Exception e) { - log.error("未处理的异常 ", e); - log.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage()); + } else { + // 获取开启了消息推送的设备和平台 + List parentPlatforms = mobilePositionService.queryEnablePlatformListWithAsMessageChannel(); + if (!parentPlatforms.isEmpty()) { + for (Platform parentPlatform : parentPlatforms) { + try { + deviceAlarm.setChannelId(parentPlatform.getDeviceGBId()); + commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm); + } catch (SipException | InvalidArgumentException | ParseException e) { + log.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage()); + } + } + } + + } + // 获取开启了消息推送的设备和平台 + List devices = channelService.queryDeviceWithAsMessageChannel(); + if (!devices.isEmpty()) { + for (Device device : devices) { + try { + deviceAlarm.setChannelId(device.getDeviceId()); + commander.sendAlarmMessage(device, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + log.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + } + } + + } else { + Device device = deviceService.getDeviceByDeviceId(gbId); + Platform platform = platformService.queryPlatformByServerGBId(gbId); + if (device != null && platform == null) { + try { + commander.sendAlarmMessage(device, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + log.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + } else if (device == null && platform != null) { + try { + commanderForPlatform.sendAlarmMessage(platform, deviceAlarm); + } catch (InvalidArgumentException | SipException | ParseException e) { + log.error("[命令发送失败] 发送报警: {}", e.getMessage()); + } + } else { + log.warn("无法确定" + gbId + "是平台还是设备"); } } - }); + } catch (Exception e) { + log.error("未处理的异常 ", e); + log.warn("[REDIS的ALARM通知] 发现未处理的异常, {}", e.getMessage()); + } } } } + diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java index e0224839f..f0ba94259 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisCloseStreamMsgListener.java @@ -6,12 +6,13 @@ import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; /** @@ -26,31 +27,40 @@ public class RedisCloseStreamMsgListener implements MessageListener { @Autowired private IStreamPushService pushService; - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Override public void onMessage(@NotNull Message message, byte[] bytes) { - boolean isEmpty = taskQueue.isEmpty(); + log.info("[REDIS: 关闭流]: {}", new String(message.getBody())); taskQueue.offer(message); - if (isEmpty) { - taskExecutor.execute(() -> { - while (!taskQueue.isEmpty()) { - Message msg = taskQueue.poll(); - try { - JSONObject jsonObject = JSON.parseObject(msg.getBody()); - String app = jsonObject.getString("app"); - String stream = jsonObject.getString("stream"); - pushService.stopByAppAndStream(app, stream); - }catch (Exception e) { - log.warn("[REDIS的关闭推流通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); - log.error("[REDIS的关闭推流通知] 异常内容: ", e); - } - } - }); + } + + @Scheduled(fixedDelay = 100) + public void executeTaskQueue() { + if (taskQueue.isEmpty()) { + return; + } + List messageDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + Message msg = taskQueue.poll(); + if (msg != null) { + messageDataList.add(msg); + } + } + if (messageDataList.isEmpty()) { + return; + } + for (Message msg : messageDataList) { + try { + JSONObject jsonObject = JSON.parseObject(msg.getBody()); + String app = jsonObject.getString("app"); + String stream = jsonObject.getString("stream"); + pushService.stopByAppAndStream(app, stream); + }catch (Exception e) { + log.warn("[REDIS的关闭推流通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(msg)); + log.error("[REDIS的关闭推流通知] 异常内容: ", e); + } } } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java index 32dddddfc..bd34dd6f6 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java @@ -1,24 +1,24 @@ package com.genersoft.iot.vmp.service.redisMsg; import com.alibaba.fastjson2.JSON; -import com.genersoft.iot.vmp.service.IMobilePositionService; import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; +import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; import lombok.extern.slf4j.Slf4j; import org.jetbrains.annotations.NotNull; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; import org.springframework.scheduling.annotation.Scheduled; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; +import java.util.ArrayList; import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; /** - * 接收来自redis的GPS更新通知 + * 接收来自redis的GPS更新通知, 此处只针对推流设备 + * * @author lin * 监听: SUBSCRIBE VM_MSG_GPS * 发布 PUBLISH VM_MSG_GPS '{"messageId":"1727228507555","id":"24212345671381000047","lng":116.30307666666667,"lat":40.03295833333333,"time":"2024-09-25T09:41:47","direction":"56.0","speed":0.0,"altitude":60.0,"unitNo":"100000000","memberNo":"10000047"}' @@ -31,45 +31,55 @@ public class RedisGpsMsgListener implements MessageListener { private IRedisCatchStorage redisCatchStorage; @Autowired - private IMobilePositionService mobilePositionService; + private IStreamPushService streamPushService; - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Override public void onMessage(@NotNull Message message, byte[] bytes) { - boolean isEmpty = taskQueue.isEmpty(); + log.debug("[REDIS: GPS]: {}", new String(message.getBody())); taskQueue.offer(message); - if (isEmpty) { - taskExecutor.execute(() -> { - while (!taskQueue.isEmpty()) { - Message msg = taskQueue.poll(); - try { - GPSMsgInfo gpsMsgInfo = JSON.parseObject(msg.getBody(), GPSMsgInfo.class); - log.info("[REDIS的位置变化通知], {}", JSON.toJSONString(gpsMsgInfo)); - // 只是放入redis缓存起来 - redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); - }catch (Exception e) { - log.warn("[REDIS的位置变化通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); - log.error("[REDIS的位置变化通知] 异常内容: ", e); - } - } - }); + } + + @Scheduled(fixedDelay = 200) //每400毫秒执行一次 + public void executeTaskQueue() { + if (taskQueue.isEmpty()) { + return; + } + List messageDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + Message msg = taskQueue.poll(); + if (msg != null) { + messageDataList.add(msg); + } + } + if (messageDataList.isEmpty()) { + return; + } + for (Message msg : messageDataList) { + try { + GPSMsgInfo gpsMsgInfo = JSON.parseObject(msg.getBody(), GPSMsgInfo.class); + log.info("[REDIS的位置变化通知], {}", JSON.toJSONString(gpsMsgInfo)); + // 只是放入redis缓存起来 + redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo); + } catch (Exception e) { + log.warn("[REDIS的位置变化通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(msg)); + log.error("[REDIS的位置变化通知] 异常内容: ", e); + } } } /** * 定时将经纬度更新到数据库 */ - @Scheduled(fixedRate = 2 * 1000) //每2秒执行一次 - public void execute(){ + @Scheduled(fixedDelay = 2 * 1000) //每2秒执行一次 + public void execute() { + // 需要查询到 List gpsMsgInfoList = redisCatchStorage.getAllGpsMsgInfo(); if (!gpsMsgInfoList.isEmpty()) { - mobilePositionService.updateStreamGPS(gpsMsgInfoList); + streamPushService.updateGPSFromGPSMsgInfo(gpsMsgInfoList); for (GPSMsgInfo msgInfo : gpsMsgInfoList) { msgInfo.setStored(true); redisCatchStorage.updateGpsMsgInfo(msgInfo); diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamListMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamListMsgListener.java index 1c2845a49..ca7048288 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamListMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamListMsgListener.java @@ -8,11 +8,9 @@ import com.genersoft.iot.vmp.streamPush.bean.StreamPush; import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; import com.genersoft.iot.vmp.utils.DateUtil; import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; import javax.annotation.Resource; @@ -38,86 +36,93 @@ public class RedisPushStreamListMsgListener implements MessageListener { @Resource private IStreamPushService streamPushService; - private ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; + private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); @Override public void onMessage(Message message, byte[] bytes) { - log.info("[REDIS消息-推流设备列表更新]: {}", new String(message.getBody())); - boolean isEmpty = taskQueue.isEmpty(); + log.info("[REDIS: 流设备列表更新]: {}", new String(message.getBody())); taskQueue.offer(message); - if (isEmpty) { - taskExecutor.execute(() -> { - while (!taskQueue.isEmpty()) { - Message msg = taskQueue.poll(); - try { - List streamPushItems = JSON.parseArray(new String(msg.getBody()), RedisPushStreamMessage.class); - //查询全部的app+stream 用于判断是添加还是修改 - Map allAppAndStream = streamPushService.getAllAppAndStreamMap(); - Map allGBId = streamPushService.getAllGBId(); + } - /** - * 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 - */ - List streamPushItemForSave = new ArrayList<>(); - List streamPushItemForUpdate = new ArrayList<>(); - for (RedisPushStreamMessage pushStreamMessage : streamPushItems) { - String app = pushStreamMessage.getApp(); - String stream = pushStreamMessage.getStream(); - boolean contains = allAppAndStream.containsKey(app + stream); - //不存在就添加 - if (!contains) { - if (allGBId.containsKey(pushStreamMessage.getGbId())) { - StreamPush streamPushInDb = allGBId.get(pushStreamMessage.getGbId()); - log.warn("[REDIS消息-推流设备列表更新-INSERT] 国标编号重复: {}, 已分配给{}/{}", - streamPushInDb.getGbDeviceId(), streamPushInDb.getApp(), streamPushInDb.getStream()); - continue; - } - StreamPush streamPush = pushStreamMessage.buildstreamPush(); - streamPush.setCreateTime(DateUtil.getNow()); - streamPush.setUpdateTime(DateUtil.getNow()); - streamPush.setMediaServerId(mediaServerService.getDefaultMediaServer().getId()); - streamPushItemForSave.add(streamPush); - allGBId.put(streamPush.getGbDeviceId(), streamPush); - } else { - StreamPush streamPushForGbDeviceId = allGBId.get(pushStreamMessage.getGbId()); - if (streamPushForGbDeviceId != null - && (!streamPushForGbDeviceId.getApp().equals(pushStreamMessage.getApp()) - || !streamPushForGbDeviceId.getStream().equals(pushStreamMessage.getStream()))) { - StreamPush streamPushInDb = allGBId.get(pushStreamMessage.getGbId()); - log.warn("[REDIS消息-推流设备列表更新-UPDATE] 国标编号重复: {}, 已分配给{}/{}", - pushStreamMessage.getGbId(), streamPushInDb.getApp(), streamPushInDb.getStream()); - continue; - } - StreamPush streamPush = allAppAndStream.get(app + stream); - streamPush.setUpdateTime(DateUtil.getNow()); - streamPush.setGbDeviceId(pushStreamMessage.getGbId()); - streamPush.setGbName(pushStreamMessage.getName()); - streamPush.setGbStatus(pushStreamMessage.isStatus()?"ON":"OFF"); - //存在就只修改 name和gbId - streamPushItemForUpdate.add(streamPush); - } - } - if (!streamPushItemForSave.isEmpty()) { - log.info("添加{}条",streamPushItemForSave.size()); - log.info(JSONObject.toJSONString(streamPushItemForSave)); - streamPushService.batchAdd(streamPushItemForSave); + @Scheduled(fixedDelay = 100) + public void executeTaskQueue() { + if (taskQueue.isEmpty()) { + return; + } + List messageDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + Message msg = taskQueue.poll(); + if (msg != null) { + messageDataList.add(msg); + } + } + if (messageDataList.isEmpty()) { + return; + } + for (Message msg : messageDataList) { + try { + List streamPushItems = JSON.parseArray(new String(msg.getBody()), RedisPushStreamMessage.class); + //查询全部的app+stream 用于判断是添加还是修改 + Map allAppAndStream = streamPushService.getAllAppAndStreamMap(); + Map allGBId = streamPushService.getAllGBId(); + // 用于存储更具APP+Stream过滤后的数据,可以直接存入stream_push表与gb_stream表 + List streamPushItemForSave = new ArrayList<>(); + List streamPushItemForUpdate = new ArrayList<>(); + for (RedisPushStreamMessage pushStreamMessage : streamPushItems) { + String app = pushStreamMessage.getApp(); + String stream = pushStreamMessage.getStream(); + boolean contains = allAppAndStream.containsKey(app + stream); + //不存在就添加 + if (!contains) { + if (allGBId.containsKey(pushStreamMessage.getGbId())) { + StreamPush streamPushInDb = allGBId.get(pushStreamMessage.getGbId()); + log.warn("[REDIS消息-推流设备列表更新-INSERT] 国标编号重复: {}, 已分配给{}/{}", + streamPushInDb.getGbDeviceId(), streamPushInDb.getApp(), streamPushInDb.getStream()); + continue; } - if(!streamPushItemForUpdate.isEmpty()){ - log.info("修改{}条",streamPushItemForUpdate.size()); - log.info(JSONObject.toJSONString(streamPushItemForUpdate)); - streamPushService.batchUpdate(streamPushItemForUpdate); + StreamPush streamPush = pushStreamMessage.buildstreamPush(); + streamPush.setCreateTime(DateUtil.getNow()); + streamPush.setUpdateTime(DateUtil.getNow()); + streamPush.setMediaServerId(mediaServerService.getDefaultMediaServer().getId()); + streamPushItemForSave.add(streamPush); + allGBId.put(streamPush.getGbDeviceId(), streamPush); + } else { + StreamPush streamPushForGbDeviceId = allGBId.get(pushStreamMessage.getGbId()); + if (streamPushForGbDeviceId != null + && (!streamPushForGbDeviceId.getApp().equals(pushStreamMessage.getApp()) + || !streamPushForGbDeviceId.getStream().equals(pushStreamMessage.getStream()))) { + StreamPush streamPushInDb = allGBId.get(pushStreamMessage.getGbId()); + log.warn("[REDIS消息-推流设备列表更新-UPDATE] 国标编号重复: {}, 已分配给{}/{}", + pushStreamMessage.getGbId(), streamPushInDb.getApp(), streamPushInDb.getStream()); + continue; } - }catch (Exception e) { - log.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", new String(message.getBody())); - log.error("[REDIS消息-推流设备列表更新] 异常内容: ", e); + StreamPush streamPush = allAppAndStream.get(app + stream); + streamPush.setUpdateTime(DateUtil.getNow()); + streamPush.setGbDeviceId(pushStreamMessage.getGbId()); + streamPush.setGbName(pushStreamMessage.getName()); + streamPush.setGbStatus(pushStreamMessage.isStatus() ? "ON" : "OFF"); + //存在就只修改 name和gbId + streamPushItemForUpdate.add(streamPush); } } - }); + if (!streamPushItemForSave.isEmpty()) { + log.info("添加{}条", streamPushItemForSave.size()); + log.info(JSONObject.toJSONString(streamPushItemForSave)); + streamPushService.batchAdd(streamPushItemForSave); + + } + if (!streamPushItemForUpdate.isEmpty()) { + log.info("修改{}条", streamPushItemForUpdate.size()); + log.info(JSONObject.toJSONString(streamPushItemForUpdate)); + streamPushService.batchUpdate(streamPushItemForUpdate); + } + } catch (Exception e) { + log.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", new String(msg.getBody())); + log.error("[REDIS消息-推流设备列表更新] 异常内容: ", e); + } } + } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java index 47c93ecfe..f8a78dca7 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java @@ -7,16 +7,20 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; import org.springframework.stereotype.Component; import org.springframework.util.ObjectUtils; +import java.util.ArrayList; +import java.util.List; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentLinkedQueue; /** * 接收redis返回的推流结果 + * * @author lin * PUBLISH VM_MSG_STREAM_PUSH_RESPONSE '{"code":0,"msg":"失败","app":"1000","stream":"10000022"}' */ @@ -30,37 +34,49 @@ public class RedisPushStreamResponseListener implements MessageListener { @Autowired private ThreadPoolTaskExecutor taskExecutor; - private Map responseEvents = new ConcurrentHashMap<>(); + private final Map responseEvents = new ConcurrentHashMap<>(); - public interface PushStreamResponseEvent{ + public interface PushStreamResponseEvent { void run(MessageForPushChannelResponse response); } @Override public void onMessage(Message message, byte[] bytes) { - log.info("[REDIS消息-请求推流结果]: {}", new String(message.getBody())); - boolean isEmpty = taskQueue.isEmpty(); + log.info("[REDIS: 推流结果]: {}", new String(message.getBody())); taskQueue.offer(message); - if (isEmpty) { - taskExecutor.execute(() -> { - while (!taskQueue.isEmpty()) { - Message msg = taskQueue.poll(); - try { - MessageForPushChannelResponse response = JSON.parseObject(new String(msg.getBody()), MessageForPushChannelResponse.class); - if (response == null || ObjectUtils.isEmpty(response.getApp()) || ObjectUtils.isEmpty(response.getStream())){ - log.info("[REDIS消息-请求推流结果]:参数不全"); - continue; - } - // 查看正在等待的invite消息 - if (responseEvents.get(response.getApp() + response.getStream()) != null) { - responseEvents.get(response.getApp() + response.getStream()).run(response); - } - }catch (Exception e) { - log.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); - log.error("[REDIS消息-请求推流结果] 异常内容: ", e); - } + } + + @Scheduled(fixedDelay = 100) + public void executeTaskQueue() { + if (taskQueue.isEmpty()) { + return; + } + List messageDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + Message msg = taskQueue.poll(); + if (msg != null) { + messageDataList.add(msg); + } + } + if (messageDataList.isEmpty()) { + return; + } + for (Message msg : messageDataList) { + try { + MessageForPushChannelResponse response = JSON.parseObject(new String(msg.getBody()), MessageForPushChannelResponse.class); + if (response == null || ObjectUtils.isEmpty(response.getApp()) || ObjectUtils.isEmpty(response.getStream())) { + log.info("[REDIS消息-请求推流结果]:参数不全"); + continue; } - }); + // 查看正在等待的invite消息 + if (responseEvents.get(response.getApp() + response.getStream()) != null) { + responseEvents.get(response.getApp() + response.getStream()).run(response); + } + } catch (Exception e) { + log.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(msg)); + log.error("[REDIS消息-请求推流结果] 异常内容: ", e); + } } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java index 428f743f7..6f1b3d109 100755 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java @@ -9,19 +9,21 @@ import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.streamPush.service.IStreamPushService; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.beans.factory.annotation.Qualifier; import org.springframework.boot.ApplicationArguments; import org.springframework.boot.ApplicationRunner; import org.springframework.data.redis.connection.Message; import org.springframework.data.redis.connection.MessageListener; -import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor; +import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Component; +import java.util.ArrayList; +import java.util.List; import java.util.concurrent.ConcurrentLinkedQueue; /** * 接收redis发送的推流设备上线下线通知 + * * @author lin * 发送 PUBLISH VM_MSG_PUSH_STREAM_STATUS_CHANGE '{"setAllOffline":false,"offlineStreams":[{"app":"1000","stream":"10000022","timeStamp":1726729716551}]}' * 订阅 SUBSCRIBE VM_MSG_PUSH_STREAM_STATUS_CHANGE @@ -44,62 +46,75 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic private final ConcurrentLinkedQueue taskQueue = new ConcurrentLinkedQueue<>(); - @Qualifier("taskExecutor") - @Autowired - private ThreadPoolTaskExecutor taskExecutor; - @Override public void onMessage(Message message, byte[] bytes) { - boolean isEmpty = taskQueue.isEmpty(); - log.warn("[REDIS消息-推流设备状态变化]: {}", new String(message.getBody())); + log.info("[REDIS: 流设备状态变化]: {}", new String(message.getBody())); taskQueue.offer(message); + } - if (isEmpty) { - taskExecutor.execute(() -> { - while (!taskQueue.isEmpty()) { - Message msg = taskQueue.poll(); - try { - PushStreamStatusChangeFromRedisDto streamStatusMessage = JSON.parseObject(msg.getBody(), PushStreamStatusChangeFromRedisDto.class); - if (streamStatusMessage == null) { - log.warn("[REDIS消息]推流设备状态变化消息解析失败"); - continue; - } - // 取消定时任务 - dynamicTask.stop(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED); - if (streamStatusMessage.isSetAllOffline()) { - // 所有设备离线 - streamPushService.allOffline(); - } - if (streamStatusMessage.getOfflineStreams() != null - && !streamStatusMessage.getOfflineStreams().isEmpty()) { - // 更新部分设备离线 - streamPushService.offline(streamStatusMessage.getOfflineStreams()); - } - if (streamStatusMessage.getOnlineStreams() != null && - !streamStatusMessage.getOnlineStreams().isEmpty()) { - // 更新部分设备上线 - streamPushService.online(streamStatusMessage.getOnlineStreams()); - } - }catch (Exception e) { - log.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message)); - log.error("[REDIS消息-推流设备状态变化] 异常内容: ", e); - } + @Scheduled(fixedDelay = 100) + public void executeTaskQueue() { + if (taskQueue.isEmpty()) { + return; + } + List messageDataList = new ArrayList<>(); + int size = taskQueue.size(); + for (int i = 0; i < size; i++) { + Message msg = taskQueue.poll(); + if (msg != null) { + messageDataList.add(msg); + } + } + if (messageDataList.isEmpty()) { + return; + } + for (Message msg : messageDataList) { + try { + PushStreamStatusChangeFromRedisDto streamStatusMessage = JSON.parseObject(msg.getBody(), PushStreamStatusChangeFromRedisDto.class); + if (streamStatusMessage == null) { + log.warn("[REDIS消息]推流设备状态变化消息解析失败"); + continue; } - }); + // 取消定时任务 + dynamicTask.stop(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED); + if (streamStatusMessage.isSetAllOffline()) { + // 所有设备离线 + streamPushService.allOffline(); + } + if (streamStatusMessage.getOfflineStreams() != null + && !streamStatusMessage.getOfflineStreams().isEmpty()) { + // 更新部分设备离线 + streamPushService.offline(streamStatusMessage.getOfflineStreams()); + } + if (streamStatusMessage.getOnlineStreams() != null && + !streamStatusMessage.getOnlineStreams().isEmpty()) { + // 更新部分设备上线 + streamPushService.online(streamStatusMessage.getOnlineStreams()); + } + } catch (Exception e) { + log.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(msg)); + log.error("[REDIS消息-推流设备状态变化] 异常内容: ", e); + } } } @Override public void run(ApplicationArguments args) throws Exception { - if (!userSetting.isUsePushingAsStatus()) { - // 启动时设置所有推流通道离线,发起查询请求 - redisCatchStorage.sendStreamPushRequestedMsgForStatus(); - dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, ()->{ - log.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线"); - // 五秒收不到请求就设置通道离线,然后通知上级离线 - streamPushService.allOffline(); - }, 5000); + if (userSetting.getUsePushingAsStatus()) { + return; } + // 查询是否存在推流设备,没有则不发送 + List allAppAndStream = streamPushService.getAllAppAndStream(); + if (allAppAndStream == null || allAppAndStream.isEmpty()) { + return; + } + // 启动时设置所有推流通道离线,发起查询请求 + redisCatchStorage.sendStreamPushRequestedMsgForStatus(); + dynamicTask.startDelay(VideoManagerConstants.VM_MSG_GET_ALL_ONLINE_REQUESTED, () -> { + log.info("[REDIS消息]未收到redis回复推流设备状态,执行推流设备离线"); + // 五秒收不到请求就设置通道离线,然后通知上级离线 + streamPushService.allOffline(); + }, 5000); } } diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java index dde2860a5..b74df842f 100644 --- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java +++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/control/RedisRpcController.java @@ -10,16 +10,13 @@ import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcRequest; import com.genersoft.iot.vmp.conf.redis.bean.RedisRpcResponse; import com.genersoft.iot.vmp.gb28181.bean.SendRtpInfo; import com.genersoft.iot.vmp.gb28181.session.SSRCFactory; -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform; import com.genersoft.iot.vmp.media.bean.MediaInfo; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookType; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; import com.genersoft.iot.vmp.service.ISendRtpServerService; -import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; import com.genersoft.iot.vmp.vmanager.bean.WVPResult; import lombok.extern.slf4j.Slf4j; @@ -41,10 +38,7 @@ public class RedisRpcController { private IMediaServerService mediaServerService; @Autowired - private SendRtpPortManager sendRtpPortManager; - - @Autowired - private IRedisCatchStorage redisCatchStorage; + private ISendRtpServerService sendRtpServerService; @Autowired private UserSetting userSetting; @@ -56,13 +50,6 @@ public class RedisRpcController { private RedisTemplate redisTemplate; - @Autowired - private ISIPCommanderForPlatform commanderFroPlatform; - - @Autowired - private ISendRtpServerService sendRtpServerService; - - /** * 获取发流的信息 */ @@ -83,7 +70,7 @@ public class RedisRpcController { response.setStatusCode(200); } // 自平台内容 - int localPort = sendRtpPortManager.getNextPort(mediaServerItem); + int localPort = sendRtpServerService.getNextPort(mediaServerItem); if (localPort == 0) { log.info("[redis-rpc] getSendRtpItem->服务器端口资源不足" ); RedisRpcResponse response = request.getResponse(); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java index 6d7051010..238134a9b 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/IRedisCatchStorage.java @@ -28,10 +28,6 @@ public interface IRedisCatchStorage { void delPlatformCatchInfo(String platformGbId); - void delPlatformKeepalive(String platformGbId); - - void delPlatformRegister(String platformGbId); - void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo); PlatformRegisterInfo queryPlatformRegisterInfo(String callId); @@ -98,12 +94,9 @@ public interface IRedisCatchStorage { void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo); GPSMsgInfo getGpsMsgInfo(String gbId); + List getAllGpsMsgInfo(); - Long getSN(String method); - - void resetAllSN(); - MediaInfo getStreamInfo(String app, String streamId, String mediaServerId); MediaInfo getProxyStream(String app, String streamId); diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java deleted file mode 100755 index f1c45850f..000000000 --- a/src/main/java/com/genersoft/iot/vmp/storager/dao/LogMapper.java +++ /dev/null @@ -1,37 +0,0 @@ -package com.genersoft.iot.vmp.storager.dao; - -import com.genersoft.iot.vmp.storager.dao.dto.LogDto; -import org.apache.ibatis.annotations.Delete; -import org.apache.ibatis.annotations.Insert; -import org.apache.ibatis.annotations.Mapper; -import org.apache.ibatis.annotations.Param; -import org.apache.ibatis.annotations.Select; -import org.springframework.stereotype.Repository; - -import java.util.List; - -/** - * 用于存储设服务的日志 - */ -@Mapper -@Repository -public interface LogMapper { - - @Insert("insert into wvp_log ( name,type,uri,address,result,timing,username,create_time) " + - "values (#{name}, #{type}, #{uri}, #{address}, #{result}, #{timing}, #{username}, #{createTime})") - int add(LogDto logDto); - - @Select(value = {""}) - List query(@Param("query") String query, @Param("type") String type, @Param("startTime") String startTime, @Param("endTime") String endTime); - - @Delete("DELETE FROM wvp_log") - int clear(); -} diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java index 81c8b3dad..be73a17cb 100755 --- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java @@ -66,34 +66,12 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { return result; } - @Override - public Long getSN(String method) { - String key = VideoManagerConstants.SIP_SN_PREFIX + userSetting.getServerId() + "_" + method; - - Long result = redisTemplate.opsForValue().increment(key, 1L); - if (result != null && result > Integer.MAX_VALUE) { - redisTemplate.opsForValue().set(key, 1); - result = 1L; - } - return result; - } - @Override public void resetAllCSEQ() { String key = VideoManagerConstants.SIP_CSEQ_PREFIX + userSetting.getServerId(); redisTemplate.opsForValue().set(key, 1); } - @Override - public void resetAllSN() { - String scanKey = VideoManagerConstants.SIP_SN_PREFIX + userSetting.getServerId() + "_*"; - List keys = RedisUtil.scan(redisTemplate, scanKey); - for (Object o : keys) { - String key = (String) o; - redisTemplate.opsForValue().set(key, 1); - } - } - @Override public void updatePlatformCatchInfo(PlatformCatch parentPlatformCatch) { String key = VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + parentPlatformCatch.getId(); @@ -110,17 +88,6 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { redisTemplate.delete(VideoManagerConstants.PLATFORM_CATCH_PREFIX + userSetting.getServerId() + "_" + platformGbId); } - @Override - public void delPlatformKeepalive(String platformGbId) { - redisTemplate.delete(VideoManagerConstants.PLATFORM_KEEPALIVE_PREFIX + userSetting.getServerId() + "_" + platformGbId); - } - - @Override - public void delPlatformRegister(String platformGbId) { - redisTemplate.delete(VideoManagerConstants.PLATFORM_REGISTER_PREFIX + userSetting.getServerId() + "_" + platformGbId); - } - - @Override public void updatePlatformRegisterInfo(String callId, PlatformRegisterInfo platformRegisterInfo) { String key = VideoManagerConstants.PLATFORM_REGISTER_INFO_PREFIX + userSetting.getServerId() + "_" + callId; @@ -159,7 +126,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { public void addStream(MediaServer mediaServerItem, String type, String app, String streamId, MediaInfo mediaInfo) { // 查找是否使用了callID StreamAuthorityInfo streamAuthorityInfo = getStreamAuthorityInfo(app, streamId); - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerItem.getId(); + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type.toUpperCase() + "_" + app + "_" + streamId + "_" + mediaServerItem.getId(); if (streamAuthorityInfo != null) { mediaInfo.setCallId(streamAuthorityInfo.getCallId()); } @@ -168,13 +135,13 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void removeStream(String mediaServerId, String type, String app, String streamId) { - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_" + app + "_" + streamId + "_" + mediaServerId; + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type.toUpperCase() + "_" + app + "_" + streamId + "_" + mediaServerId; redisTemplate.delete(key); } @Override public void removeStream(String mediaServerId, String type) { - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_*_*_" + mediaServerId; + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type.toUpperCase() + "_*_*_" + mediaServerId; List streams = RedisUtil.scan(redisTemplate, key); for (Object stream : streams) { redisTemplate.delete(stream); @@ -184,7 +151,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public List getStreams(String mediaServerId, String type) { List result = new ArrayList<>(); - String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type + "_*_*_" + mediaServerId; + String key = VideoManagerConstants.WVP_SERVER_STREAM_PREFIX + userSetting.getServerId() + "_" + type.toUpperCase() + "_*_*_" + mediaServerId; List streams = RedisUtil.scan(redisTemplate, key); for (Object stream : streams) { MediaInfo mediaInfo = (MediaInfo)redisTemplate.opsForValue().get(stream); @@ -195,112 +162,106 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public void updateDevice(Device device) { - String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + device.getDeviceId(); - redisTemplate.opsForValue().set(key, device); + String key = VideoManagerConstants.DEVICE_PREFIX; + redisTemplate.opsForHash().put(key, device.getDeviceId(), device); } @Override public void removeDevice(String deviceId) { - String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; - redisTemplate.delete(key); + String key = VideoManagerConstants.DEVICE_PREFIX; + redisTemplate.opsForHash().delete(key, deviceId); } @Override public void removeAllDevice() { - String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*"; - List keys = RedisUtil.scan(redisTemplate, scanKey); - for (Object key : keys) { - redisTemplate.delete(key); - } + String key = VideoManagerConstants.DEVICE_PREFIX; + redisTemplate.delete(key); } @Override public List getAllDevices() { - String scanKey = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_*"; + String key = VideoManagerConstants.DEVICE_PREFIX; List result = new ArrayList<>(); - List keys = RedisUtil.scan(redisTemplate, scanKey); - for (Object o : keys) { - String key = (String) o; - Device device = JsonUtil.redisJsonToObject(redisTemplate, key, Device.class); - if (Objects.nonNull(device)) { - // 只取没有存过得 - result.add(JsonUtil.redisJsonToObject(redisTemplate, key, Device.class)); + List values = redisTemplate.opsForHash().values(key); + for (Object value : values) { + if (Objects.nonNull(value)) { + result.add((Device)value); } } - return result; } @Override public Device getDevice(String deviceId) { - String key = VideoManagerConstants.DEVICE_PREFIX + userSetting.getServerId() + "_" + deviceId; - Device device = JsonUtil.redisJsonToObject(redisTemplate, key, Device.class); - if (device == null){ + String key = VideoManagerConstants.DEVICE_PREFIX; + Device device; + Object object = redisTemplate.opsForHash().get(key, deviceId); + if (object == null){ device = deviceMapper.getDeviceByDeviceId(deviceId); if (device != null) { updateDevice(device); } + }else { + device = (Device)object; } return device; } @Override public void updateGpsMsgInfo(GPSMsgInfo gpsMsgInfo) { - String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + gpsMsgInfo.getId(); + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId(); Duration duration = Duration.ofSeconds(60L); - redisTemplate.opsForValue().set(key, gpsMsgInfo, duration); + redisTemplate.opsForHash().put(key, gpsMsgInfo.getId(),gpsMsgInfo); + redisTemplate.expire(key, duration); // 默认GPS消息保存1分钟 } @Override public GPSMsgInfo getGpsMsgInfo(String channelId) { - String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_" + channelId; - return JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class); + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId(); + return (GPSMsgInfo) redisTemplate.opsForHash().get(key, channelId); } @Override public List getAllGpsMsgInfo() { - String scanKey = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId() + "_*"; + String key = VideoManagerConstants.WVP_STREAM_GPS_MSG_PREFIX + userSetting.getServerId(); List result = new ArrayList<>(); - List keys = RedisUtil.scan(redisTemplate, scanKey); - for (Object o : keys) { - String key = (String) o; - GPSMsgInfo gpsMsgInfo = JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class); - if (Objects.nonNull(gpsMsgInfo) && !gpsMsgInfo.isStored()) { // 只取没有存过得 - result.add(JsonUtil.redisJsonToObject(redisTemplate, key, GPSMsgInfo.class)); - } + List values = redisTemplate.opsForHash().values(key); + for (Object value : values) { + result.add((GPSMsgInfo)value); } - return result; } @Override public void updateStreamAuthorityInfo(String app, String stream, StreamAuthorityInfo streamAuthorityInfo) { - String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream; - redisTemplate.opsForValue().set(key, streamAuthorityInfo); + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId(); + String objectKey = app+ "_" + stream; + redisTemplate.opsForHash().put(key, objectKey, streamAuthorityInfo); } @Override public void removeStreamAuthorityInfo(String app, String stream) { - String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; - redisTemplate.delete(key); + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId(); + String objectKey = app+ "_" + stream; + redisTemplate.opsForHash().delete(key, objectKey); } @Override public StreamAuthorityInfo getStreamAuthorityInfo(String app, String stream) { - String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_" + app+ "_" + stream ; - return JsonUtil.redisJsonToObject(redisTemplate, key, StreamAuthorityInfo.class); + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId(); + String objectKey = app+ "_" + stream; + return (StreamAuthorityInfo)redisTemplate.opsForHash().get(key, objectKey); } @Override public List getAllStreamAuthorityInfo() { - String scanKey = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId() + "_*_*" ; + String key = VideoManagerConstants.MEDIA_STREAM_AUTHORITY + userSetting.getServerId(); List result = new ArrayList<>(); - List keys = RedisUtil.scan(redisTemplate, scanKey); - for (Object o : keys) { - String key = (String) o; - result.add(JsonUtil.redisJsonToObject(redisTemplate, key, StreamAuthorityInfo.class)); + List values = redisTemplate.opsForHash().values(key); + for (Object value : values) { + result.add((StreamAuthorityInfo)value); } return result; } @@ -458,9 +419,8 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage { @Override public int getGbSendCount(String id) { - String key = VideoManagerConstants.SEND_RTP_INFO - + userSetting.getServerId() + "_*_" + id + "_*"; - return RedisUtil.scan(redisTemplate, key).size(); + String key = VideoManagerConstants.SEND_RTP_INFO_CALLID; + return redisTemplate.opsForHash().size(key).intValue(); } @Override diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyService.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyService.java index f612abd94..1d848b64d 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyService.java +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/IStreamProxyService.java @@ -64,17 +64,17 @@ public interface IStreamProxyService { /** * 新的节点加入 - * @param mediaServerId + * @param mediaServer * @return */ - void zlmServerOnline(String mediaServerId); + void zlmServerOnline(MediaServer mediaServer); /** * 节点离线 - * @param mediaServerId + * @param mediaServer * @return */ - void zlmServerOffline(String mediaServerId); + void zlmServerOffline(MediaServer mediaServer); /** * 更新代理流 diff --git a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java index c5909c0bf..3721edb1e 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/streamProxy/service/impl/StreamProxyServiceImpl.java @@ -121,7 +121,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @EventListener @Transactional public void onApplicationEvent(MediaServerOnlineEvent event) { - zlmServerOnline(event.getMediaServerId()); + zlmServerOnline(event.getMediaServer()); } /** @@ -131,7 +131,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @EventListener @Transactional public void onApplicationEvent(MediaServerOfflineEvent event) { - zlmServerOffline(event.getMediaServerId()); + zlmServerOffline(event.getMediaServer()); } @@ -284,15 +284,14 @@ public class StreamProxyServiceImpl implements IStreamProxyService { @Override @Transactional - public void zlmServerOnline(String mediaServerId) { - MediaServer mediaServer = mediaServerService.getOne(mediaServerId); + public void zlmServerOnline(MediaServer mediaServer) { if (mediaServer == null) { return; } // 这里主要是控制数据库/redis缓存/以及zlm中存在的代理流 三者状态一致。以数据库中数据为根本 - redisCatchStorage.removeStream(mediaServerId, "pull"); + redisCatchStorage.removeStream(mediaServer.getId(), "PULL"); - List streamProxies = streamProxyMapper.selectForEnableInMediaServer(mediaServerId, true); + List streamProxies = streamProxyMapper.selectForEnableInMediaServer(mediaServer.getId(), true); if (streamProxies.isEmpty()) { return; } @@ -359,11 +358,11 @@ public class StreamProxyServiceImpl implements IStreamProxyService { } @Override - public void zlmServerOffline(String mediaServerId) { - List streamProxies = streamProxyMapper.selectForEnableInMediaServer(mediaServerId, true); + public void zlmServerOffline(MediaServer mediaServer) { + List streamProxies = streamProxyMapper.selectForEnableInMediaServer(mediaServer.getId(), true); // 清理redis相关的缓存 - redisCatchStorage.removeStream(mediaServerId, "pull"); + redisCatchStorage.removeStream(mediaServer.getId(), "PULL"); if (streamProxies.isEmpty()) { return; @@ -395,7 +394,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService { jsonObject.put("app", streamProxy.getApp()); jsonObject.put("stream", streamProxy.getStream()); jsonObject.put("register", false); - jsonObject.put("mediaServerId", mediaServerId); + jsonObject.put("mediaServerId", mediaServer); redisCatchStorage.sendStreamChangeMsg("pull", jsonObject); } } diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushService.java b/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushService.java index 23dae58c3..56bde718b 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushService.java +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/service/IStreamPushService.java @@ -1,5 +1,7 @@ package com.genersoft.iot.vmp.streamPush.service; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; import com.genersoft.iot.vmp.streamPush.bean.StreamPush; import com.genersoft.iot.vmp.vmanager.bean.ResourceBaseInfo; @@ -35,12 +37,12 @@ public interface IStreamPushService { /** * 新的节点加入 */ - void zlmServerOnline(String mediaServerId); + void zlmServerOnline(MediaServer mediaServer); /** * 节点离线 */ - void zlmServerOffline(String mediaServerId); + void zlmServerOffline(MediaServer mediaServer); /** * 批量添加 @@ -97,4 +99,6 @@ public interface IStreamPushService { int delete(int id); void batchRemove(Set ids); + + void updateGPSFromGPSMsgInfo(List gpsMsgInfoList); } diff --git a/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushServiceImpl.java index aeabcc839..15028e982 100755 --- a/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushServiceImpl.java +++ b/src/main/java/com/genersoft/iot/vmp/streamPush/service/impl/StreamPushServiceImpl.java @@ -17,6 +17,7 @@ import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.media.zlm.dto.StreamAuthorityInfo; import com.genersoft.iot.vmp.media.zlm.dto.hook.OriginType; import com.genersoft.iot.vmp.service.ISendRtpServerService; +import com.genersoft.iot.vmp.service.bean.GPSMsgInfo; import com.genersoft.iot.vmp.service.bean.StreamPushItemFromRedis; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.streamPush.bean.StreamPush; @@ -158,7 +159,7 @@ public class StreamPushServiceImpl implements IStreamPushService { @EventListener @Transactional public void onApplicationEvent(MediaServerOnlineEvent event) { - zlmServerOnline(event.getMediaServerId()); + zlmServerOnline(event.getMediaServer()); } /** @@ -168,7 +169,7 @@ public class StreamPushServiceImpl implements IStreamPushService { @EventListener @Transactional public void onApplicationEvent(MediaServerOfflineEvent event) { - zlmServerOffline(event.getMediaServerId()); + zlmServerOffline(event.getMediaServer()); } @Override @@ -283,7 +284,7 @@ public class StreamPushServiceImpl implements IStreamPushService { mediaServerService.closeStreams(mediaServer, streamPush.getApp(), streamPush.getStream()); } streamPush.setPushing(false); - if (userSetting.isUsePushingAsStatus()) { + if (userSetting.getUsePushingAsStatus()) { CommonGBChannel commonGBChannel = streamPush.buildCommonGBChannel(); if (commonGBChannel != null) { gbChannelService.offline(commonGBChannel); @@ -309,17 +310,16 @@ public class StreamPushServiceImpl implements IStreamPushService { @Override @Transactional - public void zlmServerOnline(String mediaServerId) { + public void zlmServerOnline(MediaServer mediaServer) { // 同步zlm推流信息 - MediaServer mediaServerItem = mediaServerService.getOne(mediaServerId); - if (mediaServerItem == null) { + if (mediaServer == null) { return; } // 数据库记录 - List pushList = getPushList(mediaServerId); + List pushList = getPushList(mediaServer.getId()); Map pushItemMap = new HashMap<>(); // redis记录 - List mediaInfoList = redisCatchStorage.getStreams(mediaServerId, "PUSH"); + List mediaInfoList = redisCatchStorage.getStreams(mediaServer.getId(), "PUSH"); Map streamInfoPushItemMap = new HashMap<>(); if (!pushList.isEmpty()) { for (StreamPush streamPushItem : pushList) { @@ -339,7 +339,7 @@ public class StreamPushServiceImpl implements IStreamPushService { for (StreamAuthorityInfo streamAuthorityInfo : allStreamAuthorityInfo) { streamAuthorityInfoInfoMap.put(streamAuthorityInfo.getApp() + streamAuthorityInfo.getStream(), streamAuthorityInfo); } - List mediaList = mediaServerService.getMediaList(mediaServerItem, null, null, null); + List mediaList = mediaServerService.getMediaList(mediaServer, null, null, null); if (mediaList == null) { return; } @@ -367,12 +367,12 @@ public class StreamPushServiceImpl implements IStreamPushService { jsonObject.put("app", mediaInfo.getApp()); jsonObject.put("stream", mediaInfo.getStream()); jsonObject.put("register", false); - jsonObject.put("mediaServerId", mediaServerId); + jsonObject.put("mediaServerId", mediaServer.getId()); redisCatchStorage.sendStreamChangeMsg(type, jsonObject); // 移除redis内流的信息 - redisCatchStorage.removeStream(mediaServerItem.getId(), "PUSH", mediaInfo.getApp(), mediaInfo.getStream()); + redisCatchStorage.removeStream(mediaServer.getId(), "PUSH", mediaInfo.getApp(), mediaInfo.getStream()); // 冗余数据,自己系统中自用 - redisCatchStorage.removePushListItem(mediaInfo.getApp(), mediaInfo.getStream(), mediaServerItem.getId()); + redisCatchStorage.removePushListItem(mediaInfo.getApp(), mediaInfo.getStream(), mediaServer.getId()); } } @@ -387,8 +387,8 @@ public class StreamPushServiceImpl implements IStreamPushService { @Override @Transactional - public void zlmServerOffline(String mediaServerId) { - List streamPushItems = streamPushMapper.selectAllByMediaServerId(mediaServerId); + public void zlmServerOffline(MediaServer mediaServer) { + List streamPushItems = streamPushMapper.selectAllByMediaServerId(mediaServer.getId()); if (!streamPushItems.isEmpty()) { for (StreamPush streamPushItem : streamPushItems) { stop(streamPushItem); @@ -402,21 +402,21 @@ public class StreamPushServiceImpl implements IStreamPushService { // 发送流停止消息 String type = "PUSH"; // 发送redis消息 - List mediaInfoList = redisCatchStorage.getStreams(mediaServerId, type); + List mediaInfoList = redisCatchStorage.getStreams(mediaServer.getId(), type); if (!mediaInfoList.isEmpty()) { for (MediaInfo mediaInfo : mediaInfoList) { // 移除redis内流的信息 - redisCatchStorage.removeStream(mediaServerId, type, mediaInfo.getApp(), mediaInfo.getStream()); + redisCatchStorage.removeStream(mediaServer.getId(), type, mediaInfo.getApp(), mediaInfo.getStream()); JSONObject jsonObject = new JSONObject(); jsonObject.put("serverId", userSetting.getServerId()); jsonObject.put("app", mediaInfo.getApp()); jsonObject.put("stream", mediaInfo.getStream()); jsonObject.put("register", false); - jsonObject.put("mediaServerId", mediaServerId); + jsonObject.put("mediaServerId", mediaServer.getId()); redisCatchStorage.sendStreamChangeMsg(type, jsonObject); // 冗余数据,自己系统中自用 - redisCatchStorage.removePushListItem(mediaInfo.getApp(), mediaInfo.getStream(), mediaServerId); + redisCatchStorage.removePushListItem(mediaInfo.getApp(), mediaInfo.getStream(), mediaServer.getId()); } } } @@ -474,7 +474,7 @@ public class StreamPushServiceImpl implements IStreamPushService { @Override public ResourceBaseInfo getOverview() { int total = streamPushMapper.getAllCount(); - int online = streamPushMapper.getAllPushing(userSetting.isUsePushingAsStatus()); + int online = streamPushMapper.getAllPushing(userSetting.getUsePushingAsStatus()); return new ResourceBaseInfo(total, online); } @@ -500,7 +500,7 @@ public class StreamPushServiceImpl implements IStreamPushService { @Transactional public void updatePushStatus(StreamPush streamPush, boolean pushIng) { streamPush.setPushing(pushIng); - if (userSetting.isUsePushingAsStatus()) { + if (userSetting.getUsePushingAsStatus()) { streamPush.setGbStatus(pushIng?"ON":"OFF"); } streamPush.setPushTime(DateUtil.getNow()); @@ -508,7 +508,7 @@ public class StreamPushServiceImpl implements IStreamPushService { if (ObjectUtils.isEmpty(streamPush.getGbDeviceId())) { return; } - if (userSetting.isUsePushingAsStatus()) { + if (userSetting.getUsePushingAsStatus()) { if ("ON".equalsIgnoreCase(streamPush.getGbStatus()) ) { gbChannelService.online(streamPush.buildCommonGBChannel()); }else { @@ -583,4 +583,17 @@ public class StreamPushServiceImpl implements IStreamPushService { streamPushMapper.batchDel(streamPushList); gbChannelService.delete(ids); } + + @Override + public void updateGPSFromGPSMsgInfo(List gpsMsgInfoList) { + List channels = new ArrayList<>(); + for (GPSMsgInfo gpsMsgInfo : gpsMsgInfoList) { + CommonGBChannel channel = new CommonGBChannel(); + channel.setGbDeviceId(gpsMsgInfo.getId()); + channel.setGbLongitude(gpsMsgInfo.getLng()); + channel.setGbLatitude(gpsMsgInfo.getLat()); + channels.add(channel); + } + gbChannelService.updateGpsByDeviceIdForStreamPush(channels); + } } diff --git a/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java b/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java index de8675f12..46c71b4bb 100755 --- a/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java +++ b/src/main/java/com/genersoft/iot/vmp/utils/JsonUtil.java @@ -31,4 +31,12 @@ public final class JsonUtil { } return clazz.cast(jsonObject); } + + public static T redisHashJsonToObject(RedisTemplate redisTemplate, String key, String objKey, Class clazz) { + Object jsonObject = redisTemplate.opsForHash().get(key, objKey); + if (Objects.isNull(jsonObject)) { + return null; + } + return clazz.cast(jsonObject); + } } \ No newline at end of file diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/TestController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/TestController.java index ec4f126b3..6df13ba79 100644 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/TestController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/TestController.java @@ -1,16 +1,21 @@ package com.genersoft.iot.vmp.vmanager; -import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.common.InviteInfo; +import com.genersoft.iot.vmp.common.InviteSessionType; +import com.genersoft.iot.vmp.common.VideoManagerConstants; import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.security.SecurityRequirement; import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.data.redis.core.Cursor; +import org.springframework.data.redis.core.RedisTemplate; +import org.springframework.data.redis.core.ScanOptions; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import java.util.ArrayList; import java.util.List; +import java.util.Map; @RestController @RequestMapping("/api/test") @@ -19,13 +24,42 @@ public class TestController { @Autowired private HookSubscribe subscribe; + @Autowired + private RedisTemplate redisTemplate; @GetMapping("/hook/list") - @Operation(summary = "查询角色", security = @SecurityRequirement(name = JwtUtils.HEADER)) public List all(){ return subscribe.getAll(); } + + @GetMapping("/redis") + public List redis(){ + InviteSessionType type = InviteSessionType.PLAY; + String channelId = null; + String stream = null; + + String key = VideoManagerConstants.INVITE_PREFIX; + String keyPattern = (type != null ? type : "*") + + ":" + (channelId != null ? channelId : "*") + + ":" + (stream != null ? stream : "*") + + ":*"; + ScanOptions options = ScanOptions.scanOptions().match(keyPattern).count(20).build(); + Cursor> cursor = redisTemplate.opsForHash().scan(key, options); + List result = new ArrayList<>(); + try { + while (cursor.hasNext()) { + System.out.println(cursor.next().getKey()); + result.add((InviteInfo) cursor.next().getValue()); + } + }catch (Exception e) { + + }finally { + cursor.close(); + } + return result; + } + // @Bean // public ServletRegistrationBean druidStatViewServlet() { // ServletRegistrationBean registrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*"); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SystemConfigInfo.java b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SystemConfigInfo.java index f5a52b973..20a046cda 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SystemConfigInfo.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/bean/SystemConfigInfo.java @@ -3,8 +3,9 @@ package com.genersoft.iot.vmp.vmanager.bean; import com.genersoft.iot.vmp.common.VersionPo; import com.genersoft.iot.vmp.conf.SipConfig; import com.genersoft.iot.vmp.conf.UserSetting; -import com.genersoft.iot.vmp.conf.VersionInfo; +import lombok.Data; +@Data public class SystemConfigInfo { private int serverPort; @@ -12,36 +13,5 @@ public class SystemConfigInfo { private UserSetting addOn; private VersionPo version; - public int getServerPort() { - return serverPort; - } - - public void setServerPort(int serverPort) { - this.serverPort = serverPort; - } - - public SipConfig getSip() { - return sip; - } - - public void setSip(SipConfig sip) { - this.sip = sip; - } - - public UserSetting getAddOn() { - return addOn; - } - - public void setAddOn(UserSetting addOn) { - this.addOn = addOn; - } - - public VersionPo getVersion() { - return version; - } - - public void setVersion(VersionPo version) { - this.version = version; - } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java index f19a9e0b0..78f4fef2c 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/cloudRecord/CloudRecordController.java @@ -3,9 +3,9 @@ package com.genersoft.iot.vmp.vmanager.cloudRecord; import com.alibaba.fastjson2.JSONArray; import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService; import com.genersoft.iot.vmp.media.bean.MediaServer; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService; import com.genersoft.iot.vmp.service.bean.CloudRecordItem; import com.genersoft.iot.vmp.service.bean.DownloadFileInfo; import com.genersoft.iot.vmp.utils.DateUtil; @@ -55,16 +55,10 @@ public class CloudRecordController { @Parameter(name = "year", description = "年,置空则查询当年", required = false) @Parameter(name = "month", description = "月,置空则查询当月", required = false) @Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部", required = false) - public List openRtpServer( - @RequestParam(required = true) String app, - @RequestParam(required = true) String stream, - @RequestParam(required = false) int year, - @RequestParam(required = false) int month, - @RequestParam(required = false) String mediaServerId + public List openRtpServer(@RequestParam(required = true) String app, @RequestParam(required = true) String stream, @RequestParam(required = false) Integer year, @RequestParam(required = false) Integer month, @RequestParam(required = false) String mediaServerId ) { - log.info("[云端录像] 查询存在云端录像的日期 app->{}, stream->{}, mediaServerId->{}, year->{}, month->{}", - app, stream, mediaServerId, year, month); + log.info("[云端录像] 查询存在云端录像的日期 app->{}, stream->{}, mediaServerId->{}, year->{}, month->{}", app, stream, mediaServerId, year, month); Calendar calendar = Calendar.getInstance(); if (ObjectUtils.isEmpty(year)) { year = calendar.get(Calendar.YEAR); @@ -102,20 +96,10 @@ public class CloudRecordController { @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false) @Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部流媒体", required = false) @Parameter(name = "callId", description = "每次录像的唯一标识,置空则查询全部流媒体", required = false) - public PageInfo openRtpServer( - @RequestParam(required = false) String query, - @RequestParam(required = false) String app, - @RequestParam(required = false) String stream, - @RequestParam int page, - @RequestParam int count, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime, - @RequestParam(required = false) String mediaServerId, - @RequestParam(required = false) String callId + public PageInfo openRtpServer(@RequestParam(required = false) String query, @RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam int page, @RequestParam int count, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String callId ) { - log.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", - app, stream, mediaServerId, page, count, startTime, endTime, callId); + log.info("[云端录像] 查询 app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, page, count, startTime, endTime, callId); List mediaServers; if (!ObjectUtils.isEmpty(mediaServerId)) { @@ -162,25 +146,16 @@ public class CloudRecordController { @Parameter(name = "endTime", description = "鉴权ID", required = false) @Parameter(name = "callId", description = "鉴权ID", required = false) @Parameter(name = "remoteHost", description = "返回地址时的远程地址", required = false) - public String addTask( - HttpServletRequest request, - @RequestParam(required = false) String app, - @RequestParam(required = false) String stream, - @RequestParam(required = false) String mediaServerId, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime, - @RequestParam(required = false) String callId, - @RequestParam(required = false) String remoteHost - ){ + public String addTask(HttpServletRequest request, @RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String callId, @RequestParam(required = false) String remoteHost) { MediaServer mediaServer; if (mediaServerId == null) { mediaServer = mediaServerService.getDefaultMediaServer(); - }else { + } else { mediaServer = mediaServerService.getOne(mediaServerId); } if (mediaServer == null) { throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到可用的流媒体"); - }else { + } else { if (remoteHost == null) { remoteHost = request.getScheme() + "://" + mediaServer.getIp() + ":" + mediaServer.getRecordAssistPort(); } @@ -194,20 +169,12 @@ public class CloudRecordController { @Parameter(name = "taskId", description = "任务Id", required = false) @Parameter(name = "mediaServerId", description = "流媒体ID", required = false) @Parameter(name = "isEnd", description = "是否结束", required = false) - public JSONArray queryTaskList( - HttpServletRequest request, - @RequestParam(required = false) String app, - @RequestParam(required = false) String stream, - @RequestParam(required = false) String callId, - @RequestParam(required = false) String taskId, - @RequestParam(required = false) String mediaServerId, - @RequestParam(required = false) Boolean isEnd - ){ - if (ObjectUtils.isEmpty(mediaServerId)) { - mediaServerId = null; - } + public JSONArray queryTaskList(HttpServletRequest request, @RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam(required = false) String callId, @RequestParam(required = false) String taskId, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) Boolean isEnd) { + if (ObjectUtils.isEmpty(mediaServerId)) { + mediaServerId = null; + } - return cloudRecordService.queryTask(app, stream, callId, taskId, mediaServerId, isEnd, request.getScheme()); + return cloudRecordService.queryTask(app, stream, callId, taskId, mediaServerId, isEnd, request.getScheme()); } @ResponseBody @@ -220,20 +187,11 @@ public class CloudRecordController { @Parameter(name = "endTime", description = "鉴权ID", required = false) @Parameter(name = "callId", description = "鉴权ID", required = false) @Parameter(name = "recordId", description = "录像记录的ID,用于精准收藏一个视频文件", required = false) - public int addCollect( - @RequestParam(required = false) String app, - @RequestParam(required = false) String stream, - @RequestParam(required = false) String mediaServerId, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime, - @RequestParam(required = false) String callId, - @RequestParam(required = false) Integer recordId - ){ - log.info("[云端录像] 添加收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}", - app, stream, mediaServerId, startTime, endTime, callId, recordId); + public int addCollect(@RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String callId, @RequestParam(required = false) Integer recordId) { + log.info("[云端录像] 添加收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}", app, stream, mediaServerId, startTime, endTime, callId, recordId); if (recordId != null) { return cloudRecordService.changeCollectById(recordId, true); - }else { + } else { return cloudRecordService.changeCollect(true, app, stream, mediaServerId, startTime, endTime, callId); } } @@ -248,20 +206,11 @@ public class CloudRecordController { @Parameter(name = "endTime", description = "鉴权ID", required = false) @Parameter(name = "callId", description = "鉴权ID", required = false) @Parameter(name = "recordId", description = "录像记录的ID,用于精准精准移除一个视频文件的收藏", required = false) - public int deleteCollect( - @RequestParam(required = false) String app, - @RequestParam(required = false) String stream, - @RequestParam(required = false) String mediaServerId, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime, - @RequestParam(required = false) String callId, - @RequestParam(required = false) Integer recordId - ){ - log.info("[云端录像] 移除收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}", - app, stream, mediaServerId, startTime, endTime, callId, recordId); + public int deleteCollect(@RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String callId, @RequestParam(required = false) Integer recordId) { + log.info("[云端录像] 移除收藏,app={},stream={},mediaServerId={},startTime={},endTime={},callId={},recordId={}", app, stream, mediaServerId, startTime, endTime, callId, recordId); if (recordId != null) { return cloudRecordService.changeCollectById(recordId, false); - }else { + } else { return cloudRecordService.changeCollect(false, app, stream, mediaServerId, startTime, endTime, callId); } } @@ -270,9 +219,7 @@ public class CloudRecordController { @GetMapping("/play/path") @Operation(summary = "获取播放地址") @Parameter(name = "recordId", description = "录像记录的ID", required = true) - public DownloadFileInfo getPlayUrlPath( - @RequestParam(required = true) Integer recordId - ){ + public DownloadFileInfo getPlayUrlPath(@RequestParam(required = true) Integer recordId) { return cloudRecordService.getPlayUrlPath(recordId); } @@ -291,20 +238,10 @@ public class CloudRecordController { */ @ResponseBody @GetMapping("/zip") - public void downloadZipFile( - HttpServletResponse response, - @RequestParam(required = false) String query, - @RequestParam(required = false) String app, - @RequestParam(required = false) String stream, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime, - @RequestParam(required = false) String mediaServerId, - @RequestParam(required = false) String callId, - @RequestParam(required = false) List ids + public void downloadZipFile(HttpServletResponse response, @RequestParam(required = false) String query, @RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String callId, @RequestParam(required = false) List ids ) { - log.info("[下载指定录像文件的压缩包] 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}", - app, stream, mediaServerId, startTime, endTime, callId); + log.info("[下载指定录像文件的压缩包] 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, startTime, endTime, callId); List mediaServers; if (!ObjectUtils.isEmpty(mediaServerId)) { @@ -339,7 +276,7 @@ public class CloudRecordController { callId = null; } if (stream != null && callId != null) { - response.addHeader( "Content-Disposition", "attachment;filename=" + stream + "_" + callId + ".zip" ); + response.addHeader("Content-Disposition", "attachment;filename=" + stream + "_" + callId + ".zip"); } List cloudRecordItemList = cloudRecordService.getAllList(query, app, stream, startTime, endTime, mediaServers, callId, ids); if (ObjectUtils.isEmpty(cloudRecordItemList)) { @@ -354,9 +291,9 @@ public class CloudRecordController { continue; } FileInputStream fis = new FileInputStream(cloudRecordItem.getFilePath()); - byte[] buf = new byte[2*1024]; + byte[] buf = new byte[2 * 1024]; int len; - while ((len = fis.read(buf)) != -1){ + while ((len = fis.read(buf)) != -1) { zos.write(buf, 0, len); } zos.closeEntry(); @@ -364,8 +301,7 @@ public class CloudRecordController { } zos.close(); } catch (IOException e) { - log.error("[下载指定录像文件的压缩包] 失败: 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}", - app, stream, mediaServerId, startTime, endTime, callId, e); + log.error("[下载指定录像文件的压缩包] 失败: 查询 app->{}, stream->{}, mediaServerId->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, startTime, endTime, callId, e); } } @@ -392,22 +328,10 @@ public class CloudRecordController { @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false) @Parameter(name = "mediaServerId", description = "流媒体ID,置空则查询全部流媒体", required = false) @Parameter(name = "callId", description = "每次录像的唯一标识,置空则查询全部流媒体", required = false) - public PageInfo getListWithUrl( - HttpServletRequest request, - @RequestParam(required = false) String query, - @RequestParam(required = false) String app, - @RequestParam(required = false) String stream, - @RequestParam int page, - @RequestParam int count, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime, - @RequestParam(required = false) String mediaServerId, - @RequestParam(required = false) String callId, - @RequestParam(required = false) String remoteHost + public PageInfo getListWithUrl(HttpServletRequest request, @RequestParam(required = false) String query, @RequestParam(required = false) String app, @RequestParam(required = false) String stream, @RequestParam int page, @RequestParam int count, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime, @RequestParam(required = false) String mediaServerId, @RequestParam(required = false) String callId, @RequestParam(required = false) String remoteHost ) { - log.info("[云端录像] 查询URL app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", - app, stream, mediaServerId, page, count, startTime, endTime, callId); + log.info("[云端录像] 查询URL app->{}, stream->{}, mediaServerId->{}, page->{}, count->{}, startTime->{}, endTime->{}, callId->{}", app, stream, mediaServerId, page, count, startTime, endTime, callId); List mediaServers; if (!ObjectUtils.isEmpty(mediaServerId)) { @@ -446,8 +370,7 @@ public class CloudRecordController { throw new ControllerException(ErrorCode.ERROR100.getCode(), "未找到流媒体节点"); } if (remoteHost == null) { - remoteHost = request.getScheme() + "://" + request.getLocalAddr() + ":" + - (request.getScheme().equals("https")? mediaServer.getHttpSSlPort() : mediaServer.getHttpPort()); + remoteHost = request.getScheme() + "://" + request.getLocalAddr() + ":" + (request.getScheme().equals("https") ? mediaServer.getHttpSSlPort() : mediaServer.getHttpPort()); } PageInfo cloudRecordItemPageInfo = cloudRecordService.getList(page, count, query, app, stream, startTime, endTime, mediaServers, callId); PageInfo cloudRecordUrlPageInfo = new PageInfo<>(); @@ -474,8 +397,7 @@ public class CloudRecordController { for (CloudRecordItem cloudRecordItem : cloudRecordItemList) { CloudRecordUrl cloudRecordUrl = new CloudRecordUrl(); cloudRecordUrl.setId(cloudRecordItem.getId()); - cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath() - + "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime()) ); + cloudRecordUrl.setDownloadUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath() + "&save_name=" + cloudRecordItem.getStream() + "_" + cloudRecordItem.getCallId() + "_" + DateUtil.timestampMsToUrlToyyyy_MM_dd_HH_mm_ss(cloudRecordItem.getStartTime())); cloudRecordUrl.setPlayUrl(remoteHost + "/index/api/downloadFile?file_path=" + cloudRecordItem.getFilePath()); cloudRecordUrlList.add(cloudRecordUrl); } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java index ea5b11cce..050a0c5cb 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java @@ -1,24 +1,49 @@ package com.genersoft.iot.vmp.vmanager.log; -import com.genersoft.iot.vmp.conf.UserSetting; +import ch.qos.logback.classic.Logger; +import ch.qos.logback.core.rolling.RollingFileAppender; +import com.alibaba.fastjson2.JSONArray; import com.genersoft.iot.vmp.conf.exception.ControllerException; -import com.genersoft.iot.vmp.conf.redis.RedisRpcConfig; import com.genersoft.iot.vmp.conf.security.JwtUtils; +import com.genersoft.iot.vmp.gb28181.service.ICloudRecordService; +import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.service.ILogService; -import com.genersoft.iot.vmp.storager.dao.dto.LogDto; +import com.genersoft.iot.vmp.service.bean.CloudRecordItem; +import com.genersoft.iot.vmp.service.bean.DownloadFileInfo; +import com.genersoft.iot.vmp.service.bean.LogFileInfo; import com.genersoft.iot.vmp.utils.DateUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; +import com.genersoft.iot.vmp.vmanager.cloudRecord.bean.CloudRecordUrl; import com.github.pagehelper.PageInfo; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.extern.slf4j.Slf4j; +import org.apache.commons.compress.utils.IOUtils; +import org.apache.commons.lang3.ObjectUtils; +import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.util.ObjectUtils; +import org.springframework.http.MediaType; import org.springframework.web.bind.annotation.*; -@Tag(name = "日志管理") +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; +import java.io.File; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.nio.file.Files; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.List; +import java.util.zip.ZipEntry; +import java.util.zip.ZipOutputStream; + +@SuppressWarnings("rawtypes") +@Tag(name = "日志文件查询接口") @Slf4j @RestController @RequestMapping("/api/log") @@ -27,77 +52,48 @@ public class LogController { @Autowired private ILogService logService; - @Autowired - private UserSetting userSetting; - /** - * 分页查询日志 - * - * @param query 查询内容 - * @param page 当前页 - * @param count 每页查询数量 - * @param type 类型 - * @param startTime 开始时间 - * @param endTime 结束时间 - * @return - */ - @GetMapping("/all") - @Operation(summary = "分页查询日志", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @Parameter(name = "query", description = "查询内容", required = true) - @Parameter(name = "page", description = "当前页", required = true) - @Parameter(name = "count", description = "每页查询数量", required = true) - @Parameter(name = "type", description = "类型", required = true) - @Parameter(name = "startTime", description = "开始时间", required = true) - @Parameter(name = "endTime", description = "结束时间", required = true) - public PageInfo getAll( - @RequestParam int page, - @RequestParam int count, - @RequestParam(required = false) String query, - @RequestParam(required = false) String type, - @RequestParam(required = false) String startTime, - @RequestParam(required = false) String endTime + @ResponseBody + @GetMapping("/list") + @Operation(summary = "分页查询日志文件", security = @SecurityRequirement(name = JwtUtils.HEADER)) + @Parameter(name = "query", description = "检索内容", required = false) + @Parameter(name = "startTime", description = "开始时间(yyyy-MM-dd HH:mm:ss)", required = false) + @Parameter(name = "endTime", description = "结束时间(yyyy-MM-dd HH:mm:ss)", required = false) + public List queryList(@RequestParam(required = false) String query, @RequestParam(required = false) String startTime, @RequestParam(required = false) String endTime + ) { if (ObjectUtils.isEmpty(query)) { query = null; } - - if (!userSetting.getLogInDatabase()) { - log.warn("自动记录日志功能已关闭,查询结果可能不完整。"); - } - if (ObjectUtils.isEmpty(startTime)) { startTime = null; - }else if (!DateUtil.verification(startTime, DateUtil.formatter) ){ - throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN); } - if (ObjectUtils.isEmpty(endTime)) { endTime = null; - }else if (!DateUtil.verification(endTime, DateUtil.formatter) ){ - throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN); } - - return logService.getAll(page, count, query, type, startTime, endTime); + return logService.queryList(query, startTime, endTime); } /** - * 清空日志 - * + * 下载指定日志文件 */ - @Operation(summary = "清空日志", security = @SecurityRequirement(name = JwtUtils.HEADER)) - @DeleteMapping("/clear") - public void clear() { - logService.clear(); + @ResponseBody + @GetMapping("/file/{fileName}") + public void downloadFile(HttpServletResponse response, @PathVariable String fileName) { + try { + File file = logService.getFileByName(fileName); + if (file == null || !file.exists() || !file.isFile()) { + throw new ControllerException(ErrorCode.ERROR400); + } + final InputStream in = Files.newInputStream(file.toPath()); + response.setContentType(MediaType.TEXT_PLAIN_VALUE); + ServletOutputStream outputStream = response.getOutputStream(); + IOUtils.copy(in, response.getOutputStream()); + in.close(); + outputStream.close(); + } catch (IOException e) { + response.setStatus(HttpServletResponse.SC_NO_CONTENT); + } } - @Autowired - private RedisRpcConfig redisRpcConfig; - - @GetMapping("/test/count") - public Object count() { - return redisRpcConfig.getCallbackCount(); - } - - - } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java index 787defa28..efffbf909 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/ps/PsController.java @@ -11,7 +11,7 @@ import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookType; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; @@ -28,9 +28,7 @@ import org.springframework.data.redis.core.RedisTemplate; import org.springframework.web.bind.annotation.*; import java.io.IOException; -import java.util.HashMap; import java.util.List; -import java.util.Map; import java.util.UUID; import java.util.concurrent.TimeUnit; @@ -48,7 +46,7 @@ public class PsController { private IMediaServerService mediaServerService; @Autowired - private SendRtpPortManager sendRtpPortManager; + private ISendRtpServerService sendRtpServerService; @Autowired private UserSetting userSetting; @@ -133,7 +131,7 @@ public class PsController { if (isSend != null && isSend) { String key = VideoManagerConstants.WVP_OTHER_SEND_PS_INFO + userSetting.getServerId() + "_" + callId; // 预创建发流信息 - int port = sendRtpPortManager.getNextPort(mediaServer); + int port = sendRtpServerService.getNextPort(mediaServer); otherPsSendInfo.setSendLocalIp(mediaServer.getSdpIp()); otherPsSendInfo.setSendLocalPort(port); @@ -249,11 +247,6 @@ public class PsController { if (sendInfo == null){ throw new ControllerException(ErrorCode.ERROR100.getCode(), "未开启发流"); } - Map param = new HashMap<>(); - param.put("vhost","__defaultVhost__"); - param.put("app",sendInfo.getPushApp()); - param.put("stream",sendInfo.getPushStream()); - param.put("ssrc",sendInfo.getPushSSRC()); MediaServer mediaServerItem = mediaServerService.getDefaultMediaServer(); boolean result = mediaServerService.stopSendRtp(mediaServerItem, sendInfo.getPushApp(), sendInfo.getStream(), sendInfo.getPushSSRC()); if (!result) { @@ -283,6 +276,6 @@ public class PsController { // }).start(); // } - return sendRtpPortManager.getNextPort(defaultMediaServer); + return sendRtpServerService.getNextPort(defaultMediaServer); } } diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java index a8ef9df2f..b78e9a67c 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/rtp/RtpController.java @@ -11,7 +11,7 @@ import com.genersoft.iot.vmp.media.event.hook.Hook; import com.genersoft.iot.vmp.media.event.hook.HookSubscribe; import com.genersoft.iot.vmp.media.event.hook.HookType; import com.genersoft.iot.vmp.media.service.IMediaServerService; -import com.genersoft.iot.vmp.media.zlm.SendRtpPortManager; +import com.genersoft.iot.vmp.service.ISendRtpServerService; import com.genersoft.iot.vmp.service.bean.SSRCInfo; import com.genersoft.iot.vmp.utils.redis.RedisUtil; import com.genersoft.iot.vmp.vmanager.bean.ErrorCode; @@ -41,7 +41,7 @@ import java.util.concurrent.TimeUnit; public class RtpController { @Autowired - private SendRtpPortManager sendRtpPortManager; + private ISendRtpServerService sendRtpServerService; @Autowired private HookSubscribe hookSubscribe; @@ -130,8 +130,8 @@ public class RtpController { redisTemplate.opsForValue().set(receiveKey, otherRtpSendInfo); if (isSend != null && isSend) { // 预创建发流信息 - int portForVideo = sendRtpPortManager.getNextPort(mediaServer); - int portForAudio = sendRtpPortManager.getNextPort(mediaServer); + int portForVideo = sendRtpServerService.getNextPort(mediaServer); + int portForAudio = sendRtpServerService.getNextPort(mediaServer); otherRtpSendInfo.setSendLocalIp(mediaServer.getSdpIp()); otherRtpSendInfo.setSendLocalPortForVideo(portForVideo); diff --git a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java index bc72ce702..ad7af6084 100755 --- a/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java +++ b/src/main/java/com/genersoft/iot/vmp/vmanager/server/ServerController.java @@ -11,8 +11,8 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException; import com.genersoft.iot.vmp.conf.security.JwtUtils; import com.genersoft.iot.vmp.gb28181.service.IDeviceChannelService; import com.genersoft.iot.vmp.gb28181.service.IDeviceService; -import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.media.bean.MediaServer; +import com.genersoft.iot.vmp.media.service.IMediaServerService; import com.genersoft.iot.vmp.service.bean.MediaServerLoad; import com.genersoft.iot.vmp.storager.IRedisCatchStorage; import com.genersoft.iot.vmp.streamProxy.service.IStreamProxyService; @@ -25,13 +25,24 @@ import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.Parameter; import io.swagger.v3.oas.annotations.security.SecurityRequirement; import io.swagger.v3.oas.annotations.tags.Tag; +import org.apache.commons.lang3.StringUtils; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.annotation.Value; import org.springframework.util.ObjectUtils; import org.springframework.web.bind.annotation.*; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.GlobalMemory; +import oshi.hardware.HardwareAbstractionLayer; +import oshi.hardware.NetworkIF; +import oshi.software.os.OperatingSystem; +import java.io.File; +import java.text.DecimalFormat; import java.util.ArrayList; +import java.util.LinkedHashMap; import java.util.List; +import java.util.Map; @SuppressWarnings("rawtypes") @Tag(name = "服务控制") @@ -75,7 +86,6 @@ public class ServerController { private IRedisCatchStorage redisCatchStorage; - @GetMapping(value = "/media_server/list") @ResponseBody @Operation(summary = "流媒体服务列表", security = @SecurityRequirement(name = JwtUtils.HEADER)) @@ -139,7 +149,11 @@ public class ServerController { @DeleteMapping(value = "/media_server/delete") @ResponseBody public void deleteMediaServer(@RequestParam String id) { - mediaServerService.delete(id); + MediaServer mediaServer = mediaServerService.getOne(id); + if (mediaServer == null) { + throw new ControllerException(ErrorCode.ERROR100.getCode(), "流媒体不存在"); + } + mediaServerService.delete(mediaServer); } @@ -166,9 +180,11 @@ public class ServerController { // throw new ControllerException(ErrorCode.ERROR100.getCode(), e.getMessage()); // } // }); - }; + } - @Operation(summary = "获取系统信息信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) + ; + + @Operation(summary = "获取系统配置信息", security = @SecurityRequirement(name = JwtUtils.HEADER)) @GetMapping(value = "/system/configInfo") @ResponseBody public SystemConfigInfo getConfigInfo() { @@ -228,9 +244,9 @@ public class ServerController { public List getMediaLoad() { List result = new ArrayList<>(); List allOnline = mediaServerService.getAllOnline(); - if (allOnline.size() == 0) { + if (allOnline.isEmpty()) { return result; - }else { + } else { for (MediaServer mediaServerItem : allOnline) { result.add(mediaServerService.getLoad(mediaServerItem)); } @@ -254,4 +270,80 @@ public class ServerController { return result; } + + @GetMapping(value = "/info") + @ResponseBody + @Operation(summary = "获取系统信息") + public Map> getInfo() { + Map> result = new LinkedHashMap<>(); + Map hardwareMap = new LinkedHashMap<>(); + result.put("硬件信息", hardwareMap); + + SystemInfo systemInfo = new SystemInfo(); + HardwareAbstractionLayer hardware = systemInfo.getHardware(); + // 获取CPU信息 + CentralProcessor.ProcessorIdentifier processorIdentifier = hardware.getProcessor().getProcessorIdentifier(); + hardwareMap.put("CPU", processorIdentifier.getName()); + // 获取内存 + GlobalMemory memory = hardware.getMemory(); + hardwareMap.put("内存", formatByte(memory.getTotal() - memory.getAvailable()) + "/" + formatByte(memory.getTotal())); + hardwareMap.put("制造商", systemInfo.getHardware().getComputerSystem().getManufacturer()); + hardwareMap.put("产品名称", systemInfo.getHardware().getComputerSystem().getModel()); + // 网卡 + List networkIFs = hardware.getNetworkIFs(); + StringBuilder ips = new StringBuilder(); + for (int i = 0; i < networkIFs.size(); i++) { + NetworkIF networkIF = networkIFs.get(i); + String ipsStr = StringUtils.join(networkIF.getIPv4addr()); + if (ObjectUtils.isEmpty(ipsStr)) { + continue; + } + ips.append(ipsStr); + if (i < networkIFs.size() - 1) { + ips.append(","); + } + } + hardwareMap.put("网卡", ips.toString()); + + Map operatingSystemMap = new LinkedHashMap<>(); + result.put("操作系统", operatingSystemMap); + OperatingSystem operatingSystem = systemInfo.getOperatingSystem(); + operatingSystemMap.put("名称", operatingSystem.getFamily() + " " + operatingSystem.getVersionInfo().getVersion()); + operatingSystemMap.put("类型", operatingSystem.getManufacturer()); + + Map platformMap = new LinkedHashMap<>(); + result.put("平台信息", platformMap); + VersionPo version = versionInfo.getVersion(); + platformMap.put("版本", version.getVersion()); + platformMap.put("构建日期", version.getBUILD_DATE()); + platformMap.put("GIT分支", version.getGIT_BRANCH()); + platformMap.put("GIT地址", version.getGIT_URL()); + platformMap.put("GIT日期", version.getGIT_DATE()); + platformMap.put("GIT版本", version.getGIT_Revision_SHORT()); + platformMap.put("DOCKER环境", new File("/.dockerenv").exists()?"是":"否"); + + return result; + } + + /** + * 单位转换 + */ + private static String formatByte(long byteNumber) { + //换算单位 + double FORMAT = 1024.0; + double kbNumber = byteNumber / FORMAT; + if (kbNumber < FORMAT) { + return new DecimalFormat("#.##KB").format(kbNumber); + } + double mbNumber = kbNumber / FORMAT; + if (mbNumber < FORMAT) { + return new DecimalFormat("#.##MB").format(mbNumber); + } + double gbNumber = mbNumber / FORMAT; + if (gbNumber < FORMAT) { + return new DecimalFormat("#.##GB").format(gbNumber); + } + double tbNumber = gbNumber / FORMAT; + return new DecimalFormat("#.##TB").format(tbNumber); + } } diff --git a/src/main/resources/logback-spring.xml b/src/main/resources/logback-spring.xml index e2b03ee0b..308f148b2 100644 --- a/src/main/resources/logback-spring.xml +++ b/src/main/resources/logback-spring.xml @@ -24,6 +24,14 @@ + + + + ${log.pattern} + UTF-8 + + + @@ -47,27 +55,6 @@ - - - - - - ${LOG_HOME}/error-%d{yyyy-MM-dd}.%i.log - - 30 - 20MB - - - - %d{yyyy-MM-dd HH:mm:ss.SSS} [%thread] %-5level %logger{50}:%L - %msg%n - UTF-8 - - - - WARN - - - @@ -88,12 +75,9 @@ - - - - + - + diff --git a/src/main/resources/配置详情.yml b/src/main/resources/配置详情.yml index 2f8e8da15..cef721516 100644 --- a/src/main/resources/配置详情.yml +++ b/src/main/resources/配置详情.yml @@ -188,7 +188,7 @@ logging: # [根据业务需求配置] user-settings: - # [可选] 服务ID,不写则为000000 + # 服务ID,不写则为000000 server-id: # [可选] 自动点播, 使用固定流地址进行播放时,如果未点播则自动进行点播, 需要rtp.enable=true auto-apply-play: false @@ -209,8 +209,6 @@ user-settings: record-push-live: true # 国标是否录制 record-sip: true - # 是否将日志存储进数据库 - logInDatabase: true # 使用推流状态作为推流通道状态 use-pushing-as-status: true # 使用来源请求ip作为streamIp,当且仅当你只有zlm节点它与wvp在一起的情况下开启 @@ -229,8 +227,6 @@ user-settings: sip-log: true # 是否开启sql日志 sql-log: true - # 收到ack消息后开始发流,默认false, 回复200ok后直接开始发流 - push-stream-after-ack: false # 消息通道功能-缺少国标ID是否给所有上级发送消息 send-to-platforms-when-id-lost: true # 保持通道状态,不接受notify通道状态变化, 兼容海康平台发送错误消息 diff --git a/web_src/package.json b/web_src/package.json index 847b5d04b..c314b9ba8 100644 --- a/web_src/package.json +++ b/web_src/package.json @@ -10,6 +10,8 @@ "build": "node build/build.js" }, "dependencies": { + "@babel/runtime": "^7.3.0", + "@femessage/log-viewer": "^1.5.0", "@liveqing/liveplayer": "^2.7.10", "@wchbrad/vue-easy-tree": "^1.0.12", "axios": "^0.24.0", @@ -21,6 +23,7 @@ "ol": "^6.14.1", "postcss-pxtorem": "^5.1.1", "screenfull": "5.1.0", + "strip-ansi": "^7.1.0", "uuid": "^8.3.2", "v-charts": "^1.19.0", "vue": "^2.6.11", diff --git a/web_src/src/components/CloudRecord.vue b/web_src/src/components/CloudRecord.vue index 3517587ba..d9f512e42 100755 --- a/web_src/src/components/CloudRecord.vue +++ b/web_src/src/components/CloudRecord.vue @@ -52,17 +52,17 @@ -