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
This commit is contained in:
648540858
2024-11-10 15:29:53 +08:00
170 changed files with 4812 additions and 3710 deletions

View File

@@ -40,6 +40,7 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
![3-1](doc/_media/3-1.png "3-1.png")
![3-2](doc/_media/3-2.png "3-2.png")
![build_1](https://images.gitee.com/uploads/images/2022/0304/101919_ee5b8c79_1018729.png "2022-03-04_10-13.png")
![运维中心](doc/_media/log.jpg "log.jpg")
# 功能特性
- [X] 集成web界面

Binary file not shown.

After

Width:  |  Height:  |  Size: 84 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 31 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 81 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 124 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 68 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 47 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 98 KiB

View File

@@ -33,6 +33,10 @@
![_media/img_11.png](_media/img_11.png)
### 3. 宇视科技
![_media/img_25.png](_media/img_25.png)
### 3. 艾科威视摄像头
![_media/img_15.png](_media/img_15.png)

View File

@@ -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

Binary file not shown.

After

Width:  |  Height:  |  Size: 195 KiB

25
pom.xml
View File

@@ -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>

View File

@@ -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());
}
}
// 项目重启

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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_";

View File

@@ -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;
}
}

View File

@@ -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(){
}

View File

@@ -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();
}

View File

@@ -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-ACTIVEtcp主动模式 TCP-PASSIVEtcp被动模式
*/
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;
}
}

View File

@@ -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());

View File

@@ -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)) {

View File

@@ -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()

View File

@@ -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);
}
});
}
}

View File

@@ -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));
}
}

View File

@@ -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;
}
}

View File

@@ -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;
}

View File

@@ -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;
}
}

View File

@@ -19,4 +19,7 @@ public class GroupTree extends Group{
@Schema(description = "类型, 行政区划:0 摄像头: 1")
private int type;
@Schema(description = "在线状态")
private String status;
}

View File

@@ -18,4 +18,7 @@ public class RegionTree extends Region {
@Schema(description = "类型, 行政区划:0 摄像头: 1")
private int type;
@Schema(description = "在线状态")
private String status;
}

View File

@@ -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))

View File

@@ -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);

View File

@@ -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);
}
}

View File

@@ -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

View File

@@ -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);
}

View File

@@ -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")

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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" +

View File

@@ -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);
}

View File

@@ -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 " +

View File

@@ -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();
}

View File

@@ -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'");

View File

@@ -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);
}

View File

@@ -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();
}
}

View File

@@ -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());
}
}

View File

@@ -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

View File

@@ -119,4 +119,7 @@ public interface IDeviceChannelService {
void updateChannelForNotify(DeviceChannel channel);
DeviceChannel getOneForSource(int deviceDbId, String channelId);
DeviceChannel getOneBySourceId(int deviceDbId, String channelId);
}

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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();
/**
* 获取点播的状态信息

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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);
}

View File

@@ -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

View File

@@ -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);
}
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}
}
}

View File

@@ -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);

View File

@@ -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);

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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);
}
}

View 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();
}
}

View File

@@ -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;
}

View File

@@ -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);
}
}
}
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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);

View File

@@ -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)) {

View File

@@ -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(),

View File

@@ -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;

View File

@@ -99,7 +99,7 @@ public class AckRequestProcessor extends SIPRequestProcessorParent implements In
}
// tcp主动时此时是级联下级平台在回复200ok时本地已经请求zlm开启监听跳过下面步骤
if (sendRtpItem.isTcpActive()) {
log.info("收到ACKrtp/{} TCP主动方式后续处理", sendRtpItem.getStream());
log.info("收到ACKrtp/{} TCP主动方式等收到上级连接后开始发流", sendRtpItem.getStream());
return;
}
MediaServer mediaServer = mediaServerService.getOne(sendRtpItem.getMediaServerId());

View File

@@ -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);
}

View File

@@ -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");
// 通道存在发100TRYING
try {
responseAck(request, Response.BAD_REQUEST);
} catch (SipException | InvalidArgumentException | ParseException e) {
log.error("[命令发送失败] 上级Invite TRYING: {}", e.getMessage());
}
return;
}
// 通道存在发100TRYING
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;

View File

@@ -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;

View File

@@ -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;

View File

@@ -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");

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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"));

View File

@@ -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)->{

View File

@@ -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) {

View File

@@ -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");

View File

@@ -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);
}
}

View File

@@ -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);
}

View File

@@ -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;
}
}
}

View File

@@ -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) {

View File

@@ -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);
}

View File

@@ -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;
}
}

View File

@@ -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());
}
}

View File

@@ -67,4 +67,7 @@ public interface IMediaNodeServerService {
StreamInfo startProxy(MediaServer mediaServer, StreamProxy streamProxy);
void stopProxy(MediaServer mediaServer, String streamKey);
List<String> listRtpServer(MediaServer mediaServer);
}

View File

@@ -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);
}

View File

@@ -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);
}
}
}

View File

@@ -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;
}
}

View File

@@ -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);

Some files were not shown because too many files have changed in this diff Show More