diff --git a/README.md b/README.md
index f95d7a03e..d9e44ab9d 100644
--- a/README.md
+++ b/README.md
@@ -99,15 +99,16 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
- [X] 支持接口鉴权
- [X] 云端录像,推流/代理/国标视频均可以录制在云端服务器,支持预览和下载
- [X] 支持打包可执行jar和war
+- [X] 支持跨域请求,支持前后端分离部署
# 遇到问题如何解决
国标最麻烦的地方在于设备的兼容性,所以需要大量的设备来测试,目前作者手里的设备有限,再加上作者水平有限,所以遇到问题在所难免;
-1. 查看wiki,仔细的阅读可以帮你避免几乎所有的问题
+1. 查看文档网站,仔细的阅读可以帮你避免几乎所有的问题
2. 搜索issues,这里有大部分的答案
3. 加QQ群(901799015),这里有大量热心的小伙伴,但是前提新希望你已经仔细阅读了wiki和搜索了issues。
4. 你可以请作者为你解答,但是我不是免费的。
-5. 你可以把遇到问题的设备寄给我,可以更容易的复现问题。
+5. 你可以把遇到问题的设备寄给我,可以更容易的兼容设备和解决问题。
# 使用帮助
QQ群: 901799015, ZLM使用文档[https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)
diff --git a/doc/_content/ability/gis.md b/doc/_content/ability/gis.md
index 8796dba05..f77a428c0 100644
--- a/doc/_content/ability/gis.md
+++ b/doc/_content/ability/gis.md
@@ -14,7 +14,7 @@ WVP提供了简单的电子地图用于设备的定位以及移动设备的轨
PS: 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。
### 更换底图以及底图配置
-目前WVP支持使用了更换底图,配置文件在web_src/static/js/mapConfig.js,请修改后重新编译前端文件。
+目前WVP支持使用了更换底图,配置文件在web_src/static/js/config.js,请修改后重新编译前端文件。
```javascript
window.mapParam = {
// 开启/关闭地图功能
diff --git a/doc/_content/introduction/deployment.md b/doc/_content/introduction/deployment.md
index a04aa45d6..45c1a83a6 100644
--- a/doc/_content/introduction/deployment.md
+++ b/doc/_content/introduction/deployment.md
@@ -27,13 +27,32 @@
```shell
nohup java -jar wvp-pro-*.jar &
```
-war包:
+**war包:**
下载Tomcat后将war包放入webapps中,启动Tomcat以解压war包,停止Tomcat后,删除ROOT目录以及war包,将解压后的war包目录重命名为ROOT,将配置文件中的Server.port配置为与Tomcat端口一致
然后启动Tomcat。
**启动ZLM**
```shell
nohup ./MediaServer -d -m 3 &
```
-
+### 前后端分离部署
+前后端部署目前在最新的版本已经支持,请使用3月15日之后的版本部署
+前端编译后的文件在`src/main/resources/static`中,将此目录下的文件部署。
+前后端分离部署最大的问题是跨域的解决,之前版本使用cookie完成登录流程,而cookie是不可以在复杂跨域中使用的。所以当前版本使用JWT生成的TOKEN作为认证凭据,
+部署前端后需要在wvp中配置前端访问的地址以完成跨域流程。
+**配置前端服务器**
+1. 假如你的服务有公网域名为xxx.com,公网IP为11.11.11.11, 那么你可以在wvp中这样配置:
+```yaml
+user-settings:
+ # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个
+ allowed-origins:
+ - http://xxx.com:8008
+ - http://11.11.11.11:8008
+```
+配置不是必须的,你使用哪个ip/域名访问就配置哪个即可。修改配置后重启wvp以使配置生效。
+2. 在`src/main/resources/static/static/js/config.js`下配置服务器的地址,也就是wvp服务的地址
+```javascript
+window.baseUrl = "http://xxx.com:18080"
+```
+`这里的地址是需要客户电脑能访问到的,因为请求是客户端电脑发起,与代理不同`
[接入设备](./_content/ability/device.md)
diff --git a/pom.xml b/pom.xml
index 75d29f2bb..553426472 100644
--- a/pom.xml
+++ b/pom.xml
@@ -11,7 +11,7 @@
com.genersoft
wvp-pro
- 2.6.7
+ 2.6.8
web video platform
国标28181视频平台
${project.packaging}
@@ -123,11 +123,9 @@
spring-boot-starter-security
-
- com.alibaba
- druid-spring-boot-starter
- 1.2.11
+ org.springframework.boot
+ spring-boot-starter-jdbc
@@ -216,8 +214,6 @@
4.10.0
-
-
io.github.rburgst
@@ -226,10 +222,17 @@
+
+
+
+
+
+
+
- net.sf.kxml
- kxml2
- 2.3.0
+ org.bitbucket.b_c
+ jose4j
+ 0.9.3
@@ -289,7 +292,7 @@
org.springframework.boot
spring-boot-maven-plugin
- 2.3.5.RELEASE
+ 2.7.2
true
diff --git a/sql/2.6.6-2.6.7更新.sql b/sql/2.6.6-2.6.7更新.sql
new file mode 100755
index 000000000..09732b308
--- /dev/null
+++ b/sql/2.6.6-2.6.7更新.sql
@@ -0,0 +1,12 @@
+alter table device
+ add asMessageChannel int default 0;
+
+alter table parent_platform
+ add asMessageChannel int default 0;
+
+alter table device
+ add mediaServerId varchar(50) default null;
+
+
+
+
diff --git a/src/main/resources/db/migration/V2.6.7_20230201__初始化.sql b/sql/初始化.sql
similarity index 99%
rename from src/main/resources/db/migration/V2.6.7_20230201__初始化.sql
rename to sql/初始化.sql
index 7a15f905c..7b8be6602 100644
--- a/src/main/resources/db/migration/V2.6.7_20230201__初始化.sql
+++ b/sql/初始化.sql
@@ -32,6 +32,7 @@ CREATE TABLE `device` (
`transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
+ `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
`ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
@@ -47,6 +48,7 @@ CREATE TABLE `device` (
`mobilePositionSubmissionInterval` int DEFAULT '5',
`subscribeCycleForAlarm` int DEFAULT NULL,
`ssrcCheck` int DEFAULT '0',
+ `asMessageChannel` int DEFAULT '0',
`geoCoordSys` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`treeType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`custom_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
@@ -329,6 +331,7 @@ CREATE TABLE `parent_platform` (
`catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
`ptz` int DEFAULT NULL,
`rtcp` int DEFAULT NULL,
+ `asMessageChannel` int DEFAULT '0',
`status` bit(1) DEFAULT NULL,
`startOfflinePush` int DEFAULT '0',
`administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
diff --git a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
index eab220793..886d6fa17 100644
--- a/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
+++ b/src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
@@ -1,6 +1,5 @@
package com.genersoft.iot.vmp;
-import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
import com.genersoft.iot.vmp.utils.GitUtil;
import com.genersoft.iot.vmp.utils.SpringBeanFactory;
import org.slf4j.Logger;
@@ -25,7 +24,6 @@ import java.util.Collections;
@ServletComponentScan("com.genersoft.iot.vmp.conf")
@SpringBootApplication
@EnableScheduling
-@EnableDruidSupport
public class VManageBootstrap extends SpringBootServletInitializer {
private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
diff --git a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
index 0d6f5ee73..a501dafac 100644
--- a/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
+++ b/src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
@@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.common;
import io.swagger.v3.oas.annotations.media.Schema;
import java.io.Serializable;
+import java.util.Objects;
@Schema(description = "流信息")
public class StreamInfo implements Serializable, Cloneable{
@@ -168,7 +169,7 @@ public class StreamInfo implements Serializable, Cloneable{
}
public void setRtmp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
- String file = String.format("%s/%s/%s", app, stream, callIdParam);
+ String file = String.format("%s/%s%s", app, stream, callIdParam);
if (port > 0) {
this.rtmp = new StreamURL("rtmp", host, port, file);
}
@@ -178,7 +179,7 @@ public class StreamInfo implements Serializable, Cloneable{
}
public void setRtsp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
- String file = String.format("%s/%s/%s", app, stream, callIdParam);
+ String file = String.format("%s/%s%s", app, stream, callIdParam);
if (port > 0) {
this.rtsp = new StreamURL("rtsp", host, port, file);
}
@@ -236,8 +237,11 @@ public class StreamInfo implements Serializable, Cloneable{
}
}
- public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam, boolean isPlay) {
- String file = String.format("index/api/webrtc?app=%s&stream=%s&type=%s%s", app, stream, isPlay?"play":"push", callIdParam);
+ public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) {
+ if (callIdParam != null) {
+ callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&");
+ }
+ String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam);
if (port > 0) {
this.rtc = new StreamURL("http", host, port, file);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
index c2dbbc671..4d885e86c 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
@@ -66,9 +66,7 @@ public class ApiAccessFilter extends OncePerRequestFilter {
logDto.setUri(servletRequest.getRequestURI());
logDto.setCreateTime(DateUtil.getNow());
logService.add(logDto);
-// logger.warn("[Api Access] [{}] [{}] [{}] [{}] [{}] {}ms",
-// uriName, servletRequest.getMethod(), servletRequest.getRequestURI(), servletRequest.getRemoteAddr(), HttpStatus.valueOf(servletResponse.getStatus()),
-// System.currentTimeMillis() - start);
+
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java
index 728afb96b..0333e0dc3 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java
@@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.authentication.BadCredentialsException;
+import org.springframework.web.HttpRequestMethodNotSupportedException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;
@@ -32,6 +33,28 @@ public class GlobalExceptionHandler {
return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage());
}
+ /**
+ * 默认异常处理
+ * @param e 异常
+ * @return 统一返回结果
+ */
+ @ExceptionHandler(IllegalStateException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public WVPResult exceptionHandler(IllegalStateException e) {
+ return WVPResult.fail(ErrorCode.ERROR400);
+ }
+
+ /**
+ * 默认异常处理
+ * @param e 异常
+ * @return 统一返回结果
+ */
+ @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public WVPResult exceptionHandler(HttpRequestMethodNotSupportedException e) {
+ return WVPResult.fail(ErrorCode.ERROR400);
+ }
+
/**
* 自定义异常处理, 处理controller中返回的错误
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java b/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java
index 881611d53..33fdd23df 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java
@@ -10,14 +10,11 @@ import org.springframework.context.annotation.Bean;
import org.springframework.core.MethodParameter;
import org.springframework.http.MediaType;
import org.springframework.http.converter.HttpMessageConverter;
-import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
import org.springframework.http.server.ServerHttpRequest;
import org.springframework.http.server.ServerHttpResponse;
import org.springframework.web.bind.annotation.RestControllerAdvice;
import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
-import java.util.List;
-
/**
* 全局统一返回结果
* @author lin
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
index d24380a3f..6cc3b415b 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.conf;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.IMediaServerService;
+import org.apache.catalina.connector.ClientAbortException;
import org.apache.http.HttpHost;
import org.apache.http.HttpRequest;
import org.apache.http.HttpResponse;
@@ -169,13 +170,14 @@ public class ProxyServletConfig {
protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
- String remoteHost = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
- if (mediaInfo != null) {
- if (!ObjectUtils.isEmpty(queryStr)) {
- queryStr += "&remoteHost=" + remoteHost;
- }else {
- queryStr = "remoteHost=" + remoteHost;
- }
+ if (mediaInfo == null) {
+ return null;
+ }
+ String remoteHost = String.format("http://%s:%s", mediaInfo.getStreamIp(), mediaInfo.getRecordAssistPort());
+ if (!ObjectUtils.isEmpty(queryStr)) {
+ queryStr += "&remoteHost=" + remoteHost;
+ }else {
+ queryStr = "remoteHost=" + remoteHost;
}
return queryStr;
}
@@ -192,6 +194,12 @@ public class ProxyServletConfig {
} catch (IOException ioException) {
if (ioException instanceof ConnectException) {
logger.error("录像服务 连接失败");
+ }else if (ioException instanceof ClientAbortException) {
+ /**
+ * TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常,
+ * TODO 暂时去除异常处理。后续使用其他代理框架修改测试
+ */
+
}else {
logger.error("录像服务 代理失败: ", e);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
index f32c250a4..600b9e8df 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
@@ -4,7 +4,6 @@ package com.genersoft.iot.vmp.conf;
import org.junit.jupiter.api.Order;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.stereotype.Component;
-import org.springframework.util.ObjectUtils;
@Component
@ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
@@ -13,6 +12,8 @@ public class SipConfig {
private String ip;
+ private String showIp;
+
private Integer port;
private String domain;
@@ -96,4 +97,14 @@ public class SipConfig {
this.alarm = alarm;
}
+ public String getShowIp() {
+ if (this.showIp == null) {
+ return this.ip;
+ }
+ return showIp;
+ }
+
+ public void setShowIp(String showIp) {
+ this.showIp = showIp;
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java b/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
index b3f144a79..0a8405b16 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
@@ -40,17 +40,20 @@ public class SipPlatformRunner implements CommandLineRunner {
List parentPlatforms = storager.queryEnableParentPlatformList(true);
for (ParentPlatform parentPlatform : parentPlatforms) {
+
+ ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
+
// 更新缓存
ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
parentPlatformCatch.setParentPlatform(parentPlatform);
parentPlatformCatch.setId(parentPlatform.getServerGBId());
redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
- // 设置所有平台离线
- platformService.offline(parentPlatform, true);
// 取消订阅
- sipCommanderForPlatform.unregister(parentPlatform, null, (eventResult)->{
+ sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
platformService.login(parentPlatform);
});
+ // 设置所有平台离线
+ platformService.offline(parentPlatform, true);
}
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
index 811f7e969..242efb4a3 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
@@ -50,15 +50,22 @@ public class UserSetting {
private Boolean pushStreamAfterAck = Boolean.FALSE;
private Boolean sipLog = Boolean.FALSE;
+ private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE;
+
+ private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
private String serverId = "000000";
+ private String recordPath = null;
+
private String thirdPartyGBIdReg = "[\\s\\S]*";
private String broadcastForPlatform = "UDP";
private List interfaceAuthenticationExcludes = new ArrayList<>();
+ private List allowedOrigins = new ArrayList<>();
+
public Boolean getSavePositionHistory() {
return savePositionHistory;
}
@@ -238,4 +245,36 @@ public class UserSetting {
public void setSipLog(Boolean sipLog) {
this.sipLog = sipLog;
}
+
+ public List getAllowedOrigins() {
+ return allowedOrigins;
+ }
+
+ public void setAllowedOrigins(List allowedOrigins) {
+ this.allowedOrigins = allowedOrigins;
+ }
+
+ public Boolean getSendToPlatformsWhenIdLost() {
+ return sendToPlatformsWhenIdLost;
+ }
+
+ public void setSendToPlatformsWhenIdLost(Boolean sendToPlatformsWhenIdLost) {
+ this.sendToPlatformsWhenIdLost = sendToPlatformsWhenIdLost;
+ }
+
+ public Boolean getRefuseChannelStatusChannelFormNotify() {
+ return refuseChannelStatusChannelFormNotify;
+ }
+
+ public void setRefuseChannelStatusChannelFormNotify(Boolean refuseChannelStatusChannelFormNotify) {
+ this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify;
+ }
+
+ public String getRecordPath() {
+ return recordPath;
+ }
+
+ public void setRecordPath(String recordPath) {
+ this.recordPath = recordPath;
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java b/src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java
deleted file mode 100644
index 12f6e6362..000000000
--- a/src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java
+++ /dev/null
@@ -1,64 +0,0 @@
-package com.genersoft.iot.vmp.conf.druid;
-
-import com.alibaba.druid.support.http.StatViewServlet;
-import com.alibaba.druid.support.http.WebStatFilter;
-import org.springframework.beans.factory.annotation.Value;
-import org.springframework.boot.web.servlet.FilterRegistrationBean;
-import org.springframework.boot.web.servlet.ServletRegistrationBean;
-import org.springframework.context.annotation.Bean;
-
-import javax.servlet.Filter;
-import javax.servlet.Servlet;
-
-/**
- * druid监控配置
- * @author
- */
-public class DruidConfiguration {
-
- @Value("${rj-druid-manage.allow:127.0.0.1}")
- private String allow;
-
- @Value("${rj-druid-manage.deny:}")
- private String deny;
-
- @Value("${rj-druid-manage.loginUsername:admin}")
- private String loginUsername;
-
- @Value("${rj-druid-manage.loginPassword:admin}")
- private String loginPassword;
-
- @Value("${rj-druid-manage.resetEnable:false}")
- private String resetEnable;
-
- /**
- * druid监控页面开启
- */
- @Bean
- public ServletRegistrationBean druidServlet() {
- ServletRegistrationBean servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
- // IP白名单
- servletRegistrationBean.addInitParameter("allow", allow);
- // IP黑名单(共同存在时,deny优先于allow)
- servletRegistrationBean.addInitParameter("deny", deny);
- //控制台管理用户
- servletRegistrationBean.addInitParameter("loginUsername", loginUsername);
- servletRegistrationBean.addInitParameter("loginPassword", loginPassword);
- //是否能够重置数据 禁用HTML页面上的“Reset All”功能
- servletRegistrationBean.addInitParameter("resetEnable", resetEnable);
- return servletRegistrationBean;
- }
-
- /**
- * druid url监控配置
- */
- @Bean
- public FilterRegistrationBean filterRegistrationBean() {
- FilterRegistrationBean filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
- filterRegistrationBean.addUrlPatterns("/*");
- filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
- return filterRegistrationBean;
- }
-
-
-}
\ No newline at end of file
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java b/src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java
deleted file mode 100644
index 5fd710be6..000000000
--- a/src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.genersoft.iot.vmp.conf.druid;
-
-import org.springframework.boot.web.servlet.ServletComponentScan;
-import org.springframework.context.annotation.Import;
-
-import java.lang.annotation.*;
-
-/**
- * druid监控支持注解
- *
- * @author
- * {@link DruidConfiguration} druid监控页面安全配置支持
- * {@link ServletComponentScan} druid监控页面需要扫描servlet
- */
-@Target(ElementType.TYPE)
-@Retention(RetentionPolicy.RUNTIME)
-@Documented
-@Inherited
-@Import({
- DruidConfiguration.class,
-})
-@ServletComponentScan
-public @interface EnableDruidSupport {
-}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
index cd5042088..7d617cc30 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
@@ -1,11 +1,11 @@
package com.genersoft.iot.vmp.conf.security;
import com.alibaba.fastjson2.JSONObject;
+import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
-import org.apache.poi.hssf.eventmodel.ERFListener;
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.AuthenticationException;
+import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.web.AuthenticationEntryPoint;
import org.springframework.stereotype.Component;
@@ -18,17 +18,15 @@ import java.io.IOException;
* @author lin
*/
@Component
-public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
-
- private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
+public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
@Override
public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
- // 允许跨域
- response.setHeader("Access-Control-Allow-Origin", "*");
- // 允许自定义请求头token(允许head跨域)
- response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");
- response.setHeader("Content-type", "application/json;charset=UTF-8");
+ String jwt = request.getHeader(JwtUtils.getHeader());
+ JwtUser jwtUser = JwtUtils.verifyToken(jwt);
+ String username = jwtUser.getUserName();
+ UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() );
+ SecurityContextHolder.getContext().setAuthentication(token);
JSONObject jsonObject = new JSONObject();
jsonObject.put("code", ErrorCode.ERROR401.getCode());
jsonObject.put("msg", ErrorCode.ERROR401.getMsg());
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java
index 509a1e038..0cda4a5cd 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java
@@ -1,7 +1,9 @@
package com.genersoft.iot.vmp.conf.security;
-import java.time.LocalDateTime;
-
+import com.alibaba.excel.util.StringUtils;
+import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
+import com.genersoft.iot.vmp.service.IUserService;
+import com.genersoft.iot.vmp.storager.dao.dto.User;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -10,10 +12,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.core.userdetails.UsernameNotFoundException;
import org.springframework.stereotype.Component;
-import com.alibaba.excel.util.StringUtils;
-import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
-import com.genersoft.iot.vmp.service.IUserService;
-import com.genersoft.iot.vmp.storager.dao.dto.User;
+import java.time.LocalDateTime;
/**
* 用户登录认证逻辑
@@ -45,4 +44,8 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService {
}
+
+
+
+
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java
deleted file mode 100644
index f3fd06852..000000000
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java
+++ /dev/null
@@ -1,24 +0,0 @@
-package com.genersoft.iot.vmp.conf.security;
-
-import org.slf4j.Logger;
-import org.slf4j.LoggerFactory;
-import org.springframework.security.web.session.InvalidSessionStrategy;
-
-import javax.servlet.ServletException;
-import javax.servlet.http.HttpServletRequest;
-import javax.servlet.http.HttpServletResponse;
-import java.io.IOException;
-
-/**
- * 登录超时的处理
- */
-public class InvalidSessionHandler implements InvalidSessionStrategy {
-
- private final static Logger logger = LoggerFactory.getLogger(InvalidSessionHandler.class);
-
- @Override
- public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException, ServletException {
- String username = request.getParameter("username");
- logger.info("[登录超时] - [{}]", username);
- }
-}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
new file mode 100644
index 000000000..e50a8b0ec
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java
@@ -0,0 +1,84 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import com.genersoft.iot.vmp.conf.UserSetting;
+import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
+import org.apache.commons.lang3.StringUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
+import org.springframework.security.core.context.SecurityContextHolder;
+import org.springframework.stereotype.Component;
+import org.springframework.web.filter.OncePerRequestFilter;
+
+import javax.servlet.FilterChain;
+import javax.servlet.ServletException;
+import javax.servlet.http.HttpServletRequest;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.ArrayList;
+
+/**
+ * jwt token 过滤器
+ */
+
+@Component
+public class JwtAuthenticationFilter extends OncePerRequestFilter {
+
+
+ @Autowired
+ private UserSetting userSetting;
+
+
+ @Override
+ protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
+
+ // 忽略登录请求的token验证
+ String requestURI = request.getRequestURI();
+ if (requestURI.equalsIgnoreCase("/api/user/login")) {
+ chain.doFilter(request, response);
+ return;
+ }
+ if (!userSetting.isInterfaceAuthentication()) {
+ // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
+ 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相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口
+ if (StringUtils.isBlank(jwt)) {
+ jwt = request.getParameter(JwtUtils.getHeader());
+ if (StringUtils.isBlank(jwt)) {
+ chain.doFilter(request, response);
+ return;
+ }
+ }
+
+ JwtUser jwtUser = JwtUtils.verifyToken(jwt);
+ String username = jwtUser.getUserName();
+ // TODO 处理各个状态
+ switch (jwtUser.getStatus()){
+ case EXPIRED:
+ response.setStatus(400);
+ chain.doFilter(request, response);
+ // 异常
+ return;
+ case EXCEPTION:
+ // 过期
+ response.setStatus(400);
+ chain.doFilter(request, response);
+ return;
+ case EXPIRING_SOON:
+ // 即将过期
+// return;
+ default:
+ }
+
+ // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
+ UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword(), new ArrayList<>() );
+ SecurityContextHolder.getContext().setAuthentication(token);
+ chain.doFilter(request, response);
+ }
+
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
new file mode 100644
index 000000000..57911b045
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java
@@ -0,0 +1,138 @@
+package com.genersoft.iot.vmp.conf.security;
+
+import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
+import org.jose4j.json.JsonUtil;
+import org.jose4j.jwk.RsaJsonWebKey;
+import org.jose4j.jws.AlgorithmIdentifiers;
+import org.jose4j.jws.JsonWebSignature;
+import org.jose4j.jwt.JwtClaims;
+import org.jose4j.jwt.NumericDate;
+import org.jose4j.jwt.consumer.ErrorCodes;
+import org.jose4j.jwt.consumer.InvalidJwtException;
+import org.jose4j.jwt.consumer.JwtConsumer;
+import org.jose4j.jwt.consumer.JwtConsumerBuilder;
+import org.jose4j.lang.JoseException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import java.security.PrivateKey;
+import java.time.LocalDateTime;
+import java.time.ZoneOffset;
+
+public class JwtUtils {
+
+ private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
+
+ private static final String HEADER = "access-token";
+ private static final String AUDIENCE = "Audience";
+
+ private static final long EXPIRED_THRESHOLD = 10 * 60;
+
+ private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae";
+ private static final String privateKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\",\"d\":\"ed7U_k3rJ4yTk70JtRSIfjKGiEb67BO1TabcymnljKO7RU8nage84zZYuSu_XpQsHk6P1f0Gzxkicghm_Er-FrfVn2pp70Xu52z3yRd6BJUgWLDFk97ngScIyw5OiULKU9SrZk2frDpftNCSUcIgb50F8m0QAnBa_CdPsQKbuuhLv8V8tBAV7F_lAwvSBgu56wRo3hPz5dWH8YeXM7XBfQ9viFMNEKd21sP_j5C7ueUnXT66nBxe3ZJEU3iuMYM6D6dB_KW2GfZC6WmTgvGhhxJD0h7aYmfjkD99MDleB7SkpbvoODOqiQ5Epb7Nyh6kv5u4KUv2CJYtATLZkUeMkQ\",\"p\":\"uBUjWPWtlGksmOqsqCNWksfqJvMcnP_8TDYN7e4-WnHL4N-9HjRuPDnp6kHvCIEi9SEfxm7gNxlRcWegvNQr3IZCz7TnCTexXc5NOklB9OavWFla6u-s3Thn6Tz45-EUjpJr0VJMxhO-KxGmuTwUXBBp4vN6K2qV6rQNFmgkWzk\",\"q\":\"tW_i7cCec56bHkhITL_79dXHz_PLC_f7xlynmlZJGU_d6mqOKmLBNBbTMLnYW8uAFiFzWxDeDHh1o5uF0mSQR-Z1Fg35OftnpbWpy0Cbc2la5WgXQjOwtG1eLYIY2BD3-wQ1VYDBCvowr4FDi-sngxwLqvwmrJ0xjhi99O-Gzcs\",\"dp\":\"q1d5jE85Hz_6M-eTh_lEluEf0NtPEc-vvhw-QO4V-cecNpbrCBdTWBmr4dE3NdpFeJc5ZVFEv-SACyei1MBEh0ItI_pFZi4BmMfy2ELh8ptaMMkTOESYyVy8U7veDq9RnBcr5i1Nqr0rsBkA77-9T6gzdvycBZdzLYAkAmwzEvk\",\"dq\":\"q29A2K08Crs-jmp2Bi8Q_8QzvIX6wSBbwZ4ir24AO-5_HNP56IrPS0yV2GCB0pqCOGb6_Hz_koDvhtuYoqdqvMVAtMoXR3YJBUaVXPt65p4RyNmFwIPe31zHs_BNUTsXVRMw4c16mci03-Af1sEm4HdLfxAp6sfM3xr5wcnhcek\",\"qi\":\"rHPgVTyHUHuYzcxfouyBfb1XAY8nshwn0ddo81o1BccD4Z7zo5It6SefDHjxCAbcmbiCcXBSooLcY-NF5FMv3fg19UE21VyLQltHcVjRRp2tRs4OHcM8yaXIU2x6N6Z6BP2tOksHb9MOBY1wAQzFOAKg_G4Sxev6-_6ud6RISuc\"}";
+ private static final String publicKeyStr = "{\"kty\":\"RSA\",\"kid\":\"3e79646c4dbc408383a9eed09f2b85ae\",\"alg\":\"RS256\",\"n\":\"gndmVdiOTSJ5et2HIeTM5f1m61x5ojLUi5HDfvr-jRrESQ5kbKuySGHVwR4QhwinpY1wQqBnwc80tx7cb_6SSqsTOoGln6T_l3k2Pb54ClVnGWiW_u1kmX78V2TZOsVmZmwtdZCMi-2zWIyAdIEXE-gncIehoAgEoq2VAhaCURbJWro_EwzzQwNmCTkDodLAx4npXRd_qSu0Ayp0txym9OFovBXBULRvk4DPiy3i_bPUmCDxzC46pTtFOe9p82uybTehZfULZtXXqRm85FL9n5zkrsTllPNAyEGhgb0RK9sE5nK1m_wNNysDyfLC4EFf1VXTrKm14XNVjc2vqLb7Mw\",\"e\":\"AQAB\"}";
+
+ /**
+ * token过期时间(分钟)
+ */
+ public static final long expirationTime = 30;
+
+ public static String createToken(String username, String password) {
+ try {
+ /**
+ * “iss” (issuer) 发行人
+ *
+ * “sub” (subject) 主题
+ *
+ * “aud” (audience) 接收方 用户
+ *
+ * “exp” (expiration time) 到期时间
+ *
+ * “nbf” (not before) 在此之前不可用
+ *
+ * “iat” (issued at) jwt的签发时间
+ */
+ //Payload
+ JwtClaims claims = new JwtClaims();
+ claims.setGeneratedJwtId();
+ claims.setIssuedAtToNow();
+ // 令牌将过期的时间 分钟
+ claims.setExpirationTimeMinutesInTheFuture(expirationTime);
+ claims.setNotBeforeMinutesInThePast(0);
+ claims.setSubject("login");
+ claims.setAudience(AUDIENCE);
+ //添加自定义参数,必须是字符串类型
+ claims.setClaim("username", username);
+ claims.setClaim("password", password);
+
+ //jws
+ JsonWebSignature jws = new JsonWebSignature();
+ //签名算法RS256
+ jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
+ jws.setKeyIdHeaderValue(keyId);
+ jws.setPayload(claims.toJson());
+
+ PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyStr)).getPrivateKey();
+ jws.setKey(privateKey);
+
+ //get token
+ String idToken = jws.getCompactSerialization();
+ return idToken;
+ } catch (JoseException e) {
+ logger.error("[Token生成失败]: {}", e.getMessage());
+ }
+
+ return null;
+ }
+
+ public static String getHeader() {
+ return HEADER;
+ }
+
+
+ public static JwtUser verifyToken(String token) {
+
+ JwtUser jwtUser = new JwtUser();
+
+ try {
+ JwtConsumer consumer = new JwtConsumerBuilder()
+ .setRequireExpirationTime()
+ .setMaxFutureValidityInMinutes(5256000)
+ .setAllowedClockSkewInSeconds(30)
+ .setRequireSubject()
+ //.setExpectedIssuer("")
+ .setExpectedAudience(AUDIENCE)
+ .setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKeyStr)).getPublicKey())
+ .build();
+
+ JwtClaims claims = consumer.processToClaims(token);
+ NumericDate expirationTime = claims.getExpirationTime();
+ // 判断是否即将过期, 默认剩余时间小于5分钟未即将过期
+ // 剩余时间 (秒)
+ long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
+ if (timeRemaining < 5 * 60) {
+ jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
+ }else {
+ jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
+ }
+
+ String username = (String) claims.getClaimValue("username");
+ String password = (String) claims.getClaimValue("password");
+ jwtUser.setUserName(username);
+ jwtUser.setPassword(password);
+
+ return jwtUser;
+ } catch (InvalidJwtException e) {
+ if (e.hasErrorCode(ErrorCodes.EXPIRED)) {
+ jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
+ }else {
+ jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION);
+ }
+ return jwtUser;
+ }catch (Exception e) {
+ logger.error("[Token解析失败]: {}", e.getMessage());
+ jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
+ return jwtUser;
+ }
+ }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
index 2d7e8a1b0..d26342ef5 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
@@ -21,7 +21,16 @@ public class LoginSuccessHandler implements AuthenticationSuccessHandler {
@Override
public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
- String username = request.getParameter("username");
- logger.info("[登录成功] - [{}]", username);
+// String username = request.getParameter("username");
+// httpServletResponse.setContentType("application/json;charset=UTF-8");
+// // 生成JWT,并放置到请求头中
+// String jwt = JwtUtils.createToken(authentication.getName(), );
+// httpServletResponse.setHeader(JwtUtils.getHeader(), jwt);
+// ServletOutputStream outputStream = httpServletResponse.getOutputStream();
+// outputStream.write(JSON.toJSONString(ErrorCode.SUCCESS).getBytes(StandardCharsets.UTF_8));
+// outputStream.flush();
+// outputStream.close();
+
+// logger.info("[登录成功] - [{}]", username);
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
index fd29d1128..a8d35681e 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.conf.security;
import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
+import com.genersoft.iot.vmp.storager.dao.dto.User;
import org.springframework.security.authentication.AuthenticationManager;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.Authentication;
@@ -9,6 +10,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import javax.security.sasl.AuthenticationException;
+import java.time.LocalDateTime;
public class SecurityUtils {
@@ -25,9 +27,12 @@ public class SecurityUtils {
public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
//使用security框架自带的验证token生成器 也可以自定义。
UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
+ //认证 如果失败,这里会自动异常后返回,所以这里不需要判断返回值是否为空,确定是否登录成功
Authentication authenticate = authenticationManager.authenticate(token);
- SecurityContextHolder.getContext().setAuthentication(authenticate);
LoginUser user = (LoginUser) authenticate.getPrincipal();
+
+ SecurityContextHolder.getContext().setAuthentication(token);
+
return user;
}
@@ -49,8 +54,13 @@ public class SecurityUtils {
if(authentication!=null){
Object principal = authentication.getPrincipal();
if(principal!=null && !"anonymousUser".equals(principal)){
- LoginUser user = (LoginUser) authentication.getPrincipal();
- return user;
+// LoginUser user = (LoginUser) authentication.getPrincipal();
+
+ String username = (String) principal;
+ User user = new User();
+ user.setUsername(username);
+ LoginUser loginUser = new LoginUser(user, LocalDateTime.now());
+ return loginUser;
}
}
return null;
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
index cce0d11c5..a905e4424 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
@@ -15,9 +15,16 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.builders.WebSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
+import org.springframework.security.config.http.SessionCreationPolicy;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
+import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
+import org.springframework.web.cors.CorsConfiguration;
+import org.springframework.web.cors.CorsConfigurationSource;
+import org.springframework.web.cors.CorsUtils;
+import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
-import java.util.List;
+import java.util.ArrayList;
+import java.util.Arrays;
/**
* 配置Spring Security
@@ -56,22 +63,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
*/
@Autowired
private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
-// /**
-// * 超时处理
-// */
-// @Autowired
-// private InvalidSessionHandler invalidSessionHandler;
-
-// /**
-// * 顶号处理
-// */
-// @Autowired
-// private SessionInformationExpiredHandler sessionInformationExpiredHandler;
-// /**
-// * 登录用户没有权限访问资源
-// */
-// @Autowired
-// private LoginUserAccessDeniedHandler accessDeniedHandler;
+ @Autowired
+ private JwtAuthenticationFilter jwtAuthenticationFilter;
/**
@@ -80,31 +73,21 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
public void configure(WebSecurity web) {
- if (!userSetting.isInterfaceAuthentication()) {
- web.ignoring().antMatchers("**");
- }else {
- // 可以直接访问的静态数据
- web.ignoring()
- .antMatchers("/")
- .antMatchers("/#/**")
- .antMatchers("/static/**")
- .antMatchers("/index.html")
- .antMatchers("/doc.html") // "/webjars/**", "/swagger-resources/**", "/v3/api-docs/**"
- .antMatchers("/webjars/**")
- .antMatchers("/swagger-resources/**")
- .antMatchers("/v3/api-docs/**")
- .antMatchers("/favicon.ico")
- .antMatchers("/js/**");
- List interfaceAuthenticationExcludes = userSetting.getInterfaceAuthenticationExcludes();
- for (String interfaceAuthenticationExclude : interfaceAuthenticationExcludes) {
- if (interfaceAuthenticationExclude.split("/").length < 4 ) {
- logger.warn("{}不满足两级目录,已忽略", interfaceAuthenticationExclude);
- }else {
- web.ignoring().antMatchers(interfaceAuthenticationExclude);
- }
-
- }
- }
+ ArrayList matchers = new ArrayList<>();
+ matchers.add("/");
+ matchers.add("/#/**");
+ matchers.add("/static/**");
+ matchers.add("/index.html");
+ matchers.add("/doc.html");
+ matchers.add("/webjars/**");
+ matchers.add("/swagger-resources/**");
+ matchers.add("/v3/api-docs/**");
+ matchers.add("/js/**");
+ matchers.add("/api/device/query/snap/**");
+ matchers.add("/record_proxy/*/**");
+ matchers.addAll(userSetting.getInterfaceAuthenticationExcludes());
+ // 可以直接访问的静态数据
+ web.ignoring().antMatchers(matchers.toArray(new String[0]));
}
/**
@@ -126,36 +109,43 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http) throws Exception {
- http.cors().and().csrf().disable();
- // 设置允许添加静态文件
- http.headers().contentTypeOptions().disable();
- http.authorizeRequests()
- // 放行接口
- .antMatchers("/api/user/login","/index/hook/**").permitAll()
- // 除上面外的所有请求全部需要鉴权认证
- .anyRequest().authenticated()
- // 异常处理(权限拒绝、登录失效等)
- .and().exceptionHandling()
- //匿名用户访问无权限资源时的异常处理
- .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
-// .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
- // 登入 允许所有用户
- .and().formLogin().permitAll()
- //登录成功处理逻辑
- .successHandler(loginSuccessHandler)
- //登录失败处理逻辑
- .failureHandler(loginFailureHandler)
- // 登出
- .and().logout().logoutUrl("/api/user/logout").permitAll()
- //登出成功处理逻辑
- .logoutSuccessHandler(logoutHandler)
- .deleteCookies("JSESSIONID")
- // 会话管理
-// .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
-// .maximumSessions(1)//同一账号同时登录最大用户数
-// .expiredSessionStrategy(sessionInformationExpiredHandler) // 顶号处理
- ;
+ http.headers().contentTypeOptions().disable()
+ .and().cors().configurationSource(configurationSource())
+ .and().csrf().disable()
+ .sessionManagement()
+ .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
+ // 配置拦截规则
+ .and()
+ .authorizeRequests()
+ .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
+ .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
+ .antMatchers("/api/user/login","/index/hook/**","/zlm_Proxy/FhTuMYqB2HeCuNOb/record/t/1/2023-03-25/16:35:07-16:35:16-9353.mp4").permitAll()
+ .anyRequest().authenticated()
+ // 异常处理器
+ .and()
+ .exceptionHandling()
+ .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
+ .and().logout().logoutUrl("/api/user/logout").permitAll()
+ .logoutSuccessHandler(logoutHandler)
+ ;
+ http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
+
+ }
+
+ CorsConfigurationSource configurationSource(){
+ // 配置跨域
+ CorsConfiguration corsConfiguration = new CorsConfiguration();
+ corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
+ corsConfiguration.setAllowedMethods(Arrays.asList("*"));
+ corsConfiguration.setMaxAge(3600L);
+ corsConfiguration.setAllowCredentials(true);
+ corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins());
+ corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader()));
+
+ UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource();
+ url.registerCorsConfiguration("/**",corsConfiguration);
+ return url;
}
/**
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java
new file mode 100644
index 000000000..1639d1fc2
--- /dev/null
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java
@@ -0,0 +1,53 @@
+package com.genersoft.iot.vmp.conf.security.dto;
+
+public class JwtUser {
+
+ public enum TokenStatus{
+ /**
+ * 正常的使用状态
+ */
+ NORMAL,
+ /**
+ * 过期而失效
+ */
+ EXPIRED,
+ /**
+ * 即将过期
+ */
+ EXPIRING_SOON,
+ /**
+ * 异常
+ */
+ EXCEPTION
+ }
+
+ private String userName;
+
+ private String password;
+
+ private TokenStatus status;
+
+ public String getUserName() {
+ return userName;
+ }
+
+ public void setUserName(String userName) {
+ this.userName = userName;
+ }
+
+ public TokenStatus getStatus() {
+ return status;
+ }
+
+ public void setStatus(TokenStatus status) {
+ this.status = status;
+ }
+
+ public String getPassword() {
+ return password;
+ }
+
+ public void setPassword(String password) {
+ this.password = password;
+ }
+}
diff --git a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java
index 902bfb5b9..67075bc8d 100644
--- a/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java
+++ b/src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java
@@ -19,6 +19,8 @@ public class LoginUser implements UserDetails, CredentialsContainer {
*/
private User user;
+ private String accessToken;
+
/**
* 登录时间
@@ -102,4 +104,11 @@ public class LoginUser implements UserDetails, CredentialsContainer {
return user.getPushKey();
}
+ public String getAccessToken() {
+ return accessToken;
+ }
+
+ public void setAccessToken(String accessToken) {
+ this.accessToken = accessToken;
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
index fdb05e56a..05ecb078c 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
@@ -171,7 +171,7 @@ public class DigestServerAuthenticationHelper {
*/
public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
- if ( authHeader == null ) {
+ if ( authHeader == null || authHeader.getRealm() == null) {
return false;
}
String realm = authHeader.getRealm().trim();
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
index 5de93862b..9e9d338fc 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
@@ -188,6 +188,13 @@ public class Device {
@Schema(description = "SIP交互IP(设备访问平台的IP)")
private String localIp;
+ @Schema(description = "是否作为消息通道")
+ private boolean asMessageChannel;
+
+ @Schema(description = "设备注册的事务信息")
+ private SipTransactionInfo sipTransactionInfo;
+
+
public String getDeviceId() {
return deviceId;
}
@@ -428,4 +435,20 @@ public class Device {
public void setKeepaliveIntervalTime(int keepaliveIntervalTime) {
this.keepaliveIntervalTime = keepaliveIntervalTime;
}
+
+ public boolean isAsMessageChannel() {
+ return asMessageChannel;
+ }
+
+ public void setAsMessageChannel(boolean asMessageChannel) {
+ this.asMessageChannel = asMessageChannel;
+ }
+
+ public SipTransactionInfo getSipTransactionInfo() {
+ return sipTransactionInfo;
+ }
+
+ public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
+ this.sipTransactionInfo = sipTransactionInfo;
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
index 262ac5596..7ea0df176 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
@@ -189,6 +189,9 @@ public class ParentPlatform {
@Schema(description = "树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGrou")
private String treeType;
+ @Schema(description = "是否作为消息通道")
+ private boolean asMessageChannel;
+
public Integer getId() {
return id;
}
@@ -428,4 +431,12 @@ public class ParentPlatform {
public void setTreeType(String treeType) {
this.treeType = treeType;
}
+
+ public boolean isAsMessageChannel() {
+ return asMessageChannel;
+ }
+
+ public void setAsMessageChannel(boolean asMessageChannel) {
+ this.asMessageChannel = asMessageChannel;
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java
index a53d26e48..6ff2fe32e 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java
@@ -16,6 +16,8 @@ public class ParentPlatformCatch {
private ParentPlatform parentPlatform;
+ private SipTransactionInfo sipTransactionInfo;
+
public String getId() {
return id;
}
@@ -55,4 +57,12 @@ public class ParentPlatformCatch {
public void setCallId(String callId) {
this.callId = callId;
}
+
+ public SipTransactionInfo getSipTransactionInfo() {
+ return sipTransactionInfo;
+ }
+
+ public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
+ this.sipTransactionInfo = sipTransactionInfo;
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
index 41aebf5d9..7ff528306 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
@@ -1,6 +1,8 @@
package com.genersoft.iot.vmp.gb28181.bean;
+import io.swagger.v3.oas.annotations.media.Schema;
+
import java.time.Instant;
import java.util.List;
@@ -9,22 +11,29 @@ import java.util.List;
* @author: swwheihei
* @date: 2020年5月8日 下午2:05:56
*/
+@Schema(description = "设备录像查询结果信息")
public class RecordInfo {
+ @Schema(description = "设备编号")
private String deviceId;
+ @Schema(description = "通道编号")
private String channelId;
+ @Schema(description = "命令序列号")
private String sn;
+ @Schema(description = "设备名称")
private String name;
-
+
+ @Schema(description = "列表总数")
private int sumNum;
private int count;
private Instant lastTime;
-
+
+ @Schema(description = "")
private List recordList;
public String getDeviceId() {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
index a47147a1c..07e559c81 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
@@ -2,9 +2,9 @@ package com.genersoft.iot.vmp.gb28181.bean;
import com.genersoft.iot.vmp.utils.DateUtil;
+import io.swagger.v3.oas.annotations.media.Schema;
import org.jetbrains.annotations.NotNull;
-import java.text.ParseException;
import java.time.Instant;
import java.time.temporal.TemporalAccessor;
@@ -13,26 +13,37 @@ import java.time.temporal.TemporalAccessor;
* @author: swwheihei
* @date: 2020年5月8日 下午2:06:54
*/
+@Schema(description = "设备录像详情")
public class RecordItem implements Comparable{
+ @Schema(description = "设备编号")
private String deviceId;
-
+
+ @Schema(description = "名称")
private String name;
-
+
+ @Schema(description = "文件路径名 (可选)")
private String filePath;
+ @Schema(description = "录像文件大小,单位:Byte(可选)")
private String fileSize;
+ @Schema(description = "录像地址(可选)")
private String address;
-
+
+ @Schema(description = "录像开始时间(可选)")
private String startTime;
-
+
+ @Schema(description = "录像结束时间(可选)")
private String endTime;
-
+
+ @Schema(description = "保密属性(必选)缺省为0;0:不涉密,1:涉密")
private int secrecy;
-
+
+ @Schema(description = "录像产生类型(可选)time或alarm 或 manua")
private String type;
-
+
+ @Schema(description = "录像触发者ID(可选)")
private String recorderId;
public String getDeviceId() {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java b/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java
index bffa4cb98..2008f22e3 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java
@@ -1,7 +1,6 @@
package com.genersoft.iot.vmp.gb28181.event.device;
import com.genersoft.iot.vmp.gb28181.bean.Device;
-import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.service.IDeviceService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
@@ -9,8 +8,6 @@ import org.springframework.stereotype.Component;
import javax.sip.ClientTransaction;
import javax.sip.address.SipURI;
-import javax.sip.header.CallIdHeader;
-import javax.sip.header.ToHeader;
import javax.sip.message.Request;
/**
@@ -34,7 +31,7 @@ public class RequestTimeoutEventImpl implements ApplicationListener viaHeaders = new ArrayList();
- ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), parentPlatform.getServerPort(), parentPlatform.getTransport(), viaTag);
+ ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(),
+ parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag());
viaHeader.setRPort();
viaHeaders.add(viaHeader);
//from
@@ -64,7 +65,7 @@ public class SIPRequestHeaderPlarformProvider {
//to
SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
- ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
+ ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,toTag);
//Forwards
MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
@@ -86,15 +87,21 @@ public class SIPRequestHeaderPlarformProvider {
return request;
}
- public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String viaTag,
- String callId, WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException {
+ public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String toTag,
+ WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException {
- Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, viaTag, callIdHeader, isRegister);
+ Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, toTag, callIdHeader, isRegister);
SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
if (www == null) {
AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader("Digest");
- authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
+ String username = parentPlatform.getUsername();
+ if ( username == null || username == "" )
+ {
+ authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
+ } else {
+ authorizationHeader.setUsername(username);
+ }
authorizationHeader.setURI(requestURI);
authorizationHeader.setAlgorithm("MD5");
registerRequest.addHeader(authorizationHeader);
@@ -108,8 +115,6 @@ public class SIPRequestHeaderPlarformProvider {
// qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
String qop = www.getQop();
- callIdHeader.setCallId(callId);
-
String cNonce = null;
String nc = "00000001";
if (qop != null) {
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
index 48551149c..0e2d04e59 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
@@ -276,7 +276,7 @@ public class SIPCommander implements ISIPCommander {
return;
}
- logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+ logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
if (event != null) {
@@ -377,7 +377,7 @@ public class SIPCommander implements ISIPCommander {
SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
- logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+ logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
String sdpIp;
if (!ObjectUtils.isEmpty(device.getSdpIp())) {
sdpIp = device.getSdpIp();
@@ -479,10 +479,11 @@ public class SIPCommander implements ISIPCommander {
*/
@Override
public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
- String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
- SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
+ String startTime, String endTime, int downloadSpeed,
+ InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
+ SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
- logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
+ logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
String sdpIp;
if (!ObjectUtils.isEmpty(device.getSdpIp())) {
sdpIp = device.getSdpIp();
@@ -549,11 +550,14 @@ public class SIPCommander implements ISIPCommander {
content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
-
+ logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
// 添加订阅
+ CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
+ String callId=newCallIdHeader.getCallId();
subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
- hookEvent.call(new InviteStreamInfo(mediaServerItem, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
+ logger.debug("sipc 添加订阅===callId {}",callId);
+ hookEvent.call(new InviteStreamInfo(mediaServerItem, json,callId, "rtp", ssrcInfo.getStream()));
subscribe.removeSubscribe(hookSubscribe);
hookSubscribe.getContent().put("regist", false);
hookSubscribe.getContent().put("schema", "rtsp");
@@ -562,7 +566,7 @@ public class SIPCommander implements ISIPCommander {
(MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
logger.info("[录像]下载结束, 发送BYE");
try {
- streamByeCmd(device, channelId, ssrcInfo.getStream(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId());
+ streamByeCmd(device, channelId, ssrcInfo.getStream(),callId);
} catch (InvalidArgumentException | ParseException | SipException |
SsrcTransactionNotFoundException e) {
logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
@@ -570,15 +574,24 @@ public class SIPCommander implements ISIPCommander {
});
});
- Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
+ Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc());
if (inviteStreamCallback != null) {
- inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
+ inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,callId, "rtp", ssrcInfo.getStream()));
}
- sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent -> {
- ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
+ sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
+ ResponseEvent responseEvent = (ResponseEvent) event.event;
SIPResponse response = (SIPResponse) responseEvent.getResponse();
- streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
+ String contentString =new String(response.getRawContent());
+ int ssrcIndex = contentString.indexOf("y=");
+ String ssrc=ssrcInfo.getSsrc();
+ if (ssrcIndex >= 0) {
+ ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+ }
+ logger.debug("接收到的下载响应ssrc====>{}",ssrcInfo.getSsrc());
+ logger.debug("接收到的下载响应ssrc====>{}",ssrc);
+ streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
+ okEvent.response(event);
});
}
@@ -778,7 +791,7 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("" + guardCmdStr + "\r\n");
cmdXml.append("\r\n");
-
+
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
@@ -854,7 +867,6 @@ public class SIPCommander implements ISIPCommander {
*
* @param device 视频设备
* @param channelId 通道id,非通道则是设备本身
- * @param frontCmd 上级平台的指令,如果存在则直接下发
* @param enabled 看守位使能:1 = 开启,0 = 关闭
* @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
* @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
@@ -978,7 +990,7 @@ public class SIPCommander implements ISIPCommander {
catalogXml.append("" + device.getDeviceId() + "\r\n");
catalogXml.append("\r\n");
-
+
Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
@@ -1181,7 +1193,6 @@ public class SIPCommander implements ISIPCommander {
cmdXml.append("\r\n");
-
Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
}
@@ -1427,7 +1438,7 @@ public class SIPCommander implements ISIPCommander {
if (device == null) {
return;
}
- logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
+ logger.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
String characterSet = device.getCharset();
@@ -1439,7 +1450,7 @@ public class SIPCommander implements ISIPCommander {
deviceStatusXml.append("" + deviceAlarm.getChannelId() + "\r\n");
deviceStatusXml.append("" + deviceAlarm.getAlarmPriority() + "\r\n");
deviceStatusXml.append("" + deviceAlarm.getAlarmMethod() + "\r\n");
- deviceStatusXml.append("" + deviceAlarm.getAlarmTime() + "\r\n");
+ deviceStatusXml.append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "\r\n");
deviceStatusXml.append("" + deviceAlarm.getAlarmDescription() + "\r\n");
deviceStatusXml.append("" + deviceAlarm.getLongitude() + "\r\n");
deviceStatusXml.append("" + deviceAlarm.getLatitude() + "\r\n");
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
index 1bc03c724..452da8eea 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommanderFroPlatform.java
@@ -24,6 +24,7 @@ import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
import com.genersoft.iot.vmp.utils.DateUtil;
+import com.genersoft.iot.vmp.utils.GitUtil;
import gov.nist.javax.sip.message.MessageFactoryImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
@@ -85,26 +86,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
@Autowired
private DynamicTask dynamicTask;
+ @Autowired
+ private GitUtil gitUtil;
+
@Override
public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
register(parentPlatform, null, null, errorEvent, okEvent, false, true);
}
@Override
- public void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
- register(parentPlatform, null, null, errorEvent, okEvent, false, false);
+ public void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
+
+ register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false, true);
}
@Override
- public void register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www,
+ public void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
+ register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false, false);
+ }
+
+ @Override
+ public void register(ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo, @Nullable WWWAuthenticateHeader www,
SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException {
Request request;
- if (!registerAgain ) {
- CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
+ CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
+ String fromTag = SipUtils.getNewFromTag();
+ String toTag = null;
+ if (sipTransactionInfo != null ) {
+ if (sipTransactionInfo.getCallId() != null) {
+ callIdHeader.setCallId(sipTransactionInfo.getCallId());
+ }
+ if (sipTransactionInfo.getFromTag() != null) {
+ fromTag = sipTransactionInfo.getFromTag();
+ }
+ if (sipTransactionInfo.getToTag() != null) {
+ toTag = sipTransactionInfo.getToTag();
+ }
+ }
+
+ if (!registerAgain ) {
request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform,
- redisCatchStorage.getCSEQ(), SipUtils.getNewFromTag(),
- SipUtils.getNewViaTag(), callIdHeader, isRegister);
+ redisCatchStorage.getCSEQ(), fromTag,
+ toTag, callIdHeader, isRegister);
// 将 callid 写入缓存, 等注册成功可以更新状态
String callIdFromHeader = callIdHeader.getCallId();
redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister));
@@ -122,8 +146,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
});
}else {
- CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
- request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, SipUtils.getNewFromTag(), null, callId, www, callIdHeader, isRegister);
+ request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister);
}
sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent);
@@ -245,6 +268,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
catalogXml.append("" + channel.getIpAddress() + "\r\n");
catalogXml.append("" + channel.getPort() + "\r\n");
catalogXml.append("" + channel.getPort() + "\r\n");
+ catalogXml.append("" + channel.getPTZType() + "\r\n");
catalogXml.append("" + (channel.getStatus() == 1?"ON":"OFF") + "\r\n");
catalogXml.append("" +
(channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude())
@@ -285,6 +309,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
String callId = request.getCallIdHeader().getCallId();
+ logger.info("[命令发送] 国标级联{} 目录查询回复: 共{}条,已发送{}条", parentPlatform.getServerGBId(),
+ channels.size(), Math.min(index + parentPlatform.getCatalogGroup(), channels.size()));
+ logger.debug(catalogXml);
if (sendAfterResponse) {
// 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。
dynamicTask.startDelay(timeoutTaskKey, ()->{
@@ -336,17 +363,22 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
if (parentPlatform == null) {
return;
}
+ String deviceId = device == null ? parentPlatform.getDeviceGBId() : device.getDeviceId();
+ String deviceName = device == null ? parentPlatform.getName() : device.getName();
+ String manufacturer = device == null ? "WVP-28181-PRO" : device.getManufacturer();
+ String model = device == null ? "platform" : device.getModel();
+ String firmware = device == null ? gitUtil.getBuildVersion() : device.getFirmware();
String characterSet = parentPlatform.getCharacterSet();
StringBuffer deviceInfoXml = new StringBuffer(600);
deviceInfoXml.append("\r\n");
deviceInfoXml.append("\r\n");
deviceInfoXml.append("DeviceInfo\r\n");
deviceInfoXml.append("" +sn + "\r\n");
- deviceInfoXml.append("" + device.getDeviceId() + "\r\n");
- deviceInfoXml.append("" + device.getName() + "\r\n");
- deviceInfoXml.append("" + device.getManufacturer() + "\r\n");
- deviceInfoXml.append("" + device.getModel() + "\r\n");
- deviceInfoXml.append("" + device.getFirmware() + "\r\n");
+ deviceInfoXml.append("" + deviceId + "\r\n");
+ deviceInfoXml.append("" + deviceName + "\r\n");
+ deviceInfoXml.append("" + manufacturer + "\r\n");
+ deviceInfoXml.append("" + model + "\r\n");
+ deviceInfoXml.append("" + firmware + "\r\n");
deviceInfoXml.append("OK\r\n");
deviceInfoXml.append("\r\n");
@@ -423,7 +455,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
if (parentPlatform == null) {
return;
}
- logger.info("[发送报警通知] {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
+ logger.info("[发送报警通知]平台: {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
String characterSet = parentPlatform.getCharacterSet();
StringBuffer deviceStatusXml = new StringBuffer(600);
@@ -434,7 +466,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
.append("" + deviceAlarm.getChannelId() + "\r\n")
.append("" + deviceAlarm.getAlarmPriority() + "\r\n")
.append("" + deviceAlarm.getAlarmMethod() + "\r\n")
- .append("" + deviceAlarm.getAlarmTime() + "\r\n")
+ .append("" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "\r\n")
.append("" + deviceAlarm.getAlarmDescription() + "\r\n")
.append("" + deviceAlarm.getLongitude() + "\r\n")
.append("" + deviceAlarm.getLatitude() + "\r\n")
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
index 1a7f4eb8e..8e1b6d8b8 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
@@ -45,7 +45,7 @@ public abstract class SIPRequestProcessorParent {
try {
return SipFactory.getInstance().createHeaderFactory();
} catch (PeerUnavailableException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
return null;
}
@@ -54,7 +54,7 @@ public abstract class SIPRequestProcessorParent {
try {
return SipFactory.getInstance().createMessageFactory();
} catch (PeerUnavailableException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
return null;
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
index 95e0f1121..30ac3812d 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/InviteRequestProcessor.java
@@ -8,6 +8,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
+import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@@ -457,12 +458,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
if (!userSetting.getPushStreamAfterAck()) {
playService.startPushStream(sendRtpItem, sipResponse, platform, request.getCallIdHeader());
}
- } catch (SipException e) {
- e.printStackTrace();
- } catch (InvalidArgumentException e) {
- e.printStackTrace();
- } catch (ParseException e) {
- e.printStackTrace();
+ } catch (SipException | InvalidArgumentException | ParseException e) {
+ logger.error("[命令发送失败] 国标级联 回复SdpAck", e);
}
};
SipSubscribe.Event errorEvent = ((event) -> {
@@ -471,7 +468,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
} catch (ParseException | SipException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
});
sendRtpItem.setApp("rtp");
@@ -543,6 +540,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
}
} else if (gbStream != null) {
+ if(ssrc.equals(ssrcDefault))
+ {
+ SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
+ if(ssrcConfig != null)
+ {
+ ssrc = ssrcConfig.getPlaySsrc();
+ ssrcConfig.releaseSsrc(ssrc);
+ }
+ }
if("push".equals(gbStream.getStreamType())) {
if (streamPushItem != null && streamPushItem.isPushIng()) {
// 推流状态
@@ -572,7 +578,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
} catch (SdpParseException e) {
logger.error("sdp解析错误", e);
} catch (SdpException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
@@ -727,11 +733,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
responseAck(request, Response.REQUEST_TIMEOUT); // 超时
} catch (SipException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (InvalidArgumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (ParseException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}, userSetting.getPlatformPlayTimeout());
// 添加监听
@@ -750,11 +756,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
try {
responseAck(request, Response.BUSY_HERE);
} catch (SipException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (InvalidArgumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (ParseException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
return;
}
@@ -812,11 +818,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
try {
responseAck(request, Response.BUSY_HERE);
} catch (SipException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (InvalidArgumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (ParseException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
return;
}
@@ -869,7 +875,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
content.append("s=Play\r\n");
content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
content.append("t=0 0\r\n");
- content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n");
+ // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口
+ int localPort = sendRtpItem.getLocalPort();
+ if(localPort == 0)
+ {
+ localPort = new Random().nextInt(65535) + 1;
+ }
+ content.append("m=video " + localPort + " RTP/AVP 96\r\n");
content.append("a=sendonly\r\n");
content.append("a=rtpmap:96 PS/90000\r\n");
if (sendRtpItem.isTcp()) {
@@ -890,11 +902,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
}
return sipResponse;
} catch (SipException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (InvalidArgumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (ParseException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
return null;
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
index a468926f4..128897402 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
@@ -93,7 +93,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
try {
responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
}catch (SipException | InvalidArgumentException | ParseException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
boolean runed = !taskQueue.isEmpty();
taskQueue.offer(new HandlerCatchData(evt, null, null));
@@ -229,7 +229,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
jsonObject.put("speed", mobilePosition.getSpeed());
redisCatchStorage.sendMobilePositionMsg(jsonObject);
} catch (DocumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
@@ -339,7 +339,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
publisher.deviceAlarmEventPublish(deviceAlarm);
}
} catch (DocumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
@@ -397,12 +397,20 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
case CatalogEvent.OFF :
// 离线
logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
- storager.deviceChannelOffline(deviceId, channel.getChannelId());
+ if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
+ storager.deviceChannelOffline(deviceId, channel.getChannelId());
+ }else {
+ logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
+ }
break;
case CatalogEvent.VLOST:
// 视频丢失
logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
- storager.deviceChannelOffline(deviceId, channel.getChannelId());
+ if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
+ storager.deviceChannelOffline(deviceId, channel.getChannelId());
+ }else {
+ logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
+ }
break;
case CatalogEvent.DEFECT:
// 故障
@@ -432,7 +440,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
}
}
} catch (DocumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
index 492776378..9033963da 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/RegisterRequestProcessor.java
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
@@ -18,6 +19,7 @@ import gov.nist.javax.sip.address.AddressImpl;
import gov.nist.javax.sip.address.SipUri;
import gov.nist.javax.sip.header.SIPDateHeader;
import gov.nist.javax.sip.message.SIPRequest;
+import gov.nist.javax.sip.message.SIPResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.InitializingBean;
@@ -34,6 +36,7 @@ import javax.sip.header.AuthorizationHeader;
import javax.sip.header.ContactHeader;
import javax.sip.header.FromHeader;
import javax.sip.header.ViaHeader;
+import javax.sip.message.Request;
import javax.sip.message.Response;
import java.security.NoSuchAlgorithmException;
import java.text.ParseException;
@@ -105,6 +108,30 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
SipUri uri = (SipUri) address.getURI();
String deviceId = uri.getUser();
Device device = deviceService.getDevice(deviceId);
+
+ RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
+ userSetting.getSipUseSourceIpAsRemoteAddress());
+
+ if (device != null &&
+ device.getSipTransactionInfo() != null &&
+ request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
+ logger.info("[注册请求] 注册续订: {}", device.getDeviceId());
+ device.setExpires(request.getExpires().getExpires());
+ device.setIp(remoteAddressInfo.getIp());
+ device.setPort(remoteAddressInfo.getPort());
+ device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
+ device.setLocalIp(request.getLocalAddress().getHostAddress());
+ Response registerOkResponse = getRegisterOkResponse(request);
+ // 判断TCP还是UDP
+ ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
+ String transport = reqViaHeader.getTransport();
+ device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
+ sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
+ device.setRegisterTime(DateUtil.getNow());
+ SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
+ deviceService.online(device, sipTransactionInfo);
+ return;
+ }
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)) {
@@ -147,9 +174,6 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
// 添加Expires头
response.addHeader(request.getExpires());
- RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
- userSetting.getSipUseSourceIpAsRemoteAddress());
-
if (device == null) {
device = new Device();
device.setStreamMode("UDP");
@@ -182,13 +206,33 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
if (registerFlag) {
logger.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress);
device.setRegisterTime(DateUtil.getNow());
- deviceService.online(device);
+ SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)response);
+ deviceService.online(device, sipTransactionInfo);
} else {
logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress);
- deviceService.offline(deviceId);
+ deviceService.offline(deviceId, "主动注销");
}
} catch (SipException | NoSuchAlgorithmException | ParseException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
+
+ private Response getRegisterOkResponse(Request request) throws ParseException {
+ // 携带授权头并且密码正确
+ Response response = getMessageFactory().createResponse(Response.OK, request);
+ // 添加date头
+ SIPDateHeader dateHeader = new SIPDateHeader();
+ // 使用自己修改的
+ WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
+ dateHeader.setDate(wvpSipDate);
+ response.addHeader(dateHeader);
+
+ // 添加Contact头
+ response.addHeader(request.getHeader(ContactHeader.NAME));
+ // 添加Expires头
+ response.addHeader(request.getExpires());
+
+ return response;
+
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
index 2e1dbe37f..207a792fd 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
@@ -1,5 +1,6 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
+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.CmdType;
@@ -8,14 +9,11 @@ import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
-import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
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.utils.XmlUtil;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
-import gov.nist.javax.sip.SipProviderImpl;
import gov.nist.javax.sip.message.SIPRequest;
import gov.nist.javax.sip.message.SIPResponse;
import org.dom4j.DocumentException;
@@ -26,7 +24,9 @@ import org.springframework.beans.factory.InitializingBean;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
-import javax.sip.*;
+import javax.sip.InvalidArgumentException;
+import javax.sip.RequestEvent;
+import javax.sip.SipException;
import javax.sip.header.ExpiresHeader;
import javax.sip.message.Response;
import java.text.ParseException;
@@ -93,7 +93,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
}
} catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
@@ -146,7 +146,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
}
} catch (SipException | InvalidArgumentException | ParseException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
@@ -192,7 +192,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo);
}
} catch (SipException | InvalidArgumentException | ParseException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
index 4ac83de75..b2dd76b58 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/control/cmd/DeviceControlQueryMessageHandler.java
@@ -73,35 +73,38 @@ public class DeviceControlQueryMessageHandler extends SIPRequestProcessorParent
String channelId = getText(rootElement, "DeviceID");
// 远程启动功能
if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) {
- if (parentPlatform.getServerGBId().equals(targetGBId)) {
- // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
- logger.info("执行远程启动本平台命令");
- try {
- cmderFroPlatform.unregister(parentPlatform, null, null);
- } catch (InvalidArgumentException | ParseException | SipException e) {
- logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
- }
- taskExecutor.execute(() -> {
- // 远程启动
-// try {
-// Thread.sleep(3000);
-// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
-// SipStackImpl stack = (SipStackImpl)up.getSipStack();
-// stack.stop();
-// Iterator listener = stack.getListeningPoints();
-// while (listener.hasNext()) {
-// stack.deleteListeningPoint((ListeningPoint) listener.next());
-// }
-// Iterator providers = stack.getSipProviders();
-// while (providers.hasNext()) {
-// stack.deleteSipProvider((SipProvider) providers.next());
-// }
-// VManageBootstrap.restart();
-// } catch (InterruptedException | ObjectInUseException e) {
-// logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
-// }
- });
- }
+ // TODO 拒绝远程启动命令
+ logger.warn("[国标级联]收到平台的远程启动命令, 不处理");
+
+// if (parentPlatform.getServerGBId().equals(targetGBId)) {
+// // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
+// logger.info("执行远程启动本平台命令");
+// try {
+// cmderFroPlatform.unregister(parentPlatform, null, null);
+// } catch (InvalidArgumentException | ParseException | SipException e) {
+// logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
+// }
+// taskExecutor.execute(() -> {
+// // 远程启动
+//// try {
+//// Thread.sleep(3000);
+//// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
+//// SipStackImpl stack = (SipStackImpl)up.getSipStack();
+//// stack.stop();
+//// Iterator listener = stack.getListeningPoints();
+//// while (listener.hasNext()) {
+//// stack.deleteListeningPoint((ListeningPoint) listener.next());
+//// }
+//// Iterator providers = stack.getSipProviders();
+//// while (providers.hasNext()) {
+//// stack.deleteSipProvider((SipProvider) providers.next());
+//// }
+//// VManageBootstrap.restart();
+//// } catch (InterruptedException | ObjectInUseException e) {
+//// logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
+//// }
+// });
+// }
}
DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
logger.info("[接受deviceControl命令] 命令: {}", deviceControlType);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
index e89e88abe..9268e9b51 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/AlarmNotifyMessageHandler.java
@@ -186,9 +186,13 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
// 发送给平台的报警信息。 发送redis通知
logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm));
AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
- alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
+ if (deviceAlarm.getAlarmMethod() != null) {
+ alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
+ }
alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
- alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
+ if (deviceAlarm.getAlarmType() != null) {
+ alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
+ }
alarmChannelMessage.setGbId(channelId);
redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
continue;
@@ -204,6 +208,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
publisher.deviceAlarmEventPublish(deviceAlarm);
}
}catch (Exception e) {
+ logger.error("未处理的异常 ", e);
logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
}
}
@@ -264,12 +269,15 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
if (channelId.equals(parentPlatform.getDeviceGBId())) {
// 发送给平台的报警信息。 发送redis通知
AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
- alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
+ if (deviceAlarm.getAlarmMethod() != null) {
+ alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
+ }
alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
alarmChannelMessage.setGbId(channelId);
- alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
+ if (deviceAlarm.getAlarmType() != null) {
+ alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
+ }
redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
- return;
}
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
index 98c1a96f2..865b6623c 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/KeepaliveNotifyMessageHandler.java
@@ -88,13 +88,13 @@ public class KeepaliveNotifyMessageHandler extends SIPRequestProcessorParent imp
// 对于已经离线的设备判断他的注册是否已经过期
if (!deviceService.expire(device)){
device.setOnline(0);
- deviceService.online(device);
+ deviceService.online(device, null);
}
}
// 刷新过期任务
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
// 如果三次心跳失败,则设置设备离线
- dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId()), device.getKeepaliveIntervalTime()*1000*3);
+ dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime()*1000*3);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
index 40d1dcc90..1f0bdf142 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/notify/cmd/MobilePositionNotifyMessageHandler.java
@@ -149,9 +149,10 @@ public class MobilePositionNotifyMessageHandler extends SIPRequestProcessorParen
redisCatchStorage.sendMobilePositionMsg(jsonObject);
} catch (DocumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
} catch (Exception e) {
- logger.warn("[移动位置通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
+ logger.warn("[移动位置通知] 发现未处理的异常, \r\n{}", evt.getRequest());
+ logger.error("[移动位置通知] 异常内容: ", e);
}
}
});
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
index 0faf29452..8b774e100 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/query/cmd/DeviceInfoQueryMessageHandler.java
@@ -60,17 +60,24 @@ public class DeviceInfoQueryMessageHandler extends SIPRequestProcessorParent imp
return;
}
String sn = rootElement.element("SN").getText();
+
/*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理
大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。
我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/
+
String channelId = getText(rootElement, "DeviceID");
- Device device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
- if (device ==null){
- logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId);
- return;
+ // 查询这是通道id还是设备id
+ Device device = null;
+ // 如果id指向平台的国标编号,那么就是查询平台的信息
+ if (!parentPlatform.getDeviceGBId().equals(channelId)) {
+ device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
+ if (device ==null){
+ logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId);
+ return;
+ }
}
try {
- cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag());
+ cmderFroPlatform.deviceInfoResponse(parentPlatform, device, sn, fromHeader.getTag());
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage());
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
index 119b56254..763c46646 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/CatalogResponseMessageHandler.java
@@ -132,7 +132,8 @@ public class CatalogResponseMessageHandler extends SIPRequestProcessorParent imp
}
}catch (Exception e) {
- logger.warn("[收到通道] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
+ logger.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest());
+ logger.error("[收到通道] 异常内容: ", e);
}
}
});
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
index 0c0c71fb7..da4743f68 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
@@ -1,10 +1,8 @@
package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
import com.alibaba.fastjson2.JSONObject;
-import com.genersoft.iot.vmp.common.VideoManagerConstants;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
-import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
@@ -26,7 +24,6 @@ import javax.sip.RequestEvent;
import javax.sip.SipException;
import javax.sip.message.Response;
import java.text.ParseException;
-import java.util.Objects;
@Component
public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
@@ -74,9 +71,9 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen
}
String text = onlineElement.getText();
if ("ONLINE".equalsIgnoreCase(text.trim())) {
- deviceService.online(device);
+ deviceService.online(device, null);
}else {
- deviceService.offline(device.getDeviceId());
+ deviceService.offline(device.getDeviceId(), "设备状态查询结果:" + text.trim());
}
RequestMessage msg = new RequestMessage();
msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
index 226799a4c..332f3635a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/MobilePositionResponseMessageHandler.java
@@ -142,7 +142,7 @@ public class MobilePositionResponseMessageHandler extends SIPRequestProcessorPar
}
} catch (DocumentException e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
index 6d8d8f56e..a667c67d8 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/RecordInfoResponseMessageHandler.java
@@ -150,7 +150,8 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
}
}
} catch (Exception e) {
- logger.error("[国标录像] 发现未处理的异常, "+e.getMessage(), e);
+ logger.error("[国标录像] 发现未处理的异常, \r\n{}", evt.getRequest());
+ logger.error("[国标录像] 异常内容: ", e);
}
});
}
@@ -163,7 +164,11 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
public void releaseRequest(String deviceId, String sn,RecordInfo recordInfo){
String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn;
// 对数据进行排序
- Collections.sort(recordInfo.getRecordList());
+ if(recordInfo!=null && recordInfo.getRecordList()!=null) {
+ Collections.sort(recordInfo.getRecordList());
+ }else{
+ recordInfo.setRecordList(new ArrayList<>());
+ }
RequestMessage msg = new RequestMessage();
msg.setKey(key);
diff --git a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
index 14d1f84bf..0294ba27a 100644
--- a/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
+++ b/src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/response/impl/RegisterResponseProcessor.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.gb28181.transmit.event.response.impl;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
@@ -10,6 +11,7 @@ import com.genersoft.iot.vmp.service.IPlatformService;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
+import gov.nist.javax.sip.message.SIPResponse;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
@@ -18,7 +20,6 @@ import org.springframework.stereotype.Component;
import javax.sip.InvalidArgumentException;
import javax.sip.ResponseEvent;
import javax.sip.SipException;
-import javax.sip.header.CallIdHeader;
import javax.sip.header.WWWAuthenticateHeader;
import javax.sip.message.Response;
import java.text.ParseException;
@@ -65,9 +66,8 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
*/
@Override
public void process(ResponseEvent evt) {
- Response response = evt.getResponse();
- CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
- String callId = callIdHeader.getCallId();
+ SIPResponse response = (SIPResponse)evt.getResponse();
+ String callId = response.getCallIdHeader().getCallId();
PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId);
if (platformRegisterInfo == null) {
logger.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId ));
@@ -90,15 +90,17 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
if (response.getStatusCode() == Response.UNAUTHORIZED) {
WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
+ SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
try {
- sipCommanderForPlatform.register(parentPlatform, callId, www, null, null, true, platformRegisterInfo.isRegister());
+ sipCommanderForPlatform.register(parentPlatform, sipTransactionInfo, www, null, null, true, platformRegisterInfo.isRegister());
} catch (SipException | InvalidArgumentException | ParseException e) {
logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage());
}
}else if (response.getStatusCode() == Response.OK){
if (platformRegisterInfo.isRegister()) {
- platformService.online(parentPlatform);
+ SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
+ platformService.online(parentPlatform, sipTransactionInfo);
}else {
platformService.offline(parentPlatform, false);
}
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
index 5ba519155..863ff32cb 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
@@ -9,17 +9,12 @@ import org.jetbrains.annotations.NotNull;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
-import org.springframework.util.ObjectUtils;
-import org.springframework.util.StringUtils;
-import java.io.File;
-import java.io.FileOutputStream;
import java.io.IOException;
import java.net.ConnectException;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
-import java.util.concurrent.TimeUnit;
@Component
public class AssistRESTfulUtils {
@@ -137,6 +132,11 @@ public class AssistRESTfulUtils {
return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
}
+ public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
+ Map param = new HashMap<>();
+ return sendGet(mediaServerItem, "api/record/info",param, callback);
+ }
+
public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
Map param = new HashMap<>();
param.put("app",app);
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
index 984a377e5..db40961c3 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
@@ -119,10 +119,11 @@ public class ZLMHttpHookListener {
* 服务器定时上报时间,上报间隔可配置,默认10s上报一次
*/
@ResponseBody
+
@PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
- logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
+// logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
taskExecutor.execute(() -> {
List subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
@@ -142,6 +143,7 @@ public class ZLMHttpHookListener {
* 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
*/
@ResponseBody
+
@PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
public HookResult onPlay(@RequestBody OnPlayHookParam param) {
if (logger.isDebugEnabled()) {
@@ -264,9 +266,28 @@ public class ZLMHttpHookListener {
}
}
+ if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
+ logger.info("推流时发现尚未设置录像路径,从assist服务中读取");
+ JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
+ if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
+ JSONObject dataJson = info.getJSONObject("data");
+ if (dataJson != null) {
+ String recordPath = dataJson.getString("record");
+ userSetting.setRecordPath(recordPath);
+ result.setMp4_save_path(recordPath);
+ // 修改zlm中的录像路径
+ if (mediaInfo.isAutoConfig()) {
+ taskExecutor.execute(() -> {
+ mediaServerService.setZLMConfig(mediaInfo, false);
+ });
+ }
+ }
+ }
+ }
return result;
}
+
/**
* rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
*/
@@ -293,8 +314,12 @@ public class ZLMHttpHookListener {
subscribe.response(mediaInfo, json);
}
}
- // 流消失移除redis play
+
+ List tracks = param.getTracks();
+ // TODO 重构此处逻辑
+
if (param.isRegist()) {
+ // 处理流注册的鉴权信息
if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
|| param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
index 9e1020ab4..9a4677839 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
@@ -334,9 +334,9 @@ public class ZLMRESTfulUtils {
sendPost(mediaServerItem, "kick_sessions",param, null);
}
- public void getSnap(MediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
+ public void getSnap(MediaServerItem mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
Map param = new HashMap<>(3);
- param.put("url", flvUrl);
+ param.put("url", streamUrl);
param.put("timeout_sec", timeout_sec);
param.put("expire_sec", expire_sec);
sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName);
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
index bb391ad14..a1524dd70 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
@@ -262,8 +262,11 @@ public class ZLMRTPServerFactory {
logger.info("[保持端口] {}->监听端口到期继续保持监听", ssrc);
keepPort(serverItem, ssrc);
});
- }
logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
+ logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
+ }else {
+ logger.info("[保持端口] 监听端口失败: {}", ssrc);
+ }
return localPort;
}
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
index fcf240123..36862c0b8 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
@@ -10,21 +10,87 @@ public class ZLMServerConfig {
@JSONField(name = "api.secret")
private String apiSecret;
+ @JSONField(name = "api.snapRoot")
+ private String apiSnapRoot;
+
+ @JSONField(name = "api.defaultSnap")
+ private String apiDefaultSnap;
+
@JSONField(name = "ffmpeg.bin")
private String ffmpegBin;
@JSONField(name = "ffmpeg.cmd")
private String ffmpegCmd;
+ @JSONField(name = "ffmpeg.snap")
+ private String ffmpegSnap;
+
@JSONField(name = "ffmpeg.log")
private String ffmpegLog;
+ @JSONField(name = "ffmpeg.restart_sec")
+ private String ffmpegRestartSec;
+
+ @JSONField(name = "protocol.modify_stamp")
+ private String protocolModifyStamp;
+
+ @JSONField(name = "protocol.enable_audio")
+ private String protocolEnableAudio;
+
+ @JSONField(name = "protocol.add_mute_audio")
+ private String protocolAddMuteAudio;
+
+ @JSONField(name = "protocol.continue_push_ms")
+ private String protocolContinuePushMs;
+
+ @JSONField(name = "protocol.enable_hls")
+ private String protocolEnableHls;
+
+ @JSONField(name = "protocol.enable_mp4")
+ private String protocolEnableMp4;
+
+ @JSONField(name = "protocol.enable_rtsp")
+ private String protocolEnableRtsp;
+
+ @JSONField(name = "protocol.enable_rtmp")
+ private String protocolEnableRtmp;
+
+ @JSONField(name = "protocol.enable_ts")
+ private String protocolEnableTs;
+
+ @JSONField(name = "protocol.enable_fmp4")
+ private String protocolEnableFmp4;
+
+ @JSONField(name = "protocol.mp4_as_player")
+ private String protocolMp4AsPlayer;
+
+ @JSONField(name = "protocol.mp4_max_second")
+ private String protocolMp4MaxSecond;
+
+ @JSONField(name = "protocol.mp4_save_path")
+ private String protocolMp4SavePath;
+
+ @JSONField(name = "protocol.hls_save_path")
+ private String protocolHlsSavePath;
+
+ @JSONField(name = "protocol.hls_demand")
+ private String protocolHlsDemand;
+
+ @JSONField(name = "protocol.rtsp_demand")
+ private String protocolRtspDemand;
+
+ @JSONField(name = "protocol.rtmp_demand")
+ private String protocolRtmpDemand;
+
+ @JSONField(name = "protocol.ts_demand")
+ private String protocolTsDemand;
+
+ @JSONField(name = "protocol.fmp4_demand")
+ private String protocolFmp4Demand;
+
@JSONField(name = "general.enableVhost")
private String generalEnableVhost;
- @JSONField(name = "general.mediaServerId")
- private String generalMediaServerId;
-
@JSONField(name = "general.flowThreshold")
private String generalFlowThreshold;
@@ -34,6 +100,25 @@ public class ZLMServerConfig {
@JSONField(name = "general.streamNoneReaderDelayMS")
private int generalStreamNoneReaderDelayMS;
+ @JSONField(name = "general.resetWhenRePlay")
+ private String generalResetWhenRePlay;
+
+ @JSONField(name = "general.mergeWriteMS")
+ private String generalMergeWriteMS;
+
+ @JSONField(name = "general.mediaServerId")
+ private String generalMediaServerId;
+
+ @JSONField(name = "general.wait_track_ready_ms")
+ private String generalWaitTrackReadyMs;
+
+ @JSONField(name = "general.wait_add_track_ms")
+ private String generalWaitAddTrackMs;
+
+ @JSONField(name = "general.unready_frame_cache")
+ private String generalUnreadyFrameCache;
+
+
@JSONField(name = "ip")
private String ip;
@@ -59,6 +144,18 @@ public class ZLMServerConfig {
@JSONField(name = "hls.segNum")
private String hlsSegNum;
+ @JSONField(name = "hls.segRetain")
+ private String hlsSegRetain;
+
+ @JSONField(name = "hls.broadcastRecordTs")
+ private String hlsBroadcastRecordTs;
+
+ @JSONField(name = "hls.deleteDelaySec")
+ private String hlsDeleteDelaySec;
+
+ @JSONField(name = "hls.segKeep")
+ private String hlsSegKeep;
+
@JSONField(name = "hook.access_file_except_hls")
private String hookAccessFileExceptHLS;
@@ -104,6 +201,18 @@ public class ZLMServerConfig {
@JSONField(name = "hook.on_stream_not_found")
private String hookOnStreamNotFound;
+ @JSONField(name = "hook.on_server_started")
+ private String hookOnServerStarted;
+
+ @JSONField(name = "hook.on_server_keepalive")
+ private String hookOnServerKeepalive;
+
+ @JSONField(name = "hook.on_send_rtp_stopped")
+ private String hookOnSendRtpStopped;
+
+ @JSONField(name = "hook.on_rtp_server_timeout")
+ private String hookOnRtpServerTimeout;
+
@JSONField(name = "hook.timeoutSec")
private String hookTimeoutSec;
@@ -813,4 +922,292 @@ public class ZLMServerConfig {
public void setPortRange(String portRange) {
this.portRange = portRange;
}
+
+ public String getApiSnapRoot() {
+ return apiSnapRoot;
+ }
+
+ public void setApiSnapRoot(String apiSnapRoot) {
+ this.apiSnapRoot = apiSnapRoot;
+ }
+
+ public String getApiDefaultSnap() {
+ return apiDefaultSnap;
+ }
+
+ public void setApiDefaultSnap(String apiDefaultSnap) {
+ this.apiDefaultSnap = apiDefaultSnap;
+ }
+
+ public String getFfmpegSnap() {
+ return ffmpegSnap;
+ }
+
+ public void setFfmpegSnap(String ffmpegSnap) {
+ this.ffmpegSnap = ffmpegSnap;
+ }
+
+ public String getFfmpegRestartSec() {
+ return ffmpegRestartSec;
+ }
+
+ public void setFfmpegRestartSec(String ffmpegRestartSec) {
+ this.ffmpegRestartSec = ffmpegRestartSec;
+ }
+
+ public String getProtocolModifyStamp() {
+ return protocolModifyStamp;
+ }
+
+ public void setProtocolModifyStamp(String protocolModifyStamp) {
+ this.protocolModifyStamp = protocolModifyStamp;
+ }
+
+ public String getProtocolEnableAudio() {
+ return protocolEnableAudio;
+ }
+
+ public void setProtocolEnableAudio(String protocolEnableAudio) {
+ this.protocolEnableAudio = protocolEnableAudio;
+ }
+
+ public String getProtocolAddMuteAudio() {
+ return protocolAddMuteAudio;
+ }
+
+ public void setProtocolAddMuteAudio(String protocolAddMuteAudio) {
+ this.protocolAddMuteAudio = protocolAddMuteAudio;
+ }
+
+ public String getProtocolContinuePushMs() {
+ return protocolContinuePushMs;
+ }
+
+ public void setProtocolContinuePushMs(String protocolContinuePushMs) {
+ this.protocolContinuePushMs = protocolContinuePushMs;
+ }
+
+ public String getProtocolEnableHls() {
+ return protocolEnableHls;
+ }
+
+ public void setProtocolEnableHls(String protocolEnableHls) {
+ this.protocolEnableHls = protocolEnableHls;
+ }
+
+ public String getProtocolEnableMp4() {
+ return protocolEnableMp4;
+ }
+
+ public void setProtocolEnableMp4(String protocolEnableMp4) {
+ this.protocolEnableMp4 = protocolEnableMp4;
+ }
+
+ public String getProtocolEnableRtsp() {
+ return protocolEnableRtsp;
+ }
+
+ public void setProtocolEnableRtsp(String protocolEnableRtsp) {
+ this.protocolEnableRtsp = protocolEnableRtsp;
+ }
+
+ public String getProtocolEnableRtmp() {
+ return protocolEnableRtmp;
+ }
+
+ public void setProtocolEnableRtmp(String protocolEnableRtmp) {
+ this.protocolEnableRtmp = protocolEnableRtmp;
+ }
+
+ public String getProtocolEnableTs() {
+ return protocolEnableTs;
+ }
+
+ public void setProtocolEnableTs(String protocolEnableTs) {
+ this.protocolEnableTs = protocolEnableTs;
+ }
+
+ public String getProtocolEnableFmp4() {
+ return protocolEnableFmp4;
+ }
+
+ public void setProtocolEnableFmp4(String protocolEnableFmp4) {
+ this.protocolEnableFmp4 = protocolEnableFmp4;
+ }
+
+ public String getProtocolMp4AsPlayer() {
+ return protocolMp4AsPlayer;
+ }
+
+ public void setProtocolMp4AsPlayer(String protocolMp4AsPlayer) {
+ this.protocolMp4AsPlayer = protocolMp4AsPlayer;
+ }
+
+ public String getProtocolMp4MaxSecond() {
+ return protocolMp4MaxSecond;
+ }
+
+ public void setProtocolMp4MaxSecond(String protocolMp4MaxSecond) {
+ this.protocolMp4MaxSecond = protocolMp4MaxSecond;
+ }
+
+ public String getProtocolMp4SavePath() {
+ return protocolMp4SavePath;
+ }
+
+ public void setProtocolMp4SavePath(String protocolMp4SavePath) {
+ this.protocolMp4SavePath = protocolMp4SavePath;
+ }
+
+ public String getProtocolHlsSavePath() {
+ return protocolHlsSavePath;
+ }
+
+ public void setProtocolHlsSavePath(String protocolHlsSavePath) {
+ this.protocolHlsSavePath = protocolHlsSavePath;
+ }
+
+ public String getProtocolHlsDemand() {
+ return protocolHlsDemand;
+ }
+
+ public void setProtocolHlsDemand(String protocolHlsDemand) {
+ this.protocolHlsDemand = protocolHlsDemand;
+ }
+
+ public String getProtocolRtspDemand() {
+ return protocolRtspDemand;
+ }
+
+ public void setProtocolRtspDemand(String protocolRtspDemand) {
+ this.protocolRtspDemand = protocolRtspDemand;
+ }
+
+ public String getProtocolRtmpDemand() {
+ return protocolRtmpDemand;
+ }
+
+ public void setProtocolRtmpDemand(String protocolRtmpDemand) {
+ this.protocolRtmpDemand = protocolRtmpDemand;
+ }
+
+ public String getProtocolTsDemand() {
+ return protocolTsDemand;
+ }
+
+ public void setProtocolTsDemand(String protocolTsDemand) {
+ this.protocolTsDemand = protocolTsDemand;
+ }
+
+ public String getProtocolFmp4Demand() {
+ return protocolFmp4Demand;
+ }
+
+ public void setProtocolFmp4Demand(String protocolFmp4Demand) {
+ this.protocolFmp4Demand = protocolFmp4Demand;
+ }
+
+ public String getGeneralResetWhenRePlay() {
+ return generalResetWhenRePlay;
+ }
+
+ public void setGeneralResetWhenRePlay(String generalResetWhenRePlay) {
+ this.generalResetWhenRePlay = generalResetWhenRePlay;
+ }
+
+ public String getGeneralMergeWriteMS() {
+ return generalMergeWriteMS;
+ }
+
+ public void setGeneralMergeWriteMS(String generalMergeWriteMS) {
+ this.generalMergeWriteMS = generalMergeWriteMS;
+ }
+
+ public String getGeneralWaitTrackReadyMs() {
+ return generalWaitTrackReadyMs;
+ }
+
+ public void setGeneralWaitTrackReadyMs(String generalWaitTrackReadyMs) {
+ this.generalWaitTrackReadyMs = generalWaitTrackReadyMs;
+ }
+
+ public String getGeneralWaitAddTrackMs() {
+ return generalWaitAddTrackMs;
+ }
+
+ public void setGeneralWaitAddTrackMs(String generalWaitAddTrackMs) {
+ this.generalWaitAddTrackMs = generalWaitAddTrackMs;
+ }
+
+ public String getGeneralUnreadyFrameCache() {
+ return generalUnreadyFrameCache;
+ }
+
+ public void setGeneralUnreadyFrameCache(String generalUnreadyFrameCache) {
+ this.generalUnreadyFrameCache = generalUnreadyFrameCache;
+ }
+
+ public String getHlsSegRetain() {
+ return hlsSegRetain;
+ }
+
+ public void setHlsSegRetain(String hlsSegRetain) {
+ this.hlsSegRetain = hlsSegRetain;
+ }
+
+ public String getHlsBroadcastRecordTs() {
+ return hlsBroadcastRecordTs;
+ }
+
+ public void setHlsBroadcastRecordTs(String hlsBroadcastRecordTs) {
+ this.hlsBroadcastRecordTs = hlsBroadcastRecordTs;
+ }
+
+ public String getHlsDeleteDelaySec() {
+ return hlsDeleteDelaySec;
+ }
+
+ public void setHlsDeleteDelaySec(String hlsDeleteDelaySec) {
+ this.hlsDeleteDelaySec = hlsDeleteDelaySec;
+ }
+
+ public String getHlsSegKeep() {
+ return hlsSegKeep;
+ }
+
+ public void setHlsSegKeep(String hlsSegKeep) {
+ this.hlsSegKeep = hlsSegKeep;
+ }
+
+ public String getHookOnServerStarted() {
+ return hookOnServerStarted;
+ }
+
+ public void setHookOnServerStarted(String hookOnServerStarted) {
+ this.hookOnServerStarted = hookOnServerStarted;
+ }
+
+ public String getHookOnServerKeepalive() {
+ return hookOnServerKeepalive;
+ }
+
+ public void setHookOnServerKeepalive(String hookOnServerKeepalive) {
+ this.hookOnServerKeepalive = hookOnServerKeepalive;
+ }
+
+ public String getHookOnSendRtpStopped() {
+ return hookOnSendRtpStopped;
+ }
+
+ public void setHookOnSendRtpStopped(String hookOnSendRtpStopped) {
+ this.hookOnSendRtpStopped = hookOnSendRtpStopped;
+ }
+
+ public String getHookOnRtpServerTimeout() {
+ return hookOnRtpServerTimeout;
+ }
+
+ public void setHookOnRtpServerTimeout(String hookOnRtpServerTimeout) {
+ this.hookOnRtpServerTimeout = hookOnRtpServerTimeout;
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
index 38aa87b32..cb8e73839 100644
--- a/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
+++ b/src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
@@ -5,6 +5,7 @@ public class HookResultForOnPublish extends HookResult{
private boolean enable_audio;
private boolean enable_mp4;
private int mp4_max_second;
+ private String mp4_save_path;
public HookResultForOnPublish() {
}
@@ -41,4 +42,12 @@ public class HookResultForOnPublish extends HookResult{
public void setMp4_max_second(int mp4_max_second) {
this.mp4_max_second = mp4_max_second;
}
+
+ public String getMp4_save_path() {
+ return mp4_save_path;
+ }
+
+ public void setMp4_save_path(String mp4_save_path) {
+ this.mp4_save_path = mp4_save_path;
+ }
}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java b/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java
index 072519bd5..d977c4732 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java
@@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service;
import com.genersoft.iot.vmp.gb28181.bean.Device;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
import com.genersoft.iot.vmp.vmanager.bean.BaseTree;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
@@ -18,13 +19,13 @@ public interface IDeviceService {
* 设备上线
* @param device 设备信息
*/
- void online(Device device);
+ void online(Device device, SipTransactionInfo sipTransactionInfo);
/**
* 设备下线
* @param deviceId 设备编号
*/
- void offline(String deviceId);
+ void offline(String deviceId, String reason);
/**
* 添加目录订阅
diff --git a/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java b/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
index be8a8f5f9..f9bb94bdb 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
@@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
+import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
import com.github.pagehelper.PageInfo;
import javax.sip.InvalidArgumentException;
@@ -34,11 +35,17 @@ public interface IPlatformService {
*/
boolean add(ParentPlatform parentPlatform);
+ /**
+ * 添加级联平台
+ * @param parentPlatform 级联平台
+ */
+ boolean update(ParentPlatform parentPlatform);
+
/**
* 平台上线
* @param parentPlatform 平台信息
*/
- void online(ParentPlatform parentPlatform);
+ void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo);
/**
* 平台离线
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
index 399225c29..98dbaf849 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
@@ -96,7 +96,7 @@ public class DeviceServiceImpl implements IDeviceService {
private ZLMRESTfulUtils zlmresTfulUtils;
@Override
- public void online(Device device) {
+ public void online(Device device, SipTransactionInfo sipTransactionInfo) {
logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId());
Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId());
@@ -111,6 +111,14 @@ public class DeviceServiceImpl implements IDeviceService {
// 默认心跳间隔60
device.setKeepaliveIntervalTime(60);
}
+ if (sipTransactionInfo != null) {
+ device.setSipTransactionInfo(sipTransactionInfo);
+ }else {
+ if (deviceInRedis != null) {
+ device.setSipTransactionInfo(deviceInRedis.getSipTransactionInfo());
+ }
+ }
+
// 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询
if (device.getCreateTime() == null) {
device.setOnline(1);
@@ -163,12 +171,12 @@ public class DeviceServiceImpl implements IDeviceService {
// 刷新过期任务
String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
// 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线
- dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getKeepaliveIntervalTime() * 1000 * 3);
+ dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
}
@Override
- public void offline(String deviceId) {
- logger.error("[设备离线], device:{}", deviceId);
+ public void offline(String deviceId, String reason) {
+ logger.error("[设备离线],{}, device:{}", reason, deviceId);
Device device = deviceMapper.getDeviceByDeviceId(deviceId);
if (device == null) {
return;
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
index 89acb065a..37e3b9e6e 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
@@ -40,6 +40,9 @@ public class GbStreamServiceImpl implements IGbStreamService {
@Autowired
private PlatformGbStreamMapper platformGbStreamMapper;
+ @Autowired
+ private SubscribeHolder subscribeHolder;
+
@Autowired
private ParentPlatformMapper platformMapper;
@@ -73,16 +76,23 @@ public class GbStreamServiceImpl implements IGbStreamService {
}
try {
List deviceChannelList = new ArrayList<>();
- for (GbStream gbStream : gbStreams) {
+
+
+ for (int i = 0; i < gbStreams.size(); i++) {
+ GbStream gbStream = gbStreams.get(i);
gbStream.setCatalogId(catalogId);
gbStream.setPlatformId(platformId);
// TODO 修改为批量提交
platformGbStreamMapper.add(gbStream);
+ logger.info("[关联通道]直播流通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, gbStreams.size(), i + 1);
DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform);
deviceChannelList.add(deviceChannelListByStream);
}
dataSourceTransactionManager.commit(transactionStatus); //手动提交
- eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
+ if (subscribeHolder.getCatalogSubscribe(platformId) != null) {
+ eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
+ }
+
result = true;
}catch (Exception e) {
logger.error("批量保存流与平台的关系时错误", e);
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
index 226fa6fd1..dc495f9a3 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
@@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
+import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
@@ -38,6 +39,7 @@ import org.springframework.transaction.TransactionDefinition;
import org.springframework.transaction.TransactionStatus;
import org.springframework.util.ObjectUtils;
+import java.io.File;
import java.time.LocalDateTime;
import java.util.*;
@@ -63,6 +65,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
@Autowired
private UserSetting userSetting;
+ @Autowired
+ private AssistRESTfulUtils assistRESTfulUtils;
+
@Autowired
private ZLMRESTfulUtils zlmresTfulUtils;
@@ -409,13 +414,27 @@ public class MediaServerServiceImpl implements IMediaServerService {
}
RedisUtil.set(key, serverItem);
resetOnlineServerItem(serverItem);
+
+
if (serverItem.isAutoConfig()) {
+ // 查看assist服务的录像路径配置
+ if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
+ JSONObject info = assistRESTfulUtils.getInfo(serverItem, null);
+ if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
+ JSONObject dataJson = info.getJSONObject("data");
+ if (dataJson != null) {
+ String recordPath = dataJson.getString("record");
+ userSetting.setRecordPath(recordPath);
+ }
+ }
+ }
setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
}
final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
dynamicTask.stop(zlmKeepaliveKey);
dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000);
publisher.zlmOnlineEventPublish(serverItem.getId());
+
logger.info("[ZLM] 连接成功 {} - {}:{} ",
zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
}
@@ -549,6 +568,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
Map param = new HashMap<>();
param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
+ if (mediaServerItem.getRtspPort() != 0) {
+ param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -t 0.001 %s");
+ }
param.put("hook.enable","1");
param.put("hook.on_flow_report","");
param.put("hook.on_play",String.format("%s/on_play", hookPrex));
@@ -583,6 +605,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
}
+ if (userSetting.getRecordPath() != null) {
+ File recordPathFile = new File(userSetting.getRecordPath());
+ File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile();
+ param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile());
+ param.put("record.appName", recordPathFile.getName());
+ }
+
JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
if (responseJSON != null && responseJSON.getInteger("code") == 0) {
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java
index 601ff5de4..7d827d732 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java
@@ -1,9 +1,6 @@
package com.genersoft.iot.vmp.service.impl;
-import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
-import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
-import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
-import com.genersoft.iot.vmp.gb28181.bean.TreeType;
+import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
import com.genersoft.iot.vmp.service.IPlatformChannelService;
@@ -15,7 +12,10 @@ import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.jdbc.datasource.DataSourceTransactionManager;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.TransactionDefinition;
+import org.springframework.transaction.TransactionStatus;
import org.springframework.util.ObjectUtils;
import java.util.ArrayList;
@@ -34,6 +34,16 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
@Autowired
private PlatformChannelMapper platformChannelMapper;
+ @Autowired
+ TransactionDefinition transactionDefinition;
+
+ @Autowired
+ DataSourceTransactionManager dataSourceTransactionManager;
+
+ @Autowired
+ private SubscribeHolder subscribeHolder;
+
+
@Autowired
private DeviceChannelMapper deviceChannelMapper;
@@ -69,17 +79,47 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
}
List channelReducesToAdd = new ArrayList<>(deviceAndChannels.values());
// 对剩下的数据进行存储
- int result = 0;
+ int allCount = 0;
+ boolean result = false;
+ TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
+ int limitCount = 300;
if (channelReducesToAdd.size() > 0) {
- result = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
- // TODO 后续给平台增加控制开关以控制是否响应目录订阅
- List deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
- if (deviceChannelList != null) {
- eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
+ if (channelReducesToAdd.size() > limitCount) {
+ for (int i = 0; i < channelReducesToAdd.size(); i += limitCount) {
+ int toIndex = i + limitCount;
+ if (i + limitCount > channelReducesToAdd.size()) {
+ toIndex = channelReducesToAdd.size();
+ }
+ int count = platformChannelMapper.addChannels(platformId, channelReducesToAdd.subList(i, toIndex));
+ result = result || count < 0;
+ allCount += count;
+ logger.info("[关联通道]国标通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, channelReducesToAdd.size(), toIndex);
+ }
+ }else {
+ allCount = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
+ result = result || allCount < 0;
+ logger.info("[关联通道]国标通道 平台:{}, 关联通道数:{}", platformId, channelReducesToAdd.size());
}
- }
- return result;
+ if (result) {
+ //事务回滚
+ dataSourceTransactionManager.rollback(transactionStatus);
+ allCount = 0;
+ }else {
+ logger.info("[关联通道]国标通道 平台:{}, 正在存入数据库", platformId);
+ dataSourceTransactionManager.commit(transactionStatus);
+
+ }
+ SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platformId);
+ if (catalogSubscribe != null) {
+ List deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
+ if (deviceChannelList != null) {
+ eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
+ }
+ }
+ logger.info("[关联通道]国标通道 平台:{}, 存入数据库成功", platformId);
+ }
+ return allCount;
}
private List getDeviceChannelListByChannelReduceList(List channelReduces, String catalogId, ParentPlatform platform) {
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
index e9b31caad..27185139a 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
@@ -21,8 +21,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
import com.genersoft.iot.vmp.service.bean.SSRCInfo;
import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
-import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
-import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
+import com.genersoft.iot.vmp.storager.dao.*;
+import com.genersoft.iot.vmp.utils.DateUtil;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import org.slf4j.Logger;
@@ -53,6 +53,15 @@ public class PlatformServiceImpl implements IPlatformService {
@Autowired
private ParentPlatformMapper platformMapper;
+ @Autowired
+ private PlatformCatalogMapper catalogMapper;
+
+ @Autowired
+ private PlatformChannelMapper platformChannelMapper;
+
+ @Autowired
+ private PlatformGbStreamMapper platformGbStreamMapper;
+
@Autowired
private IRedisCatchStorage redisCatchStorage;
@@ -135,36 +144,106 @@ public class PlatformServiceImpl implements IPlatformService {
}
@Override
- public void online(ParentPlatform parentPlatform) {
- logger.info("[国标级联]:{}, 平台上线/更新注册", parentPlatform.getServerGBId());
+ public boolean update(ParentPlatform parentPlatform) {
+ logger.info("[国标级联]更新平台 {}", parentPlatform.getDeviceGBId());
+ parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase());
+ ParentPlatform parentPlatformOld = platformMapper.getParentPlatById(parentPlatform.getId());
+ ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatformOld.getServerGBId());
+ parentPlatform.setUpdateTime(DateUtil.getNow());
+ if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) {
+ // 目录结构发生变化,清空之前的关联关系
+ logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId());
+ catalogMapper.delByPlatformId(parentPlatformOld.getServerGBId());
+ platformChannelMapper.delByPlatformId(parentPlatformOld.getServerGBId());
+ platformGbStreamMapper.delByPlatformId(parentPlatformOld.getServerGBId());
+ }
+
+
+ // 停止心跳定时
+ final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatformOld.getServerGBId();
+ dynamicTask.stop(keepaliveTaskKey);
+ // 停止注册定时
+ final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatformOld.getServerGBId();
+ dynamicTask.stop(registerTaskKey);
+ // 注销旧的
+ try {
+ if (parentPlatformOld.isStatus()) {
+ logger.info("保存平台{}时发现救平台在线,发送注销命令", parentPlatform.getDeviceGBId());
+ commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> {
+ logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId());
+ });
+ }
+
+ } catch (InvalidArgumentException | ParseException | SipException e) {
+ logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
+ }
+
+ // 更新数据库
+ if (parentPlatform.getCatalogGroup() == 0) {
+ parentPlatform.setCatalogGroup(1);
+ }
+ if (parentPlatform.getAdministrativeDivision() == null) {
+ parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision());
+ }
+
+ platformMapper.updateParentPlatform(parentPlatform);
+ // 更新redis
+ redisCatchStorage.delPlatformCatchInfo(parentPlatformOld.getServerGBId());
+ ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
+ parentPlatformCatch.setParentPlatform(parentPlatform);
+ parentPlatformCatch.setId(parentPlatform.getServerGBId());
+ redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+ // 注册
+ if (parentPlatform.isEnable()) {
+ // 保存时启用就发送注册
+ // 注册成功时由程序直接调用了online方法
+ try {
+ commanderForPlatform.register(parentPlatform, eventResult -> {
+ logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId());
+ }, null);
+ } catch (InvalidArgumentException | ParseException | SipException e) {
+ logger.error("[命令发送失败] 国标级联: {}", e.getMessage());
+ }
+ }
+ // 重新开启定时注册, 使用续订消息
+ // 重新开始心跳保活
+
+
+ return false;
+ }
+
+
+ @Override
+ public void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) {
+ logger.info("[国标级联]:{}, 平台上线", parentPlatform.getServerGBId());
platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true);
ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
- if (parentPlatformCatch != null) {
- parentPlatformCatch.getParentPlatform().setStatus(true);
- redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
- }else {
+ if (parentPlatformCatch == null) {
parentPlatformCatch = new ParentPlatformCatch();
parentPlatformCatch.setParentPlatform(parentPlatform);
parentPlatformCatch.setId(parentPlatform.getServerGBId());
parentPlatform.setStatus(true);
parentPlatformCatch.setParentPlatform(parentPlatform);
- redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
}
+ parentPlatformCatch.getParentPlatform().setStatus(true);
+ parentPlatformCatch.setSipTransactionInfo(sipTransactionInfo);
+ redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
+
final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId();
if (!dynamicTask.isAlive(registerTaskKey)) {
+ logger.info("[国标级联]:{}, 添加定时注册任务", parentPlatform.getServerGBId());
// 添加注册任务
dynamicTask.startCron(registerTaskKey,
// 注册失败(注册成功时由程序直接调用了online方法)
- ()-> {
- registerTask(parentPlatform);
- },
- (parentPlatform.getExpires() - 10) *1000);
+ ()-> registerTask(parentPlatform, sipTransactionInfo),
+ parentPlatform.getExpires() * 1000);
}
final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId();
if (!dynamicTask.contains(keepaliveTaskKey)) {
+ logger.info("[国标级联]:{}, 添加定时心跳任务", parentPlatform.getServerGBId());
// 添加心跳任务
dynamicTask.startCron(keepaliveTaskKey,
()-> {
@@ -205,11 +284,11 @@ public class PlatformServiceImpl implements IPlatformService {
logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage());
}
},
- (parentPlatform.getKeepTimeout() - 10)*1000);
+ (parentPlatform.getKeepTimeout())*1000);
}
}
- private void registerTask(ParentPlatform parentPlatform){
+ private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo){
try {
// 设置超时重发, 后续从底层支持消息重发
String key = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId() + "_timeout";
@@ -217,10 +296,10 @@ public class PlatformServiceImpl implements IPlatformService {
return;
}
dynamicTask.startDelay(key, ()->{
- registerTask(parentPlatform);
+ registerTask(parentPlatform, sipTransactionInfo);
}, 1000);
- logger.info("[国标级联] 平台:{}注册即将到期,重新注册", parentPlatform.getServerGBId());
- commanderForPlatform.register(parentPlatform, eventResult -> {
+ logger.info("[国标级联] 平台:{}注册即将到期,开始续订", parentPlatform.getServerGBId());
+ commanderForPlatform.register(parentPlatform, sipTransactionInfo, eventResult -> {
dynamicTask.stop(key);
offline(parentPlatform, false);
},eventResult -> {
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
index f45dd9285..4d8ed2798 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
@@ -438,7 +438,12 @@ public class PlayServiceImpl implements IPlayService {
onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
hookEvent.response(mediaServerItemInuse, response);
logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
- String streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.flv", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());
+ String streamUrl;
+ if (mediaServerItemInuse.getRtspPort() != 0) {
+ streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", ssrcInfo.getStream());
+ }else {
+ streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());
+ }
String path = "snap";
String fileName = device.getDeviceId() + "_" + channelId + ".jpg";
// 请求截图
@@ -806,23 +811,75 @@ public class PlayServiceImpl implements IPlayService {
hookCallBack.call(downloadResult);
streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
};
-
+ InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> {
+ logger.info("收到订阅消息: " + inviteStreamInfo.getCallId());
+ dynamicTask.stop(downLoadTimeOutTaskKey);
+ StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
+ streamInfo.setStartTime(startTime);
+ streamInfo.setEndTime(endTime);
+ redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
+ downloadResult.setCode(ErrorCode.SUCCESS.getCode());
+ downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
+ downloadResult.setData(streamInfo);
+ downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
+ downloadResult.setResponse(inviteStreamInfo.getResponse());
+ hookCallBack.call(downloadResult);
+ };
try {
cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
- inviteStreamInfo -> {
- logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
- dynamicTask.stop(downLoadTimeOutTaskKey);
- StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
- streamInfo.setStartTime(startTime);
- streamInfo.setEndTime(endTime);
- redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
- downloadResult.setCode(ErrorCode.SUCCESS.getCode());
- downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
- downloadResult.setData(streamInfo);
- downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
- downloadResult.setResponse(inviteStreamInfo.getResponse());
- hookCallBack.call(downloadResult);
- }, errorEvent);
+ hookEvent, errorEvent, eventResult ->
+ {
+ if (eventResult.type == SipSubscribe.EventResultType.response) {
+ ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
+ String contentString = new String(responseEvent.getResponse().getRawContent());
+ // 获取ssrc
+ int ssrcIndex = contentString.indexOf("y=");
+ // 检查是否有y字段
+ if (ssrcIndex >= 0) {
+ //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
+ String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
+ // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
+ if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
+ return;
+ }
+ logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
+ if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
+ logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
+
+ if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
+ // ssrc 不可用
+ // 释放ssrc
+ mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
+ streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
+ eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
+ eventResult.statusCode = 400;
+ errorEvent.response(eventResult);
+ return;
+ }
+
+ // 单端口模式streamId也有变化,需要重新设置监听
+ if (!mediaServerItem.isRtpEnable()) {
+ // 添加订阅
+ HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
+ subscribe.removeSubscribe(hookSubscribe);
+ hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
+ subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
+ logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
+ dynamicTask.stop(downLoadTimeOutTaskKey);
+ // hook响应
+ onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, hookCallBack);
+ hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
+ });
+ }
+ // 关闭rtp server
+ mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
+ // 重新开启ssrc server
+ mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
+ }
+ }
+ }
+
+ });
} catch (InvalidArgumentException | SipException | ParseException e) {
logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
diff --git a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
index ad37a618e..acadee6e7 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
@@ -201,7 +201,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
dataSourceTransactionManager.commit(transactionStatus); //手动提交
result = true;
}catch (Exception e) {
- e.printStackTrace();
+ logger.error("未处理的异常 ", e);
dataSourceTransactionManager.rollback(transactionStatus);
}
return result;
diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
index 2e18db39d..194e90fb9 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
@@ -1,6 +1,7 @@
package com.genersoft.iot.vmp.service.redisMsg;
import com.alibaba.fastjson2.JSON;
+import com.genersoft.iot.vmp.conf.UserSetting;
import com.genersoft.iot.vmp.gb28181.bean.*;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
@@ -44,13 +45,17 @@ public class RedisAlarmMsgListener implements MessageListener {
@Autowired
private ThreadPoolTaskExecutor taskExecutor;
+ @Autowired
+ private UserSetting userSetting;
+
@Override
public void onMessage(@NotNull Message message, byte[] bytes) {
+ // 消息示例: PUBLISH alarm_receive '{ "gbId": "", "alarmSn": 1, "alarmType": "111", "alarmDescription": "222", }'
logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody()));
boolean isEmpty = taskQueue.isEmpty();
taskQueue.offer(message);
if (isEmpty) {
- logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize());
+// logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize());
taskExecutor.execute(() -> {
while (!taskQueue.isEmpty()) {
Message msg = taskQueue.poll();
@@ -69,22 +74,52 @@ public class RedisAlarmMsgListener implements MessageListener {
deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
deviceAlarm.setAlarmPriority("1");
- deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
+ deviceAlarm.setAlarmTime(DateUtil.getNow());
deviceAlarm.setLongitude(0);
deviceAlarm.setLatitude(0);
if (ObjectUtils.isEmpty(gbId)) {
- // 发送给所有的上级
- List parentPlatforms = storage.queryEnableParentPlatformList(true);
- if (parentPlatforms.size() > 0) {
- for (ParentPlatform parentPlatform : parentPlatforms) {
+ if (userSetting.getSendToPlatformsWhenIdLost()) {
+ // 发送给所有的上级
+ List parentPlatforms = storage.queryEnableParentPlatformList(true);
+ if (parentPlatforms.size() > 0) {
+ for (ParentPlatform parentPlatform : parentPlatforms) {
+ try {
+ deviceAlarm.setChannelId(parentPlatform.getDeviceGBId());
+ commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
+ } catch (SipException | InvalidArgumentException | ParseException e) {
+ logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
+ }
+ }
+ }
+ }else {
+ // 获取开启了消息推送的设备和平台
+ List parentPlatforms = storage.queryEnablePlatformListWithAsMessageChannel();
+ if (parentPlatforms.size() > 0) {
+ for (ParentPlatform parentPlatform : parentPlatforms) {
+ try {
+ deviceAlarm.setChannelId(parentPlatform.getDeviceGBId());
+ commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
+ } catch (SipException | InvalidArgumentException | ParseException e) {
+ logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
+ }
+ }
+ }
+
+ }
+ // 获取开启了消息推送的设备和平台
+ List devices = storage.queryDeviceWithAsMessageChannel();
+ if (devices.size() > 0) {
+ for (Device device : devices) {
try {
- commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
- } catch (SipException | InvalidArgumentException | ParseException e) {
- logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
+ deviceAlarm.setChannelId(device.getDeviceId());
+ commander.sendAlarmMessage(device, deviceAlarm);
+ } catch (InvalidArgumentException | SipException | ParseException e) {
+ logger.error("[命令发送失败] 发送报警: {}", e.getMessage());
}
}
}
+
}else {
Device device = storage.queryVideoDevice(gbId);
ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId);
@@ -105,6 +140,7 @@ public class RedisAlarmMsgListener implements MessageListener {
}
}
}catch (Exception e) {
+ logger.error("未处理的异常 ", e);
logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
}
}
diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
index 35ed99e45..c37264785 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
@@ -202,7 +202,8 @@ public class RedisGbPlayMsgListener implements MessageListener {
}
}catch (Exception e) {
- logger.warn("[RedisGbPlayMsg] 发现未处理的异常, {}",e.getMessage());
+ logger.warn("[RedisGbPlayMsg] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+ logger.error("[RedisGbPlayMsg] 异常内容: ", e);
}
}
});
diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java
index 0c99707ed..4e81a591d 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java
@@ -53,7 +53,8 @@ public class RedisGpsMsgListener implements MessageListener {
// 只是放入redis缓存起来
redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
}catch (Exception e) {
- logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+ logger.warn("[REDIS的ALARM通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+ logger.error("[REDIS的ALARM通知] 异常内容: ", e);
}
}
});
diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java
index 33eae1eb6..c90771be8 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java
@@ -58,7 +58,8 @@ public class RedisPushStreamResponseListener implements MessageListener {
responseEvents.get(response.getApp() + response.getStream()).run(response);
}
}catch (Exception e) {
- logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+ logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+ logger.error("[REDIS消息-请求推流结果] 异常内容: ", e);
}
}
});
diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java
index d8ed1a010..cb34ff591 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java
@@ -95,7 +95,8 @@ public class RedisPushStreamStatusListMsgListener implements MessageListener {
gbStreamService.updateGbIdOrName(streamPushItemForUpdate);
}
}catch (Exception e) {
- logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+ logger.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+ logger.error("[REDIS消息-推流设备列表更新] 异常内容: ", e);
}
}
});
diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java
index 96ff8e830..d7e02f594 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java
@@ -79,7 +79,8 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic
streamPushService.online(statusChangeFromPushStream.getOnlineStreams());
}
}catch (Exception e) {
- logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+ logger.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+ logger.error("[REDIS消息-推流设备状态变化] 异常内容: ", e);
}
}
});
diff --git a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java
index 1cdc527a7..f5f29487e 100644
--- a/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java
+++ b/src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java
@@ -82,7 +82,8 @@ public class RedisStreamMsgListener implements MessageListener {
zlmMediaListManager.removeMedia(app, stream);
}
}catch (Exception e) {
- logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
+ logger.warn("[REDIS消息-流变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
+ logger.error("[REDIS消息-流变化] 异常内容: ", e);
}
}
});
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
index 938f669be..b53c2d31f 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
+import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
import com.github.pagehelper.PageInfo;
import java.util.List;
@@ -58,7 +59,7 @@ public interface IVideoManagerStorage {
*/
public PageInfo queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count);
- public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List channelIds);
+ public List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit);
/**
@@ -374,4 +375,10 @@ public interface IVideoManagerStorage {
void cleanContentForPlatform(String serverGBId);
List queryChannelWithCatalog(String serverGBId);
+
+ List queryChannelsByDeviceId(String serial, List channelIds, Boolean online);
+
+ List queryEnablePlatformListWithAsMessageChannel();
+
+ List queryDeviceWithAsMessageChannel();
}
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
index 83f933433..93f2a0975 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
@@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
+import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
import org.apache.ibatis.annotations.*;
import org.springframework.stereotype.Repository;
@@ -82,7 +83,56 @@ public interface DeviceChannelMapper {
" " +
"ORDER BY dc.channelId " +
" "})
- List queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online,List channelIds);
+ List queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List channelIds);
+
+ @Select(value = {" "})
+ List queryChannelsWithDeviceInfo(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List channelIds);
+
+
+ @Select(value = {" "})
+ List queryChannelsByDeviceIdWithStartAndLimit(String deviceId,List channelIds, String parentChannelId, String query,
+ Boolean hasSubChannel, Boolean online, int start, int limit);
@Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}")
DeviceChannel queryChannel(String deviceId, String channelId);
@@ -245,28 +295,6 @@ public interface DeviceChannelMapper {
int batchUpdate(List updateChannels);
- @Select(value = {" "})
- List queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String parentChannelId, String query,
- Boolean hasSubChannel, Boolean online, int start, int limit,List channelIds);
-
@Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1")
List queryOnlineChannelsByDeviceId(String deviceId);
@@ -316,10 +344,10 @@ public interface DeviceChannelMapper {
"select * " +
"from device_channel " +
"where deviceId=#{deviceId}" +
- " and parentId = #{parentId} or left(channelId, #{parentId.length()}) = #{parentId} and length(channelId)=#{length} " +
+ " and parentId = #{parentId} or left(channelId, LENGTH(#{parentId})) = #{parentId} and length(channelId)=#{length} " +
" and parentId = #{parentId} or length(channelId)=#{length} " +
" and parentId = #{parentId} " +
- " and parentId = #{parentId} or left(channelId, #{parentId.length()}) = #{parentId} " +
+ " and parentId = #{parentId} or left(channelId, LENGTH(#{parentId})) = #{parentId} " +
" "})
List getChannelsWithCivilCodeAndLength(String deviceId, String parentId, Integer length);
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
index 0aed82078..fe739ea93 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
@@ -39,9 +39,12 @@ public interface DeviceMapper {
"mobilePositionSubmissionInterval," +
"subscribeCycleForAlarm," +
"ssrcCheck," +
+ "asMessageChannel," +
"geoCoordSys," +
"treeType," +
- "online" +
+ "online," +
+ "mediaServerId," +
+ "(SELECT count(0) FROM device_channel WHERE deviceId=device.deviceId) as channelCount "+
" FROM device WHERE deviceId = #{deviceId}")
Device getDeviceByDeviceId(String deviceId);
@@ -70,6 +73,7 @@ public interface DeviceMapper {
"mobilePositionSubmissionInterval," +
"subscribeCycleForAlarm," +
"ssrcCheck," +
+ "asMessageChannel," +
"geoCoordSys," +
"treeType," +
"online" +
@@ -98,6 +102,7 @@ public interface DeviceMapper {
"#{mobilePositionSubmissionInterval}," +
"#{subscribeCycleForAlarm}," +
"#{ssrcCheck}," +
+ "#{asMessageChannel}," +
"#{geoCoordSys}," +
"#{treeType}," +
"#{online}" +
@@ -152,9 +157,11 @@ public interface DeviceMapper {
"mobilePositionSubmissionInterval," +
"subscribeCycleForAlarm," +
"ssrcCheck," +
+ "asMessageChannel," +
"geoCoordSys," +
"treeType," +
"online," +
+ "mediaServerId," +
"(SELECT count(0) FROM device_channel WHERE deviceId=de.deviceId) as channelCount FROM device de" +
" where online=${online}"+
" "
@@ -164,9 +171,6 @@ public interface DeviceMapper {
@Delete("DELETE FROM device WHERE deviceId=#{deviceId}")
int del(String deviceId);
- @Update("UPDATE device SET online=0")
- int outlineForAll();
-
@Select("SELECT " +
"deviceId, " +
"coalesce(custom_name, name) as name, " +
@@ -192,6 +196,7 @@ public interface DeviceMapper {
"mobilePositionSubmissionInterval," +
"subscribeCycleForAlarm," +
"ssrcCheck," +
+ "asMessageChannel," +
"geoCoordSys," +
"treeType," +
"online " +
@@ -222,6 +227,7 @@ public interface DeviceMapper {
"mobilePositionSubmissionInterval," +
"subscribeCycleForAlarm," +
"ssrcCheck," +
+ "asMessageChannel," +
"geoCoordSys," +
"treeType," +
"online" +
@@ -243,12 +249,13 @@ public interface DeviceMapper {
", mobilePositionSubmissionInterval=#{mobilePositionSubmissionInterval}" +
", subscribeCycleForAlarm=#{subscribeCycleForAlarm}" +
", ssrcCheck=#{ssrcCheck}" +
+ ", asMessageChannel=#{asMessageChannel}" +
", geoCoordSys=#{geoCoordSys}" +
", treeType=#{treeType}" +
", mediaServerId=#{mediaServerId}" +
"WHERE deviceId=#{deviceId}"+
" "})
- int updateCustom(Device device);
+ void updateCustom(Device device);
@Insert("INSERT INTO device (" +
"deviceId, " +
@@ -259,9 +266,11 @@ public interface DeviceMapper {
"updateTime," +
"charset," +
"ssrcCheck," +
+ "asMessageChannel," +
"geoCoordSys," +
"treeType," +
- "online" +
+ "online," +
+ "mediaServerId" +
") VALUES (" +
"#{deviceId}," +
"#{name}," +
@@ -271,9 +280,11 @@ public interface DeviceMapper {
"#{updateTime}," +
"#{charset}," +
"#{ssrcCheck}," +
+ "#{asMessageChannel}," +
"#{geoCoordSys}," +
"#{treeType}," +
- "#{online}" +
+ "#{online}," +
+ "#{mediaServerId}" +
")")
void addCustomDevice(Device device);
@@ -282,4 +293,7 @@ public interface DeviceMapper {
@Select("select * from device")
List getAll();
+
+ @Select("select * from device where asMessageChannel = 1")
+ List queryDeviceWithAsMessageChannel();
}
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java b/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
index 52025eb53..89ebd69f0 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
@@ -15,10 +15,10 @@ import java.util.List;
public interface ParentPlatformMapper {
@Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp, " +
- " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, " +
+ " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, asMessageChannel, " +
" status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " +
" VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " +
- " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, " +
+ " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, " +
" #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})")
int addParentPlatform(ParentPlatform parentPlatform);
@@ -40,6 +40,7 @@ public interface ParentPlatformMapper {
"characterSet=#{characterSet}, " +
"ptz=#{ptz}, " +
"rtcp=#{rtcp}, " +
+ "asMessageChannel=#{asMessageChannel}, " +
"status=#{status}, " +
"startOfflinePush=#{startOfflinePush}, " +
"catalogGroup=#{catalogGroup}, " +
@@ -68,9 +69,12 @@ public interface ParentPlatformMapper {
"FROM parent_platform pp ")
List getParentPlatformList();
- @Select("SELECT * FROM parent_platform WHERE enable=#{enable}")
+ @Select("SELECT * FROM parent_platform WHERE enable=#{enable} ")
List getEnableParentPlatformList(boolean enable);
+ @Select("SELECT * FROM parent_platform WHERE enable=1 and asMessageChannel = 1")
+ List queryEnablePlatformListWithAsMessageChannel();
+
@Select("SELECT * FROM parent_platform WHERE serverGBId=#{platformGbId}")
ParentPlatform getParentPlatByServerGBId(String platformGbId);
diff --git a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
index 2c30d6ce6..ef5648bf5 100644
--- a/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
+++ b/src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
@@ -177,12 +177,14 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
@Override
public boolean startDownload(StreamInfo stream, String callId) {
boolean result;
+ String key=String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
+ userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId);
if (stream.getProgress() == 1) {
- result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
- userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream);
+ logger.debug("添加下载缓存==已完成下载=》{}",key);
+ result = RedisUtil.set(key, stream);
}else {
- result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
- userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream, 60*60);
+ logger.debug("添加下载缓存==未完成下载=》{}",key);
+ result = RedisUtil.set(key, stream, 60*60);
}
return result;
}
@@ -617,7 +619,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
stream,
callId
);
- List