Merge branch 'master' into 重构/1078
# Conflicts: # pom.xml # src/main/java/com/genersoft/iot/vmp/conf/SpringDocConfig.java # src/main/java/com/genersoft/iot/vmp/gb28181/service/impl/DeviceChannelServiceImpl.java # src/main/java/com/genersoft/iot/vmp/service/impl/MediaServiceImpl.java
@@ -40,6 +40,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
|
||||

|
||||

|
||||

|
||||

|
||||
|
||||
# 功能特性
|
||||
- [X] 集成web界面
|
||||
|
||||
BIN
doc/_content/ability/_media/img_19.png
Normal file
|
After Width: | Height: | Size: 84 KiB |
BIN
doc/_content/ability/_media/img_20.png
Normal file
|
After Width: | Height: | Size: 31 KiB |
BIN
doc/_content/ability/_media/img_21.png
Normal file
|
After Width: | Height: | Size: 81 KiB |
BIN
doc/_content/ability/_media/img_22.png
Normal file
|
After Width: | Height: | Size: 124 KiB |
BIN
doc/_content/ability/_media/img_23.png
Normal file
|
After Width: | Height: | Size: 68 KiB |
BIN
doc/_content/ability/_media/img_24.png
Normal file
|
After Width: | Height: | Size: 47 KiB |
BIN
doc/_content/ability/_media/img_25.png
Normal file
|
After Width: | Height: | Size: 98 KiB |
@@ -33,6 +33,10 @@
|
||||
|
||||

|
||||
|
||||
### 3. 宇视科技
|
||||
|
||||

|
||||
|
||||
### 3. 艾科威视摄像头
|
||||
|
||||

|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
|
||||
BIN
doc/_media/log.jpg
Normal file
|
After Width: | Height: | Size: 195 KiB |
25
pom.xml
@@ -107,6 +107,12 @@
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-web</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-starter-websocket</artifactId>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
<artifactId>spring-boot-configuration-processor</artifactId>
|
||||
@@ -374,6 +380,18 @@
|
||||
<version>1.18.30</version>
|
||||
<scope>provided</scope>
|
||||
</dependency>
|
||||
<!--LogViewer-->
|
||||
<!-- <dependency>-->
|
||||
<!-- <groupId>io.github.sevdokimov.logviewer</groupId>-->
|
||||
<!-- <artifactId>log-generator</artifactId>-->
|
||||
<!-- <version>1.0.10</version>-->
|
||||
<!-- </dependency>-->
|
||||
|
||||
<dependency>
|
||||
<groupId>io.github.sevdokimov.logviewer</groupId>
|
||||
<artifactId>log-viewer-spring-boot</artifactId>
|
||||
<version>1.0.10</version>
|
||||
</dependency>
|
||||
|
||||
<dependency>
|
||||
<groupId>org.springframework.boot</groupId>
|
||||
@@ -401,6 +419,13 @@
|
||||
<configuration>
|
||||
<source>1.8</source>
|
||||
<target>1.8</target>
|
||||
<annotationProcessorPaths>
|
||||
<path>
|
||||
<groupId>org.projectlombok</groupId>
|
||||
<artifactId>lombok</artifactId>
|
||||
<version>1.18.30</version>
|
||||
</path>
|
||||
</annotationProcessorPaths>
|
||||
</configuration>
|
||||
</plugin>
|
||||
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
// 项目重启
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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_";
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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(){
|
||||
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String> 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<String> 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<String> 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<String> 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<String> getAllowedOrigins() {
|
||||
return allowedOrigins;
|
||||
}
|
||||
|
||||
public void setAllowedOrigins(List<String> 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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
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)) {
|
||||
|
||||
@@ -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<String> 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<String> 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()
|
||||
|
||||
@@ -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<String, LogChannel> 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);
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
@@ -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<ILoggingEvent> {
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
@@ -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<DeviceChannel> channelList;
|
||||
private List<Region> regionListList;
|
||||
private List<Group> groupListListList;
|
||||
private Instant lastTime;
|
||||
private Instant time;
|
||||
private Device device;
|
||||
private String errorMsg;
|
||||
private Set<String> redisKeysForChannel = new HashSet<>();
|
||||
private Set<String> redisKeysForRegion = new HashSet<>();
|
||||
private Set<String> redisKeysForGroup = new HashSet<>();
|
||||
|
||||
public enum CatalogDataStatus{
|
||||
ready, runIng, end
|
||||
}
|
||||
private CatalogDataStatus status;
|
||||
|
||||
}
|
||||
|
||||
@@ -1,25 +1,27 @@
|
||||
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;
|
||||
|
||||
/**
|
||||
* 设备Id
|
||||
*/
|
||||
@Schema(description = "设备的国标编号")
|
||||
private String deviceId;
|
||||
|
||||
@Schema(description = "设备名称")
|
||||
private String deviceName;
|
||||
|
||||
/**
|
||||
* 通道Id
|
||||
*/
|
||||
@@ -32,6 +34,24 @@ public class DeviceAlarm {
|
||||
@Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情")
|
||||
private String alarmPriority;
|
||||
|
||||
@Schema(description = "报警级别, 1为一级警情, 2为二级警情, 3为三级警情, 4为四级警情")
|
||||
private String alarmPriorityDescription;
|
||||
|
||||
public String getAlarmPriorityDescription() {
|
||||
switch (alarmPriority) {
|
||||
case "1":
|
||||
return "一级警情";
|
||||
case "2":
|
||||
return "二级警情";
|
||||
case "3":
|
||||
return "三级警情";
|
||||
case "4":
|
||||
return "四级警情";
|
||||
default:
|
||||
return alarmPriority;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 报警方式 , 1为电话报警, 2为设备报警, 3为短信报警, 4为 GPS报警, 5为视频报警, 6为设备故障报警,
|
||||
* 7其他报警;可以为直接组合如12为电话报警或 设备报警-
|
||||
@@ -40,6 +60,41 @@ public class DeviceAlarm {
|
||||
"\t * 7其他报警;可以为直接组合如12为电话报警或设备报警")
|
||||
private String alarmMethod;
|
||||
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
/**
|
||||
* 报警时间
|
||||
*/
|
||||
@@ -93,95 +148,122 @@ public class DeviceAlarm {
|
||||
@Schema(description = "报警类型")
|
||||
private String alarmType;
|
||||
|
||||
public String getAlarmTypeDescription() {
|
||||
if (alarmType == null) {
|
||||
return "";
|
||||
}
|
||||
char[] charArray = alarmMethod.toCharArray();
|
||||
Set<String> 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;
|
||||
}
|
||||
|
||||
@Schema(description = "报警类型描述")
|
||||
private String alarmTypeDescription;
|
||||
|
||||
@Schema(description = "创建时间")
|
||||
private String createTime;
|
||||
|
||||
|
||||
public String getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public void setId(String id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public String getDeviceId() {
|
||||
return deviceId;
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,4 +19,7 @@ public class GroupTree extends Group{
|
||||
@Schema(description = "类型, 行政区划:0 摄像头: 1")
|
||||
private int type;
|
||||
|
||||
@Schema(description = "在线状态")
|
||||
private String status;
|
||||
|
||||
}
|
||||
|
||||
@@ -18,4 +18,7 @@ public class RegionTree extends Region {
|
||||
|
||||
@Schema(description = "类型, 行政区划:0 摄像头: 1")
|
||||
private int type;
|
||||
|
||||
@Schema(description = "在线状态")
|
||||
private String status;
|
||||
}
|
||||
|
||||
@@ -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<CommonGBChannel> queryList(int page, int count,
|
||||
@Parameter(name = "civilCode", description = "行政区划")
|
||||
@GetMapping("/civilcode/list")
|
||||
public PageInfo<CommonGBChannel> 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<CommonGBChannel> 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))
|
||||
|
||||
@@ -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,7 +81,7 @@ public class DeviceControl {
|
||||
@Parameter(name = "channelId", description = "通道国标编号", required = true)
|
||||
@Parameter(name = "recordCmdStr", description = "命令, 可选值:Record(手动录像),StopRecord(停止手动录像)", required = true)
|
||||
@GetMapping("/record/{deviceId}/{recordCmdStr}")
|
||||
public DeferredResult<ResponseEntity<String>> recordApi(@PathVariable String deviceId,
|
||||
public DeferredResult<ResponseEntity<WVPResult<String>>> recordApi(@PathVariable String deviceId,
|
||||
@PathVariable String recordCmdStr, String channelId) {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.debug("开始/停止录像API调用");
|
||||
@@ -88,14 +89,14 @@ public class DeviceControl {
|
||||
Device device = deviceService.getDeviceByDeviceId(deviceId);
|
||||
String uuid = UUID.randomUUID().toString();
|
||||
String key = DeferredResultHolder.CALLBACK_CMD_DEVICECONTROL + deviceId + channelId;
|
||||
DeferredResult<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
|
||||
DeferredResult<ResponseEntity<WVPResult<String>>> 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<String> guardApi(@PathVariable String deviceId, @PathVariable String guardCmdStr) {
|
||||
public DeferredResult<WVPResult<String>> 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<String> result = new DeferredResult<>(3 * 1000L);
|
||||
DeferredResult<WVPResult<String>> 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<ResponseEntity<String>> resetAlarmApi(@PathVariable String deviceId, String channelId,
|
||||
public DeferredResult<ResponseEntity<WVPResult<String>>> 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<ResponseEntity<String>> result = new DeferredResult<ResponseEntity<String>>(3 * 1000L);
|
||||
DeferredResult<ResponseEntity<WVPResult<String>>> 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<String> homePositionApi(String deviceId, String channelId, Boolean enabled,
|
||||
public DeferredResult<WVPResult<String>> 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<String> result = new DeferredResult<>(3 * 1000L);
|
||||
DeferredResult<WVPResult<String>> 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);
|
||||
|
||||
@@ -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,14 +296,11 @@ 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);
|
||||
if (device == null || device.getDeviceId() == null || device.getId() <= 0) {
|
||||
throw new ControllerException(ErrorCode.ERROR400);
|
||||
}
|
||||
deviceService.updateCustomDevice(device);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 设备状态查询请求API接口
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -40,12 +40,13 @@ public class GroupController {
|
||||
@GetMapping("/tree/list")
|
||||
public List<GroupTree> 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<Group> getPath(String deviceId, String businessGroup){
|
||||
return groupService.getPath(deviceId, businessGroup);
|
||||
}
|
||||
|
||||
// @Operation(summary = "根据分组Id查询分组")
|
||||
// @Parameter(name = "groupDeviceId", description = "分组节点编号", required = true)
|
||||
// @ResponseBody
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -57,12 +57,13 @@ public class RegionController {
|
||||
@GetMapping("/tree/list")
|
||||
public List<RegionTree> 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<Region> getPath(String deviceId){
|
||||
return regionService.getPath(deviceId);
|
||||
}
|
||||
|
||||
@Operation(summary = "从通道中同步行政区划")
|
||||
@ResponseBody
|
||||
@GetMapping("/sync")
|
||||
|
||||
@@ -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 <a href="mailto:xiaoQQya@126.com">xiaoQQya</a>
|
||||
* @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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<CommonGBChannel> queryList(@Param("query") String query, @Param("online") Boolean online,
|
||||
@Param("hasCivilCode") Boolean hasCivilCode,
|
||||
@Param("hasGroup") Boolean hasGroup);
|
||||
@SelectProvider(type = ChannelProvider.class, method = "queryListByCivilCode")
|
||||
List<CommonGBChannel> queryListByCivilCode(@Param("query") String query, @Param("online") Boolean online,
|
||||
@Param("channelType") Integer channelType, @Param("civilCode") String civilCode);
|
||||
|
||||
|
||||
|
||||
@SelectProvider(type = ChannelProvider.class, method = "queryListByParentId")
|
||||
List<CommonGBChannel> queryListByParentId(@Param("query") String query, @Param("online") Boolean online,
|
||||
@Param("channelType") Integer channelType, @Param("groupDeviceId") String groupDeviceId);
|
||||
|
||||
|
||||
|
||||
@Select("<script>" +
|
||||
" select " +
|
||||
@@ -267,6 +274,7 @@ public interface CommonGBChannelMapper {
|
||||
" coalesce(gb_device_id, device_id) as device_id," +
|
||||
" coalesce(gb_name, name) as name, " +
|
||||
" coalesce(gb_parent_id, parent_id) as parent_device_id, " +
|
||||
" coalesce(gb_status, status) as status, " +
|
||||
" 1 as type, " +
|
||||
" true as is_leaf " +
|
||||
" from wvp_device_channel " +
|
||||
@@ -358,6 +366,7 @@ public interface CommonGBChannelMapper {
|
||||
" coalesce(gb_name, name) as name, " +
|
||||
" coalesce(gb_parent_id, parent_id) as parent_device_id, " +
|
||||
" coalesce(gb_business_group_id, business_group_id) as business_group, " +
|
||||
" coalesce(gb_status, status) as status, " +
|
||||
" 1 as type, " +
|
||||
" true as is_leaf " +
|
||||
" from wvp_device_channel " +
|
||||
@@ -438,4 +447,13 @@ public interface CommonGBChannelMapper {
|
||||
@SelectProvider(type = ChannelProvider.class, method = "queryListByStreamPushList")
|
||||
List<CommonGBChannel> queryListByStreamPushList(List<StreamPush> streamPushList);
|
||||
|
||||
@Update(value = {" <script>" +
|
||||
" <foreach collection='channels' item='item' separator=';' >" +
|
||||
" UPDATE wvp_device_channel " +
|
||||
" SET gb_longitude=#{item.gbLongitude}, gb_latitude=#{item.gbLatitude} " +
|
||||
" WHERE stream_push_id IS NOT NULL AND gb_device_id=#{item.gbDeviceId} "+
|
||||
"</foreach>"+
|
||||
" </script>"})
|
||||
void updateGpsByDeviceIdForStreamPush(List<CommonGBChannel> channels);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<DeviceChannel> queryChannels(@Param("deviceDbId") int deviceDbId, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("channelIds") List<String> channelIds);
|
||||
List<DeviceChannel> 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<String> channelIds);
|
||||
|
||||
@SelectProvider(type = DeviceChannelProvider.class, method = "queryChannelsByDeviceDbId")
|
||||
List<DeviceChannel> queryChannelsByDeviceDbId(@Param("deviceDbId") int deviceDbId);
|
||||
@@ -148,9 +153,6 @@ public interface DeviceChannelMapper {
|
||||
" </script>"})
|
||||
List<DeviceChannelExtend> queryChannelsWithDeviceInfo(@Param("deviceId") String deviceId, @Param("parentChannelId") String parentChannelId, @Param("query") String query, @Param("hasSubChannel") Boolean hasSubChannel, @Param("online") Boolean online, @Param("channelIds") List<String> 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<DeviceChannel> 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({"<script>" +
|
||||
@@ -378,7 +380,7 @@ public interface DeviceChannelMapper {
|
||||
" from wvp_device_channel where device_db_id = #{deviceDbId}")
|
||||
List<DeviceChannel> queryAllChannelsForRefresh(@Param("deviceDbId") int deviceDbId);
|
||||
|
||||
@Select("select de.* from wvp_device de left join wvp_device_channel dc on de.device_id = dc.deviceId where dc.device_id=#{channelId}")
|
||||
@Select("select de.* from wvp_device de left join wvp_device_channel dc on de.device_id = dc.device_id where dc.device_id=#{channelId}")
|
||||
List<Device> getDeviceByChannelDeviceId(String channelId);
|
||||
|
||||
|
||||
@@ -601,4 +603,56 @@ public interface DeviceChannelMapper {
|
||||
" WHERE id = #{id}" +
|
||||
"</script>"})
|
||||
void updateChannelForNotify(DeviceChannel channel);
|
||||
|
||||
|
||||
@Select(value = {" <script>" +
|
||||
" SELECT " +
|
||||
" id,\n" +
|
||||
" device_db_id,\n" +
|
||||
" create_time,\n" +
|
||||
" update_time,\n" +
|
||||
" sub_count,\n" +
|
||||
" stream_id,\n" +
|
||||
" has_audio,\n" +
|
||||
" gps_time,\n" +
|
||||
" stream_identification,\n" +
|
||||
" channel_type,\n" +
|
||||
" device_id,\n" +
|
||||
" name,\n" +
|
||||
" manufacturer,\n" +
|
||||
" model,\n" +
|
||||
" owner,\n" +
|
||||
" civil_code,\n" +
|
||||
" block,\n" +
|
||||
" address,\n" +
|
||||
" parental,\n" +
|
||||
" parent_id,\n" +
|
||||
" safety_way,\n" +
|
||||
" register_way,\n" +
|
||||
" cert_num,\n" +
|
||||
" certifiable,\n" +
|
||||
" err_code,\n" +
|
||||
" end_time,\n" +
|
||||
" secrecy,\n" +
|
||||
" ip_address,\n" +
|
||||
" port,\n" +
|
||||
" password,\n" +
|
||||
" status,\n" +
|
||||
" longitude,\n" +
|
||||
" latitude,\n" +
|
||||
" ptz_type,\n" +
|
||||
" position_type,\n" +
|
||||
" room_type,\n" +
|
||||
" use_type,\n" +
|
||||
" supply_light_type,\n" +
|
||||
" direction_type,\n" +
|
||||
" resolution,\n" +
|
||||
" business_group_id,\n" +
|
||||
" download_speed,\n" +
|
||||
" svc_space_support_mod,\n" +
|
||||
" svc_time_support_mode\n" +
|
||||
" from wvp_device_channel " +
|
||||
" where device_db_id=#{deviceDbId} and device_id = #{channelId}" +
|
||||
" </script>"})
|
||||
DeviceChannel getOneBySourceChannelId(int deviceDbId, String channelId);
|
||||
}
|
||||
|
||||
@@ -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 = {" <script>" +
|
||||
"UPDATE wvp_device " +
|
||||
"SET update_time=#{updateTime}" +
|
||||
"<if test=\"name != null\">, custom_name=#{name}</if>" +
|
||||
"<if test=\"password != null\">, password=#{password}</if>" +
|
||||
"<if test=\"streamMode != null\">, stream_mode=#{streamMode}</if>" +
|
||||
"<if test=\"ip != null\">, ip=#{ip}</if>" +
|
||||
"<if test=\"sdpIp != null\">, sdp_ip=#{sdpIp}</if>" +
|
||||
"<if test=\"port != null\">, port=#{port}</if>" +
|
||||
"<if test=\"charset != null\">, charset=#{charset}</if>" +
|
||||
"<if test=\"subscribeCycleForCatalog != null\">, subscribe_cycle_for_catalog=#{subscribeCycleForCatalog}</if>" +
|
||||
"<if test=\"subscribeCycleForMobilePosition != null\">, subscribe_cycle_for_mobile_position=#{subscribeCycleForMobilePosition}</if>" +
|
||||
"<if test=\"mobilePositionSubmissionInterval != null\">, mobile_position_submission_interval=#{mobilePositionSubmissionInterval}</if>" +
|
||||
"<if test=\"subscribeCycleForAlarm != null\">, subscribe_cycle_for_alarm=#{subscribeCycleForAlarm}</if>" +
|
||||
"<if test=\"ssrcCheck != null\">, ssrc_check=#{ssrcCheck}</if>" +
|
||||
"<if test=\"asMessageChannel != null\">, as_message_channel=#{asMessageChannel}</if>" +
|
||||
"<if test=\"broadcastPushAfterAck != null\">, broadcast_push_after_ack=#{broadcastPushAfterAck}</if>" +
|
||||
"<if test=\"geoCoordSys != null\">, geo_coord_sys=#{geoCoordSys}</if>" +
|
||||
"<if test=\"mediaServerId != null\">, media_server_id=#{mediaServerId}</if>" +
|
||||
"WHERE device_id=#{deviceId}"+
|
||||
"SET update_time=#{updateTime}, custom_name=#{name} , password=#{password}, stream_mode=#{streamMode}" +
|
||||
", ip=#{ip}, sdp_ip=#{sdpIp}, port=#{port}, charset=#{charset}, subscribe_cycle_for_catalog=#{subscribeCycleForCatalog}" +
|
||||
", subscribe_cycle_for_mobile_position=#{subscribeCycleForMobilePosition}, mobile_position_submission_interval=#{mobilePositionSubmissionInterval}" +
|
||||
", subscribe_cycle_for_alarm=#{subscribeCycleForAlarm}, ssrc_check=#{ssrcCheck}, as_message_channel=#{asMessageChannel}" +
|
||||
", broadcast_push_after_ack=#{broadcastPushAfterAck}, geo_coord_sys=#{geoCoordSys}, media_server_id=#{mediaServerId}" +
|
||||
" WHERE id=#{id}"+
|
||||
" </script>"})
|
||||
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 "+
|
||||
" <if test='status != null'> AND de.on_line=${status}</if>"+
|
||||
" <if test='query != null'> AND (coalesce(custom_name, name) LIKE '%${query}%' OR device_id LIKE '%${query}%')</if> " +
|
||||
" <if test='query != null'> AND (coalesce(custom_name, name) LIKE '%${query}%' OR device_id LIKE '%${query}%' OR ip LIKE '%${query}%')</if> " +
|
||||
" order by create_time desc "+
|
||||
" </script>")
|
||||
List<Device> getDeviceList(@Param("query") String query, @Param("status") Boolean status);
|
||||
|
||||
@@ -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 " +
|
||||
" <if test='parentId != null'> and parent_id = #{parentId} </if> " +
|
||||
@@ -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" +
|
||||
|
||||
@@ -540,4 +540,12 @@ public interface PlatformChannelMapper {
|
||||
" </script>")
|
||||
Set<Group> queryShareGroup(@Param("platformId") Integer platformId);
|
||||
|
||||
@Select(" <script>" +
|
||||
" SELECT wcr.* " +
|
||||
" from wvp_common_region wcr" +
|
||||
" left join wvp_platform_region wpr on wpr.region_id = wcr.id " +
|
||||
" where wpr.platform_id = #{platformId}" +
|
||||
" order by wcr.id DESC" +
|
||||
" </script>")
|
||||
Set<Region> queryShareRegion(Integer id);
|
||||
}
|
||||
|
||||
@@ -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 " +
|
||||
|
||||
@@ -117,7 +117,7 @@ public class ChannelProvider {
|
||||
}
|
||||
|
||||
|
||||
public String queryList(Map<String, Object> params ){
|
||||
public String queryListByCivilCode(Map<String, Object> 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<String, Object> 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();
|
||||
}
|
||||
|
||||
|
||||
@@ -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<String, Object> 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'");
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ public class SipSubscribe {
|
||||
|
||||
private final DelayQueue<SipEvent> 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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<AlarmEvent> {
|
||||
|
||||
private static final Map<String, PrintWriter> 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 = "<strong>设备编号:</strong> <i>" + event.getAlarmInfo().getDeviceId() + "</i>"
|
||||
+ "<br><strong>通道编号:</strong> <i>" + event.getAlarmInfo().getChannelId() + "</i>"
|
||||
+ "<br><strong>报警描述:</strong> <i>" + event.getAlarmInfo().getAlarmDescription() + "</i>"
|
||||
+ "<br><strong>报警时间:</strong> <i>" + event.getAlarmInfo().getAlarmTime() + "</i>";
|
||||
|
||||
for (Iterator<Map.Entry<String, PrintWriter>> it = SSE_CACHE.entrySet().iterator(); it.hasNext(); ) {
|
||||
Map.Entry<String, PrintWriter> 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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -119,4 +119,7 @@ public interface IDeviceChannelService {
|
||||
void updateChannelForNotify(DeviceChannel channel);
|
||||
|
||||
DeviceChannel getOneForSource(int deviceDbId, String channelId);
|
||||
|
||||
DeviceChannel getOneBySourceId(int deviceDbId, String channelId);
|
||||
|
||||
}
|
||||
|
||||
@@ -41,7 +41,9 @@ public interface IGbChannelService {
|
||||
|
||||
void reset(int id);
|
||||
|
||||
PageInfo<CommonGBChannel> queryList(int page, int count, String query, Boolean online, Boolean hasCivilCode, Boolean hasGroup);
|
||||
PageInfo<CommonGBChannel> queryListByCivilCode(int page, int count, String query, Boolean online, Integer channelType, String civilCode);
|
||||
|
||||
PageInfo<CommonGBChannel> queryListByParentId(int page, int count, String query, Boolean online, Integer channelType, String groupDeviceId);
|
||||
|
||||
void removeCivilCode(List<Region> allChildren);
|
||||
|
||||
@@ -81,4 +83,5 @@ public interface IGbChannelService {
|
||||
|
||||
List<CommonGBChannel> queryListByStreamPushList(List<StreamPush> streamPushList);
|
||||
|
||||
void updateGpsByDeviceIdForStreamPush(List<CommonGBChannel> channels);
|
||||
}
|
||||
|
||||
@@ -14,11 +14,13 @@ public interface IGroupService {
|
||||
|
||||
Group queryGroupByDeviceId(String regionDeviceId);
|
||||
|
||||
List<GroupTree> queryForTree(String query, Integer parent);
|
||||
List<GroupTree> queryForTree(String query, Integer parent, Boolean hasChannel);
|
||||
|
||||
void syncFromChannel();
|
||||
|
||||
boolean delete(int id);
|
||||
|
||||
boolean batchAdd(List<Group> groupList);
|
||||
|
||||
List<Group> getPath(String deviceId, String businessGroup);
|
||||
}
|
||||
|
||||
@@ -39,7 +39,7 @@ public interface IInviteStreamService {
|
||||
*/
|
||||
void removeInviteInfoByDeviceAndChannel(InviteSessionType inviteSessionType, Integer channelId);
|
||||
|
||||
List<InviteInfo> getAllInviteInfo(InviteSessionType type, Integer channelId, String stream);
|
||||
List<InviteInfo> getAllInviteInfo();
|
||||
|
||||
/**
|
||||
* 获取点播的状态信息
|
||||
|
||||
@@ -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<Platform> queryPlatFormListByChannelDeviceId(Integer channelId, List<String> platforms);
|
||||
|
||||
CommonGBChannel queryChannelByPlatformIdAndChannelId(Integer platformId, Integer channelId);
|
||||
|
||||
void checkRegionAdd(List<CommonGBChannel> channelList);
|
||||
|
||||
void checkRegionRemove(List<CommonGBChannel> channelList, List<Region> regionList);
|
||||
}
|
||||
|
||||
@@ -30,13 +30,13 @@ public interface IPlayService {
|
||||
MediaServer getNewMediaServerItem(Device device);
|
||||
|
||||
void playBack(Device device, DeviceChannel channel, String startTime, String endTime, ErrorCallback<StreamInfo> callback);
|
||||
void zlmServerOffline(String mediaServerId);
|
||||
void zlmServerOffline(MediaServer mediaServer);
|
||||
|
||||
void download(Device device, DeviceChannel channel, String startTime, String endTime, int downloadSpeed, ErrorCallback<StreamInfo> 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);
|
||||
|
||||
|
||||
@@ -27,11 +27,13 @@ public interface IRegionService {
|
||||
|
||||
Region queryRegionByDeviceId(String regionDeviceId);
|
||||
|
||||
List<RegionTree> queryForTree(String query, Integer parent);
|
||||
List<RegionTree> queryForTree(String query, Integer parent, Boolean hasChannel);
|
||||
|
||||
void syncFromChannel();
|
||||
|
||||
boolean delete(int id);
|
||||
|
||||
boolean batchAdd(List<Region> regionList);
|
||||
|
||||
List<Region> getPath(String deviceId);
|
||||
}
|
||||
|
||||
@@ -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<DeviceChannel> 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<DeviceChannel> allChannels = channelMapper.queryAllChannelsForRefresh(deviceDbId);
|
||||
Map<String,DeviceChannel> allChannelMap = new ConcurrentHashMap<>();
|
||||
Map<String,DeviceChannel> 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<String, Integer> subContMap = new HashMap<>();
|
||||
|
||||
// 数据去重
|
||||
Set<String> 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<DeviceChannel> getSubChannels(int deviceDbId, String channelId, String query, Boolean channelType, Boolean online, int page, int count) {
|
||||
PageHelper.startPage(page, count);
|
||||
List<DeviceChannel> 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<DeviceChannel> 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<DeviceChannel> all = channelMapper.queryChannels(device.getId(), null, query, hasSubChannel, online,null);
|
||||
List<DeviceChannel> all = channelMapper.queryChannels(device.getId(), null,null, null, query, hasSubChannel, online,null);
|
||||
return new PageInfo<>(all);
|
||||
}
|
||||
|
||||
|
||||
@@ -168,15 +168,13 @@ public class DeviceServiceImpl implements IDeviceService {
|
||||
}
|
||||
|
||||
}else {
|
||||
deviceMapper.update(device);
|
||||
redisCatchStorage.updateDevice(device);
|
||||
}
|
||||
if (deviceChannelMapper.queryChannelsByDeviceDbId(device.getId()).isEmpty()) {
|
||||
log.info("[设备上线]: {},通道数为0,查询通道信息", device.getDeviceId());
|
||||
sync(device);
|
||||
}
|
||||
|
||||
deviceMapper.update(device);
|
||||
redisCatchStorage.updateDevice(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
|
||||
|
||||
@@ -156,7 +156,7 @@ public class GbChannelServiceImpl implements IGbChannelService {
|
||||
}
|
||||
List<CommonGBChannel> 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<CommonGBChannel> queryList(int page, int count, String query, Boolean online, Boolean hasCivilCode,
|
||||
Boolean hasGroup) {
|
||||
public PageInfo<CommonGBChannel> queryListByCivilCode(int page, int count, String query, Boolean online, Integer channelType, String civilCode) {
|
||||
PageHelper.startPage(page, count);
|
||||
List<CommonGBChannel> all = commonGBChannelMapper.queryList(query, online, hasCivilCode, hasGroup);
|
||||
List<CommonGBChannel> all = commonGBChannelMapper.queryListByCivilCode(query, online, channelType, civilCode);
|
||||
return new PageInfo<>(all);
|
||||
}
|
||||
|
||||
@Override
|
||||
public PageInfo<CommonGBChannel> queryListByParentId(int page, int count, String query, Boolean online, Integer channelType, String groupDeviceId) {
|
||||
PageHelper.startPage(page, count);
|
||||
List<CommonGBChannel> 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<Region> 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<CommonGBChannel> queryListByStreamPushList(List<StreamPush> streamPushList) {
|
||||
return commonGBChannelMapper.queryListByStreamPushList(streamPushList);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void updateGpsByDeviceIdForStreamPush(List<CommonGBChannel> channels) {
|
||||
commonGBChannelMapper.updateGpsByDeviceIdForStreamPush(channels);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<GroupTree> queryForTree(String query, Integer parentId) {
|
||||
public List<GroupTree> queryForTree(String query, Integer parentId, Boolean hasChannel) {
|
||||
|
||||
List<GroupTree> 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<GroupTree> 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<Group> getPath(String deviceId, String businessGroup) {
|
||||
Group businessGroupInDb = groupManager.queryBusinessGroup(businessGroup);
|
||||
if (businessGroupInDb == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "业务分组不存在");
|
||||
}
|
||||
List<Group> 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<Group> allParent = getAllParent(group);
|
||||
groupList.addAll(allParent);
|
||||
return groupList;
|
||||
}
|
||||
|
||||
private List<Group> getAllParent(Group group) {
|
||||
if (group.getParentId() == null || group.getBusinessGroup() == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Group> groupList = new ArrayList<>();
|
||||
Group parent = groupManager.queryOneByDeviceId(group.getParentDeviceId(), group.getBusinessGroup());
|
||||
if (parent == null) {
|
||||
return groupList;
|
||||
}
|
||||
List<Group> allParent = getAllParent(parent);
|
||||
allParent.add(parent);
|
||||
return allParent;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Object> 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<Map.Entry<Object, Object>> 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<InviteInfo> getAllInviteInfo(InviteSessionType type, Integer channelId, String stream) {
|
||||
String key = VideoManagerConstants.INVITE_PREFIX +
|
||||
":" + (type != null ? type : "*") +
|
||||
":*" +
|
||||
":" + (channelId != null ? channelId : "*") +
|
||||
":" + (stream != null ? stream : "*")
|
||||
+ ":*";
|
||||
List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
|
||||
if (scanResult.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
public List<InviteInfo> getAllInviteInfo() {
|
||||
List<InviteInfo> result = new ArrayList<>();
|
||||
for (Object keyObj : scanResult) {
|
||||
result.add((InviteInfo) redisTemplate.opsForValue().get(keyObj));
|
||||
String key = VideoManagerConstants.INVITE_PREFIX;
|
||||
List<Object> values = redisTemplate.opsForHash().values(key);
|
||||
if(values.isEmpty()) {
|
||||
return result;
|
||||
}
|
||||
for (Object value : values) {
|
||||
result.add((InviteInfo)value);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -206,23 +201,17 @@ 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<Object> 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()));
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +245,7 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
|
||||
|
||||
@Override
|
||||
public void clearInviteInfo(String deviceId) {
|
||||
List<InviteInfo> inviteInfoList = getAllInviteInfo(null, null, null);
|
||||
List<InviteInfo> inviteInfoList = getAllInviteInfo();
|
||||
for (InviteInfo inviteInfo : inviteInfoList) {
|
||||
if (inviteInfo.getDeviceId().equals(deviceId)) {
|
||||
removeInviteInfo(inviteInfo);
|
||||
@@ -267,14 +256,13 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
|
||||
@Override
|
||||
public int getStreamInfoCount(String mediaServerId) {
|
||||
int count = 0;
|
||||
String key = VideoManagerConstants.INVITE_PREFIX + ":*:*:*:*:*";
|
||||
List<Object> 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);
|
||||
String key = VideoManagerConstants.INVITE_PREFIX;
|
||||
List<Object> 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
|
||||
@@ -285,7 +273,6 @@ public class InviteStreamServiceImpl implements IInviteStreamService {
|
||||
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<Object> scanResult = RedisUtil.scan(redisTemplate, key);
|
||||
if (scanResult.size() != 1) {
|
||||
List<InviteInfo> 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<Object> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<PlatformChannel> 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<CommonGBChannel> channelList, List<Region> regionList) {
|
||||
List<Integer> channelIds = new ArrayList<>();
|
||||
channelList.stream().forEach(commonGBChannel -> {
|
||||
channelIds.add(commonGBChannel.getGbId());
|
||||
});
|
||||
// 获取关联这些通道的平台
|
||||
List<Platform> platformList = platformChannelMapper.queryPlatFormListByChannelList(channelIds);
|
||||
if (platformList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Platform platform : platformList) {
|
||||
Set<Region> regionSet;
|
||||
if (regionList == null || regionList.isEmpty()) {
|
||||
regionSet = platformChannelMapper.queryShareRegion(platform.getId());
|
||||
}else {
|
||||
regionSet = new HashSet<>(regionList);
|
||||
}
|
||||
// 清理空的分组并发送消息
|
||||
Set<Region> deleteRegion = deleteEmptyRegion(regionSet, platform.getId());
|
||||
|
||||
List<CommonGBChannel> 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<CommonGBChannel> channelList) {
|
||||
@@ -505,6 +540,36 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void checkRegionAdd(List<CommonGBChannel> channelList) {
|
||||
List<Integer> channelIds = new ArrayList<>();
|
||||
channelList.stream().forEach(commonGBChannel -> {
|
||||
channelIds.add(commonGBChannel.getGbId());
|
||||
});
|
||||
List<Platform> platformList = platformChannelMapper.queryPlatFormListByChannelList(channelIds);
|
||||
if (platformList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
for (Platform platform : platformList) {
|
||||
|
||||
Set<Region> addRegion = getRegionNotShareByChannelList(channelList, platform.getId());
|
||||
List<CommonGBChannel> 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<Platform> queryPlatFormListByChannelDeviceId(Integer channelId, List<String> platforms) {
|
||||
return platformChannelMapper.queryPlatFormListForGBWithGBId(channelId, platforms);
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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<SendRtpInfo> 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<SsrcTransaction> 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<InviteInfo> inviteInfoList = inviteStreamService.getAllInviteInfo();
|
||||
if (inviteInfoList.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
|
||||
List<String> 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);
|
||||
}
|
||||
|
||||
|
||||
@@ -139,9 +139,9 @@ public class RegionServiceImpl implements IRegionService {
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<RegionTree> queryForTree(String query, Integer parent) {
|
||||
public List<RegionTree> queryForTree(String query, Integer parent, Boolean hasChannel) {
|
||||
List<RegionTree> regionList = regionMapper.queryForTree(query, parent);
|
||||
if (parent != null) {
|
||||
if (parent != null && hasChannel != null && hasChannel) {
|
||||
Region parentRegion = regionMapper.queryOne(parent);
|
||||
if (parentRegion != null) {
|
||||
List<RegionTree> channelList = commonGBChannelMapper.queryForRegionTreeByCivilCode(query, parentRegion.getDeviceId());
|
||||
@@ -224,4 +224,32 @@ public class RegionServiceImpl implements IRegionService {
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<Region> getPath(String deviceId) {
|
||||
Region region = regionMapper.queryByDeviceId(deviceId);
|
||||
if (region == null) {
|
||||
throw new ControllerException(ErrorCode.ERROR100.getCode(), "行政区划不存在");
|
||||
}
|
||||
List<Region> allParent = getAllParent(region);
|
||||
allParent.add(region);
|
||||
return allParent;
|
||||
}
|
||||
|
||||
|
||||
private List<Region> getAllParent(Region region) {
|
||||
if (region.getParentId() == null) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
|
||||
List<Region> regionList = new LinkedList<>();
|
||||
Region parent = regionMapper.queryByDeviceId(region.getParentDeviceId());
|
||||
if (parent == null) {
|
||||
return regionList;
|
||||
}
|
||||
regionList.add(parent);
|
||||
List<Region> allParent = getAllParent(parent);
|
||||
regionList.addAll(allParent);
|
||||
return regionList;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String, CatalogData> 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<DeviceChannel> deviceChannelList,
|
||||
List<Region> regionList, List<Group> 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<DeviceChannel> getDeviceChannelList(String deviceId) {
|
||||
CatalogData catalogData = data.get(deviceId);
|
||||
if (catalogData == null) {
|
||||
return null;
|
||||
}
|
||||
return catalogData.getChannelList();
|
||||
}
|
||||
|
||||
public List<Region> getRegionList(String deviceId) {
|
||||
CatalogData catalogData = data.get(deviceId);
|
||||
if (catalogData == null) {
|
||||
return null;
|
||||
}
|
||||
return catalogData.getRegionListList();
|
||||
}
|
||||
|
||||
public List<Group> 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<String> 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);
|
||||
}
|
||||
}
|
||||
290
src/main/java/com/genersoft/iot/vmp/gb28181/session/CatalogDataManager.java
Executable file
@@ -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<Object, Object> redisTemplate;
|
||||
|
||||
private final Map<String, CatalogData> 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<String> redisKeysForChannel = catalogData.getRedisKeysForChannel();
|
||||
if (redisKeysForChannel != null && !redisKeysForChannel.isEmpty()) {
|
||||
for (String deleteKey : redisKeysForChannel) {
|
||||
redisTemplate.opsForHash().delete(key, deleteKey);
|
||||
}
|
||||
}
|
||||
Set<String> redisKeysForRegion = catalogData.getRedisKeysForRegion();
|
||||
if (redisKeysForRegion != null && !redisKeysForRegion.isEmpty()) {
|
||||
for (String deleteKey : redisKeysForRegion) {
|
||||
redisTemplate.opsForHash().delete(key, deleteKey);
|
||||
}
|
||||
}
|
||||
Set<String> 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<DeviceChannel> deviceChannelList,
|
||||
List<Region> regionList, List<Group> 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<DeviceChannel> getDeviceChannelList(String deviceId, int sn) {
|
||||
List<DeviceChannel> 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<Region> getRegionList(String deviceId, int sn) {
|
||||
List<Region> 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<Group> getGroupList(String deviceId, int sn) {
|
||||
List<Group> 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<String> 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<String> 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<String> 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<DeviceChannel> deviceChannelList = getDeviceChannelList(deviceId, sn);
|
||||
if (catalogData.getTotal() == deviceChannelList.size()) {
|
||||
deviceChannelService.resetChannels(catalogData.getDevice().getId(), deviceChannelList);
|
||||
}else {
|
||||
deviceChannelService.updateChannels(catalogData.getDevice(), deviceChannelList);
|
||||
}
|
||||
List<Region> regionList = getRegionList(deviceId, sn);
|
||||
if ( regionList!= null && !regionList.isEmpty()) {
|
||||
regionService.batchAdd(regionList);
|
||||
}
|
||||
List<Group> 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<String> redisKeysForChannel = catalogData.getRedisKeysForChannel();
|
||||
if (redisKeysForChannel != null && !redisKeysForChannel.isEmpty()) {
|
||||
for (String deleteKey : redisKeysForChannel) {
|
||||
redisTemplate.opsForHash().delete(key, deleteKey);
|
||||
}
|
||||
}
|
||||
Set<String> redisKeysForRegion = catalogData.getRedisKeysForRegion();
|
||||
if (redisKeysForRegion != null && !redisKeysForRegion.isEmpty()) {
|
||||
for (String deleteKey : redisKeysForRegion) {
|
||||
redisTemplate.opsForHash().delete(key, deleteKey);
|
||||
}
|
||||
}
|
||||
Set<String> 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();
|
||||
}
|
||||
}
|
||||
@@ -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<SsrcTransaction> getSsrcTransactionByDeviceId(String deviceId){
|
||||
String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":*";
|
||||
|
||||
List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
|
||||
if (scanResult.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId();
|
||||
List<Object> values = redisTemplate.opsForHash().values(key);
|
||||
List<SsrcTransaction> 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<SsrcTransaction> getAll() {
|
||||
String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId() + ":*";
|
||||
|
||||
List<Object> scanResult = RedisUtil.scan(redisTemplate, key);
|
||||
if (scanResult.isEmpty()) {
|
||||
return new ArrayList<>();
|
||||
}
|
||||
String key = VideoManagerConstants.SIP_INVITE_SESSION_CALL_ID + userSetting.getServerId();
|
||||
List<Object> values = redisTemplate.opsForHash().values(key);
|
||||
List<SsrcTransaction> 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;
|
||||
}
|
||||
|
||||
@@ -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<String, SseEmitter> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -97,11 +97,13 @@ public class SIPProcessorObserver implements ISIPProcessorObserver {
|
||||
CallIdHeader callIdHeader = response.getCallIdHeader();
|
||||
if (callIdHeader != null) {
|
||||
SipEvent sipEvent = sipSubscribe.getSubscribe(callIdHeader.getCallId());
|
||||
if (sipEvent != null && sipEvent.getOkEvent() != null) {
|
||||
if (sipEvent != null) {
|
||||
if (sipEvent.getOkEvent() != null) {
|
||||
SipSubscribe.EventResult<ResponseEvent> eventResult = new SipSubscribe.EventResult<>(responseEvent);
|
||||
sipSubscribe.removeSubscribe(callIdHeader.getCallId());
|
||||
sipEvent.getOkEvent().response(eventResult);
|
||||
}
|
||||
sipSubscribe.removeSubscribe(callIdHeader.getCallId());
|
||||
}
|
||||
}
|
||||
}
|
||||
ISIPResponseProcessor sipRequestProcessor = responseProcessorMap.get(response.getCSeqHeader().getMethod());
|
||||
@@ -118,11 +120,13 @@ 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);
|
||||
sipSubscribe.removeSubscribe(callIdHeader.getCallId());
|
||||
if (sipEvent != null ) {
|
||||
if (sipEvent.getErrorEvent() != null) {
|
||||
SipSubscribe.EventResult<ResponseEvent> eventResult = new SipSubscribe.EventResult<>(responseEvent);
|
||||
sipEvent.getErrorEvent().response(eventResult);
|
||||
}
|
||||
sipSubscribe.removeSubscribe(callIdHeader.getCallId());
|
||||
}
|
||||
}
|
||||
}
|
||||
if (responseEvent.getDialog() != null) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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)) {
|
||||
|
||||
@@ -222,7 +222,7 @@ public class SIPCommanderForPlatform implements ISIPCommanderForPlatform {
|
||||
}
|
||||
|
||||
private void sendCatalogResponse(List<CommonGBChannel> 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<CommonGBChannel> 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(),
|
||||
|
||||
@@ -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<Byte> 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;
|
||||
|
||||
@@ -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());
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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<Object, Object> 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;
|
||||
|
||||
@@ -35,7 +35,7 @@ public class NotifyRequestForCatalogProcessor extends SIPRequestProcessorParent
|
||||
|
||||
private final ConcurrentLinkedQueue<NotifyCatalogChannel> channelList = new ConcurrentLinkedQueue<>();
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<HandlerCatchData> 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;
|
||||
|
||||
@@ -35,7 +35,7 @@ import java.util.concurrent.ConcurrentLinkedQueue;
|
||||
@Component
|
||||
public class NotifyRequestForMobilePositionProcessor extends SIPRequestProcessorParent {
|
||||
|
||||
private ConcurrentLinkedQueue<HandlerCatchData> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
private final ConcurrentLinkedQueue<HandlerCatchData> 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;
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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 ? "[注册请求]": "[注销请求]";
|
||||
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");
|
||||
}
|
||||
@@ -209,10 +207,10 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
|
||||
if (registerFlag) {
|
||||
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) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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<SipMsgInfo> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Qualifier("taskExecutor")
|
||||
@Autowired
|
||||
private ThreadPoolTaskExecutor taskExecutor;
|
||||
|
||||
private final ConcurrentLinkedQueue<SipMsgInfo> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Override
|
||||
public void afterPropertiesSet() throws Exception {
|
||||
@@ -78,29 +74,49 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
|
||||
@Override
|
||||
public void handForDevice(RequestEvent evt, Device device, Element rootElement) {
|
||||
boolean isEmpty = taskQueue.isEmpty();
|
||||
if (taskQueue.size() >= userSetting.getMaxNotifyCountQueue()) {
|
||||
log.error("[Alarm] 待处理消息队列已满 {},返回486 BUSY_HERE,消息不做处理", userSetting.getMaxNotifyCountQueue());
|
||||
return;
|
||||
}
|
||||
taskQueue.offer(new SipMsgInfo(evt, device, rootElement));
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 200)
|
||||
public void executeTaskQueue() {
|
||||
if (taskQueue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<SipMsgInfo> 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());
|
||||
}
|
||||
if (isEmpty) {
|
||||
taskExecutor.execute(() -> {
|
||||
if (log.isDebugEnabled()) {
|
||||
log.info("[处理报警通知]待处理数量:{}", taskQueue.size() );
|
||||
}
|
||||
while (!taskQueue.isEmpty()) {
|
||||
try {
|
||||
SipMsgInfo sipMsgInfo = taskQueue.poll();
|
||||
|
||||
Device device = sipMsgInfo.getDevice();
|
||||
Element deviceIdElement = sipMsgInfo.getRootElement().element("DeviceID");
|
||||
String channelId = deviceIdElement.getText().toString();
|
||||
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"));
|
||||
@@ -184,13 +200,11 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
|
||||
if (redisCatchStorage.deviceIsOnline(sipMsgInfo.getDevice().getDeviceId())) {
|
||||
publisher.deviceAlarmEventPublish(deviceAlarm);
|
||||
}
|
||||
}catch (Exception e) {
|
||||
} catch (Exception e) {
|
||||
log.error("未处理的异常 ", e);
|
||||
log.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
|
||||
log.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}", e.getMessage(), evt.getRequest());
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -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"));
|
||||
|
||||
@@ -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)->{
|
||||
|
||||
@@ -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<SipMsgInfo> taskQueue = new ConcurrentLinkedQueue<>();
|
||||
|
||||
@Autowired
|
||||
private NotifyMessageHandler notifyMessageHandler;
|
||||
|
||||
@@ -54,48 +58,68 @@ 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;
|
||||
}
|
||||
List<SipMsgInfo> 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(request, Response.OK);
|
||||
responseAck((SIPRequest) evt.getRequest(), Response.OK);
|
||||
} catch (SipException | InvalidArgumentException | ParseException e) {
|
||||
log.error("[命令发送失败] 心跳回复: {}", e.getMessage());
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
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());
|
||||
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.getRemoteAddress().getHostAddress());
|
||||
device.setLocalIp(request.getLocalAddress().getHostAddress());
|
||||
// 设备地址变化会引起目录订阅任务失效,需要重新添加
|
||||
if (device.getSubscribeCycleForCatalog() > 0) {
|
||||
deviceService.removeCatalogSubscribe(device, result->{
|
||||
deviceService.removeCatalogSubscribe(device, result -> {
|
||||
deviceService.addCatalogSubscribe(device);
|
||||
});
|
||||
}
|
||||
}
|
||||
if (device.getKeepaliveTime() == null) {
|
||||
device.setKeepaliveIntervalTime(60);
|
||||
}else {
|
||||
} 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());
|
||||
if (System.currentTimeMillis() / 1000 - lastTime > 10) {
|
||||
device.setKeepaliveIntervalTime(Long.valueOf(System.currentTimeMillis() / 1000 - lastTime).intValue());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -103,7 +127,7 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
|
||||
|
||||
if (device.isOnLine()) {
|
||||
deviceService.updateDevice(device);
|
||||
}else {
|
||||
} else {
|
||||
if (userSetting.getGbDeviceOnline() == 1) {
|
||||
// 对于已经离线的设备判断他的注册是否已经过期
|
||||
device.setOnLine(true);
|
||||
@@ -114,9 +138,10 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
|
||||
// 刷新过期任务
|
||||
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
|
||||
// 如果三次心跳失败,则设置设备离线
|
||||
dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime()*1000*3);
|
||||
dynamicTask.startDelay(registerExpireTaskKey, () -> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime() * 1000 * 3);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public void handForPlatform(RequestEvent evt, Platform parentPlatform, Element element) {
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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,13 +72,33 @@ 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()) {
|
||||
}
|
||||
|
||||
@Scheduled(fixedDelay = 50)
|
||||
@Transactional
|
||||
public void executeTaskQueue(){
|
||||
if (taskQueue.isEmpty()) {
|
||||
return;
|
||||
}
|
||||
List<HandlerCatchData> 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 {
|
||||
HandlerCatchData take = taskQueue.poll();
|
||||
Element rootElement = null;
|
||||
try {
|
||||
rootElement = getRootElement(take.getEvt(), take.getDevice().getCharset());
|
||||
@@ -107,7 +119,7 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
log.info("[收到通道]设备:{}的: 0个", take.getDevice().getDeviceId());
|
||||
// 数据已经完整接收
|
||||
deviceChannelService.cleanChannelsForDevice(take.getDevice().getId());
|
||||
catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), null);
|
||||
catalogDataCatch.setChannelSyncEnd(take.getDevice().getDeviceId(), sn, null);
|
||||
} else {
|
||||
Iterator<Element> deviceListIterator = deviceListElement.elementIterator();
|
||||
if (deviceListIterator != null) {
|
||||
@@ -121,12 +133,13 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
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(device.getId());
|
||||
channel.setDeviceDbId(take.getDevice().getId());
|
||||
if (channel.getParentId() != null && channel.getParentId().equals(sipConfig.getId())) {
|
||||
channel.setParentId(null);
|
||||
}
|
||||
@@ -140,56 +153,53 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
|
||||
// 业务分组/虚拟组织
|
||||
Group group = Group.getInstance(channel);
|
||||
if (group != null) {
|
||||
channel.setParental(1);
|
||||
channel.setChannelType(2);
|
||||
groupList.add(group);
|
||||
}
|
||||
}
|
||||
channelList.add(channel);
|
||||
}
|
||||
int sn = Integer.parseInt(snElement.getText());
|
||||
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);
|
||||
log.info("[收到通道]设备: {} -> {}个,{}/{}", take.getDevice().getDeviceId(), channelList.size(), catalogDataCatch.size(take.getDevice().getDeviceId(), sn), sumNum);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
} 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
processing.set(false);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@Transactional
|
||||
public boolean saveData(Device device) {
|
||||
public boolean saveData(Device device, int sn) {
|
||||
|
||||
boolean result = true;
|
||||
List<DeviceChannel> deviceChannelList = catalogDataCatch.getDeviceChannelList(device.getDeviceId());
|
||||
List<DeviceChannel> deviceChannelList = catalogDataCatch.getDeviceChannelList(device.getDeviceId(), sn);
|
||||
if (deviceChannelList != null && !deviceChannelList.isEmpty()) {
|
||||
result &= deviceChannelService.resetChannels(device.getId(), deviceChannelList);
|
||||
}
|
||||
|
||||
List<Region> regionList = catalogDataCatch.getRegionList(device.getDeviceId());
|
||||
List<Region> regionList = catalogDataCatch.getRegionList(device.getDeviceId(), sn);
|
||||
if ( regionList!= null && !regionList.isEmpty()) {
|
||||
result &= regionService.batchAdd(regionList);
|
||||
}
|
||||
|
||||
List<Group> groupList = catalogDataCatch.getGroupList(device.getDeviceId());
|
||||
List<Group> 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);
|
||||
}
|
||||
}
|
||||
|
||||
public boolean isSyncRunning(String deviceId) {
|
||||
if (catalogDataCatch.getDeviceChannelList(deviceId) == null) {
|
||||
return false;
|
||||
} else {
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -706,6 +706,7 @@ public class XmlUtil {
|
||||
* @return
|
||||
*/
|
||||
private static Object simpleTypeDeal(Class<?> tClass, Object val) {
|
||||
try {
|
||||
if (val == null || val.toString().equalsIgnoreCase("null")) {
|
||||
return null;
|
||||
}
|
||||
@@ -717,10 +718,14 @@ public class XmlUtil {
|
||||
}
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -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) {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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());
|
||||
}
|
||||
}
|
||||
|
||||
@@ -67,4 +67,7 @@ public interface IMediaNodeServerService {
|
||||
StreamInfo startProxy(MediaServer mediaServer, StreamProxy streamProxy);
|
||||
|
||||
void stopProxy(MediaServer mediaServer, String streamKey);
|
||||
|
||||
List<String> listRtpServer(MediaServer mediaServer);
|
||||
|
||||
}
|
||||
|
||||
@@ -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<String> listRtpServer(MediaServer mediaServer);
|
||||
}
|
||||
|
||||
@@ -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<String> 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<MediaServer> getAllOnlineList() {
|
||||
List<MediaServer> result = new ArrayList<>();
|
||||
List<Object> 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<Object> 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<Object> mediaServerIdSet = redisTemplate.opsForZSet().reverseRange(key, 0, -1);
|
||||
|
||||
List<MediaServer> 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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<Object, Object> 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<Object> queryResult = RedisUtil.scan(redisTemplate, key);
|
||||
Map<Integer, SendRtpInfo> 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<Integer, SendRtpInfo> 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;
|
||||
}
|
||||
}
|
||||
@@ -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);
|
||||
|
||||