Commit b4048fbe80dba8e7756ae557a15ab60b4f80a44b

Authored by 648540858
2 parents 786c76ba 053cd130

合并开源主线

Showing 100 changed files with 1881 additions and 660 deletions

Too many changes to show.

To preserve performance only 100 of 165 files are displayed.

README.md
... ... @@ -99,15 +99,16 @@ https://gitee.com/pan648540858/wvp-GB28181-pro.git
99 99 - [X] 支持接口鉴权
100 100 - [X] 云端录像,推流/代理/国标视频均可以录制在云端服务器,支持预览和下载
101 101 - [X] 支持打包可执行jar和war
  102 +- [X] 支持跨域请求,支持前后端分离部署
102 103  
103 104  
104 105 # 遇到问题如何解决
105 106 国标最麻烦的地方在于设备的兼容性,所以需要大量的设备来测试,目前作者手里的设备有限,再加上作者水平有限,所以遇到问题在所难免;
106   -1. 查看wiki,仔细的阅读可以帮你避免几乎所有的问题
  107 +1. 查看文档网站,仔细的阅读可以帮你避免几乎所有的问题
107 108 2. 搜索issues,这里有大部分的答案
108 109 3. 加QQ群(901799015),这里有大量热心的小伙伴,但是前提新希望你已经仔细阅读了wiki和搜索了issues。
109 110 4. 你可以请作者为你解答,但是我不是免费的。
110   -5. 你可以把遇到问题的设备寄给我,可以更容易的复现问题。
  111 +5. 你可以把遇到问题的设备寄给我,可以更容易的兼容设备和解决问题。
111 112  
112 113 # 使用帮助
113 114 QQ群: 901799015, ZLM使用文档[https://github.com/ZLMediaKit/ZLMediaKit](https://github.com/ZLMediaKit/ZLMediaKit)
... ...
doc/_content/ability/gis.md
... ... @@ -14,7 +14,7 @@ WVP提供了简单的电子地图用于设备的定位以及移动设备的轨
14 14 PS: 目前的底图仅用用作演示和学习,商用情况请自行购买授权使用。
15 15  
16 16 ### 更换底图以及底图配置
17   -目前WVP支持使用了更换底图,配置文件在web_src/static/js/mapConfig.js,请修改后重新编译前端文件。
  17 +目前WVP支持使用了更换底图,配置文件在web_src/static/js/config.js,请修改后重新编译前端文件。
18 18 ```javascript
19 19 window.mapParam = {
20 20 // 开启/关闭地图功能
... ...
doc/_content/introduction/deployment.md
... ... @@ -27,13 +27,32 @@
27 27 ```shell
28 28 nohup java -jar wvp-pro-*.jar &
29 29 ```
30   -war包:
  30 +**war包:**
31 31 下载Tomcat后将war包放入webapps中,启动Tomcat以解压war包,停止Tomcat后,删除ROOT目录以及war包,将解压后的war包目录重命名为ROOT,将配置文件中的Server.port配置为与Tomcat端口一致
32 32 然后启动Tomcat。
33 33 **启动ZLM**
34 34 ```shell
35 35 nohup ./MediaServer -d -m 3 &
36 36 ```
37   -
  37 +### 前后端分离部署
  38 +前后端部署目前在最新的版本已经支持,请使用3月15日之后的版本部署
  39 +前端编译后的文件在`src/main/resources/static`中,将此目录下的文件部署。
  40 +前后端分离部署最大的问题是跨域的解决,之前版本使用cookie完成登录流程,而cookie是不可以在复杂跨域中使用的。所以当前版本使用JWT生成的TOKEN作为认证凭据,
  41 +部署前端后需要在wvp中配置前端访问的地址以完成跨域流程。
  42 +**配置前端服务器**
  43 +1. 假如你的服务有公网域名为xxx.com,公网IP为11.11.11.11, 那么你可以在wvp中这样配置:
  44 +```yaml
  45 +user-settings:
  46 + # 跨域配置,配置你访问前端页面的地址即可, 可以配置多个
  47 + allowed-origins:
  48 + - http://xxx.com:8008
  49 + - http://11.11.11.11:8008
  50 +```
  51 +配置不是必须的,你使用哪个ip/域名访问就配置哪个即可。修改配置后重启wvp以使配置生效。
  52 +2. 在`src/main/resources/static/static/js/config.js`下配置服务器的地址,也就是wvp服务的地址
  53 +```javascript
  54 +window.baseUrl = "http://xxx.com:18080"
  55 +```
  56 +`这里的地址是需要客户电脑能访问到的,因为请求是客户端电脑发起,与代理不同`
38 57 [接入设备](./_content/ability/device.md)
39 58  
... ...
... ... @@ -11,7 +11,7 @@
11 11  
12 12 <groupId>com.genersoft</groupId>
13 13 <artifactId>wvp-pro</artifactId>
14   - <version>2.6.7</version>
  14 + <version>2.6.8</version>
15 15 <name>web video platform</name>
16 16 <description>国标28181视频平台</description>
17 17 <packaging>${project.packaging}</packaging>
... ... @@ -123,11 +123,9 @@
123 123 <artifactId>spring-boot-starter-security</artifactId>
124 124 </dependency>
125 125  
126   - <!-- druid数据库连接池 -->
127 126 <dependency>
128   - <groupId>com.alibaba</groupId>
129   - <artifactId>druid-spring-boot-starter</artifactId>
130   - <version>1.2.11</version>
  127 + <groupId>org.springframework.boot</groupId>
  128 + <artifactId>spring-boot-starter-jdbc</artifactId>
131 129 </dependency>
132 130  
133 131 <!-- mysql数据库 -->
... ... @@ -216,8 +214,6 @@
216 214 <version>4.10.0</version>
217 215 </dependency>
218 216  
219   -
220   -
221 217 <!-- okhttp-digest -->
222 218 <dependency>
223 219 <groupId>io.github.rburgst</groupId>
... ... @@ -226,10 +222,17 @@
226 222 </dependency>
227 223  
228 224 <!-- https://mvnrepository.com/artifact/net.sf.kxml/kxml2 -->
  225 +<!-- <dependency>-->
  226 +<!-- <groupId>net.sf.kxml</groupId>-->
  227 +<!-- <artifactId>kxml2</artifactId>-->
  228 +<!-- <version>2.3.0</version>-->
  229 +<!-- </dependency>-->
  230 +
  231 + <!-- jwt实现 -->
229 232 <dependency>
230   - <groupId>net.sf.kxml</groupId>
231   - <artifactId>kxml2</artifactId>
232   - <version>2.3.0</version>
  233 + <groupId>org.bitbucket.b_c</groupId>
  234 + <artifactId>jose4j</artifactId>
  235 + <version>0.9.3</version>
233 236 </dependency>
234 237  
235 238 <!--反向代理-->
... ... @@ -289,7 +292,7 @@
289 292 <plugin>
290 293 <groupId>org.springframework.boot</groupId>
291 294 <artifactId>spring-boot-maven-plugin</artifactId>
292   - <version>2.3.5.RELEASE</version>
  295 + <version>2.7.2</version>
293 296 <configuration>
294 297 <includeSystemScope>true</includeSystemScope>
295 298 </configuration>
... ...
sql/2.6.6-2.6.7更新.sql 0 → 100755
  1 +alter table device
  2 + add asMessageChannel int default 0;
  3 +
  4 +alter table parent_platform
  5 + add asMessageChannel int default 0;
  6 +
  7 +alter table device
  8 + add mediaServerId varchar(50) default null;
  9 +
  10 +
  11 +
  12 +
... ...
src/main/resources/db/migration/V2.6.7_20230201__初始化.sql renamed to sql/初始化.sql
... ... @@ -32,6 +32,7 @@ CREATE TABLE `device` (
32 32 `transport` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
33 33 `streamMode` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
34 34 `online` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
  35 + `mediaServerId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
35 36 `registerTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
36 37 `keepaliveTime` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
37 38 `ip` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
... ... @@ -47,6 +48,7 @@ CREATE TABLE `device` (
47 48 `mobilePositionSubmissionInterval` int DEFAULT '5',
48 49 `subscribeCycleForAlarm` int DEFAULT NULL,
49 50 `ssrcCheck` int DEFAULT '0',
  51 + `asMessageChannel` int DEFAULT '0',
50 52 `geoCoordSys` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
51 53 `treeType` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
52 54 `custom_name` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci DEFAULT NULL,
... ... @@ -329,6 +331,7 @@ CREATE TABLE `parent_platform` (
329 331 `catalogId` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
330 332 `ptz` int DEFAULT NULL,
331 333 `rtcp` int DEFAULT NULL,
  334 + `asMessageChannel` int DEFAULT '0',
332 335 `status` bit(1) DEFAULT NULL,
333 336 `startOfflinePush` int DEFAULT '0',
334 337 `administrativeDivision` varchar(50) CHARACTER SET utf8mb4 COLLATE utf8mb4_general_ci NOT NULL,
... ...
src/main/java/com/genersoft/iot/vmp/VManageBootstrap.java
1 1 package com.genersoft.iot.vmp;
2 2  
3   -import com.genersoft.iot.vmp.conf.druid.EnableDruidSupport;
4 3 import com.genersoft.iot.vmp.utils.GitUtil;
5 4 import com.genersoft.iot.vmp.utils.SpringBeanFactory;
6 5 import org.slf4j.Logger;
... ... @@ -25,7 +24,6 @@ import java.util.Collections;
25 24 @ServletComponentScan("com.genersoft.iot.vmp.conf")
26 25 @SpringBootApplication
27 26 @EnableScheduling
28   -@EnableDruidSupport
29 27 public class VManageBootstrap extends SpringBootServletInitializer {
30 28  
31 29 private final static Logger logger = LoggerFactory.getLogger(VManageBootstrap.class);
... ...
src/main/java/com/genersoft/iot/vmp/common/StreamInfo.java
... ... @@ -3,6 +3,7 @@ package com.genersoft.iot.vmp.common;
3 3 import io.swagger.v3.oas.annotations.media.Schema;
4 4  
5 5 import java.io.Serializable;
  6 +import java.util.Objects;
6 7  
7 8 @Schema(description = "流信息")
8 9 public class StreamInfo implements Serializable, Cloneable{
... ... @@ -168,7 +169,7 @@ public class StreamInfo implements Serializable, Cloneable{
168 169 }
169 170  
170 171 public void setRtmp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
171   - String file = String.format("%s/%s/%s", app, stream, callIdParam);
  172 + String file = String.format("%s/%s%s", app, stream, callIdParam);
172 173 if (port > 0) {
173 174 this.rtmp = new StreamURL("rtmp", host, port, file);
174 175 }
... ... @@ -178,7 +179,7 @@ public class StreamInfo implements Serializable, Cloneable{
178 179 }
179 180  
180 181 public void setRtsp(String host, int port, int sslPort, String app, String stream, String callIdParam) {
181   - String file = String.format("%s/%s/%s", app, stream, callIdParam);
  182 + String file = String.format("%s/%s%s", app, stream, callIdParam);
182 183 if (port > 0) {
183 184 this.rtsp = new StreamURL("rtsp", host, port, file);
184 185 }
... ... @@ -236,8 +237,11 @@ public class StreamInfo implements Serializable, Cloneable{
236 237 }
237 238 }
238 239  
239   - public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam, boolean isPlay) {
240   - String file = String.format("index/api/webrtc?app=%s&stream=%s&type=%s%s", app, stream, isPlay?"play":"push", callIdParam);
  240 + public void setRtc(String host, int port, int sslPort, String app, String stream, String callIdParam) {
  241 + if (callIdParam != null) {
  242 + callIdParam = Objects.equals(callIdParam, "") ? callIdParam : callIdParam.replace("?", "&");
  243 + }
  244 + String file = String.format("index/api/webrtc?app=%s&stream=%s&type=play%s", app, stream, callIdParam);
241 245 if (port > 0) {
242 246 this.rtc = new StreamURL("http", host, port, file);
243 247 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/ApiAccessFilter.java
... ... @@ -66,9 +66,7 @@ public class ApiAccessFilter extends OncePerRequestFilter {
66 66 logDto.setUri(servletRequest.getRequestURI());
67 67 logDto.setCreateTime(DateUtil.getNow());
68 68 logService.add(logDto);
69   -// logger.warn("[Api Access] [{}] [{}] [{}] [{}] [{}] {}ms",
70   -// uriName, servletRequest.getMethod(), servletRequest.getRequestURI(), servletRequest.getRemoteAddr(), HttpStatus.valueOf(servletResponse.getStatus()),
71   -// System.currentTimeMillis() - start);
  69 +
72 70  
73 71 }
74 72 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/GlobalExceptionHandler.java
... ... @@ -8,6 +8,7 @@ import org.slf4j.LoggerFactory;
8 8 import org.springframework.http.HttpStatus;
9 9 import org.springframework.http.ResponseEntity;
10 10 import org.springframework.security.authentication.BadCredentialsException;
  11 +import org.springframework.web.HttpRequestMethodNotSupportedException;
11 12 import org.springframework.web.bind.annotation.ExceptionHandler;
12 13 import org.springframework.web.bind.annotation.ResponseStatus;
13 14 import org.springframework.web.bind.annotation.RestControllerAdvice;
... ... @@ -32,6 +33,28 @@ public class GlobalExceptionHandler {
32 33 return WVPResult.fail(ErrorCode.ERROR500.getCode(), e.getMessage());
33 34 }
34 35  
  36 + /**
  37 + * 默认异常处理
  38 + * @param e 异常
  39 + * @return 统一返回结果
  40 + */
  41 + @ExceptionHandler(IllegalStateException.class)
  42 + @ResponseStatus(HttpStatus.BAD_REQUEST)
  43 + public WVPResult<String> exceptionHandler(IllegalStateException e) {
  44 + return WVPResult.fail(ErrorCode.ERROR400);
  45 + }
  46 +
  47 + /**
  48 + * 默认异常处理
  49 + * @param e 异常
  50 + * @return 统一返回结果
  51 + */
  52 + @ExceptionHandler(HttpRequestMethodNotSupportedException.class)
  53 + @ResponseStatus(HttpStatus.BAD_REQUEST)
  54 + public WVPResult<String> exceptionHandler(HttpRequestMethodNotSupportedException e) {
  55 + return WVPResult.fail(ErrorCode.ERROR400);
  56 + }
  57 +
35 58  
36 59 /**
37 60 * 自定义异常处理, 处理controller中返回的错误
... ...
src/main/java/com/genersoft/iot/vmp/conf/GlobalResponseAdvice.java
... ... @@ -10,14 +10,11 @@ import org.springframework.context.annotation.Bean;
10 10 import org.springframework.core.MethodParameter;
11 11 import org.springframework.http.MediaType;
12 12 import org.springframework.http.converter.HttpMessageConverter;
13   -import org.springframework.http.converter.json.MappingJackson2HttpMessageConverter;
14 13 import org.springframework.http.server.ServerHttpRequest;
15 14 import org.springframework.http.server.ServerHttpResponse;
16 15 import org.springframework.web.bind.annotation.RestControllerAdvice;
17 16 import org.springframework.web.servlet.mvc.method.annotation.ResponseBodyAdvice;
18 17  
19   -import java.util.List;
20   -
21 18 /**
22 19 * 全局统一返回结果
23 20 * @author lin
... ...
src/main/java/com/genersoft/iot/vmp/conf/ProxyServletConfig.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.conf;
2 2  
3 3 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
4 4 import com.genersoft.iot.vmp.service.IMediaServerService;
  5 +import org.apache.catalina.connector.ClientAbortException;
5 6 import org.apache.http.HttpHost;
6 7 import org.apache.http.HttpRequest;
7 8 import org.apache.http.HttpResponse;
... ... @@ -169,13 +170,14 @@ public class ProxyServletConfig {
169 170 protected String rewriteQueryStringFromRequest(HttpServletRequest servletRequest, String queryString) {
170 171 String queryStr = super.rewriteQueryStringFromRequest(servletRequest, queryString);
171 172 MediaServerItem mediaInfo = getMediaInfoByUri(servletRequest.getRequestURI());
172   - String remoteHost = String.format("http://%s:%s", mediaInfo.getIp(), mediaInfo.getHttpPort());
173   - if (mediaInfo != null) {
174   - if (!ObjectUtils.isEmpty(queryStr)) {
175   - queryStr += "&remoteHost=" + remoteHost;
176   - }else {
177   - queryStr = "remoteHost=" + remoteHost;
178   - }
  173 + if (mediaInfo == null) {
  174 + return null;
  175 + }
  176 + String remoteHost = String.format("http://%s:%s", mediaInfo.getStreamIp(), mediaInfo.getRecordAssistPort());
  177 + if (!ObjectUtils.isEmpty(queryStr)) {
  178 + queryStr += "&remoteHost=" + remoteHost;
  179 + }else {
  180 + queryStr = "remoteHost=" + remoteHost;
179 181 }
180 182 return queryStr;
181 183 }
... ... @@ -192,6 +194,12 @@ public class ProxyServletConfig {
192 194 } catch (IOException ioException) {
193 195 if (ioException instanceof ConnectException) {
194 196 logger.error("录像服务 连接失败");
  197 + }else if (ioException instanceof ClientAbortException) {
  198 + /**
  199 + * TODO 使用这个代理库实现代理在遇到代理视频文件时,如果是206结果,会遇到报错蛋市目前功能正常,
  200 + * TODO 暂时去除异常处理。后续使用其他代理框架修改测试
  201 + */
  202 +
195 203 }else {
196 204 logger.error("录像服务 代理失败: ", e);
197 205 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/SipConfig.java
... ... @@ -4,7 +4,6 @@ package com.genersoft.iot.vmp.conf;
4 4 import org.junit.jupiter.api.Order;
5 5 import org.springframework.boot.context.properties.ConfigurationProperties;
6 6 import org.springframework.stereotype.Component;
7   -import org.springframework.util.ObjectUtils;
8 7  
9 8 @Component
10 9 @ConfigurationProperties(prefix = "sip", ignoreInvalidFields = true)
... ... @@ -13,6 +12,8 @@ public class SipConfig {
13 12  
14 13 private String ip;
15 14  
  15 + private String showIp;
  16 +
16 17 private Integer port;
17 18  
18 19 private String domain;
... ... @@ -96,4 +97,14 @@ public class SipConfig {
96 97 this.alarm = alarm;
97 98 }
98 99  
  100 + public String getShowIp() {
  101 + if (this.showIp == null) {
  102 + return this.ip;
  103 + }
  104 + return showIp;
  105 + }
  106 +
  107 + public void setShowIp(String showIp) {
  108 + this.showIp = showIp;
  109 + }
99 110 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/SipPlatformRunner.java
... ... @@ -40,17 +40,20 @@ public class SipPlatformRunner implements CommandLineRunner {
40 40 List<ParentPlatform> parentPlatforms = storager.queryEnableParentPlatformList(true);
41 41  
42 42 for (ParentPlatform parentPlatform : parentPlatforms) {
  43 +
  44 + ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
  45 +
43 46 // 更新缓存
44 47 ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
45 48 parentPlatformCatch.setParentPlatform(parentPlatform);
46 49 parentPlatformCatch.setId(parentPlatform.getServerGBId());
47 50 redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
48   - // 设置所有平台离线
49   - platformService.offline(parentPlatform, true);
50 51 // 取消订阅
51   - sipCommanderForPlatform.unregister(parentPlatform, null, (eventResult)->{
  52 + sipCommanderForPlatform.unregister(parentPlatform, parentPlatformCatchOld.getSipTransactionInfo(), null, (eventResult)->{
52 53 platformService.login(parentPlatform);
53 54 });
  55 + // 设置所有平台离线
  56 + platformService.offline(parentPlatform, true);
54 57 }
55 58 }
56 59 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/UserSetting.java
... ... @@ -50,15 +50,22 @@ public class UserSetting {
50 50 private Boolean pushStreamAfterAck = Boolean.FALSE;
51 51  
52 52 private Boolean sipLog = Boolean.FALSE;
  53 + private Boolean sendToPlatformsWhenIdLost = Boolean.FALSE;
  54 +
  55 + private Boolean refuseChannelStatusChannelFormNotify = Boolean.FALSE;
53 56  
54 57 private String serverId = "000000";
55 58  
  59 + private String recordPath = null;
  60 +
56 61 private String thirdPartyGBIdReg = "[\\s\\S]*";
57 62  
58 63 private String broadcastForPlatform = "UDP";
59 64  
60 65 private List<String> interfaceAuthenticationExcludes = new ArrayList<>();
61 66  
  67 + private List<String> allowedOrigins = new ArrayList<>();
  68 +
62 69 public Boolean getSavePositionHistory() {
63 70 return savePositionHistory;
64 71 }
... ... @@ -238,4 +245,36 @@ public class UserSetting {
238 245 public void setSipLog(Boolean sipLog) {
239 246 this.sipLog = sipLog;
240 247 }
  248 +
  249 + public List<String> getAllowedOrigins() {
  250 + return allowedOrigins;
  251 + }
  252 +
  253 + public void setAllowedOrigins(List<String> allowedOrigins) {
  254 + this.allowedOrigins = allowedOrigins;
  255 + }
  256 +
  257 + public Boolean getSendToPlatformsWhenIdLost() {
  258 + return sendToPlatformsWhenIdLost;
  259 + }
  260 +
  261 + public void setSendToPlatformsWhenIdLost(Boolean sendToPlatformsWhenIdLost) {
  262 + this.sendToPlatformsWhenIdLost = sendToPlatformsWhenIdLost;
  263 + }
  264 +
  265 + public Boolean getRefuseChannelStatusChannelFormNotify() {
  266 + return refuseChannelStatusChannelFormNotify;
  267 + }
  268 +
  269 + public void setRefuseChannelStatusChannelFormNotify(Boolean refuseChannelStatusChannelFormNotify) {
  270 + this.refuseChannelStatusChannelFormNotify = refuseChannelStatusChannelFormNotify;
  271 + }
  272 +
  273 + public String getRecordPath() {
  274 + return recordPath;
  275 + }
  276 +
  277 + public void setRecordPath(String recordPath) {
  278 + this.recordPath = recordPath;
  279 + }
241 280 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/druid/DruidConfiguration.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.conf.druid;
2   -
3   -import com.alibaba.druid.support.http.StatViewServlet;
4   -import com.alibaba.druid.support.http.WebStatFilter;
5   -import org.springframework.beans.factory.annotation.Value;
6   -import org.springframework.boot.web.servlet.FilterRegistrationBean;
7   -import org.springframework.boot.web.servlet.ServletRegistrationBean;
8   -import org.springframework.context.annotation.Bean;
9   -
10   -import javax.servlet.Filter;
11   -import javax.servlet.Servlet;
12   -
13   -/**
14   - * druid监控配置
15   - * @author
16   - */
17   -public class DruidConfiguration {
18   -
19   - @Value("${rj-druid-manage.allow:127.0.0.1}")
20   - private String allow;
21   -
22   - @Value("${rj-druid-manage.deny:}")
23   - private String deny;
24   -
25   - @Value("${rj-druid-manage.loginUsername:admin}")
26   - private String loginUsername;
27   -
28   - @Value("${rj-druid-manage.loginPassword:admin}")
29   - private String loginPassword;
30   -
31   - @Value("${rj-druid-manage.resetEnable:false}")
32   - private String resetEnable;
33   -
34   - /**
35   - * druid监控页面开启
36   - */
37   - @Bean
38   - public ServletRegistrationBean druidServlet() {
39   - ServletRegistrationBean<Servlet> servletRegistrationBean = new ServletRegistrationBean<>(new StatViewServlet(), "/druid/*");
40   - // IP白名单
41   - servletRegistrationBean.addInitParameter("allow", allow);
42   - // IP黑名单(共同存在时,deny优先于allow)
43   - servletRegistrationBean.addInitParameter("deny", deny);
44   - //控制台管理用户
45   - servletRegistrationBean.addInitParameter("loginUsername", loginUsername);
46   - servletRegistrationBean.addInitParameter("loginPassword", loginPassword);
47   - //是否能够重置数据 禁用HTML页面上的“Reset All”功能
48   - servletRegistrationBean.addInitParameter("resetEnable", resetEnable);
49   - return servletRegistrationBean;
50   - }
51   -
52   - /**
53   - * druid url监控配置
54   - */
55   - @Bean
56   - public FilterRegistrationBean filterRegistrationBean() {
57   - FilterRegistrationBean<Filter> filterRegistrationBean = new FilterRegistrationBean<>(new WebStatFilter());
58   - filterRegistrationBean.addUrlPatterns("/*");
59   - filterRegistrationBean.addInitParameter("exclusions", "*.js,*.gif,*.jpg,*.png,*.css,*.ico,/druid/*");
60   - return filterRegistrationBean;
61   - }
62   -
63   -
64   -}
65 0 \ No newline at end of file
src/main/java/com/genersoft/iot/vmp/conf/druid/EnableDruidSupport.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.conf.druid;
2   -
3   -import org.springframework.boot.web.servlet.ServletComponentScan;
4   -import org.springframework.context.annotation.Import;
5   -
6   -import java.lang.annotation.*;
7   -
8   -/**
9   - * druid监控支持注解
10   - *
11   - * @author
12   - * {@link DruidConfiguration} druid监控页面安全配置支持
13   - * {@link ServletComponentScan} druid监控页面需要扫描servlet
14   - */
15   -@Target(ElementType.TYPE)
16   -@Retention(RetentionPolicy.RUNTIME)
17   -@Documented
18   -@Inherited
19   -@Import({
20   - DruidConfiguration.class,
21   -})
22   -@ServletComponentScan
23   -public @interface EnableDruidSupport {
24   -}
src/main/java/com/genersoft/iot/vmp/conf/security/AnonymousAuthenticationEntryPoint.java
1 1 package com.genersoft.iot.vmp.conf.security;
2 2  
3 3 import com.alibaba.fastjson2.JSONObject;
  4 +import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
4 5 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
5   -import org.apache.poi.hssf.eventmodel.ERFListener;
6   -import org.slf4j.Logger;
7   -import org.slf4j.LoggerFactory;
  6 +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
8 7 import org.springframework.security.core.AuthenticationException;
  8 +import org.springframework.security.core.context.SecurityContextHolder;
9 9 import org.springframework.security.web.AuthenticationEntryPoint;
10 10 import org.springframework.stereotype.Component;
11 11  
... ... @@ -18,17 +18,15 @@ import java.io.IOException;
18 18 * @author lin
19 19 */
20 20 @Component
21   -public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
22   -
23   - private final static Logger logger = LoggerFactory.getLogger(DefaultUserDetailsServiceImpl.class);
  21 +public class AnonymousAuthenticationEntryPoint implements AuthenticationEntryPoint {
24 22  
25 23 @Override
26 24 public void commence(HttpServletRequest request, HttpServletResponse response, AuthenticationException e) {
27   - // 允许跨域
28   - response.setHeader("Access-Control-Allow-Origin", "*");
29   - // 允许自定义请求头token(允许head跨域)
30   - response.setHeader("Access-Control-Allow-Headers", "token, Accept, Origin, X-Requested-With, Content-Type, Last-Modified");
31   - response.setHeader("Content-type", "application/json;charset=UTF-8");
  25 + String jwt = request.getHeader(JwtUtils.getHeader());
  26 + JwtUser jwtUser = JwtUtils.verifyToken(jwt);
  27 + String username = jwtUser.getUserName();
  28 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword() );
  29 + SecurityContextHolder.getContext().setAuthentication(token);
32 30 JSONObject jsonObject = new JSONObject();
33 31 jsonObject.put("code", ErrorCode.ERROR401.getCode());
34 32 jsonObject.put("msg", ErrorCode.ERROR401.getMsg());
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/DefaultUserDetailsServiceImpl.java
1 1 package com.genersoft.iot.vmp.conf.security;
2 2  
3   -import java.time.LocalDateTime;
4   -
  3 +import com.alibaba.excel.util.StringUtils;
  4 +import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
  5 +import com.genersoft.iot.vmp.service.IUserService;
  6 +import com.genersoft.iot.vmp.storager.dao.dto.User;
5 7 import org.slf4j.Logger;
6 8 import org.slf4j.LoggerFactory;
7 9 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -10,10 +12,7 @@ import org.springframework.security.core.userdetails.UserDetailsService;
10 12 import org.springframework.security.core.userdetails.UsernameNotFoundException;
11 13 import org.springframework.stereotype.Component;
12 14  
13   -import com.alibaba.excel.util.StringUtils;
14   -import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
15   -import com.genersoft.iot.vmp.service.IUserService;
16   -import com.genersoft.iot.vmp.storager.dao.dto.User;
  15 +import java.time.LocalDateTime;
17 16  
18 17 /**
19 18 * 用户登录认证逻辑
... ... @@ -45,4 +44,8 @@ public class DefaultUserDetailsServiceImpl implements UserDetailsService {
45 44 }
46 45  
47 46  
  47 +
  48 +
  49 +
  50 +
48 51 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/InvalidSessionHandler.java deleted 100644 → 0
1   -package com.genersoft.iot.vmp.conf.security;
2   -
3   -import org.slf4j.Logger;
4   -import org.slf4j.LoggerFactory;
5   -import org.springframework.security.web.session.InvalidSessionStrategy;
6   -
7   -import javax.servlet.ServletException;
8   -import javax.servlet.http.HttpServletRequest;
9   -import javax.servlet.http.HttpServletResponse;
10   -import java.io.IOException;
11   -
12   -/**
13   - * 登录超时的处理
14   - */
15   -public class InvalidSessionHandler implements InvalidSessionStrategy {
16   -
17   - private final static Logger logger = LoggerFactory.getLogger(InvalidSessionHandler.class);
18   -
19   - @Override
20   - public void onInvalidSessionDetected(HttpServletRequest request, HttpServletResponse httpServletResponse) throws IOException, ServletException {
21   - String username = request.getParameter("username");
22   - logger.info("[登录超时] - [{}]", username);
23   - }
24   -}
src/main/java/com/genersoft/iot/vmp/conf/security/JwtAuthenticationFilter.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf.security;
  2 +
  3 +import com.genersoft.iot.vmp.conf.UserSetting;
  4 +import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
  5 +import org.apache.commons.lang3.StringUtils;
  6 +import org.springframework.beans.factory.annotation.Autowired;
  7 +import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
  8 +import org.springframework.security.core.context.SecurityContextHolder;
  9 +import org.springframework.stereotype.Component;
  10 +import org.springframework.web.filter.OncePerRequestFilter;
  11 +
  12 +import javax.servlet.FilterChain;
  13 +import javax.servlet.ServletException;
  14 +import javax.servlet.http.HttpServletRequest;
  15 +import javax.servlet.http.HttpServletResponse;
  16 +import java.io.IOException;
  17 +import java.util.ArrayList;
  18 +
  19 +/**
  20 + * jwt token 过滤器
  21 + */
  22 +
  23 +@Component
  24 +public class JwtAuthenticationFilter extends OncePerRequestFilter {
  25 +
  26 +
  27 + @Autowired
  28 + private UserSetting userSetting;
  29 +
  30 +
  31 + @Override
  32 + protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain chain) throws IOException, ServletException {
  33 +
  34 + // 忽略登录请求的token验证
  35 + String requestURI = request.getRequestURI();
  36 + if (requestURI.equalsIgnoreCase("/api/user/login")) {
  37 + chain.doFilter(request, response);
  38 + return;
  39 + }
  40 + if (!userSetting.isInterfaceAuthentication()) {
  41 + // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
  42 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(null, null, new ArrayList<>() );
  43 + SecurityContextHolder.getContext().setAuthentication(token);
  44 + chain.doFilter(request, response);
  45 + return;
  46 + }
  47 + String jwt = request.getHeader(JwtUtils.getHeader());
  48 + // 这里如果没有jwt,继续往后走,因为后面还有鉴权管理器等去判断是否拥有身份凭证,所以是可以放行的
  49 + // 没有jwt相当于匿名访问,若有一些接口是需要权限的,则不能访问这些接口
  50 + if (StringUtils.isBlank(jwt)) {
  51 + jwt = request.getParameter(JwtUtils.getHeader());
  52 + if (StringUtils.isBlank(jwt)) {
  53 + chain.doFilter(request, response);
  54 + return;
  55 + }
  56 + }
  57 +
  58 + JwtUser jwtUser = JwtUtils.verifyToken(jwt);
  59 + String username = jwtUser.getUserName();
  60 + // TODO 处理各个状态
  61 + switch (jwtUser.getStatus()){
  62 + case EXPIRED:
  63 + response.setStatus(400);
  64 + chain.doFilter(request, response);
  65 + // 异常
  66 + return;
  67 + case EXCEPTION:
  68 + // 过期
  69 + response.setStatus(400);
  70 + chain.doFilter(request, response);
  71 + return;
  72 + case EXPIRING_SOON:
  73 + // 即将过期
  74 +// return;
  75 + default:
  76 + }
  77 +
  78 + // 构建UsernamePasswordAuthenticationToken,这里密码为null,是因为提供了正确的JWT,实现自动登录
  79 + UsernamePasswordAuthenticationToken token = new UsernamePasswordAuthenticationToken(username, jwtUser.getPassword(), new ArrayList<>() );
  80 + SecurityContextHolder.getContext().setAuthentication(token);
  81 + chain.doFilter(request, response);
  82 + }
  83 +
  84 +}
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/JwtUtils.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf.security;
  2 +
  3 +import com.genersoft.iot.vmp.conf.security.dto.JwtUser;
  4 +import org.jose4j.json.JsonUtil;
  5 +import org.jose4j.jwk.RsaJsonWebKey;
  6 +import org.jose4j.jws.AlgorithmIdentifiers;
  7 +import org.jose4j.jws.JsonWebSignature;
  8 +import org.jose4j.jwt.JwtClaims;
  9 +import org.jose4j.jwt.NumericDate;
  10 +import org.jose4j.jwt.consumer.ErrorCodes;
  11 +import org.jose4j.jwt.consumer.InvalidJwtException;
  12 +import org.jose4j.jwt.consumer.JwtConsumer;
  13 +import org.jose4j.jwt.consumer.JwtConsumerBuilder;
  14 +import org.jose4j.lang.JoseException;
  15 +import org.slf4j.Logger;
  16 +import org.slf4j.LoggerFactory;
  17 +
  18 +import java.security.PrivateKey;
  19 +import java.time.LocalDateTime;
  20 +import java.time.ZoneOffset;
  21 +
  22 +public class JwtUtils {
  23 +
  24 + private static final Logger logger = LoggerFactory.getLogger(JwtUtils.class);
  25 +
  26 + private static final String HEADER = "access-token";
  27 + private static final String AUDIENCE = "Audience";
  28 +
  29 + private static final long EXPIRED_THRESHOLD = 10 * 60;
  30 +
  31 + private static final String keyId = "3e79646c4dbc408383a9eed09f2b85ae";
  32 + 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\"}";
  33 + 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\"}";
  34 +
  35 + /**
  36 + * token过期时间(分钟)
  37 + */
  38 + public static final long expirationTime = 30;
  39 +
  40 + public static String createToken(String username, String password) {
  41 + try {
  42 + /**
  43 + * “iss” (issuer) 发行人
  44 + *
  45 + * “sub” (subject) 主题
  46 + *
  47 + * “aud” (audience) 接收方 用户
  48 + *
  49 + * “exp” (expiration time) 到期时间
  50 + *
  51 + * “nbf” (not before) 在此之前不可用
  52 + *
  53 + * “iat” (issued at) jwt的签发时间
  54 + */
  55 + //Payload
  56 + JwtClaims claims = new JwtClaims();
  57 + claims.setGeneratedJwtId();
  58 + claims.setIssuedAtToNow();
  59 + // 令牌将过期的时间 分钟
  60 + claims.setExpirationTimeMinutesInTheFuture(expirationTime);
  61 + claims.setNotBeforeMinutesInThePast(0);
  62 + claims.setSubject("login");
  63 + claims.setAudience(AUDIENCE);
  64 + //添加自定义参数,必须是字符串类型
  65 + claims.setClaim("username", username);
  66 + claims.setClaim("password", password);
  67 +
  68 + //jws
  69 + JsonWebSignature jws = new JsonWebSignature();
  70 + //签名算法RS256
  71 + jws.setAlgorithmHeaderValue(AlgorithmIdentifiers.RSA_USING_SHA256);
  72 + jws.setKeyIdHeaderValue(keyId);
  73 + jws.setPayload(claims.toJson());
  74 +
  75 + PrivateKey privateKey = new RsaJsonWebKey(JsonUtil.parseJson(privateKeyStr)).getPrivateKey();
  76 + jws.setKey(privateKey);
  77 +
  78 + //get token
  79 + String idToken = jws.getCompactSerialization();
  80 + return idToken;
  81 + } catch (JoseException e) {
  82 + logger.error("[Token生成失败]: {}", e.getMessage());
  83 + }
  84 +
  85 + return null;
  86 + }
  87 +
  88 + public static String getHeader() {
  89 + return HEADER;
  90 + }
  91 +
  92 +
  93 + public static JwtUser verifyToken(String token) {
  94 +
  95 + JwtUser jwtUser = new JwtUser();
  96 +
  97 + try {
  98 + JwtConsumer consumer = new JwtConsumerBuilder()
  99 + .setRequireExpirationTime()
  100 + .setMaxFutureValidityInMinutes(5256000)
  101 + .setAllowedClockSkewInSeconds(30)
  102 + .setRequireSubject()
  103 + //.setExpectedIssuer("")
  104 + .setExpectedAudience(AUDIENCE)
  105 + .setVerificationKey(new RsaJsonWebKey(JsonUtil.parseJson(publicKeyStr)).getPublicKey())
  106 + .build();
  107 +
  108 + JwtClaims claims = consumer.processToClaims(token);
  109 + NumericDate expirationTime = claims.getExpirationTime();
  110 + // 判断是否即将过期, 默认剩余时间小于5分钟未即将过期
  111 + // 剩余时间 (秒)
  112 + long timeRemaining = LocalDateTime.now().toEpochSecond(ZoneOffset.ofHours(8)) - expirationTime.getValue();
  113 + if (timeRemaining < 5 * 60) {
  114 + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRING_SOON);
  115 + }else {
  116 + jwtUser.setStatus(JwtUser.TokenStatus.NORMAL);
  117 + }
  118 +
  119 + String username = (String) claims.getClaimValue("username");
  120 + String password = (String) claims.getClaimValue("password");
  121 + jwtUser.setUserName(username);
  122 + jwtUser.setPassword(password);
  123 +
  124 + return jwtUser;
  125 + } catch (InvalidJwtException e) {
  126 + if (e.hasErrorCode(ErrorCodes.EXPIRED)) {
  127 + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
  128 + }else {
  129 + jwtUser.setStatus(JwtUser.TokenStatus.EXCEPTION);
  130 + }
  131 + return jwtUser;
  132 + }catch (Exception e) {
  133 + logger.error("[Token解析失败]: {}", e.getMessage());
  134 + jwtUser.setStatus(JwtUser.TokenStatus.EXPIRED);
  135 + return jwtUser;
  136 + }
  137 + }
  138 +}
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/LoginSuccessHandler.java
... ... @@ -21,7 +21,16 @@ public class LoginSuccessHandler implements AuthenticationSuccessHandler {
21 21  
22 22 @Override
23 23 public void onAuthenticationSuccess(HttpServletRequest request, HttpServletResponse httpServletResponse, Authentication authentication) throws IOException, ServletException {
24   - String username = request.getParameter("username");
25   - logger.info("[登录成功] - [{}]", username);
  24 +// String username = request.getParameter("username");
  25 +// httpServletResponse.setContentType("application/json;charset=UTF-8");
  26 +// // 生成JWT,并放置到请求头中
  27 +// String jwt = JwtUtils.createToken(authentication.getName(), );
  28 +// httpServletResponse.setHeader(JwtUtils.getHeader(), jwt);
  29 +// ServletOutputStream outputStream = httpServletResponse.getOutputStream();
  30 +// outputStream.write(JSON.toJSONString(ErrorCode.SUCCESS).getBytes(StandardCharsets.UTF_8));
  31 +// outputStream.flush();
  32 +// outputStream.close();
  33 +
  34 +// logger.info("[登录成功] - [{}]", username);
26 35 }
27 36 }
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/SecurityUtils.java
1 1 package com.genersoft.iot.vmp.conf.security;
2 2  
3 3 import com.genersoft.iot.vmp.conf.security.dto.LoginUser;
  4 +import com.genersoft.iot.vmp.storager.dao.dto.User;
4 5 import org.springframework.security.authentication.AuthenticationManager;
5 6 import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
6 7 import org.springframework.security.core.Authentication;
... ... @@ -9,6 +10,7 @@ import org.springframework.security.core.context.SecurityContextHolder;
9 10 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
10 11  
11 12 import javax.security.sasl.AuthenticationException;
  13 +import java.time.LocalDateTime;
12 14  
13 15 public class SecurityUtils {
14 16  
... ... @@ -25,9 +27,12 @@ public class SecurityUtils {
25 27 public static LoginUser login(String username, String password, AuthenticationManager authenticationManager) throws AuthenticationException {
26 28 //使用security框架自带的验证token生成器 也可以自定义。
27 29 UsernamePasswordAuthenticationToken token =new UsernamePasswordAuthenticationToken(username,password);
  30 + //认证 如果失败,这里会自动异常后返回,所以这里不需要判断返回值是否为空,确定是否登录成功
28 31 Authentication authenticate = authenticationManager.authenticate(token);
29   - SecurityContextHolder.getContext().setAuthentication(authenticate);
30 32 LoginUser user = (LoginUser) authenticate.getPrincipal();
  33 +
  34 + SecurityContextHolder.getContext().setAuthentication(token);
  35 +
31 36 return user;
32 37 }
33 38  
... ... @@ -49,8 +54,13 @@ public class SecurityUtils {
49 54 if(authentication!=null){
50 55 Object principal = authentication.getPrincipal();
51 56 if(principal!=null && !"anonymousUser".equals(principal)){
52   - LoginUser user = (LoginUser) authentication.getPrincipal();
53   - return user;
  57 +// LoginUser user = (LoginUser) authentication.getPrincipal();
  58 +
  59 + String username = (String) principal;
  60 + User user = new User();
  61 + user.setUsername(username);
  62 + LoginUser loginUser = new LoginUser(user, LocalDateTime.now());
  63 + return loginUser;
54 64 }
55 65 }
56 66 return null;
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/WebSecurityConfig.java
... ... @@ -15,9 +15,16 @@ import org.springframework.security.config.annotation.web.builders.HttpSecurity;
15 15 import org.springframework.security.config.annotation.web.builders.WebSecurity;
16 16 import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
17 17 import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
  18 +import org.springframework.security.config.http.SessionCreationPolicy;
18 19 import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
  20 +import org.springframework.security.web.authentication.UsernamePasswordAuthenticationFilter;
  21 +import org.springframework.web.cors.CorsConfiguration;
  22 +import org.springframework.web.cors.CorsConfigurationSource;
  23 +import org.springframework.web.cors.CorsUtils;
  24 +import org.springframework.web.cors.UrlBasedCorsConfigurationSource;
19 25  
20   -import java.util.List;
  26 +import java.util.ArrayList;
  27 +import java.util.Arrays;
21 28  
22 29 /**
23 30 * 配置Spring Security
... ... @@ -56,22 +63,8 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
56 63 */
57 64 @Autowired
58 65 private AnonymousAuthenticationEntryPoint anonymousAuthenticationEntryPoint;
59   -// /**
60   -// * 超时处理
61   -// */
62   -// @Autowired
63   -// private InvalidSessionHandler invalidSessionHandler;
64   -
65   -// /**
66   -// * 顶号处理
67   -// */
68   -// @Autowired
69   -// private SessionInformationExpiredHandler sessionInformationExpiredHandler;
70   -// /**
71   -// * 登录用户没有权限访问资源
72   -// */
73   -// @Autowired
74   -// private LoginUserAccessDeniedHandler accessDeniedHandler;
  66 + @Autowired
  67 + private JwtAuthenticationFilter jwtAuthenticationFilter;
75 68  
76 69  
77 70 /**
... ... @@ -80,31 +73,21 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
80 73 @Override
81 74 public void configure(WebSecurity web) {
82 75  
83   - if (!userSetting.isInterfaceAuthentication()) {
84   - web.ignoring().antMatchers("**");
85   - }else {
86   - // 可以直接访问的静态数据
87   - web.ignoring()
88   - .antMatchers("/")
89   - .antMatchers("/#/**")
90   - .antMatchers("/static/**")
91   - .antMatchers("/index.html")
92   - .antMatchers("/doc.html") // "/webjars/**", "/swagger-resources/**", "/v3/api-docs/**"
93   - .antMatchers("/webjars/**")
94   - .antMatchers("/swagger-resources/**")
95   - .antMatchers("/v3/api-docs/**")
96   - .antMatchers("/favicon.ico")
97   - .antMatchers("/js/**");
98   - List<String> interfaceAuthenticationExcludes = userSetting.getInterfaceAuthenticationExcludes();
99   - for (String interfaceAuthenticationExclude : interfaceAuthenticationExcludes) {
100   - if (interfaceAuthenticationExclude.split("/").length < 4 ) {
101   - logger.warn("{}不满足两级目录,已忽略", interfaceAuthenticationExclude);
102   - }else {
103   - web.ignoring().antMatchers(interfaceAuthenticationExclude);
104   - }
105   -
106   - }
107   - }
  76 + ArrayList<String> matchers = new ArrayList<>();
  77 + matchers.add("/");
  78 + matchers.add("/#/**");
  79 + matchers.add("/static/**");
  80 + matchers.add("/index.html");
  81 + matchers.add("/doc.html");
  82 + matchers.add("/webjars/**");
  83 + matchers.add("/swagger-resources/**");
  84 + matchers.add("/v3/api-docs/**");
  85 + matchers.add("/js/**");
  86 + matchers.add("/api/device/query/snap/**");
  87 + matchers.add("/record_proxy/*/**");
  88 + matchers.addAll(userSetting.getInterfaceAuthenticationExcludes());
  89 + // 可以直接访问的静态数据
  90 + web.ignoring().antMatchers(matchers.toArray(new String[0]));
108 91 }
109 92  
110 93 /**
... ... @@ -126,36 +109,43 @@ public class WebSecurityConfig extends WebSecurityConfigurerAdapter {
126 109  
127 110 @Override
128 111 protected void configure(HttpSecurity http) throws Exception {
129   - http.cors().and().csrf().disable();
130   - // 设置允许添加静态文件
131   - http.headers().contentTypeOptions().disable();
132   - http.authorizeRequests()
133   - // 放行接口
134   - .antMatchers("/api/user/login","/index/hook/**").permitAll()
135   - // 除上面外的所有请求全部需要鉴权认证
  112 + http.headers().contentTypeOptions().disable()
  113 + .and().cors().configurationSource(configurationSource())
  114 + .and().csrf().disable()
  115 + .sessionManagement()
  116 + .sessionCreationPolicy(SessionCreationPolicy.STATELESS)
  117 +
  118 + // 配置拦截规则
  119 + .and()
  120 + .authorizeRequests()
  121 + .requestMatchers(CorsUtils::isPreFlightRequest).permitAll()
  122 + .antMatchers(userSetting.getInterfaceAuthenticationExcludes().toArray(new String[0])).permitAll()
  123 + .antMatchers("/api/user/login","/index/hook/**","/zlm_Proxy/FhTuMYqB2HeCuNOb/record/t/1/2023-03-25/16:35:07-16:35:16-9353.mp4").permitAll()
136 124 .anyRequest().authenticated()
137   - // 异常处理(权限拒绝、登录失效等)
138   - .and().exceptionHandling()
139   - //匿名用户访问无权限资源时的异常处理
  125 + // 异常处理器
  126 + .and()
  127 + .exceptionHandling()
140 128 .authenticationEntryPoint(anonymousAuthenticationEntryPoint)
141   -// .accessDeniedHandler(accessDeniedHandler)//登录用户没有权限访问资源
142   - // 登入 允许所有用户
143   - .and().formLogin().permitAll()
144   - //登录成功处理逻辑
145   - .successHandler(loginSuccessHandler)
146   - //登录失败处理逻辑
147   - .failureHandler(loginFailureHandler)
148   - // 登出
149 129 .and().logout().logoutUrl("/api/user/logout").permitAll()
150   - //登出成功处理逻辑
151 130 .logoutSuccessHandler(logoutHandler)
152   - .deleteCookies("JSESSIONID")
153   - // 会话管理
154   -// .and().sessionManagement().invalidSessionStrategy(invalidSessionHandler) // 超时处理
155   -// .maximumSessions(1)//同一账号同时登录最大用户数
156   -// .expiredSessionStrategy(sessionInformationExpiredHandler) // 顶号处理
157 131 ;
  132 + http.addFilterBefore(jwtAuthenticationFilter, UsernamePasswordAuthenticationFilter.class);
  133 +
  134 + }
158 135  
  136 + CorsConfigurationSource configurationSource(){
  137 + // 配置跨域
  138 + CorsConfiguration corsConfiguration = new CorsConfiguration();
  139 + corsConfiguration.setAllowedHeaders(Arrays.asList("*"));
  140 + corsConfiguration.setAllowedMethods(Arrays.asList("*"));
  141 + corsConfiguration.setMaxAge(3600L);
  142 + corsConfiguration.setAllowCredentials(true);
  143 + corsConfiguration.setAllowedOrigins(userSetting.getAllowedOrigins());
  144 + corsConfiguration.setExposedHeaders(Arrays.asList(JwtUtils.getHeader()));
  145 +
  146 + UrlBasedCorsConfigurationSource url = new UrlBasedCorsConfigurationSource();
  147 + url.registerCorsConfiguration("/**",corsConfiguration);
  148 + return url;
159 149 }
160 150  
161 151 /**
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/dto/JwtUser.java 0 → 100644
  1 +package com.genersoft.iot.vmp.conf.security.dto;
  2 +
  3 +public class JwtUser {
  4 +
  5 + public enum TokenStatus{
  6 + /**
  7 + * 正常的使用状态
  8 + */
  9 + NORMAL,
  10 + /**
  11 + * 过期而失效
  12 + */
  13 + EXPIRED,
  14 + /**
  15 + * 即将过期
  16 + */
  17 + EXPIRING_SOON,
  18 + /**
  19 + * 异常
  20 + */
  21 + EXCEPTION
  22 + }
  23 +
  24 + private String userName;
  25 +
  26 + private String password;
  27 +
  28 + private TokenStatus status;
  29 +
  30 + public String getUserName() {
  31 + return userName;
  32 + }
  33 +
  34 + public void setUserName(String userName) {
  35 + this.userName = userName;
  36 + }
  37 +
  38 + public TokenStatus getStatus() {
  39 + return status;
  40 + }
  41 +
  42 + public void setStatus(TokenStatus status) {
  43 + this.status = status;
  44 + }
  45 +
  46 + public String getPassword() {
  47 + return password;
  48 + }
  49 +
  50 + public void setPassword(String password) {
  51 + this.password = password;
  52 + }
  53 +}
... ...
src/main/java/com/genersoft/iot/vmp/conf/security/dto/LoginUser.java
... ... @@ -19,6 +19,8 @@ public class LoginUser implements UserDetails, CredentialsContainer {
19 19 */
20 20 private User user;
21 21  
  22 + private String accessToken;
  23 +
22 24  
23 25 /**
24 26 * 登录时间
... ... @@ -102,4 +104,11 @@ public class LoginUser implements UserDetails, CredentialsContainer {
102 104 return user.getPushKey();
103 105 }
104 106  
  107 + public String getAccessToken() {
  108 + return accessToken;
  109 + }
  110 +
  111 + public void setAccessToken(String accessToken) {
  112 + this.accessToken = accessToken;
  113 + }
105 114 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/auth/DigestServerAuthenticationHelper.java
... ... @@ -171,7 +171,7 @@ public class DigestServerAuthenticationHelper {
171 171 */
172 172 public boolean doAuthenticatePlainTextPassword(Request request, String pass) {
173 173 AuthorizationHeader authHeader = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
174   - if ( authHeader == null ) {
  174 + if ( authHeader == null || authHeader.getRealm() == null) {
175 175 return false;
176 176 }
177 177 String realm = authHeader.getRealm().trim();
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/Device.java
... ... @@ -188,6 +188,13 @@ public class Device {
188 188 @Schema(description = "SIP交互IP(设备访问平台的IP)")
189 189 private String localIp;
190 190  
  191 + @Schema(description = "是否作为消息通道")
  192 + private boolean asMessageChannel;
  193 +
  194 + @Schema(description = "设备注册的事务信息")
  195 + private SipTransactionInfo sipTransactionInfo;
  196 +
  197 +
191 198 public String getDeviceId() {
192 199 return deviceId;
193 200 }
... ... @@ -428,4 +435,20 @@ public class Device {
428 435 public void setKeepaliveIntervalTime(int keepaliveIntervalTime) {
429 436 this.keepaliveIntervalTime = keepaliveIntervalTime;
430 437 }
  438 +
  439 + public boolean isAsMessageChannel() {
  440 + return asMessageChannel;
  441 + }
  442 +
  443 + public void setAsMessageChannel(boolean asMessageChannel) {
  444 + this.asMessageChannel = asMessageChannel;
  445 + }
  446 +
  447 + public SipTransactionInfo getSipTransactionInfo() {
  448 + return sipTransactionInfo;
  449 + }
  450 +
  451 + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
  452 + this.sipTransactionInfo = sipTransactionInfo;
  453 + }
431 454 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatform.java
... ... @@ -189,6 +189,9 @@ public class ParentPlatform {
189 189 @Schema(description = "树类型 国标规定了两种树的展现方式 行政区划 CivilCode 和业务分组:BusinessGrou")
190 190 private String treeType;
191 191  
  192 + @Schema(description = "是否作为消息通道")
  193 + private boolean asMessageChannel;
  194 +
192 195 public Integer getId() {
193 196 return id;
194 197 }
... ... @@ -428,4 +431,12 @@ public class ParentPlatform {
428 431 public void setTreeType(String treeType) {
429 432 this.treeType = treeType;
430 433 }
  434 +
  435 + public boolean isAsMessageChannel() {
  436 + return asMessageChannel;
  437 + }
  438 +
  439 + public void setAsMessageChannel(boolean asMessageChannel) {
  440 + this.asMessageChannel = asMessageChannel;
  441 + }
431 442 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/ParentPlatformCatch.java
... ... @@ -16,6 +16,8 @@ public class ParentPlatformCatch {
16 16  
17 17 private ParentPlatform parentPlatform;
18 18  
  19 + private SipTransactionInfo sipTransactionInfo;
  20 +
19 21 public String getId() {
20 22 return id;
21 23 }
... ... @@ -55,4 +57,12 @@ public class ParentPlatformCatch {
55 57 public void setCallId(String callId) {
56 58 this.callId = callId;
57 59 }
  60 +
  61 + public SipTransactionInfo getSipTransactionInfo() {
  62 + return sipTransactionInfo;
  63 + }
  64 +
  65 + public void setSipTransactionInfo(SipTransactionInfo sipTransactionInfo) {
  66 + this.sipTransactionInfo = sipTransactionInfo;
  67 + }
58 68 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordInfo.java
1 1 package com.genersoft.iot.vmp.gb28181.bean;
2 2  
3 3  
  4 +import io.swagger.v3.oas.annotations.media.Schema;
  5 +
4 6 import java.time.Instant;
5 7 import java.util.List;
6 8  
... ... @@ -9,22 +11,29 @@ import java.util.List;
9 11 * @author: swwheihei
10 12 * @date: 2020年5月8日 下午2:05:56
11 13 */
  14 +@Schema(description = "设备录像查询结果信息")
12 15 public class RecordInfo {
13 16  
  17 + @Schema(description = "设备编号")
14 18 private String deviceId;
15 19  
  20 + @Schema(description = "通道编号")
16 21 private String channelId;
17 22  
  23 + @Schema(description = "命令序列号")
18 24 private String sn;
19 25  
  26 + @Schema(description = "设备名称")
20 27 private String name;
21   -
  28 +
  29 + @Schema(description = "列表总数")
22 30 private int sumNum;
23 31  
24 32 private int count;
25 33  
26 34 private Instant lastTime;
27   -
  35 +
  36 + @Schema(description = "")
28 37 private List<RecordItem> recordList;
29 38  
30 39 public String getDeviceId() {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/bean/RecordItem.java
... ... @@ -2,9 +2,9 @@ package com.genersoft.iot.vmp.gb28181.bean;
2 2  
3 3  
4 4 import com.genersoft.iot.vmp.utils.DateUtil;
  5 +import io.swagger.v3.oas.annotations.media.Schema;
5 6 import org.jetbrains.annotations.NotNull;
6 7  
7   -import java.text.ParseException;
8 8 import java.time.Instant;
9 9 import java.time.temporal.TemporalAccessor;
10 10  
... ... @@ -13,26 +13,37 @@ import java.time.temporal.TemporalAccessor;
13 13 * @author: swwheihei
14 14 * @date: 2020年5月8日 下午2:06:54
15 15 */
  16 +@Schema(description = "设备录像详情")
16 17 public class RecordItem implements Comparable<RecordItem>{
17 18  
  19 + @Schema(description = "设备编号")
18 20 private String deviceId;
19   -
  21 +
  22 + @Schema(description = "名称")
20 23 private String name;
21   -
  24 +
  25 + @Schema(description = "文件路径名 (可选)")
22 26 private String filePath;
23 27  
  28 + @Schema(description = "录像文件大小,单位:Byte(可选)")
24 29 private String fileSize;
25 30  
  31 + @Schema(description = "录像地址(可选)")
26 32 private String address;
27   -
  33 +
  34 + @Schema(description = "录像开始时间(可选)")
28 35 private String startTime;
29   -
  36 +
  37 + @Schema(description = "录像结束时间(可选)")
30 38 private String endTime;
31   -
  39 +
  40 + @Schema(description = "保密属性(必选)缺省为0;0:不涉密,1:涉密")
32 41 private int secrecy;
33   -
  42 +
  43 + @Schema(description = "录像产生类型(可选)time或alarm 或 manua")
34 44 private String type;
35   -
  45 +
  46 + @Schema(description = "录像触发者ID(可选)")
36 47 private String recorderId;
37 48  
38 49 public String getDeviceId() {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/event/device/RequestTimeoutEventImpl.java
1 1 package com.genersoft.iot.vmp.gb28181.event.device;
2 2  
3 3 import com.genersoft.iot.vmp.gb28181.bean.Device;
4   -import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
5 4 import com.genersoft.iot.vmp.service.IDeviceService;
6 5 import org.springframework.beans.factory.annotation.Autowired;
7 6 import org.springframework.context.ApplicationListener;
... ... @@ -9,8 +8,6 @@ import org.springframework.stereotype.Component;
9 8  
10 9 import javax.sip.ClientTransaction;
11 10 import javax.sip.address.SipURI;
12   -import javax.sip.header.CallIdHeader;
13   -import javax.sip.header.ToHeader;
14 11 import javax.sip.message.Request;
15 12  
16 13 /**
... ... @@ -34,7 +31,7 @@ public class RequestTimeoutEventImpl implements ApplicationListener&lt;RequestTimeo
34 31 if (device == null) {
35 32 return;
36 33 }
37   - deviceService.offline(device.getDeviceId());
  34 + deviceService.offline(device.getDeviceId(), "等待消息超时");
38 35 }
39 36  
40 37 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/task/SipRunner.java
... ... @@ -61,9 +61,9 @@ public class SipRunner implements CommandLineRunner {
61 61  
62 62 for (Device device : deviceList) {
63 63 if (deviceService.expire(device)){
64   - deviceService.offline(device.getDeviceId());
  64 + deviceService.offline(device.getDeviceId(), "注册已过期");
65 65 }else {
66   - deviceService.online(device);
  66 + deviceService.online(device, null);
67 67 }
68 68 }
69 69 // 重置cseq计数
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommander.java
... ... @@ -120,7 +120,7 @@ public interface ISIPCommander {
120 120 */
121 121 void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
122 122 String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
123   - SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException;
  123 + SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException;
124 124  
125 125  
126 126 /**
... ... @@ -217,7 +217,6 @@ public interface ISIPCommander {
217 217 *
218 218 * @param device 视频设备
219 219 * @param channelId 通道id,非通道则是设备本身
220   - * @param frontCmd 上级平台的指令,如果存在则直接下发
221 220 * @param enabled 看守位使能:1 = 开启,0 = 关闭
222 221 * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
223 222 * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/ISIPCommanderForPlatform.java
... ... @@ -22,12 +22,10 @@ public interface ISIPCommanderForPlatform {
22 22 * @param parentPlatform
23 23 * @return
24 24 */
25   - void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
26   - throws InvalidArgumentException, ParseException, SipException;
  25 + void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
27 26  
28   - void register(ParentPlatform parentPlatform, String callId, WWWAuthenticateHeader www,
29   - SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister)
30   - throws SipException, InvalidArgumentException, ParseException;
  27 + void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
  28 + void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, WWWAuthenticateHeader www, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException;
31 29  
32 30 /**
33 31 * 向上级平台注销
... ... @@ -35,8 +33,7 @@ public interface ISIPCommanderForPlatform {
35 33 * @param parentPlatform
36 34 * @return
37 35 */
38   - void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent, SipSubscribe.Event okEvent)
39   - throws InvalidArgumentException, ParseException, SipException;
  36 + void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException;
40 37  
41 38  
42 39 /**
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/SIPRequestHeaderPlarformProvider.java
... ... @@ -46,7 +46,7 @@ public class SIPRequestHeaderPlarformProvider {
46 46 @Autowired
47 47 private IRedisCatchStorage redisCatchStorage;
48 48  
49   - public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String viaTag, CallIdHeader callIdHeader, boolean isRegister) throws ParseException, InvalidArgumentException, PeerUnavailableException {
  49 + public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, long CSeq, String fromTag, String toTag, CallIdHeader callIdHeader, boolean isRegister) throws ParseException, InvalidArgumentException, PeerUnavailableException {
50 50 Request request = null;
51 51 String sipAddress = parentPlatform.getDeviceIp() + ":" + parentPlatform.getDevicePort();
52 52 //请求行
... ... @@ -54,7 +54,8 @@ public class SIPRequestHeaderPlarformProvider {
54 54 parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
55 55 //via
56 56 ArrayList<ViaHeader> viaHeaders = new ArrayList<ViaHeader>();
57   - ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(), parentPlatform.getServerPort(), parentPlatform.getTransport(), viaTag);
  57 + ViaHeader viaHeader = sipLayer.getSipFactory().createHeaderFactory().createViaHeader(parentPlatform.getServerIP(),
  58 + parentPlatform.getServerPort(), parentPlatform.getTransport(), SipUtils.getNewViaTag());
58 59 viaHeader.setRPort();
59 60 viaHeaders.add(viaHeader);
60 61 //from
... ... @@ -64,7 +65,7 @@ public class SIPRequestHeaderPlarformProvider {
64 65 //to
65 66 SipURI toSipURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getDeviceGBId(), sipConfig.getDomain());
66 67 Address toAddress = sipLayer.getSipFactory().createAddressFactory().createAddress(toSipURI);
67   - ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,null);
  68 + ToHeader toHeader = sipLayer.getSipFactory().createHeaderFactory().createToHeader(toAddress,toTag);
68 69  
69 70 //Forwards
70 71 MaxForwardsHeader maxForwards = sipLayer.getSipFactory().createHeaderFactory().createMaxForwardsHeader(70);
... ... @@ -86,15 +87,21 @@ public class SIPRequestHeaderPlarformProvider {
86 87 return request;
87 88 }
88 89  
89   - public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String viaTag,
90   - String callId, WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException {
  90 + public Request createRegisterRequest(@NotNull ParentPlatform parentPlatform, String fromTag, String toTag,
  91 + WWWAuthenticateHeader www , CallIdHeader callIdHeader, boolean isRegister) throws ParseException, PeerUnavailableException, InvalidArgumentException {
91 92  
92 93  
93   - Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, viaTag, callIdHeader, isRegister);
  94 + Request registerRequest = createRegisterRequest(parentPlatform, redisCatchStorage.getCSEQ(), fromTag, toTag, callIdHeader, isRegister);
94 95 SipURI requestURI = sipLayer.getSipFactory().createAddressFactory().createSipURI(parentPlatform.getServerGBId(), parentPlatform.getServerIP() + ":" + parentPlatform.getServerPort());
95 96 if (www == null) {
96 97 AuthorizationHeader authorizationHeader = sipLayer.getSipFactory().createHeaderFactory().createAuthorizationHeader("Digest");
97   - authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
  98 + String username = parentPlatform.getUsername();
  99 + if ( username == null || username == "" )
  100 + {
  101 + authorizationHeader.setUsername(parentPlatform.getDeviceGBId());
  102 + } else {
  103 + authorizationHeader.setUsername(username);
  104 + }
98 105 authorizationHeader.setURI(requestURI);
99 106 authorizationHeader.setAlgorithm("MD5");
100 107 registerRequest.addHeader(authorizationHeader);
... ... @@ -108,8 +115,6 @@ public class SIPRequestHeaderPlarformProvider {
108 115 // qop 保护质量 包含auth(默认的)和auth-int(增加了报文完整性检测)两种策略
109 116 String qop = www.getQop();
110 117  
111   - callIdHeader.setCallId(callId);
112   -
113 118 String cNonce = null;
114 119 String nc = "00000001";
115 120 if (qop != null) {
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/cmd/impl/SIPCommander.java
... ... @@ -276,7 +276,7 @@ public class SIPCommander implements ISIPCommander {
276 276 return;
277 277 }
278 278  
279   - logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
  279 + logger.info("{} 分配的ZLM为: {} [{}:{}]", stream, mediaServerItem.getId(), mediaServerItem.getSdpIp(), ssrcInfo.getPort());
280 280 HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", stream, true, "rtsp", mediaServerItem.getId());
281 281 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
282 282 if (event != null) {
... ... @@ -377,7 +377,7 @@ public class SIPCommander implements ISIPCommander {
377 377 SipSubscribe.Event okEvent, SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
378 378  
379 379  
380   - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
  380 + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
381 381 String sdpIp;
382 382 if (!ObjectUtils.isEmpty(device.getSdpIp())) {
383 383 sdpIp = device.getSdpIp();
... ... @@ -479,10 +479,11 @@ public class SIPCommander implements ISIPCommander {
479 479 */
480 480 @Override
481 481 public void downloadStreamCmd(MediaServerItem mediaServerItem, SSRCInfo ssrcInfo, Device device, String channelId,
482   - String startTime, String endTime, int downloadSpeed, InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
483   - SipSubscribe.Event errorEvent) throws InvalidArgumentException, SipException, ParseException {
  482 + String startTime, String endTime, int downloadSpeed,
  483 + InviteStreamCallback inviteStreamCallback, InviteStreamCallback hookEvent,
  484 + SipSubscribe.Event errorEvent,SipSubscribe.Event okEvent) throws InvalidArgumentException, SipException, ParseException {
484 485  
485   - logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getId(), mediaServerItem.getIp(), ssrcInfo.getPort());
  486 + logger.info("{} 分配的ZLM为: {} [{}:{}]", ssrcInfo.getStream(), mediaServerItem.getSdpIp(), mediaServerItem.getIp(), ssrcInfo.getPort());
486 487 String sdpIp;
487 488 if (!ObjectUtils.isEmpty(device.getSdpIp())) {
488 489 sdpIp = device.getSdpIp();
... ... @@ -549,11 +550,14 @@ public class SIPCommander implements ISIPCommander {
549 550 content.append("a=downloadspeed:" + downloadSpeed + "\r\n");
550 551  
551 552 content.append("y=" + ssrcInfo.getSsrc() + "\r\n");//ssrc
552   -
  553 + logger.debug("此时请求下载信令的ssrc===>{}",ssrcInfo.getSsrc());
553 554 HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, null, mediaServerItem.getId());
554 555 // 添加订阅
  556 + CallIdHeader newCallIdHeader = sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()), device.getTransport());
  557 + String callId=newCallIdHeader.getCallId();
555 558 subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject json) -> {
556   - hookEvent.call(new InviteStreamInfo(mediaServerItem, json,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
  559 + logger.debug("sipc 添加订阅===callId {}",callId);
  560 + hookEvent.call(new InviteStreamInfo(mediaServerItem, json,callId, "rtp", ssrcInfo.getStream()));
557 561 subscribe.removeSubscribe(hookSubscribe);
558 562 hookSubscribe.getContent().put("regist", false);
559 563 hookSubscribe.getContent().put("schema", "rtsp");
... ... @@ -562,7 +566,7 @@ public class SIPCommander implements ISIPCommander {
562 566 (MediaServerItem mediaServerItemForEnd, JSONObject jsonForEnd) -> {
563 567 logger.info("[录像]下载结束, 发送BYE");
564 568 try {
565   - streamByeCmd(device, channelId, ssrcInfo.getStream(),sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId());
  569 + streamByeCmd(device, channelId, ssrcInfo.getStream(),callId);
566 570 } catch (InvalidArgumentException | ParseException | SipException |
567 571 SsrcTransactionNotFoundException e) {
568 572 logger.error("[录像]下载结束, 发送BYE失败 {}", e.getMessage());
... ... @@ -570,15 +574,24 @@ public class SIPCommander implements ISIPCommander {
570 574 });
571 575 });
572 576  
573   - Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()), ssrcInfo.getSsrc());
  577 + Request request = headerProvider.createPlaybackInviteRequest(device, channelId, content.toString(), null, SipUtils.getNewFromTag(), null,newCallIdHeader, ssrcInfo.getSsrc());
574 578 if (inviteStreamCallback != null) {
575   - inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()).getCallId(), "rtp", ssrcInfo.getStream()));
  579 + inviteStreamCallback.call(new InviteStreamInfo(mediaServerItem, null,callId, "rtp", ssrcInfo.getStream()));
576 580 }
577 581  
578   - sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, okEvent -> {
579   - ResponseEvent responseEvent = (ResponseEvent) okEvent.event;
  582 + sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent, event -> {
  583 + ResponseEvent responseEvent = (ResponseEvent) event.event;
580 584 SIPResponse response = (SIPResponse) responseEvent.getResponse();
581   - streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrcInfo.getSsrc(), mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
  585 + String contentString =new String(response.getRawContent());
  586 + int ssrcIndex = contentString.indexOf("y=");
  587 + String ssrc=ssrcInfo.getSsrc();
  588 + if (ssrcIndex >= 0) {
  589 + ssrc = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  590 + }
  591 + logger.debug("接收到的下载响应ssrc====>{}",ssrcInfo.getSsrc());
  592 + logger.debug("接收到的下载响应ssrc====>{}",ssrc);
  593 + streamSession.put(device.getDeviceId(), channelId, response.getCallIdHeader().getCallId(), ssrcInfo.getStream(), ssrc, mediaServerItem.getId(), response, VideoStreamSessionManager.SessionType.download);
  594 + okEvent.response(event);
582 595 });
583 596 }
584 597  
... ... @@ -778,7 +791,7 @@ public class SIPCommander implements ISIPCommander {
778 791 cmdXml.append("<GuardCmd>" + guardCmdStr + "</GuardCmd>\r\n");
779 792 cmdXml.append("</Control>\r\n");
780 793  
781   -
  794 +
782 795  
783 796 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
784 797 sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent,okEvent);
... ... @@ -854,7 +867,6 @@ public class SIPCommander implements ISIPCommander {
854 867 *
855 868 * @param device 视频设备
856 869 * @param channelId 通道id,非通道则是设备本身
857   - * @param frontCmd 上级平台的指令,如果存在则直接下发
858 870 * @param enabled 看守位使能:1 = 开启,0 = 关闭
859 871 * @param resetTime 自动归位时间间隔,开启看守位时使用,单位:秒(s)
860 872 * @param presetIndex 调用预置位编号,开启看守位时使用,取值范围0~255
... ... @@ -978,7 +990,7 @@ public class SIPCommander implements ISIPCommander {
978 990 catalogXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
979 991 catalogXml.append("</Query>\r\n");
980 992  
981   -
  993 +
982 994  
983 995 Request request = headerProvider.createMessageRequest(device, catalogXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
984 996  
... ... @@ -1181,7 +1193,6 @@ public class SIPCommander implements ISIPCommander {
1181 1193 cmdXml.append("</Query>\r\n");
1182 1194  
1183 1195  
1184   -
1185 1196 Request request = headerProvider.createMessageRequest(device, cmdXml.toString(), null, SipUtils.getNewFromTag(), null,sipSender.getNewCallIdHeader(sipLayer.getLocalIp(device.getLocalIp()),device.getTransport()));
1186 1197 sipSender.transmitRequest(sipLayer.getLocalIp(device.getLocalIp()), request, errorEvent);
1187 1198 }
... ... @@ -1427,7 +1438,7 @@ public class SIPCommander implements ISIPCommander {
1427 1438 if (device == null) {
1428 1439 return;
1429 1440 }
1430   - logger.info("[发送 报警通知] {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
  1441 + logger.info("[发送报警通知]设备: {}/{}->{},{}", device.getDeviceId(), deviceAlarm.getChannelId(),
1431 1442 deviceAlarm.getLongitude(), deviceAlarm.getLatitude());
1432 1443  
1433 1444 String characterSet = device.getCharset();
... ... @@ -1439,7 +1450,7 @@ public class SIPCommander implements ISIPCommander {
1439 1450 deviceStatusXml.append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n");
1440 1451 deviceStatusXml.append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n");
1441 1452 deviceStatusXml.append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n");
1442   - deviceStatusXml.append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n");
  1453 + deviceStatusXml.append("<AlarmTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "</AlarmTime>\r\n");
1443 1454 deviceStatusXml.append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n");
1444 1455 deviceStatusXml.append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n");
1445 1456 deviceStatusXml.append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n");
... ...
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;
24 24 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
25 25 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
26 26 import com.genersoft.iot.vmp.utils.DateUtil;
  27 +import com.genersoft.iot.vmp.utils.GitUtil;
27 28 import gov.nist.javax.sip.message.MessageFactoryImpl;
28 29 import gov.nist.javax.sip.message.SIPRequest;
29 30 import gov.nist.javax.sip.message.SIPResponse;
... ... @@ -85,26 +86,49 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
85 86 @Autowired
86 87 private DynamicTask dynamicTask;
87 88  
  89 + @Autowired
  90 + private GitUtil gitUtil;
  91 +
88 92 @Override
89 93 public void register(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
90 94 register(parentPlatform, null, null, errorEvent, okEvent, false, true);
91 95 }
92 96  
93 97 @Override
94   - public void unregister(ParentPlatform parentPlatform, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
95   - register(parentPlatform, null, null, errorEvent, okEvent, false, false);
  98 + public void register(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
  99 +
  100 + register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false, true);
96 101 }
97 102  
98 103 @Override
99   - public void register(ParentPlatform parentPlatform, @Nullable String callId, @Nullable WWWAuthenticateHeader www,
  104 + public void unregister(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo, SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent) throws InvalidArgumentException, ParseException, SipException {
  105 + register(parentPlatform, sipTransactionInfo, null, errorEvent, okEvent, false, false);
  106 + }
  107 +
  108 + @Override
  109 + public void register(ParentPlatform parentPlatform, @Nullable SipTransactionInfo sipTransactionInfo, @Nullable WWWAuthenticateHeader www,
100 110 SipSubscribe.Event errorEvent , SipSubscribe.Event okEvent, boolean registerAgain, boolean isRegister) throws SipException, InvalidArgumentException, ParseException {
101 111 Request request;
102   - if (!registerAgain ) {
103   - CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
104 112  
  113 + CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
  114 + String fromTag = SipUtils.getNewFromTag();
  115 + String toTag = null;
  116 + if (sipTransactionInfo != null ) {
  117 + if (sipTransactionInfo.getCallId() != null) {
  118 + callIdHeader.setCallId(sipTransactionInfo.getCallId());
  119 + }
  120 + if (sipTransactionInfo.getFromTag() != null) {
  121 + fromTag = sipTransactionInfo.getFromTag();
  122 + }
  123 + if (sipTransactionInfo.getToTag() != null) {
  124 + toTag = sipTransactionInfo.getToTag();
  125 + }
  126 + }
  127 +
  128 + if (!registerAgain ) {
105 129 request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform,
106   - redisCatchStorage.getCSEQ(), SipUtils.getNewFromTag(),
107   - SipUtils.getNewViaTag(), callIdHeader, isRegister);
  130 + redisCatchStorage.getCSEQ(), fromTag,
  131 + toTag, callIdHeader, isRegister);
108 132 // 将 callid 写入缓存, 等注册成功可以更新状态
109 133 String callIdFromHeader = callIdHeader.getCallId();
110 134 redisCatchStorage.updatePlatformRegisterInfo(callIdFromHeader, PlatformRegisterInfo.getInstance(parentPlatform.getServerGBId(), isRegister));
... ... @@ -122,8 +146,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
122 146 });
123 147  
124 148 }else {
125   - CallIdHeader callIdHeader = sipSender.getNewCallIdHeader(parentPlatform.getDeviceIp(),parentPlatform.getTransport());
126   - request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, SipUtils.getNewFromTag(), null, callId, www, callIdHeader, isRegister);
  149 + request = headerProviderPlatformProvider.createRegisterRequest(parentPlatform, fromTag, toTag, www, callIdHeader, isRegister);
127 150 }
128 151  
129 152 sipSender.transmitRequest(parentPlatform.getDeviceIp(), request, null, okEvent);
... ... @@ -245,6 +268,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
245 268 catalogXml.append("<IPAddress>" + channel.getIpAddress() + "</IPAddress>\r\n");
246 269 catalogXml.append("<Port>" + channel.getPort() + "</Port>\r\n");
247 270 catalogXml.append("<Password>" + channel.getPort() + "</Password>\r\n");
  271 + catalogXml.append("<PTZType>" + channel.getPTZType() + "</PTZType>\r\n");
248 272 catalogXml.append("<Status>" + (channel.getStatus() == 1?"ON":"OFF") + "</Status>\r\n");
249 273 catalogXml.append("<Longitude>" +
250 274 (channel.getLongitudeWgs84() != 0? channel.getLongitudeWgs84():channel.getLongitude())
... ... @@ -285,6 +309,9 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
285 309  
286 310 String callId = request.getCallIdHeader().getCallId();
287 311  
  312 + logger.info("[命令发送] 国标级联{} 目录查询回复: 共{}条,已发送{}条", parentPlatform.getServerGBId(),
  313 + channels.size(), Math.min(index + parentPlatform.getCatalogGroup(), channels.size()));
  314 + logger.debug(catalogXml);
288 315 if (sendAfterResponse) {
289 316 // 默认按照收到200回复后发送下一条, 如果超时收不到回复,就以30毫秒的间隔直接发送。
290 317 dynamicTask.startDelay(timeoutTaskKey, ()->{
... ... @@ -336,17 +363,22 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
336 363 if (parentPlatform == null) {
337 364 return;
338 365 }
  366 + String deviceId = device == null ? parentPlatform.getDeviceGBId() : device.getDeviceId();
  367 + String deviceName = device == null ? parentPlatform.getName() : device.getName();
  368 + String manufacturer = device == null ? "WVP-28181-PRO" : device.getManufacturer();
  369 + String model = device == null ? "platform" : device.getModel();
  370 + String firmware = device == null ? gitUtil.getBuildVersion() : device.getFirmware();
339 371 String characterSet = parentPlatform.getCharacterSet();
340 372 StringBuffer deviceInfoXml = new StringBuffer(600);
341 373 deviceInfoXml.append("<?xml version=\"1.0\" encoding=\"" + characterSet + "\"?>\r\n");
342 374 deviceInfoXml.append("<Response>\r\n");
343 375 deviceInfoXml.append("<CmdType>DeviceInfo</CmdType>\r\n");
344 376 deviceInfoXml.append("<SN>" +sn + "</SN>\r\n");
345   - deviceInfoXml.append("<DeviceID>" + device.getDeviceId() + "</DeviceID>\r\n");
346   - deviceInfoXml.append("<DeviceName>" + device.getName() + "</DeviceName>\r\n");
347   - deviceInfoXml.append("<Manufacturer>" + device.getManufacturer() + "</Manufacturer>\r\n");
348   - deviceInfoXml.append("<Model>" + device.getModel() + "</Model>\r\n");
349   - deviceInfoXml.append("<Firmware>" + device.getFirmware() + "</Firmware>\r\n");
  377 + deviceInfoXml.append("<DeviceID>" + deviceId + "</DeviceID>\r\n");
  378 + deviceInfoXml.append("<DeviceName>" + deviceName + "</DeviceName>\r\n");
  379 + deviceInfoXml.append("<Manufacturer>" + manufacturer + "</Manufacturer>\r\n");
  380 + deviceInfoXml.append("<Model>" + model + "</Model>\r\n");
  381 + deviceInfoXml.append("<Firmware>" + firmware + "</Firmware>\r\n");
350 382 deviceInfoXml.append("<Result>OK</Result>\r\n");
351 383 deviceInfoXml.append("</Response>\r\n");
352 384  
... ... @@ -423,7 +455,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
423 455 if (parentPlatform == null) {
424 456 return;
425 457 }
426   - logger.info("[发送报警通知] {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
  458 + logger.info("[发送报警通知]平台: {}/{}->{},{}: {}", parentPlatform.getServerGBId(), deviceAlarm.getChannelId(),
427 459 deviceAlarm.getLongitude(), deviceAlarm.getLatitude(), JSON.toJSONString(deviceAlarm));
428 460 String characterSet = parentPlatform.getCharacterSet();
429 461 StringBuffer deviceStatusXml = new StringBuffer(600);
... ... @@ -434,7 +466,7 @@ public class SIPCommanderFroPlatform implements ISIPCommanderForPlatform {
434 466 .append("<DeviceID>" + deviceAlarm.getChannelId() + "</DeviceID>\r\n")
435 467 .append("<AlarmPriority>" + deviceAlarm.getAlarmPriority() + "</AlarmPriority>\r\n")
436 468 .append("<AlarmMethod>" + deviceAlarm.getAlarmMethod() + "</AlarmMethod>\r\n")
437   - .append("<AlarmTime>" + deviceAlarm.getAlarmTime() + "</AlarmTime>\r\n")
  469 + .append("<AlarmTime>" + DateUtil.yyyy_MM_dd_HH_mm_ssToISO8601(deviceAlarm.getAlarmTime()) + "</AlarmTime>\r\n")
438 470 .append("<AlarmDescription>" + deviceAlarm.getAlarmDescription() + "</AlarmDescription>\r\n")
439 471 .append("<Longitude>" + deviceAlarm.getLongitude() + "</Longitude>\r\n")
440 472 .append("<Latitude>" + deviceAlarm.getLatitude() + "</Latitude>\r\n")
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/SIPRequestProcessorParent.java
... ... @@ -45,7 +45,7 @@ public abstract class SIPRequestProcessorParent {
45 45 try {
46 46 return SipFactory.getInstance().createHeaderFactory();
47 47 } catch (PeerUnavailableException e) {
48   - e.printStackTrace();
  48 + logger.error("未处理的异常 ", e);
49 49 }
50 50 return null;
51 51 }
... ... @@ -54,7 +54,7 @@ public abstract class SIPRequestProcessorParent {
54 54 try {
55 55 return SipFactory.getInstance().createMessageFactory();
56 56 } catch (PeerUnavailableException e) {
57   - e.printStackTrace();
  57 + logger.error("未处理的异常 ", e);
58 58 }
59 59 return null;
60 60 }
... ...
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;
8 8 import com.genersoft.iot.vmp.gb28181.bean.*;
9 9 import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
10 10 import com.genersoft.iot.vmp.gb28181.session.AudioBroadcastManager;
  11 +import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
11 12 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
12 13 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
13 14 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
... ... @@ -457,12 +458,8 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
457 458 if (!userSetting.getPushStreamAfterAck()) {
458 459 playService.startPushStream(sendRtpItem, sipResponse, platform, request.getCallIdHeader());
459 460 }
460   - } catch (SipException e) {
461   - e.printStackTrace();
462   - } catch (InvalidArgumentException e) {
463   - e.printStackTrace();
464   - } catch (ParseException e) {
465   - e.printStackTrace();
  461 + } catch (SipException | InvalidArgumentException | ParseException e) {
  462 + logger.error("[命令发送失败] 国标级联 回复SdpAck", e);
466 463 }
467 464 };
468 465 SipSubscribe.Event errorEvent = ((event) -> {
... ... @@ -471,7 +468,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
471 468 Response response = getMessageFactory().createResponse(event.statusCode, evt.getRequest());
472 469 sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
473 470 } catch (ParseException | SipException e) {
474   - e.printStackTrace();
  471 + logger.error("未处理的异常 ", e);
475 472 }
476 473 });
477 474 sendRtpItem.setApp("rtp");
... ... @@ -543,6 +540,15 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
543 540 }
544 541 }
545 542 } else if (gbStream != null) {
  543 + if(ssrc.equals(ssrcDefault))
  544 + {
  545 + SsrcConfig ssrcConfig = mediaServerItem.getSsrcConfig();
  546 + if(ssrcConfig != null)
  547 + {
  548 + ssrc = ssrcConfig.getPlaySsrc();
  549 + ssrcConfig.releaseSsrc(ssrc);
  550 + }
  551 + }
546 552 if("push".equals(gbStream.getStreamType())) {
547 553 if (streamPushItem != null && streamPushItem.isPushIng()) {
548 554 // 推流状态
... ... @@ -572,7 +578,7 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
572 578 } catch (SdpParseException e) {
573 579 logger.error("sdp解析错误", e);
574 580 } catch (SdpException e) {
575   - e.printStackTrace();
  581 + logger.error("未处理的异常 ", e);
576 582 }
577 583 }
578 584  
... ... @@ -727,11 +733,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
727 733 mediaListManager.removedChannelOnlineEventLister(gbStream.getApp(), gbStream.getStream());
728 734 responseAck(request, Response.REQUEST_TIMEOUT); // 超时
729 735 } catch (SipException e) {
730   - e.printStackTrace();
  736 + logger.error("未处理的异常 ", e);
731 737 } catch (InvalidArgumentException e) {
732   - e.printStackTrace();
  738 + logger.error("未处理的异常 ", e);
733 739 } catch (ParseException e) {
734   - e.printStackTrace();
  740 + logger.error("未处理的异常 ", e);
735 741 }
736 742 }, userSetting.getPlatformPlayTimeout());
737 743 // 添加监听
... ... @@ -750,11 +756,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
750 756 try {
751 757 responseAck(request, Response.BUSY_HERE);
752 758 } catch (SipException e) {
753   - e.printStackTrace();
  759 + logger.error("未处理的异常 ", e);
754 760 } catch (InvalidArgumentException e) {
755   - e.printStackTrace();
  761 + logger.error("未处理的异常 ", e);
756 762 } catch (ParseException e) {
757   - e.printStackTrace();
  763 + logger.error("未处理的异常 ", e);
758 764 }
759 765 return;
760 766 }
... ... @@ -812,11 +818,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
812 818 try {
813 819 responseAck(request, Response.BUSY_HERE);
814 820 } catch (SipException e) {
815   - e.printStackTrace();
  821 + logger.error("未处理的异常 ", e);
816 822 } catch (InvalidArgumentException e) {
817   - e.printStackTrace();
  823 + logger.error("未处理的异常 ", e);
818 824 } catch (ParseException e) {
819   - e.printStackTrace();
  825 + logger.error("未处理的异常 ", e);
820 826 }
821 827 return;
822 828 }
... ... @@ -869,7 +875,13 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
869 875 content.append("s=Play\r\n");
870 876 content.append("c=IN IP4 " + mediaServerItem.getSdpIp() + "\r\n");
871 877 content.append("t=0 0\r\n");
872   - content.append("m=video " + sendRtpItem.getLocalPort() + " RTP/AVP 96\r\n");
  878 + // 非严格模式端口不统一, 增加兼容性,修改为一个不为0的端口
  879 + int localPort = sendRtpItem.getLocalPort();
  880 + if(localPort == 0)
  881 + {
  882 + localPort = new Random().nextInt(65535) + 1;
  883 + }
  884 + content.append("m=video " + localPort + " RTP/AVP 96\r\n");
873 885 content.append("a=sendonly\r\n");
874 886 content.append("a=rtpmap:96 PS/90000\r\n");
875 887 if (sendRtpItem.isTcp()) {
... ... @@ -890,11 +902,11 @@ public class InviteRequestProcessor extends SIPRequestProcessorParent implements
890 902 }
891 903 return sipResponse;
892 904 } catch (SipException e) {
893   - e.printStackTrace();
  905 + logger.error("未处理的异常 ", e);
894 906 } catch (InvalidArgumentException e) {
895   - e.printStackTrace();
  907 + logger.error("未处理的异常 ", e);
896 908 } catch (ParseException e) {
897   - e.printStackTrace();
  909 + logger.error("未处理的异常 ", e);
898 910 }
899 911 return null;
900 912 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/NotifyRequestProcessor.java
... ... @@ -93,7 +93,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
93 93 try {
94 94 responseAck((SIPRequest) evt.getRequest(), Response.OK, null, null);
95 95 }catch (SipException | InvalidArgumentException | ParseException e) {
96   - e.printStackTrace();
  96 + logger.error("未处理的异常 ", e);
97 97 }
98 98 boolean runed = !taskQueue.isEmpty();
99 99 taskQueue.offer(new HandlerCatchData(evt, null, null));
... ... @@ -229,7 +229,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
229 229 jsonObject.put("speed", mobilePosition.getSpeed());
230 230 redisCatchStorage.sendMobilePositionMsg(jsonObject);
231 231 } catch (DocumentException e) {
232   - e.printStackTrace();
  232 + logger.error("未处理的异常 ", e);
233 233 }
234 234 }
235 235  
... ... @@ -339,7 +339,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
339 339 publisher.deviceAlarmEventPublish(deviceAlarm);
340 340 }
341 341 } catch (DocumentException e) {
342   - e.printStackTrace();
  342 + logger.error("未处理的异常 ", e);
343 343 }
344 344 }
345 345  
... ... @@ -397,12 +397,20 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
397 397 case CatalogEvent.OFF :
398 398 // 离线
399 399 logger.info("[收到通道离线通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
400   - storager.deviceChannelOffline(deviceId, channel.getChannelId());
  400 + if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
  401 + storager.deviceChannelOffline(deviceId, channel.getChannelId());
  402 + }else {
  403 + logger.info("[收到通道离线通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  404 + }
401 405 break;
402 406 case CatalogEvent.VLOST:
403 407 // 视频丢失
404 408 logger.info("[收到通道视频丢失通知] 来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
405   - storager.deviceChannelOffline(deviceId, channel.getChannelId());
  409 + if (userSetting.getRefuseChannelStatusChannelFormNotify()) {
  410 + storager.deviceChannelOffline(deviceId, channel.getChannelId());
  411 + }else {
  412 + logger.info("[收到通道视频丢失通知] 但是平台已配置拒绝此消息,来自设备: {}, 通道 {}", device.getDeviceId(), channel.getChannelId());
  413 + }
406 414 break;
407 415 case CatalogEvent.DEFECT:
408 416 // 故障
... ... @@ -432,7 +440,7 @@ public class NotifyRequestProcessor extends SIPRequestProcessorParent implements
432 440 }
433 441 }
434 442 } catch (DocumentException e) {
435   - e.printStackTrace();
  443 + logger.error("未处理的异常 ", e);
436 444 }
437 445 }
438 446  
... ...
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;
5 5 import com.genersoft.iot.vmp.gb28181.auth.DigestServerAuthenticationHelper;
6 6 import com.genersoft.iot.vmp.gb28181.bean.Device;
7 7 import com.genersoft.iot.vmp.gb28181.bean.RemoteAddressInfo;
  8 +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
8 9 import com.genersoft.iot.vmp.gb28181.bean.WvpSipDate;
9 10 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
10 11 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
... ... @@ -18,6 +19,7 @@ import gov.nist.javax.sip.address.AddressImpl;
18 19 import gov.nist.javax.sip.address.SipUri;
19 20 import gov.nist.javax.sip.header.SIPDateHeader;
20 21 import gov.nist.javax.sip.message.SIPRequest;
  22 +import gov.nist.javax.sip.message.SIPResponse;
21 23 import org.slf4j.Logger;
22 24 import org.slf4j.LoggerFactory;
23 25 import org.springframework.beans.factory.InitializingBean;
... ... @@ -34,6 +36,7 @@ import javax.sip.header.AuthorizationHeader;
34 36 import javax.sip.header.ContactHeader;
35 37 import javax.sip.header.FromHeader;
36 38 import javax.sip.header.ViaHeader;
  39 +import javax.sip.message.Request;
37 40 import javax.sip.message.Response;
38 41 import java.security.NoSuchAlgorithmException;
39 42 import java.text.ParseException;
... ... @@ -105,6 +108,30 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
105 108 SipUri uri = (SipUri) address.getURI();
106 109 String deviceId = uri.getUser();
107 110 Device device = deviceService.getDevice(deviceId);
  111 +
  112 + RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
  113 + userSetting.getSipUseSourceIpAsRemoteAddress());
  114 +
  115 + if (device != null &&
  116 + device.getSipTransactionInfo() != null &&
  117 + request.getCallIdHeader().getCallId().equals(device.getSipTransactionInfo().getCallId())) {
  118 + logger.info("[注册请求] 注册续订: {}", device.getDeviceId());
  119 + device.setExpires(request.getExpires().getExpires());
  120 + device.setIp(remoteAddressInfo.getIp());
  121 + device.setPort(remoteAddressInfo.getPort());
  122 + device.setHostAddress(remoteAddressInfo.getIp().concat(":").concat(String.valueOf(remoteAddressInfo.getPort())));
  123 + device.setLocalIp(request.getLocalAddress().getHostAddress());
  124 + Response registerOkResponse = getRegisterOkResponse(request);
  125 + // 判断TCP还是UDP
  126 + ViaHeader reqViaHeader = (ViaHeader) request.getHeader(ViaHeader.NAME);
  127 + String transport = reqViaHeader.getTransport();
  128 + device.setTransport("TCP".equalsIgnoreCase(transport) ? "TCP" : "UDP");
  129 + sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), registerOkResponse);
  130 + device.setRegisterTime(DateUtil.getNow());
  131 + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)registerOkResponse);
  132 + deviceService.online(device, sipTransactionInfo);
  133 + return;
  134 + }
108 135 String password = (device != null && !ObjectUtils.isEmpty(device.getPassword()))? device.getPassword() : sipConfig.getPassword();
109 136 AuthorizationHeader authHead = (AuthorizationHeader) request.getHeader(AuthorizationHeader.NAME);
110 137 if (authHead == null && !ObjectUtils.isEmpty(password)) {
... ... @@ -147,9 +174,6 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
147 174 // 添加Expires头
148 175 response.addHeader(request.getExpires());
149 176  
150   - RemoteAddressInfo remoteAddressInfo = SipUtils.getRemoteAddressFromRequest(request,
151   - userSetting.getSipUseSourceIpAsRemoteAddress());
152   -
153 177 if (device == null) {
154 178 device = new Device();
155 179 device.setStreamMode("UDP");
... ... @@ -182,13 +206,33 @@ public class RegisterRequestProcessor extends SIPRequestProcessorParent implemen
182 206 if (registerFlag) {
183 207 logger.info("[注册成功] deviceId: {}->{}", deviceId, requestAddress);
184 208 device.setRegisterTime(DateUtil.getNow());
185   - deviceService.online(device);
  209 + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo((SIPResponse)response);
  210 + deviceService.online(device, sipTransactionInfo);
186 211 } else {
187 212 logger.info("[注销成功] deviceId: {}->{}" ,deviceId, requestAddress);
188   - deviceService.offline(deviceId);
  213 + deviceService.offline(deviceId, "主动注销");
189 214 }
190 215 } catch (SipException | NoSuchAlgorithmException | ParseException e) {
191   - e.printStackTrace();
  216 + logger.error("未处理的异常 ", e);
192 217 }
193 218 }
  219 +
  220 + private Response getRegisterOkResponse(Request request) throws ParseException {
  221 + // 携带授权头并且密码正确
  222 + Response response = getMessageFactory().createResponse(Response.OK, request);
  223 + // 添加date头
  224 + SIPDateHeader dateHeader = new SIPDateHeader();
  225 + // 使用自己修改的
  226 + WvpSipDate wvpSipDate = new WvpSipDate(Calendar.getInstance(Locale.ENGLISH).getTimeInMillis());
  227 + dateHeader.setDate(wvpSipDate);
  228 + response.addHeader(dateHeader);
  229 +
  230 + // 添加Contact头
  231 + response.addHeader(request.getHeader(ContactHeader.NAME));
  232 + // 添加Expires头
  233 + response.addHeader(request.getExpires());
  234 +
  235 + return response;
  236 +
  237 + }
194 238 }
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/SubscribeRequestProcessor.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl;
2 2  
  3 +import com.genersoft.iot.vmp.common.VideoManagerConstants;
3 4 import com.genersoft.iot.vmp.conf.DynamicTask;
4 5 import com.genersoft.iot.vmp.conf.UserSetting;
5 6 import com.genersoft.iot.vmp.gb28181.bean.CmdType;
... ... @@ -8,14 +9,11 @@ import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
8 9 import com.genersoft.iot.vmp.gb28181.bean.SubscribeInfo;
9 10 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
10 11 import com.genersoft.iot.vmp.gb28181.transmit.SIPSender;
11   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
12   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
13 12 import com.genersoft.iot.vmp.gb28181.transmit.event.request.ISIPRequestProcessor;
14 13 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
15 14 import com.genersoft.iot.vmp.gb28181.utils.SipUtils;
16 15 import com.genersoft.iot.vmp.gb28181.utils.XmlUtil;
17 16 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
18   -import gov.nist.javax.sip.SipProviderImpl;
19 17 import gov.nist.javax.sip.message.SIPRequest;
20 18 import gov.nist.javax.sip.message.SIPResponse;
21 19 import org.dom4j.DocumentException;
... ... @@ -26,7 +24,9 @@ import org.springframework.beans.factory.InitializingBean;
26 24 import org.springframework.beans.factory.annotation.Autowired;
27 25 import org.springframework.stereotype.Component;
28 26  
29   -import javax.sip.*;
  27 +import javax.sip.InvalidArgumentException;
  28 +import javax.sip.RequestEvent;
  29 +import javax.sip.SipException;
30 30 import javax.sip.header.ExpiresHeader;
31 31 import javax.sip.message.Response;
32 32 import java.text.ParseException;
... ... @@ -93,7 +93,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
93 93 sipSender.transmitRequest(request.getLocalAddress().getHostAddress(), response);
94 94 }
95 95 } catch (ParseException | SipException | InvalidArgumentException | DocumentException e) {
96   - e.printStackTrace();
  96 + logger.error("未处理的异常 ", e);
97 97 }
98 98  
99 99 }
... ... @@ -146,7 +146,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
146 146 }
147 147  
148 148 } catch (SipException | InvalidArgumentException | ParseException e) {
149   - e.printStackTrace();
  149 + logger.error("未处理的异常 ", e);
150 150 }
151 151 }
152 152  
... ... @@ -192,7 +192,7 @@ public class SubscribeRequestProcessor extends SIPRequestProcessorParent impleme
192 192 subscribeHolder.putCatalogSubscribe(platformId, subscribeInfo);
193 193 }
194 194 } catch (SipException | InvalidArgumentException | ParseException e) {
195   - e.printStackTrace();
  195 + logger.error("未处理的异常 ", e);
196 196 }
197 197 }
198 198 }
... ...
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
73 73 String channelId = getText(rootElement, "DeviceID");
74 74 // 远程启动功能
75 75 if (!ObjectUtils.isEmpty(getText(rootElement, "TeleBoot"))) {
76   - if (parentPlatform.getServerGBId().equals(targetGBId)) {
77   - // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
78   - logger.info("执行远程启动本平台命令");
79   - try {
80   - cmderFroPlatform.unregister(parentPlatform, null, null);
81   - } catch (InvalidArgumentException | ParseException | SipException e) {
82   - logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
83   - }
84   - taskExecutor.execute(() -> {
85   - // 远程启动
86   -// try {
87   -// Thread.sleep(3000);
88   -// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
89   -// SipStackImpl stack = (SipStackImpl)up.getSipStack();
90   -// stack.stop();
91   -// Iterator listener = stack.getListeningPoints();
92   -// while (listener.hasNext()) {
93   -// stack.deleteListeningPoint((ListeningPoint) listener.next());
94   -// }
95   -// Iterator providers = stack.getSipProviders();
96   -// while (providers.hasNext()) {
97   -// stack.deleteSipProvider((SipProvider) providers.next());
98   -// }
99   -// VManageBootstrap.restart();
100   -// } catch (InterruptedException | ObjectInUseException e) {
101   -// logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
102   -// }
103   - });
104   - }
  76 + // TODO 拒绝远程启动命令
  77 + logger.warn("[国标级联]收到平台的远程启动命令, 不处理");
  78 +
  79 +// if (parentPlatform.getServerGBId().equals(targetGBId)) {
  80 +// // 远程启动本平台:需要在重新启动程序后先对SipStack解绑
  81 +// logger.info("执行远程启动本平台命令");
  82 +// try {
  83 +// cmderFroPlatform.unregister(parentPlatform, null, null);
  84 +// } catch (InvalidArgumentException | ParseException | SipException e) {
  85 +// logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
  86 +// }
  87 +// taskExecutor.execute(() -> {
  88 +// // 远程启动
  89 +//// try {
  90 +//// Thread.sleep(3000);
  91 +//// SipProvider up = (SipProvider) SpringBeanFactory.getBean("udpSipProvider");
  92 +//// SipStackImpl stack = (SipStackImpl)up.getSipStack();
  93 +//// stack.stop();
  94 +//// Iterator listener = stack.getListeningPoints();
  95 +//// while (listener.hasNext()) {
  96 +//// stack.deleteListeningPoint((ListeningPoint) listener.next());
  97 +//// }
  98 +//// Iterator providers = stack.getSipProviders();
  99 +//// while (providers.hasNext()) {
  100 +//// stack.deleteSipProvider((SipProvider) providers.next());
  101 +//// }
  102 +//// VManageBootstrap.restart();
  103 +//// } catch (InterruptedException | ObjectInUseException e) {
  104 +//// logger.error("[任务执行失败] 服务重启: {}", e.getMessage());
  105 +//// }
  106 +// });
  107 +// }
105 108 }
106 109 DeviceControlType deviceControlType = DeviceControlType.typeOf(rootElement);
107 110 logger.info("[接受deviceControl命令] 命令: {}", deviceControlType);
... ...
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
186 186 // 发送给平台的报警信息。 发送redis通知
187 187 logger.info("[发送给平台的报警信息]内容:{}", JSONObject.toJSONString(deviceAlarm));
188 188 AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
189   - alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
  189 + if (deviceAlarm.getAlarmMethod() != null) {
  190 + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
  191 + }
190 192 alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
191   - alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
  193 + if (deviceAlarm.getAlarmType() != null) {
  194 + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
  195 + }
192 196 alarmChannelMessage.setGbId(channelId);
193 197 redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
194 198 continue;
... ... @@ -204,6 +208,7 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
204 208 publisher.deviceAlarmEventPublish(deviceAlarm);
205 209 }
206 210 }catch (Exception e) {
  211 + logger.error("未处理的异常 ", e);
207 212 logger.warn("[收到报警通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
208 213 }
209 214 }
... ... @@ -264,12 +269,15 @@ public class AlarmNotifyMessageHandler extends SIPRequestProcessorParent impleme
264 269 if (channelId.equals(parentPlatform.getDeviceGBId())) {
265 270 // 发送给平台的报警信息。 发送redis通知
266 271 AlarmChannelMessage alarmChannelMessage = new AlarmChannelMessage();
267   - alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
  272 + if (deviceAlarm.getAlarmMethod() != null) {
  273 + alarmChannelMessage.setAlarmSn(Integer.parseInt(deviceAlarm.getAlarmMethod()));
  274 + }
268 275 alarmChannelMessage.setAlarmDescription(deviceAlarm.getAlarmDescription());
269 276 alarmChannelMessage.setGbId(channelId);
270   - alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
  277 + if (deviceAlarm.getAlarmType() != null) {
  278 + alarmChannelMessage.setAlarmType(Integer.parseInt(deviceAlarm.getAlarmType()));
  279 + }
271 280 redisCatchStorage.sendAlarmMsg(alarmChannelMessage);
272   - return;
273 281 }
274 282 }
275 283 }
... ...
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
88 88 // 对于已经离线的设备判断他的注册是否已经过期
89 89 if (!deviceService.expire(device)){
90 90 device.setOnline(0);
91   - deviceService.online(device);
  91 + deviceService.online(device, null);
92 92 }
93 93 }
94 94 // 刷新过期任务
95 95 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
96 96 // 如果三次心跳失败,则设置设备离线
97   - dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId()), device.getKeepaliveIntervalTime()*1000*3);
  97 + dynamicTask.startDelay(registerExpireTaskKey, ()-> deviceService.offline(device.getDeviceId(), "三次心跳失败"), device.getKeepaliveIntervalTime()*1000*3);
98 98  
99 99 }
100 100  
... ...
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
149 149 redisCatchStorage.sendMobilePositionMsg(jsonObject);
150 150  
151 151 } catch (DocumentException e) {
152   - e.printStackTrace();
  152 + logger.error("未处理的异常 ", e);
153 153 } catch (Exception e) {
154   - logger.warn("[移动位置通知] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
  154 + logger.warn("[移动位置通知] 发现未处理的异常, \r\n{}", evt.getRequest());
  155 + logger.error("[移动位置通知] 异常内容: ", e);
155 156 }
156 157 }
157 158 });
... ...
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
60 60 return;
61 61 }
62 62 String sn = rootElement.element("SN").getText();
  63 +
63 64 /*根据WVP原有的数据结构,设备和通道是分开放置,设备信息都是存放在设备表里,通道表里的设备信息不可作为真实信息处理
64 65 大部分NVR/IPC设备对他的通道信息实现都是返回默认的值没有什么参考价值。NVR/IPC通道我们统一使用设备表的设备信息来作为返回。
65 66 我们这里使用查询数据库的方式来实现这个设备信息查询的功能,在其他地方对设备信息更新达到正确的目的。*/
  67 +
66 68 String channelId = getText(rootElement, "DeviceID");
67   - Device device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
68   - if (device ==null){
69   - logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId);
70   - return;
  69 + // 查询这是通道id还是设备id
  70 + Device device = null;
  71 + // 如果id指向平台的国标编号,那么就是查询平台的信息
  72 + if (!parentPlatform.getDeviceGBId().equals(channelId)) {
  73 + device = storager.queryDeviceInfoByPlatformIdAndChannelId(parentPlatform.getServerGBId(), channelId);
  74 + if (device ==null){
  75 + logger.error("[平台没有该通道的使用权限]:platformId"+parentPlatform.getServerGBId()+" deviceID:"+channelId);
  76 + return;
  77 + }
71 78 }
72 79 try {
73   - cmderFroPlatform.deviceInfoResponse(parentPlatform,device, sn, fromHeader.getTag());
  80 + cmderFroPlatform.deviceInfoResponse(parentPlatform, device, sn, fromHeader.getTag());
74 81 } catch (SipException | InvalidArgumentException | ParseException e) {
75 82 logger.error("[命令发送失败] 国标级联 DeviceInfo查询回复: {}", e.getMessage());
76 83 }
... ...
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
132 132  
133 133 }
134 134 }catch (Exception e) {
135   - logger.warn("[收到通道] 发现未处理的异常, {}\r\n{}",e.getMessage(), evt.getRequest());
  135 + logger.warn("[收到通道] 发现未处理的异常, \r\n{}", evt.getRequest());
  136 + logger.error("[收到通道] 异常内容: ", e);
136 137 }
137 138 }
138 139 });
... ...
src/main/java/com/genersoft/iot/vmp/gb28181/transmit/event/request/impl/message/response/cmd/DeviceStatusResponseMessageHandler.java
1 1 package com.genersoft.iot.vmp.gb28181.transmit.event.request.impl.message.response.cmd;
2 2  
3 3 import com.alibaba.fastjson2.JSONObject;
4   -import com.genersoft.iot.vmp.common.VideoManagerConstants;
5 4 import com.genersoft.iot.vmp.gb28181.bean.Device;
6 5 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
7   -import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
8 6 import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
9 7 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
10 8 import com.genersoft.iot.vmp.gb28181.transmit.event.request.SIPRequestProcessorParent;
... ... @@ -26,7 +24,6 @@ import javax.sip.RequestEvent;
26 24 import javax.sip.SipException;
27 25 import javax.sip.message.Response;
28 26 import java.text.ParseException;
29   -import java.util.Objects;
30 27  
31 28 @Component
32 29 public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParent implements InitializingBean, IMessageHandler {
... ... @@ -74,9 +71,9 @@ public class DeviceStatusResponseMessageHandler extends SIPRequestProcessorParen
74 71 }
75 72 String text = onlineElement.getText();
76 73 if ("ONLINE".equalsIgnoreCase(text.trim())) {
77   - deviceService.online(device);
  74 + deviceService.online(device, null);
78 75 }else {
79   - deviceService.offline(device.getDeviceId());
  76 + deviceService.offline(device.getDeviceId(), "设备状态查询结果:" + text.trim());
80 77 }
81 78 RequestMessage msg = new RequestMessage();
82 79 msg.setKey(DeferredResultHolder.CALLBACK_CMD_DEVICESTATUS + device.getDeviceId());
... ...
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
142 142 }
143 143  
144 144 } catch (DocumentException e) {
145   - e.printStackTrace();
  145 + logger.error("未处理的异常 ", e);
146 146 }
147 147 }
148 148  
... ...
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
150 150 }
151 151 }
152 152 } catch (Exception e) {
153   - logger.error("[国标录像] 发现未处理的异常, "+e.getMessage(), e);
  153 + logger.error("[国标录像] 发现未处理的异常, \r\n{}", evt.getRequest());
  154 + logger.error("[国标录像] 异常内容: ", e);
154 155 }
155 156 });
156 157 }
... ... @@ -163,7 +164,11 @@ public class RecordInfoResponseMessageHandler extends SIPRequestProcessorParent
163 164 public void releaseRequest(String deviceId, String sn,RecordInfo recordInfo){
164 165 String key = DeferredResultHolder.CALLBACK_CMD_RECORDINFO + deviceId + sn;
165 166 // 对数据进行排序
166   - Collections.sort(recordInfo.getRecordList());
  167 + if(recordInfo!=null && recordInfo.getRecordList()!=null) {
  168 + Collections.sort(recordInfo.getRecordList());
  169 + }else{
  170 + recordInfo.setRecordList(new ArrayList<>());
  171 + }
167 172  
168 173 RequestMessage msg = new RequestMessage();
169 174 msg.setKey(key);
... ...
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;
2 2  
3 3 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
4 4 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
  5 +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
5 6 import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
6 7 import com.genersoft.iot.vmp.gb28181.transmit.SIPProcessorObserver;
7 8 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
... ... @@ -10,6 +11,7 @@ import com.genersoft.iot.vmp.service.IPlatformService;
10 11 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
11 12 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
12 13 import com.genersoft.iot.vmp.storager.dao.dto.PlatformRegisterInfo;
  14 +import gov.nist.javax.sip.message.SIPResponse;
13 15 import org.slf4j.Logger;
14 16 import org.slf4j.LoggerFactory;
15 17 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -18,7 +20,6 @@ import org.springframework.stereotype.Component;
18 20 import javax.sip.InvalidArgumentException;
19 21 import javax.sip.ResponseEvent;
20 22 import javax.sip.SipException;
21   -import javax.sip.header.CallIdHeader;
22 23 import javax.sip.header.WWWAuthenticateHeader;
23 24 import javax.sip.message.Response;
24 25 import java.text.ParseException;
... ... @@ -65,9 +66,8 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
65 66 */
66 67 @Override
67 68 public void process(ResponseEvent evt) {
68   - Response response = evt.getResponse();
69   - CallIdHeader callIdHeader = (CallIdHeader) response.getHeader(CallIdHeader.NAME);
70   - String callId = callIdHeader.getCallId();
  69 + SIPResponse response = (SIPResponse)evt.getResponse();
  70 + String callId = response.getCallIdHeader().getCallId();
71 71 PlatformRegisterInfo platformRegisterInfo = redisCatchStorage.queryPlatformRegisterInfo(callId);
72 72 if (platformRegisterInfo == null) {
73 73 logger.info(String.format("[国标级联]未找到callId: %s 的注册/注销平台id", callId ));
... ... @@ -90,15 +90,17 @@ public class RegisterResponseProcessor extends SIPResponseProcessorAbstract {
90 90  
91 91 if (response.getStatusCode() == Response.UNAUTHORIZED) {
92 92 WWWAuthenticateHeader www = (WWWAuthenticateHeader)response.getHeader(WWWAuthenticateHeader.NAME);
  93 + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
93 94 try {
94   - sipCommanderForPlatform.register(parentPlatform, callId, www, null, null, true, platformRegisterInfo.isRegister());
  95 + sipCommanderForPlatform.register(parentPlatform, sipTransactionInfo, www, null, null, true, platformRegisterInfo.isRegister());
95 96 } catch (SipException | InvalidArgumentException | ParseException e) {
96 97 logger.error("[命令发送失败] 国标级联 再次注册: {}", e.getMessage());
97 98 }
98 99 }else if (response.getStatusCode() == Response.OK){
99 100  
100 101 if (platformRegisterInfo.isRegister()) {
101   - platformService.online(parentPlatform);
  102 + SipTransactionInfo sipTransactionInfo = new SipTransactionInfo(response);
  103 + platformService.online(parentPlatform, sipTransactionInfo);
102 104 }else {
103 105 platformService.offline(parentPlatform, false);
104 106 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/AssistRESTfulUtils.java
... ... @@ -9,17 +9,12 @@ import org.jetbrains.annotations.NotNull;
9 9 import org.slf4j.Logger;
10 10 import org.slf4j.LoggerFactory;
11 11 import org.springframework.stereotype.Component;
12   -import org.springframework.util.ObjectUtils;
13   -import org.springframework.util.StringUtils;
14 12  
15   -import java.io.File;
16   -import java.io.FileOutputStream;
17 13 import java.io.IOException;
18 14 import java.net.ConnectException;
19 15 import java.util.HashMap;
20 16 import java.util.Map;
21 17 import java.util.Objects;
22   -import java.util.concurrent.TimeUnit;
23 18  
24 19 @Component
25 20 public class AssistRESTfulUtils {
... ... @@ -137,6 +132,11 @@ public class AssistRESTfulUtils {
137 132 return sendGet(mediaServerItem, "api/record/file/duration",param, callback);
138 133 }
139 134  
  135 + public JSONObject getInfo(MediaServerItem mediaServerItem, RequestCallback callback){
  136 + Map<String, Object> param = new HashMap<>();
  137 + return sendGet(mediaServerItem, "api/record/info",param, callback);
  138 + }
  139 +
140 140 public JSONObject addStreamCallInfo(MediaServerItem mediaServerItem, String app, String stream, String callId, RequestCallback callback){
141 141 Map<String, Object> param = new HashMap<>();
142 142 param.put("app",app);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMHttpHookListener.java
... ... @@ -119,10 +119,11 @@ public class ZLMHttpHookListener {
119 119 * 服务器定时上报时间,上报间隔可配置,默认10s上报一次
120 120 */
121 121 @ResponseBody
  122 +
122 123 @PostMapping(value = "/on_server_keepalive", produces = "application/json;charset=UTF-8")
123 124 public HookResult onServerKeepalive(@RequestBody OnServerKeepaliveHookParam param) {
124 125  
125   - logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
  126 +// logger.info("[ZLM HOOK] 收到zlm心跳:" + param.getMediaServerId());
126 127  
127 128 taskExecutor.execute(() -> {
128 129 List<ZlmHttpHookSubscribe.Event> subscribes = this.subscribe.getSubscribes(HookType.on_server_keepalive);
... ... @@ -142,6 +143,7 @@ public class ZLMHttpHookListener {
142 143 * 播放器鉴权事件,rtsp/rtmp/http-flv/ws-flv/hls的播放都将触发此鉴权事件。
143 144 */
144 145 @ResponseBody
  146 +
145 147 @PostMapping(value = "/on_play", produces = "application/json;charset=UTF-8")
146 148 public HookResult onPlay(@RequestBody OnPlayHookParam param) {
147 149 if (logger.isDebugEnabled()) {
... ... @@ -264,9 +266,28 @@ public class ZLMHttpHookListener {
264 266 }
265 267  
266 268 }
  269 + if (mediaInfo.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
  270 + logger.info("推流时发现尚未设置录像路径,从assist服务中读取");
  271 + JSONObject info = assistRESTfulUtils.getInfo(mediaInfo, null);
  272 + if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
  273 + JSONObject dataJson = info.getJSONObject("data");
  274 + if (dataJson != null) {
  275 + String recordPath = dataJson.getString("record");
  276 + userSetting.setRecordPath(recordPath);
  277 + result.setMp4_save_path(recordPath);
  278 + // 修改zlm中的录像路径
  279 + if (mediaInfo.isAutoConfig()) {
  280 + taskExecutor.execute(() -> {
  281 + mediaServerService.setZLMConfig(mediaInfo, false);
  282 + });
  283 + }
  284 + }
  285 + }
  286 + }
267 287 return result;
268 288 }
269 289  
  290 +
270 291 /**
271 292 * rtsp/rtmp流注册或注销时触发此事件;此事件对回复不敏感。
272 293 */
... ... @@ -293,8 +314,12 @@ public class ZLMHttpHookListener {
293 314 subscribe.response(mediaInfo, json);
294 315 }
295 316 }
296   - // 流消失移除redis play
  317 +
  318 + List<OnStreamChangedHookParam.MediaTrack> tracks = param.getTracks();
  319 + // TODO 重构此处逻辑
  320 +
297 321 if (param.isRegist()) {
  322 + // 处理流注册的鉴权信息
298 323 if (param.getOriginType() == OriginType.RTMP_PUSH.ordinal()
299 324 || param.getOriginType() == OriginType.RTSP_PUSH.ordinal()
300 325 || param.getOriginType() == OriginType.RTC_PUSH.ordinal()) {
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRESTfulUtils.java
... ... @@ -334,9 +334,9 @@ public class ZLMRESTfulUtils {
334 334 sendPost(mediaServerItem, "kick_sessions",param, null);
335 335 }
336 336  
337   - public void getSnap(MediaServerItem mediaServerItem, String flvUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
  337 + public void getSnap(MediaServerItem mediaServerItem, String streamUrl, int timeout_sec, int expire_sec, String targetPath, String fileName) {
338 338 Map<String, Object> param = new HashMap<>(3);
339   - param.put("url", flvUrl);
  339 + param.put("url", streamUrl);
340 340 param.put("timeout_sec", timeout_sec);
341 341 param.put("expire_sec", expire_sec);
342 342 sendGetForImg(mediaServerItem, "getSnap", param, targetPath, fileName);
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMRTPServerFactory.java
... ... @@ -262,8 +262,11 @@ public class ZLMRTPServerFactory {
262 262 logger.info("[保持端口] {}->监听端口到期继续保持监听", ssrc);
263 263 keepPort(serverItem, ssrc);
264 264 });
265   - }
266 265 logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
  266 + logger.info("[保持端口] {}->监听端口: {}", ssrc, localPort);
  267 + }else {
  268 + logger.info("[保持端口] 监听端口失败: {}", ssrc);
  269 + }
267 270 return localPort;
268 271 }
269 272  
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/ZLMServerConfig.java
... ... @@ -10,21 +10,87 @@ public class ZLMServerConfig {
10 10 @JSONField(name = "api.secret")
11 11 private String apiSecret;
12 12  
  13 + @JSONField(name = "api.snapRoot")
  14 + private String apiSnapRoot;
  15 +
  16 + @JSONField(name = "api.defaultSnap")
  17 + private String apiDefaultSnap;
  18 +
13 19 @JSONField(name = "ffmpeg.bin")
14 20 private String ffmpegBin;
15 21  
16 22 @JSONField(name = "ffmpeg.cmd")
17 23 private String ffmpegCmd;
18 24  
  25 + @JSONField(name = "ffmpeg.snap")
  26 + private String ffmpegSnap;
  27 +
19 28 @JSONField(name = "ffmpeg.log")
20 29 private String ffmpegLog;
21 30  
  31 + @JSONField(name = "ffmpeg.restart_sec")
  32 + private String ffmpegRestartSec;
  33 +
  34 + @JSONField(name = "protocol.modify_stamp")
  35 + private String protocolModifyStamp;
  36 +
  37 + @JSONField(name = "protocol.enable_audio")
  38 + private String protocolEnableAudio;
  39 +
  40 + @JSONField(name = "protocol.add_mute_audio")
  41 + private String protocolAddMuteAudio;
  42 +
  43 + @JSONField(name = "protocol.continue_push_ms")
  44 + private String protocolContinuePushMs;
  45 +
  46 + @JSONField(name = "protocol.enable_hls")
  47 + private String protocolEnableHls;
  48 +
  49 + @JSONField(name = "protocol.enable_mp4")
  50 + private String protocolEnableMp4;
  51 +
  52 + @JSONField(name = "protocol.enable_rtsp")
  53 + private String protocolEnableRtsp;
  54 +
  55 + @JSONField(name = "protocol.enable_rtmp")
  56 + private String protocolEnableRtmp;
  57 +
  58 + @JSONField(name = "protocol.enable_ts")
  59 + private String protocolEnableTs;
  60 +
  61 + @JSONField(name = "protocol.enable_fmp4")
  62 + private String protocolEnableFmp4;
  63 +
  64 + @JSONField(name = "protocol.mp4_as_player")
  65 + private String protocolMp4AsPlayer;
  66 +
  67 + @JSONField(name = "protocol.mp4_max_second")
  68 + private String protocolMp4MaxSecond;
  69 +
  70 + @JSONField(name = "protocol.mp4_save_path")
  71 + private String protocolMp4SavePath;
  72 +
  73 + @JSONField(name = "protocol.hls_save_path")
  74 + private String protocolHlsSavePath;
  75 +
  76 + @JSONField(name = "protocol.hls_demand")
  77 + private String protocolHlsDemand;
  78 +
  79 + @JSONField(name = "protocol.rtsp_demand")
  80 + private String protocolRtspDemand;
  81 +
  82 + @JSONField(name = "protocol.rtmp_demand")
  83 + private String protocolRtmpDemand;
  84 +
  85 + @JSONField(name = "protocol.ts_demand")
  86 + private String protocolTsDemand;
  87 +
  88 + @JSONField(name = "protocol.fmp4_demand")
  89 + private String protocolFmp4Demand;
  90 +
22 91 @JSONField(name = "general.enableVhost")
23 92 private String generalEnableVhost;
24 93  
25   - @JSONField(name = "general.mediaServerId")
26   - private String generalMediaServerId;
27   -
28 94 @JSONField(name = "general.flowThreshold")
29 95 private String generalFlowThreshold;
30 96  
... ... @@ -34,6 +100,25 @@ public class ZLMServerConfig {
34 100 @JSONField(name = "general.streamNoneReaderDelayMS")
35 101 private int generalStreamNoneReaderDelayMS;
36 102  
  103 + @JSONField(name = "general.resetWhenRePlay")
  104 + private String generalResetWhenRePlay;
  105 +
  106 + @JSONField(name = "general.mergeWriteMS")
  107 + private String generalMergeWriteMS;
  108 +
  109 + @JSONField(name = "general.mediaServerId")
  110 + private String generalMediaServerId;
  111 +
  112 + @JSONField(name = "general.wait_track_ready_ms")
  113 + private String generalWaitTrackReadyMs;
  114 +
  115 + @JSONField(name = "general.wait_add_track_ms")
  116 + private String generalWaitAddTrackMs;
  117 +
  118 + @JSONField(name = "general.unready_frame_cache")
  119 + private String generalUnreadyFrameCache;
  120 +
  121 +
37 122 @JSONField(name = "ip")
38 123 private String ip;
39 124  
... ... @@ -59,6 +144,18 @@ public class ZLMServerConfig {
59 144 @JSONField(name = "hls.segNum")
60 145 private String hlsSegNum;
61 146  
  147 + @JSONField(name = "hls.segRetain")
  148 + private String hlsSegRetain;
  149 +
  150 + @JSONField(name = "hls.broadcastRecordTs")
  151 + private String hlsBroadcastRecordTs;
  152 +
  153 + @JSONField(name = "hls.deleteDelaySec")
  154 + private String hlsDeleteDelaySec;
  155 +
  156 + @JSONField(name = "hls.segKeep")
  157 + private String hlsSegKeep;
  158 +
62 159 @JSONField(name = "hook.access_file_except_hls")
63 160 private String hookAccessFileExceptHLS;
64 161  
... ... @@ -104,6 +201,18 @@ public class ZLMServerConfig {
104 201 @JSONField(name = "hook.on_stream_not_found")
105 202 private String hookOnStreamNotFound;
106 203  
  204 + @JSONField(name = "hook.on_server_started")
  205 + private String hookOnServerStarted;
  206 +
  207 + @JSONField(name = "hook.on_server_keepalive")
  208 + private String hookOnServerKeepalive;
  209 +
  210 + @JSONField(name = "hook.on_send_rtp_stopped")
  211 + private String hookOnSendRtpStopped;
  212 +
  213 + @JSONField(name = "hook.on_rtp_server_timeout")
  214 + private String hookOnRtpServerTimeout;
  215 +
107 216 @JSONField(name = "hook.timeoutSec")
108 217 private String hookTimeoutSec;
109 218  
... ... @@ -813,4 +922,292 @@ public class ZLMServerConfig {
813 922 public void setPortRange(String portRange) {
814 923 this.portRange = portRange;
815 924 }
  925 +
  926 + public String getApiSnapRoot() {
  927 + return apiSnapRoot;
  928 + }
  929 +
  930 + public void setApiSnapRoot(String apiSnapRoot) {
  931 + this.apiSnapRoot = apiSnapRoot;
  932 + }
  933 +
  934 + public String getApiDefaultSnap() {
  935 + return apiDefaultSnap;
  936 + }
  937 +
  938 + public void setApiDefaultSnap(String apiDefaultSnap) {
  939 + this.apiDefaultSnap = apiDefaultSnap;
  940 + }
  941 +
  942 + public String getFfmpegSnap() {
  943 + return ffmpegSnap;
  944 + }
  945 +
  946 + public void setFfmpegSnap(String ffmpegSnap) {
  947 + this.ffmpegSnap = ffmpegSnap;
  948 + }
  949 +
  950 + public String getFfmpegRestartSec() {
  951 + return ffmpegRestartSec;
  952 + }
  953 +
  954 + public void setFfmpegRestartSec(String ffmpegRestartSec) {
  955 + this.ffmpegRestartSec = ffmpegRestartSec;
  956 + }
  957 +
  958 + public String getProtocolModifyStamp() {
  959 + return protocolModifyStamp;
  960 + }
  961 +
  962 + public void setProtocolModifyStamp(String protocolModifyStamp) {
  963 + this.protocolModifyStamp = protocolModifyStamp;
  964 + }
  965 +
  966 + public String getProtocolEnableAudio() {
  967 + return protocolEnableAudio;
  968 + }
  969 +
  970 + public void setProtocolEnableAudio(String protocolEnableAudio) {
  971 + this.protocolEnableAudio = protocolEnableAudio;
  972 + }
  973 +
  974 + public String getProtocolAddMuteAudio() {
  975 + return protocolAddMuteAudio;
  976 + }
  977 +
  978 + public void setProtocolAddMuteAudio(String protocolAddMuteAudio) {
  979 + this.protocolAddMuteAudio = protocolAddMuteAudio;
  980 + }
  981 +
  982 + public String getProtocolContinuePushMs() {
  983 + return protocolContinuePushMs;
  984 + }
  985 +
  986 + public void setProtocolContinuePushMs(String protocolContinuePushMs) {
  987 + this.protocolContinuePushMs = protocolContinuePushMs;
  988 + }
  989 +
  990 + public String getProtocolEnableHls() {
  991 + return protocolEnableHls;
  992 + }
  993 +
  994 + public void setProtocolEnableHls(String protocolEnableHls) {
  995 + this.protocolEnableHls = protocolEnableHls;
  996 + }
  997 +
  998 + public String getProtocolEnableMp4() {
  999 + return protocolEnableMp4;
  1000 + }
  1001 +
  1002 + public void setProtocolEnableMp4(String protocolEnableMp4) {
  1003 + this.protocolEnableMp4 = protocolEnableMp4;
  1004 + }
  1005 +
  1006 + public String getProtocolEnableRtsp() {
  1007 + return protocolEnableRtsp;
  1008 + }
  1009 +
  1010 + public void setProtocolEnableRtsp(String protocolEnableRtsp) {
  1011 + this.protocolEnableRtsp = protocolEnableRtsp;
  1012 + }
  1013 +
  1014 + public String getProtocolEnableRtmp() {
  1015 + return protocolEnableRtmp;
  1016 + }
  1017 +
  1018 + public void setProtocolEnableRtmp(String protocolEnableRtmp) {
  1019 + this.protocolEnableRtmp = protocolEnableRtmp;
  1020 + }
  1021 +
  1022 + public String getProtocolEnableTs() {
  1023 + return protocolEnableTs;
  1024 + }
  1025 +
  1026 + public void setProtocolEnableTs(String protocolEnableTs) {
  1027 + this.protocolEnableTs = protocolEnableTs;
  1028 + }
  1029 +
  1030 + public String getProtocolEnableFmp4() {
  1031 + return protocolEnableFmp4;
  1032 + }
  1033 +
  1034 + public void setProtocolEnableFmp4(String protocolEnableFmp4) {
  1035 + this.protocolEnableFmp4 = protocolEnableFmp4;
  1036 + }
  1037 +
  1038 + public String getProtocolMp4AsPlayer() {
  1039 + return protocolMp4AsPlayer;
  1040 + }
  1041 +
  1042 + public void setProtocolMp4AsPlayer(String protocolMp4AsPlayer) {
  1043 + this.protocolMp4AsPlayer = protocolMp4AsPlayer;
  1044 + }
  1045 +
  1046 + public String getProtocolMp4MaxSecond() {
  1047 + return protocolMp4MaxSecond;
  1048 + }
  1049 +
  1050 + public void setProtocolMp4MaxSecond(String protocolMp4MaxSecond) {
  1051 + this.protocolMp4MaxSecond = protocolMp4MaxSecond;
  1052 + }
  1053 +
  1054 + public String getProtocolMp4SavePath() {
  1055 + return protocolMp4SavePath;
  1056 + }
  1057 +
  1058 + public void setProtocolMp4SavePath(String protocolMp4SavePath) {
  1059 + this.protocolMp4SavePath = protocolMp4SavePath;
  1060 + }
  1061 +
  1062 + public String getProtocolHlsSavePath() {
  1063 + return protocolHlsSavePath;
  1064 + }
  1065 +
  1066 + public void setProtocolHlsSavePath(String protocolHlsSavePath) {
  1067 + this.protocolHlsSavePath = protocolHlsSavePath;
  1068 + }
  1069 +
  1070 + public String getProtocolHlsDemand() {
  1071 + return protocolHlsDemand;
  1072 + }
  1073 +
  1074 + public void setProtocolHlsDemand(String protocolHlsDemand) {
  1075 + this.protocolHlsDemand = protocolHlsDemand;
  1076 + }
  1077 +
  1078 + public String getProtocolRtspDemand() {
  1079 + return protocolRtspDemand;
  1080 + }
  1081 +
  1082 + public void setProtocolRtspDemand(String protocolRtspDemand) {
  1083 + this.protocolRtspDemand = protocolRtspDemand;
  1084 + }
  1085 +
  1086 + public String getProtocolRtmpDemand() {
  1087 + return protocolRtmpDemand;
  1088 + }
  1089 +
  1090 + public void setProtocolRtmpDemand(String protocolRtmpDemand) {
  1091 + this.protocolRtmpDemand = protocolRtmpDemand;
  1092 + }
  1093 +
  1094 + public String getProtocolTsDemand() {
  1095 + return protocolTsDemand;
  1096 + }
  1097 +
  1098 + public void setProtocolTsDemand(String protocolTsDemand) {
  1099 + this.protocolTsDemand = protocolTsDemand;
  1100 + }
  1101 +
  1102 + public String getProtocolFmp4Demand() {
  1103 + return protocolFmp4Demand;
  1104 + }
  1105 +
  1106 + public void setProtocolFmp4Demand(String protocolFmp4Demand) {
  1107 + this.protocolFmp4Demand = protocolFmp4Demand;
  1108 + }
  1109 +
  1110 + public String getGeneralResetWhenRePlay() {
  1111 + return generalResetWhenRePlay;
  1112 + }
  1113 +
  1114 + public void setGeneralResetWhenRePlay(String generalResetWhenRePlay) {
  1115 + this.generalResetWhenRePlay = generalResetWhenRePlay;
  1116 + }
  1117 +
  1118 + public String getGeneralMergeWriteMS() {
  1119 + return generalMergeWriteMS;
  1120 + }
  1121 +
  1122 + public void setGeneralMergeWriteMS(String generalMergeWriteMS) {
  1123 + this.generalMergeWriteMS = generalMergeWriteMS;
  1124 + }
  1125 +
  1126 + public String getGeneralWaitTrackReadyMs() {
  1127 + return generalWaitTrackReadyMs;
  1128 + }
  1129 +
  1130 + public void setGeneralWaitTrackReadyMs(String generalWaitTrackReadyMs) {
  1131 + this.generalWaitTrackReadyMs = generalWaitTrackReadyMs;
  1132 + }
  1133 +
  1134 + public String getGeneralWaitAddTrackMs() {
  1135 + return generalWaitAddTrackMs;
  1136 + }
  1137 +
  1138 + public void setGeneralWaitAddTrackMs(String generalWaitAddTrackMs) {
  1139 + this.generalWaitAddTrackMs = generalWaitAddTrackMs;
  1140 + }
  1141 +
  1142 + public String getGeneralUnreadyFrameCache() {
  1143 + return generalUnreadyFrameCache;
  1144 + }
  1145 +
  1146 + public void setGeneralUnreadyFrameCache(String generalUnreadyFrameCache) {
  1147 + this.generalUnreadyFrameCache = generalUnreadyFrameCache;
  1148 + }
  1149 +
  1150 + public String getHlsSegRetain() {
  1151 + return hlsSegRetain;
  1152 + }
  1153 +
  1154 + public void setHlsSegRetain(String hlsSegRetain) {
  1155 + this.hlsSegRetain = hlsSegRetain;
  1156 + }
  1157 +
  1158 + public String getHlsBroadcastRecordTs() {
  1159 + return hlsBroadcastRecordTs;
  1160 + }
  1161 +
  1162 + public void setHlsBroadcastRecordTs(String hlsBroadcastRecordTs) {
  1163 + this.hlsBroadcastRecordTs = hlsBroadcastRecordTs;
  1164 + }
  1165 +
  1166 + public String getHlsDeleteDelaySec() {
  1167 + return hlsDeleteDelaySec;
  1168 + }
  1169 +
  1170 + public void setHlsDeleteDelaySec(String hlsDeleteDelaySec) {
  1171 + this.hlsDeleteDelaySec = hlsDeleteDelaySec;
  1172 + }
  1173 +
  1174 + public String getHlsSegKeep() {
  1175 + return hlsSegKeep;
  1176 + }
  1177 +
  1178 + public void setHlsSegKeep(String hlsSegKeep) {
  1179 + this.hlsSegKeep = hlsSegKeep;
  1180 + }
  1181 +
  1182 + public String getHookOnServerStarted() {
  1183 + return hookOnServerStarted;
  1184 + }
  1185 +
  1186 + public void setHookOnServerStarted(String hookOnServerStarted) {
  1187 + this.hookOnServerStarted = hookOnServerStarted;
  1188 + }
  1189 +
  1190 + public String getHookOnServerKeepalive() {
  1191 + return hookOnServerKeepalive;
  1192 + }
  1193 +
  1194 + public void setHookOnServerKeepalive(String hookOnServerKeepalive) {
  1195 + this.hookOnServerKeepalive = hookOnServerKeepalive;
  1196 + }
  1197 +
  1198 + public String getHookOnSendRtpStopped() {
  1199 + return hookOnSendRtpStopped;
  1200 + }
  1201 +
  1202 + public void setHookOnSendRtpStopped(String hookOnSendRtpStopped) {
  1203 + this.hookOnSendRtpStopped = hookOnSendRtpStopped;
  1204 + }
  1205 +
  1206 + public String getHookOnRtpServerTimeout() {
  1207 + return hookOnRtpServerTimeout;
  1208 + }
  1209 +
  1210 + public void setHookOnRtpServerTimeout(String hookOnRtpServerTimeout) {
  1211 + this.hookOnRtpServerTimeout = hookOnRtpServerTimeout;
  1212 + }
816 1213 }
... ...
src/main/java/com/genersoft/iot/vmp/media/zlm/dto/hook/HookResultForOnPublish.java
... ... @@ -5,6 +5,7 @@ public class HookResultForOnPublish extends HookResult{
5 5 private boolean enable_audio;
6 6 private boolean enable_mp4;
7 7 private int mp4_max_second;
  8 + private String mp4_save_path;
8 9  
9 10 public HookResultForOnPublish() {
10 11 }
... ... @@ -41,4 +42,12 @@ public class HookResultForOnPublish extends HookResult{
41 42 public void setMp4_max_second(int mp4_max_second) {
42 43 this.mp4_max_second = mp4_max_second;
43 44 }
  45 +
  46 + public String getMp4_save_path() {
  47 + return mp4_save_path;
  48 + }
  49 +
  50 + public void setMp4_save_path(String mp4_save_path) {
  51 + this.mp4_save_path = mp4_save_path;
  52 + }
44 53 }
... ...
src/main/java/com/genersoft/iot/vmp/service/IDeviceService.java
... ... @@ -2,6 +2,7 @@ package com.genersoft.iot.vmp.service;
2 2  
3 3 import com.genersoft.iot.vmp.gb28181.bean.Device;
4 4 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
  5 +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
5 6 import com.genersoft.iot.vmp.gb28181.bean.SyncStatus;
6 7 import com.genersoft.iot.vmp.vmanager.bean.BaseTree;
7 8 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
... ... @@ -18,13 +19,13 @@ public interface IDeviceService {
18 19 * 设备上线
19 20 * @param device 设备信息
20 21 */
21   - void online(Device device);
  22 + void online(Device device, SipTransactionInfo sipTransactionInfo);
22 23  
23 24 /**
24 25 * 设备下线
25 26 * @param deviceId 设备编号
26 27 */
27   - void offline(String deviceId);
  28 + void offline(String deviceId, String reason);
28 29  
29 30 /**
30 31 * 添加目录订阅
... ...
src/main/java/com/genersoft/iot/vmp/service/IPlatformService.java
... ... @@ -6,6 +6,7 @@ import com.genersoft.iot.vmp.gb28181.event.SipSubscribe;
6 6 import com.genersoft.iot.vmp.media.zlm.ZlmHttpHookSubscribe;
7 7 import com.genersoft.iot.vmp.media.zlm.dto.MediaServerItem;
8 8 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
  9 +import com.genersoft.iot.vmp.gb28181.bean.SipTransactionInfo;
9 10 import com.github.pagehelper.PageInfo;
10 11  
11 12 import javax.sip.InvalidArgumentException;
... ... @@ -35,10 +36,16 @@ public interface IPlatformService {
35 36 boolean add(ParentPlatform parentPlatform);
36 37  
37 38 /**
  39 + * 添加级联平台
  40 + * @param parentPlatform 级联平台
  41 + */
  42 + boolean update(ParentPlatform parentPlatform);
  43 +
  44 + /**
38 45 * 平台上线
39 46 * @param parentPlatform 平台信息
40 47 */
41   - void online(ParentPlatform parentPlatform);
  48 + void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo);
42 49  
43 50 /**
44 51 * 平台离线
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/DeviceServiceImpl.java
... ... @@ -96,7 +96,7 @@ public class DeviceServiceImpl implements IDeviceService {
96 96 private ZLMRESTfulUtils zlmresTfulUtils;
97 97  
98 98 @Override
99   - public void online(Device device) {
  99 + public void online(Device device, SipTransactionInfo sipTransactionInfo) {
100 100 logger.info("[设备上线] deviceId:{}->{}:{}", device.getDeviceId(), device.getIp(), device.getPort());
101 101 Device deviceInRedis = redisCatchStorage.getDevice(device.getDeviceId());
102 102 Device deviceInDb = deviceMapper.getDeviceByDeviceId(device.getDeviceId());
... ... @@ -111,6 +111,14 @@ public class DeviceServiceImpl implements IDeviceService {
111 111 // 默认心跳间隔60
112 112 device.setKeepaliveIntervalTime(60);
113 113 }
  114 + if (sipTransactionInfo != null) {
  115 + device.setSipTransactionInfo(sipTransactionInfo);
  116 + }else {
  117 + if (deviceInRedis != null) {
  118 + device.setSipTransactionInfo(deviceInRedis.getSipTransactionInfo());
  119 + }
  120 + }
  121 +
114 122 // 第一次上线 或则设备之前是离线状态--进行通道同步和设备信息查询
115 123 if (device.getCreateTime() == null) {
116 124 device.setOnline(1);
... ... @@ -163,12 +171,12 @@ public class DeviceServiceImpl implements IDeviceService {
163 171 // 刷新过期任务
164 172 String registerExpireTaskKey = VideoManagerConstants.REGISTER_EXPIRE_TASK_KEY_PREFIX + device.getDeviceId();
165 173 // 如果第一次注册那么必须在60 * 3时间内收到一个心跳,否则设备离线
166   - dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId()), device.getKeepaliveIntervalTime() * 1000 * 3);
  174 + dynamicTask.startDelay(registerExpireTaskKey, ()-> offline(device.getDeviceId(), "首次注册后未能收到心跳"), device.getKeepaliveIntervalTime() * 1000 * 3);
167 175 }
168 176  
169 177 @Override
170   - public void offline(String deviceId) {
171   - logger.error("[设备离线], device:{}", deviceId);
  178 + public void offline(String deviceId, String reason) {
  179 + logger.error("[设备离线],{}, device:{}", reason, deviceId);
172 180 Device device = deviceMapper.getDeviceByDeviceId(deviceId);
173 181 if (device == null) {
174 182 return;
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/GbStreamServiceImpl.java
... ... @@ -41,6 +41,9 @@ public class GbStreamServiceImpl implements IGbStreamService {
41 41 private PlatformGbStreamMapper platformGbStreamMapper;
42 42  
43 43 @Autowired
  44 + private SubscribeHolder subscribeHolder;
  45 +
  46 + @Autowired
44 47 private ParentPlatformMapper platformMapper;
45 48  
46 49 @Autowired
... ... @@ -73,16 +76,23 @@ public class GbStreamServiceImpl implements IGbStreamService {
73 76 }
74 77 try {
75 78 List<DeviceChannel> deviceChannelList = new ArrayList<>();
76   - for (GbStream gbStream : gbStreams) {
  79 +
  80 +
  81 + for (int i = 0; i < gbStreams.size(); i++) {
  82 + GbStream gbStream = gbStreams.get(i);
77 83 gbStream.setCatalogId(catalogId);
78 84 gbStream.setPlatformId(platformId);
79 85 // TODO 修改为批量提交
80 86 platformGbStreamMapper.add(gbStream);
  87 + logger.info("[关联通道]直播流通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, gbStreams.size(), i + 1);
81 88 DeviceChannel deviceChannelListByStream = getDeviceChannelListByStreamWithStatus(gbStream, catalogId, parentPlatform);
82 89 deviceChannelList.add(deviceChannelListByStream);
83 90 }
84 91 dataSourceTransactionManager.commit(transactionStatus); //手动提交
85   - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
  92 + if (subscribeHolder.getCatalogSubscribe(platformId) != null) {
  93 + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
  94 + }
  95 +
86 96 result = true;
87 97 }catch (Exception e) {
88 98 logger.error("批量保存流与平台的关系时错误", e);
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/MediaServerServiceImpl.java
... ... @@ -11,6 +11,7 @@ import com.genersoft.iot.vmp.conf.exception.ControllerException;
11 11 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
12 12 import com.genersoft.iot.vmp.gb28181.session.SsrcConfig;
13 13 import com.genersoft.iot.vmp.gb28181.session.VideoStreamSessionManager;
  14 +import com.genersoft.iot.vmp.media.zlm.AssistRESTfulUtils;
14 15 import com.genersoft.iot.vmp.media.zlm.ZLMRESTfulUtils;
15 16 import com.genersoft.iot.vmp.media.zlm.ZLMRTPServerFactory;
16 17 import com.genersoft.iot.vmp.media.zlm.ZLMServerConfig;
... ... @@ -38,6 +39,7 @@ import org.springframework.transaction.TransactionDefinition;
38 39 import org.springframework.transaction.TransactionStatus;
39 40 import org.springframework.util.ObjectUtils;
40 41  
  42 +import java.io.File;
41 43 import java.time.LocalDateTime;
42 44 import java.util.*;
43 45  
... ... @@ -64,6 +66,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
64 66 private UserSetting userSetting;
65 67  
66 68 @Autowired
  69 + private AssistRESTfulUtils assistRESTfulUtils;
  70 +
  71 + @Autowired
67 72 private ZLMRESTfulUtils zlmresTfulUtils;
68 73  
69 74 @Autowired
... ... @@ -409,13 +414,27 @@ public class MediaServerServiceImpl implements IMediaServerService {
409 414 }
410 415 RedisUtil.set(key, serverItem);
411 416 resetOnlineServerItem(serverItem);
  417 +
  418 +
412 419 if (serverItem.isAutoConfig()) {
  420 + // 查看assist服务的录像路径配置
  421 + if (serverItem.getRecordAssistPort() > 0 && userSetting.getRecordPath() == null) {
  422 + JSONObject info = assistRESTfulUtils.getInfo(serverItem, null);
  423 + if (info != null && info.getInteger("code") != null && info.getInteger("code") == 0 ) {
  424 + JSONObject dataJson = info.getJSONObject("data");
  425 + if (dataJson != null) {
  426 + String recordPath = dataJson.getString("record");
  427 + userSetting.setRecordPath(recordPath);
  428 + }
  429 + }
  430 + }
413 431 setZLMConfig(serverItem, "0".equals(zlmServerConfig.getHookEnable()));
414 432 }
415 433 final String zlmKeepaliveKey = zlmKeepaliveKeyPrefix + serverItem.getId();
416 434 dynamicTask.stop(zlmKeepaliveKey);
417 435 dynamicTask.startDelay(zlmKeepaliveKey, new KeepAliveTimeoutRunnable(serverItem), (Math.getExponent(serverItem.getHookAliveInterval()) + 5) * 1000);
418 436 publisher.zlmOnlineEventPublish(serverItem.getId());
  437 +
419 438 logger.info("[ZLM] 连接成功 {} - {}:{} ",
420 439 zlmServerConfig.getGeneralMediaServerId(), zlmServerConfig.getIp(), zlmServerConfig.getHttpPort());
421 440 }
... ... @@ -549,6 +568,9 @@ public class MediaServerServiceImpl implements IMediaServerService {
549 568  
550 569 Map<String, Object> param = new HashMap<>();
551 570 param.put("api.secret",mediaServerItem.getSecret()); // -profile:v Baseline
  571 + if (mediaServerItem.getRtspPort() != 0) {
  572 + param.put("ffmpeg.snap", "%s -rtsp_transport tcp -i %s -y -f mjpeg -t 0.001 %s");
  573 + }
552 574 param.put("hook.enable","1");
553 575 param.put("hook.on_flow_report","");
554 576 param.put("hook.on_play",String.format("%s/on_play", hookPrex));
... ... @@ -583,6 +605,13 @@ public class MediaServerServiceImpl implements IMediaServerService {
583 605 param.put("rtp_proxy.port_range", mediaServerItem.getRtpPortRange().replace(",", "-"));
584 606 }
585 607  
  608 + if (userSetting.getRecordPath() != null) {
  609 + File recordPathFile = new File(userSetting.getRecordPath());
  610 + File mp4SavePathFile = recordPathFile.getParentFile().getAbsoluteFile();
  611 + param.put("protocol.mp4_save_path", mp4SavePathFile.getAbsoluteFile());
  612 + param.put("record.appName", recordPathFile.getName());
  613 + }
  614 +
586 615 JSONObject responseJSON = zlmresTfulUtils.setServerConfig(mediaServerItem, param);
587 616  
588 617 if (responseJSON != null && responseJSON.getInteger("code") == 0) {
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformChannelServiceImpl.java
1 1 package com.genersoft.iot.vmp.service.impl;
2 2  
3   -import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
4   -import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
5   -import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
6   -import com.genersoft.iot.vmp.gb28181.bean.TreeType;
  3 +import com.genersoft.iot.vmp.gb28181.bean.*;
7 4 import com.genersoft.iot.vmp.gb28181.event.EventPublisher;
8 5 import com.genersoft.iot.vmp.gb28181.event.subscribe.catalog.CatalogEvent;
9 6 import com.genersoft.iot.vmp.service.IPlatformChannelService;
... ... @@ -15,7 +12,10 @@ import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
15 12 import org.slf4j.Logger;
16 13 import org.slf4j.LoggerFactory;
17 14 import org.springframework.beans.factory.annotation.Autowired;
  15 +import org.springframework.jdbc.datasource.DataSourceTransactionManager;
18 16 import org.springframework.stereotype.Service;
  17 +import org.springframework.transaction.TransactionDefinition;
  18 +import org.springframework.transaction.TransactionStatus;
19 19 import org.springframework.util.ObjectUtils;
20 20  
21 21 import java.util.ArrayList;
... ... @@ -35,6 +35,16 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
35 35 private PlatformChannelMapper platformChannelMapper;
36 36  
37 37 @Autowired
  38 + TransactionDefinition transactionDefinition;
  39 +
  40 + @Autowired
  41 + DataSourceTransactionManager dataSourceTransactionManager;
  42 +
  43 + @Autowired
  44 + private SubscribeHolder subscribeHolder;
  45 +
  46 +
  47 + @Autowired
38 48 private DeviceChannelMapper deviceChannelMapper;
39 49  
40 50 @Autowired
... ... @@ -69,17 +79,47 @@ public class PlatformChannelServiceImpl implements IPlatformChannelService {
69 79 }
70 80 List<ChannelReduce> channelReducesToAdd = new ArrayList<>(deviceAndChannels.values());
71 81 // 对剩下的数据进行存储
72   - int result = 0;
  82 + int allCount = 0;
  83 + boolean result = false;
  84 + TransactionStatus transactionStatus = dataSourceTransactionManager.getTransaction(transactionDefinition);
  85 + int limitCount = 300;
73 86 if (channelReducesToAdd.size() > 0) {
74   - result = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
75   - // TODO 后续给平台增加控制开关以控制是否响应目录订阅
76   - List<DeviceChannel> deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
77   - if (deviceChannelList != null) {
78   - eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
  87 + if (channelReducesToAdd.size() > limitCount) {
  88 + for (int i = 0; i < channelReducesToAdd.size(); i += limitCount) {
  89 + int toIndex = i + limitCount;
  90 + if (i + limitCount > channelReducesToAdd.size()) {
  91 + toIndex = channelReducesToAdd.size();
  92 + }
  93 + int count = platformChannelMapper.addChannels(platformId, channelReducesToAdd.subList(i, toIndex));
  94 + result = result || count < 0;
  95 + allCount += count;
  96 + logger.info("[关联通道]国标通道 平台:{}, 共需关联通道数:{}, 已关联:{}", platformId, channelReducesToAdd.size(), toIndex);
  97 + }
  98 + }else {
  99 + allCount = platformChannelMapper.addChannels(platformId, channelReducesToAdd);
  100 + result = result || allCount < 0;
  101 + logger.info("[关联通道]国标通道 平台:{}, 关联通道数:{}", platformId, channelReducesToAdd.size());
79 102 }
80   - }
81 103  
82   - return result;
  104 + if (result) {
  105 + //事务回滚
  106 + dataSourceTransactionManager.rollback(transactionStatus);
  107 + allCount = 0;
  108 + }else {
  109 + logger.info("[关联通道]国标通道 平台:{}, 正在存入数据库", platformId);
  110 + dataSourceTransactionManager.commit(transactionStatus);
  111 +
  112 + }
  113 + SubscribeInfo catalogSubscribe = subscribeHolder.getCatalogSubscribe(platformId);
  114 + if (catalogSubscribe != null) {
  115 + List<DeviceChannel> deviceChannelList = getDeviceChannelListByChannelReduceList(channelReducesToAdd, catalogId, platform);
  116 + if (deviceChannelList != null) {
  117 + eventPublisher.catalogEventPublish(platformId, deviceChannelList, CatalogEvent.ADD);
  118 + }
  119 + }
  120 + logger.info("[关联通道]国标通道 平台:{}, 存入数据库成功", platformId);
  121 + }
  122 + return allCount;
83 123 }
84 124  
85 125 private List<DeviceChannel> getDeviceChannelListByChannelReduceList(List<ChannelReduce> channelReduces, String catalogId, ParentPlatform platform) {
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlatformServiceImpl.java
... ... @@ -21,8 +21,8 @@ import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
21 21 import com.genersoft.iot.vmp.service.bean.InviteTimeOutCallback;
22 22 import com.genersoft.iot.vmp.service.bean.SSRCInfo;
23 23 import com.genersoft.iot.vmp.storager.IRedisCatchStorage;
24   -import com.genersoft.iot.vmp.storager.dao.GbStreamMapper;
25   -import com.genersoft.iot.vmp.storager.dao.ParentPlatformMapper;
  24 +import com.genersoft.iot.vmp.storager.dao.*;
  25 +import com.genersoft.iot.vmp.utils.DateUtil;
26 26 import com.github.pagehelper.PageHelper;
27 27 import com.github.pagehelper.PageInfo;
28 28 import org.slf4j.Logger;
... ... @@ -54,6 +54,15 @@ public class PlatformServiceImpl implements IPlatformService {
54 54 private ParentPlatformMapper platformMapper;
55 55  
56 56 @Autowired
  57 + private PlatformCatalogMapper catalogMapper;
  58 +
  59 + @Autowired
  60 + private PlatformChannelMapper platformChannelMapper;
  61 +
  62 + @Autowired
  63 + private PlatformGbStreamMapper platformGbStreamMapper;
  64 +
  65 + @Autowired
57 66 private IRedisCatchStorage redisCatchStorage;
58 67  
59 68 @Autowired
... ... @@ -135,36 +144,106 @@ public class PlatformServiceImpl implements IPlatformService {
135 144 }
136 145  
137 146 @Override
138   - public void online(ParentPlatform parentPlatform) {
139   - logger.info("[国标级联]:{}, 平台上线/更新注册", parentPlatform.getServerGBId());
  147 + public boolean update(ParentPlatform parentPlatform) {
  148 + logger.info("[国标级联]更新平台 {}", parentPlatform.getDeviceGBId());
  149 + parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase());
  150 + ParentPlatform parentPlatformOld = platformMapper.getParentPlatById(parentPlatform.getId());
  151 + ParentPlatformCatch parentPlatformCatchOld = redisCatchStorage.queryPlatformCatchInfo(parentPlatformOld.getServerGBId());
  152 + parentPlatform.setUpdateTime(DateUtil.getNow());
  153 + if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) {
  154 + // 目录结构发生变化,清空之前的关联关系
  155 + logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId());
  156 + catalogMapper.delByPlatformId(parentPlatformOld.getServerGBId());
  157 + platformChannelMapper.delByPlatformId(parentPlatformOld.getServerGBId());
  158 + platformGbStreamMapper.delByPlatformId(parentPlatformOld.getServerGBId());
  159 + }
  160 +
  161 +
  162 + // 停止心跳定时
  163 + final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatformOld.getServerGBId();
  164 + dynamicTask.stop(keepaliveTaskKey);
  165 + // 停止注册定时
  166 + final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatformOld.getServerGBId();
  167 + dynamicTask.stop(registerTaskKey);
  168 + // 注销旧的
  169 + try {
  170 + if (parentPlatformOld.isStatus()) {
  171 + logger.info("保存平台{}时发现救平台在线,发送注销命令", parentPlatform.getDeviceGBId());
  172 + commanderForPlatform.unregister(parentPlatformOld, parentPlatformCatchOld.getSipTransactionInfo(), null, eventResult -> {
  173 + logger.info("[国标级联] 注销成功, 平台:{}", parentPlatformOld.getServerGBId());
  174 + });
  175 + }
  176 +
  177 + } catch (InvalidArgumentException | ParseException | SipException e) {
  178 + logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
  179 + }
  180 +
  181 + // 更新数据库
  182 + if (parentPlatform.getCatalogGroup() == 0) {
  183 + parentPlatform.setCatalogGroup(1);
  184 + }
  185 + if (parentPlatform.getAdministrativeDivision() == null) {
  186 + parentPlatform.setAdministrativeDivision(parentPlatform.getAdministrativeDivision());
  187 + }
  188 +
  189 + platformMapper.updateParentPlatform(parentPlatform);
  190 + // 更新redis
  191 + redisCatchStorage.delPlatformCatchInfo(parentPlatformOld.getServerGBId());
  192 + ParentPlatformCatch parentPlatformCatch = new ParentPlatformCatch();
  193 + parentPlatformCatch.setParentPlatform(parentPlatform);
  194 + parentPlatformCatch.setId(parentPlatform.getServerGBId());
  195 + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
  196 + // 注册
  197 + if (parentPlatform.isEnable()) {
  198 + // 保存时启用就发送注册
  199 + // 注册成功时由程序直接调用了online方法
  200 + try {
  201 + commanderForPlatform.register(parentPlatform, eventResult -> {
  202 + logger.info("[国标级联] {},添加向上级注册失败,请确定上级平台可用时重新保存", parentPlatform.getServerGBId());
  203 + }, null);
  204 + } catch (InvalidArgumentException | ParseException | SipException e) {
  205 + logger.error("[命令发送失败] 国标级联: {}", e.getMessage());
  206 + }
  207 + }
  208 + // 重新开启定时注册, 使用续订消息
  209 + // 重新开始心跳保活
  210 +
  211 +
  212 + return false;
  213 + }
  214 +
  215 +
  216 + @Override
  217 + public void online(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo) {
  218 + logger.info("[国标级联]:{}, 平台上线", parentPlatform.getServerGBId());
140 219 platformMapper.updateParentPlatformStatus(parentPlatform.getServerGBId(), true);
141 220 ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(parentPlatform.getServerGBId());
142   - if (parentPlatformCatch != null) {
143   - parentPlatformCatch.getParentPlatform().setStatus(true);
144   - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
145   - }else {
  221 + if (parentPlatformCatch == null) {
146 222 parentPlatformCatch = new ParentPlatformCatch();
147 223 parentPlatformCatch.setParentPlatform(parentPlatform);
148 224 parentPlatformCatch.setId(parentPlatform.getServerGBId());
149 225 parentPlatform.setStatus(true);
150 226 parentPlatformCatch.setParentPlatform(parentPlatform);
151   - redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
152 227 }
153 228  
  229 + parentPlatformCatch.getParentPlatform().setStatus(true);
  230 + parentPlatformCatch.setSipTransactionInfo(sipTransactionInfo);
  231 + redisCatchStorage.updatePlatformCatchInfo(parentPlatformCatch);
  232 +
154 233 final String registerTaskKey = REGISTER_KEY_PREFIX + parentPlatform.getServerGBId();
155 234 if (!dynamicTask.isAlive(registerTaskKey)) {
  235 + logger.info("[国标级联]:{}, 添加定时注册任务", parentPlatform.getServerGBId());
156 236 // 添加注册任务
157 237 dynamicTask.startCron(registerTaskKey,
158 238 // 注册失败(注册成功时由程序直接调用了online方法)
159   - ()-> {
160   - registerTask(parentPlatform);
161   - },
162   - (parentPlatform.getExpires() - 10) *1000);
  239 + ()-> registerTask(parentPlatform, sipTransactionInfo),
  240 + parentPlatform.getExpires() * 1000);
163 241 }
164 242  
165 243  
166 244 final String keepaliveTaskKey = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId();
167 245 if (!dynamicTask.contains(keepaliveTaskKey)) {
  246 + logger.info("[国标级联]:{}, 添加定时心跳任务", parentPlatform.getServerGBId());
168 247 // 添加心跳任务
169 248 dynamicTask.startCron(keepaliveTaskKey,
170 249 ()-> {
... ... @@ -205,11 +284,11 @@ public class PlatformServiceImpl implements IPlatformService {
205 284 logger.error("[命令发送失败] 国标级联 发送心跳: {}", e.getMessage());
206 285 }
207 286 },
208   - (parentPlatform.getKeepTimeout() - 10)*1000);
  287 + (parentPlatform.getKeepTimeout())*1000);
209 288 }
210 289 }
211 290  
212   - private void registerTask(ParentPlatform parentPlatform){
  291 + private void registerTask(ParentPlatform parentPlatform, SipTransactionInfo sipTransactionInfo){
213 292 try {
214 293 // 设置超时重发, 后续从底层支持消息重发
215 294 String key = KEEPALIVE_KEY_PREFIX + parentPlatform.getServerGBId() + "_timeout";
... ... @@ -217,10 +296,10 @@ public class PlatformServiceImpl implements IPlatformService {
217 296 return;
218 297 }
219 298 dynamicTask.startDelay(key, ()->{
220   - registerTask(parentPlatform);
  299 + registerTask(parentPlatform, sipTransactionInfo);
221 300 }, 1000);
222   - logger.info("[国标级联] 平台:{}注册即将到期,重新注册", parentPlatform.getServerGBId());
223   - commanderForPlatform.register(parentPlatform, eventResult -> {
  301 + logger.info("[国标级联] 平台:{}注册即将到期,开始续订", parentPlatform.getServerGBId());
  302 + commanderForPlatform.register(parentPlatform, sipTransactionInfo, eventResult -> {
224 303 dynamicTask.stop(key);
225 304 offline(parentPlatform, false);
226 305 },eventResult -> {
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/PlayServiceImpl.java
... ... @@ -438,7 +438,12 @@ public class PlayServiceImpl implements IPlayService {
438 438 onPublishHandlerForPlay(mediaServerItemInuse, response, device.getDeviceId(), channelId);
439 439 hookEvent.response(mediaServerItemInuse, response);
440 440 logger.info("[点播成功] deviceId: {}, channelId: {}", device.getDeviceId(), channelId);
441   - String streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.flv", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());
  441 + String streamUrl;
  442 + if (mediaServerItemInuse.getRtspPort() != 0) {
  443 + streamUrl = String.format("rtsp://127.0.0.1:%s/%s/%s", mediaServerItemInuse.getRtspPort(), "rtp", ssrcInfo.getStream());
  444 + }else {
  445 + streamUrl = String.format("http://127.0.0.1:%s/%s/%s.live.mp4", mediaServerItemInuse.getHttpPort(), "rtp", ssrcInfo.getStream());
  446 + }
442 447 String path = "snap";
443 448 String fileName = device.getDeviceId() + "_" + channelId + ".jpg";
444 449 // 请求截图
... ... @@ -806,23 +811,75 @@ public class PlayServiceImpl implements IPlayService {
806 811 hookCallBack.call(downloadResult);
807 812 streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
808 813 };
809   -
  814 + InviteStreamCallback hookEvent = (InviteStreamInfo inviteStreamInfo) -> {
  815 + logger.info("收到订阅消息: " + inviteStreamInfo.getCallId());
  816 + dynamicTask.stop(downLoadTimeOutTaskKey);
  817 + StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
  818 + streamInfo.setStartTime(startTime);
  819 + streamInfo.setEndTime(endTime);
  820 + redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
  821 + downloadResult.setCode(ErrorCode.SUCCESS.getCode());
  822 + downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
  823 + downloadResult.setData(streamInfo);
  824 + downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
  825 + downloadResult.setResponse(inviteStreamInfo.getResponse());
  826 + hookCallBack.call(downloadResult);
  827 + };
810 828 try {
811 829 cmder.downloadStreamCmd(mediaServerItem, ssrcInfo, device, channelId, startTime, endTime, downloadSpeed, infoCallBack,
812   - inviteStreamInfo -> {
813   - logger.info("收到订阅消息: " + inviteStreamInfo.getResponse().toJSONString());
814   - dynamicTask.stop(downLoadTimeOutTaskKey);
815   - StreamInfo streamInfo = onPublishHandler(inviteStreamInfo.getMediaServerItem(), inviteStreamInfo.getResponse(), deviceId, channelId);
816   - streamInfo.setStartTime(startTime);
817   - streamInfo.setEndTime(endTime);
818   - redisCatchStorage.startDownload(streamInfo, inviteStreamInfo.getCallId());
819   - downloadResult.setCode(ErrorCode.SUCCESS.getCode());
820   - downloadResult.setMsg(ErrorCode.SUCCESS.getMsg());
821   - downloadResult.setData(streamInfo);
822   - downloadResult.setMediaServerItem(inviteStreamInfo.getMediaServerItem());
823   - downloadResult.setResponse(inviteStreamInfo.getResponse());
824   - hookCallBack.call(downloadResult);
825   - }, errorEvent);
  830 + hookEvent, errorEvent, eventResult ->
  831 + {
  832 + if (eventResult.type == SipSubscribe.EventResultType.response) {
  833 + ResponseEvent responseEvent = (ResponseEvent) eventResult.event;
  834 + String contentString = new String(responseEvent.getResponse().getRawContent());
  835 + // 获取ssrc
  836 + int ssrcIndex = contentString.indexOf("y=");
  837 + // 检查是否有y字段
  838 + if (ssrcIndex >= 0) {
  839 + //ssrc规定长度为10字节,不取余下长度以避免后续还有“f=”字段 TODO 后续对不规范的非10位ssrc兼容
  840 + String ssrcInResponse = contentString.substring(ssrcIndex + 2, ssrcIndex + 12);
  841 + // 查询到ssrc不一致且开启了ssrc校验则需要针对处理
  842 + if (ssrcInfo.getSsrc().equals(ssrcInResponse)) {
  843 + return;
  844 + }
  845 + logger.info("[回放消息] 收到invite 200, 发现下级自定义了ssrc: {}", ssrcInResponse);
  846 + if (!mediaServerItem.isRtpEnable() || device.isSsrcCheck()) {
  847 + logger.info("[回放消息] SSRC修正 {}->{}", ssrcInfo.getSsrc(), ssrcInResponse);
  848 +
  849 + if (!mediaServerItem.getSsrcConfig().checkSsrc(ssrcInResponse)) {
  850 + // ssrc 不可用
  851 + // 释放ssrc
  852 + mediaServerService.releaseSsrc(mediaServerItem.getId(), ssrcInfo.getSsrc());
  853 + streamSession.remove(device.getDeviceId(), channelId, ssrcInfo.getStream());
  854 + eventResult.msg = "下级自定义了ssrc,但是此ssrc不可用";
  855 + eventResult.statusCode = 400;
  856 + errorEvent.response(eventResult);
  857 + return;
  858 + }
  859 +
  860 + // 单端口模式streamId也有变化,需要重新设置监听
  861 + if (!mediaServerItem.isRtpEnable()) {
  862 + // 添加订阅
  863 + HookSubscribeForStreamChange hookSubscribe = HookSubscribeFactory.on_stream_changed("rtp", ssrcInfo.getStream(), true, "rtsp", mediaServerItem.getId());
  864 + subscribe.removeSubscribe(hookSubscribe);
  865 + hookSubscribe.getContent().put("stream", String.format("%08x", Integer.parseInt(ssrcInResponse)).toUpperCase());
  866 + subscribe.addSubscribe(hookSubscribe, (MediaServerItem mediaServerItemInUse, JSONObject response) -> {
  867 + logger.info("[ZLM HOOK] ssrc修正后收到订阅消息: " + response.toJSONString());
  868 + dynamicTask.stop(downLoadTimeOutTaskKey);
  869 + // hook响应
  870 + onPublishHandlerForPlayback(mediaServerItemInUse, response, device.getDeviceId(), channelId, hookCallBack);
  871 + hookEvent.call(new InviteStreamInfo(mediaServerItem, null, eventResult.callId, "rtp", ssrcInfo.getStream()));
  872 + });
  873 + }
  874 + // 关闭rtp server
  875 + mediaServerService.closeRTPServer(mediaServerItem, ssrcInfo.getStream());
  876 + // 重新开启ssrc server
  877 + mediaServerService.openRTPServer(mediaServerItem, ssrcInfo.getStream(), ssrcInResponse, device.isSsrcCheck(), true, ssrcInfo.getPort());
  878 + }
  879 + }
  880 + }
  881 +
  882 + });
826 883 } catch (InvalidArgumentException | SipException | ParseException e) {
827 884 logger.error("[命令发送失败] 录像下载: {}", e.getMessage());
828 885  
... ...
src/main/java/com/genersoft/iot/vmp/service/impl/StreamProxyServiceImpl.java
... ... @@ -201,7 +201,7 @@ public class StreamProxyServiceImpl implements IStreamProxyService {
201 201 dataSourceTransactionManager.commit(transactionStatus); //手动提交
202 202 result = true;
203 203 }catch (Exception e) {
204   - e.printStackTrace();
  204 + logger.error("未处理的异常 ", e);
205 205 dataSourceTransactionManager.rollback(transactionStatus);
206 206 }
207 207 return result;
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisAlarmMsgListener.java
1 1 package com.genersoft.iot.vmp.service.redisMsg;
2 2  
3 3 import com.alibaba.fastjson2.JSON;
  4 +import com.genersoft.iot.vmp.conf.UserSetting;
4 5 import com.genersoft.iot.vmp.gb28181.bean.*;
5 6 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
6 7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
... ... @@ -44,13 +45,17 @@ public class RedisAlarmMsgListener implements MessageListener {
44 45 @Autowired
45 46 private ThreadPoolTaskExecutor taskExecutor;
46 47  
  48 + @Autowired
  49 + private UserSetting userSetting;
  50 +
47 51 @Override
48 52 public void onMessage(@NotNull Message message, byte[] bytes) {
  53 + // 消息示例: PUBLISH alarm_receive '{ "gbId": "", "alarmSn": 1, "alarmType": "111", "alarmDescription": "222", }'
49 54 logger.info("收到来自REDIS的ALARM通知: {}", new String(message.getBody()));
50 55 boolean isEmpty = taskQueue.isEmpty();
51 56 taskQueue.offer(message);
52 57 if (isEmpty) {
53   - logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize());
  58 +// logger.info("[线程池信息]活动线程数:{}, 最大线程数: {}", taskExecutor.getActiveCount(), taskExecutor.getMaxPoolSize());
54 59 taskExecutor.execute(() -> {
55 60 while (!taskQueue.isEmpty()) {
56 61 Message msg = taskQueue.poll();
... ... @@ -69,22 +74,52 @@ public class RedisAlarmMsgListener implements MessageListener {
69 74 deviceAlarm.setAlarmMethod("" + alarmChannelMessage.getAlarmSn());
70 75 deviceAlarm.setAlarmType("" + alarmChannelMessage.getAlarmType());
71 76 deviceAlarm.setAlarmPriority("1");
72   - deviceAlarm.setAlarmTime(DateUtil.getNowForISO8601());
  77 + deviceAlarm.setAlarmTime(DateUtil.getNow());
73 78 deviceAlarm.setLongitude(0);
74 79 deviceAlarm.setLatitude(0);
75 80  
76 81 if (ObjectUtils.isEmpty(gbId)) {
77   - // 发送给所有的上级
78   - List<ParentPlatform> parentPlatforms = storage.queryEnableParentPlatformList(true);
79   - if (parentPlatforms.size() > 0) {
80   - for (ParentPlatform parentPlatform : parentPlatforms) {
  82 + if (userSetting.getSendToPlatformsWhenIdLost()) {
  83 + // 发送给所有的上级
  84 + List<ParentPlatform> parentPlatforms = storage.queryEnableParentPlatformList(true);
  85 + if (parentPlatforms.size() > 0) {
  86 + for (ParentPlatform parentPlatform : parentPlatforms) {
  87 + try {
  88 + deviceAlarm.setChannelId(parentPlatform.getDeviceGBId());
  89 + commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
  90 + } catch (SipException | InvalidArgumentException | ParseException e) {
  91 + logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
  92 + }
  93 + }
  94 + }
  95 + }else {
  96 + // 获取开启了消息推送的设备和平台
  97 + List<ParentPlatform> parentPlatforms = storage.queryEnablePlatformListWithAsMessageChannel();
  98 + if (parentPlatforms.size() > 0) {
  99 + for (ParentPlatform parentPlatform : parentPlatforms) {
  100 + try {
  101 + deviceAlarm.setChannelId(parentPlatform.getDeviceGBId());
  102 + commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
  103 + } catch (SipException | InvalidArgumentException | ParseException e) {
  104 + logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
  105 + }
  106 + }
  107 + }
  108 +
  109 + }
  110 + // 获取开启了消息推送的设备和平台
  111 + List<Device> devices = storage.queryDeviceWithAsMessageChannel();
  112 + if (devices.size() > 0) {
  113 + for (Device device : devices) {
81 114 try {
82   - commanderForPlatform.sendAlarmMessage(parentPlatform, deviceAlarm);
83   - } catch (SipException | InvalidArgumentException | ParseException e) {
84   - logger.error("[命令发送失败] 国标级联 发送报警: {}", e.getMessage());
  115 + deviceAlarm.setChannelId(device.getDeviceId());
  116 + commander.sendAlarmMessage(device, deviceAlarm);
  117 + } catch (InvalidArgumentException | SipException | ParseException e) {
  118 + logger.error("[命令发送失败] 发送报警: {}", e.getMessage());
85 119 }
86 120 }
87 121 }
  122 +
88 123 }else {
89 124 Device device = storage.queryVideoDevice(gbId);
90 125 ParentPlatform platform = storage.queryParentPlatByServerGBId(gbId);
... ... @@ -105,6 +140,7 @@ public class RedisAlarmMsgListener implements MessageListener {
105 140 }
106 141 }
107 142 }catch (Exception e) {
  143 + logger.error("未处理的异常 ", e);
108 144 logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
109 145 }
110 146 }
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGbPlayMsgListener.java
... ... @@ -202,7 +202,8 @@ public class RedisGbPlayMsgListener implements MessageListener {
202 202  
203 203 }
204 204 }catch (Exception e) {
205   - logger.warn("[RedisGbPlayMsg] 发现未处理的异常, {}",e.getMessage());
  205 + logger.warn("[RedisGbPlayMsg] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  206 + logger.error("[RedisGbPlayMsg] 异常内容: ", e);
206 207 }
207 208 }
208 209 });
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisGpsMsgListener.java
... ... @@ -53,7 +53,8 @@ public class RedisGpsMsgListener implements MessageListener {
53 53 // 只是放入redis缓存起来
54 54 redisCatchStorage.updateGpsMsgInfo(gpsMsgInfo);
55 55 }catch (Exception e) {
56   - logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
  56 + logger.warn("[REDIS的ALARM通知] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  57 + logger.error("[REDIS的ALARM通知] 异常内容: ", e);
57 58 }
58 59 }
59 60 });
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamResponseListener.java
... ... @@ -58,7 +58,8 @@ public class RedisPushStreamResponseListener implements MessageListener {
58 58 responseEvents.get(response.getApp() + response.getStream()).run(response);
59 59 }
60 60 }catch (Exception e) {
61   - logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
  61 + logger.warn("[REDIS消息-请求推流结果] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  62 + logger.error("[REDIS消息-请求推流结果] 异常内容: ", e);
62 63 }
63 64 }
64 65 });
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusListMsgListener.java
... ... @@ -95,7 +95,8 @@ public class RedisPushStreamStatusListMsgListener implements MessageListener {
95 95 gbStreamService.updateGbIdOrName(streamPushItemForUpdate);
96 96 }
97 97 }catch (Exception e) {
98   - logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
  98 + logger.warn("[REDIS消息-推流设备列表更新] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  99 + logger.error("[REDIS消息-推流设备列表更新] 异常内容: ", e);
99 100 }
100 101 }
101 102 });
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisPushStreamStatusMsgListener.java
... ... @@ -79,7 +79,8 @@ public class RedisPushStreamStatusMsgListener implements MessageListener, Applic
79 79 streamPushService.online(statusChangeFromPushStream.getOnlineStreams());
80 80 }
81 81 }catch (Exception e) {
82   - logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
  82 + logger.warn("[REDIS消息-推流设备状态变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  83 + logger.error("[REDIS消息-推流设备状态变化] 异常内容: ", e);
83 84 }
84 85 }
85 86 });
... ...
src/main/java/com/genersoft/iot/vmp/service/redisMsg/RedisStreamMsgListener.java
... ... @@ -82,7 +82,8 @@ public class RedisStreamMsgListener implements MessageListener {
82 82 zlmMediaListManager.removeMedia(app, stream);
83 83 }
84 84 }catch (Exception e) {
85   - logger.warn("[REDIS的ALARM通知] 发现未处理的异常, {}",e.getMessage());
  85 + logger.warn("[REDIS消息-流变化] 发现未处理的异常, \r\n{}", JSON.toJSONString(message));
  86 + logger.error("[REDIS消息-流变化] 异常内容: ", e);
86 87 }
87 88 }
88 89 });
... ...
src/main/java/com/genersoft/iot/vmp/storager/IVideoManagerStorage.java
... ... @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.media.zlm.dto.StreamProxyItem;
5 5 import com.genersoft.iot.vmp.service.bean.GPSMsgInfo;
6 6 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
7 7 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
  8 +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
8 9 import com.github.pagehelper.PageInfo;
9 10  
10 11 import java.util.List;
... ... @@ -58,7 +59,7 @@ public interface IVideoManagerStorage {
58 59 */
59 60 public PageInfo<DeviceChannel> queryChannelsByDeviceId(String deviceId, String query, Boolean hasSubChannel, Boolean online, Boolean catalogUnderDevice, int page, int count);
60 61  
61   - public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds);
  62 + public List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List<String> channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit);
62 63  
63 64  
64 65 /**
... ... @@ -374,4 +375,10 @@ public interface IVideoManagerStorage {
374 375 void cleanContentForPlatform(String serverGBId);
375 376  
376 377 List<DeviceChannel> queryChannelWithCatalog(String serverGBId);
  378 +
  379 + List<DeviceChannelExtend> queryChannelsByDeviceId(String serial, List<String> channelIds, Boolean online);
  380 +
  381 + List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel();
  382 +
  383 + List<Device> queryDeviceWithAsMessageChannel();
377 384 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceChannelMapper.java
... ... @@ -5,6 +5,7 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceChannel;
5 5 import com.genersoft.iot.vmp.gb28181.bean.DeviceChannelInPlatform;
6 6 import com.genersoft.iot.vmp.vmanager.bean.ResourceBaceInfo;
7 7 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
  8 +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
8 9 import org.apache.ibatis.annotations.*;
9 10 import org.springframework.stereotype.Repository;
10 11  
... ... @@ -82,7 +83,56 @@ public interface DeviceChannelMapper {
82 83 "</foreach> </if>" +
83 84 "ORDER BY dc.channelId " +
84 85 " </script>"})
85   - List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online,List<String> channelIds);
  86 + List<DeviceChannel> queryChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List<String> channelIds);
  87 +
  88 + @Select(value = {" <script>" +
  89 + "SELECT " +
  90 + "dc.*, " +
  91 + "de.name as deviceName, " +
  92 + "de.online as deviceOnline " +
  93 + "from " +
  94 + "device_channel dc " +
  95 + "LEFT JOIN device de ON dc.deviceId = de.deviceId " +
  96 + "WHERE 1=1" +
  97 + " <if test='deviceId != null'> AND dc.deviceId = #{deviceId} </if> " +
  98 + " <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
  99 + " <if test='parentChannelId != null'> AND dc.parentId=#{parentChannelId} </if> " +
  100 + " <if test='online == true' > AND dc.status=1</if>" +
  101 + " <if test='online == false' > AND dc.status=0</if>" +
  102 + " <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" +
  103 + " <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" +
  104 + "<if test='channelIds != null'> AND dc.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
  105 + "#{item} " +
  106 + "</foreach> </if>" +
  107 + "ORDER BY dc.channelId ASC" +
  108 + " </script>"})
  109 + List<DeviceChannelExtend> queryChannelsWithDeviceInfo(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, List<String> channelIds);
  110 +
  111 +
  112 + @Select(value = {" <script>" +
  113 + "SELECT " +
  114 + "dc.*, " +
  115 + "de.name as deviceName, " +
  116 + "de.online as deviceOnline " +
  117 + "from " +
  118 + "device_channel dc " +
  119 + "LEFT JOIN device de ON dc.deviceId = de.deviceId " +
  120 + "WHERE 1=1" +
  121 + " <if test='deviceId != null'> AND dc.deviceId = #{deviceId} </if> " +
  122 + " <if test='query != null'> AND (dc.channelId LIKE '%${query}%' OR dc.name LIKE '%${query}%' OR dc.name LIKE '%${query}%')</if> " +
  123 + " <if test='parentChannelId != null'> AND dc.parentId=#{parentChannelId} </if> " +
  124 + " <if test='online == true' > AND dc.status=1</if>" +
  125 + " <if test='online == false' > AND dc.status=0</if>" +
  126 + " <if test='hasSubChannel == true' > AND dc.subCount > 0 </if>" +
  127 + " <if test='hasSubChannel == false' > AND dc.subCount = 0 </if>" +
  128 + "<if test='channelIds != null'> AND dc.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
  129 + "#{item} " +
  130 + "</foreach> </if>" +
  131 + "ORDER BY dc.channelId ASC " +
  132 + "Limit #{limit} OFFSET #{start}" +
  133 + " </script>"})
  134 + List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId,List<String> channelIds, String parentChannelId, String query,
  135 + Boolean hasSubChannel, Boolean online, int start, int limit);
86 136  
87 137 @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND channelId=#{channelId}")
88 138 DeviceChannel queryChannel(String deviceId, String channelId);
... ... @@ -245,28 +295,6 @@ public interface DeviceChannelMapper {
245 295 int batchUpdate(List<DeviceChannel> updateChannels);
246 296  
247 297  
248   - @Select(value = {" <script>" +
249   - "SELECT " +
250   - "dc1.* " +
251   - "from " +
252   - "device_channel dc1 " +
253   - "WHERE " +
254   - "dc1.deviceId = #{deviceId} " +
255   - " <if test='query != null'> AND (dc1.channelId LIKE concat('%',#{query},'%') OR dc1.name LIKE concat('%',#{query},'%') OR dc1.name LIKE concat('%',#{query},'%'))</if> " +
256   - " <if test='parentChannelId != null'> AND dc1.parentId=#{parentChannelId} </if> " +
257   - " <if test='online == true' > AND dc1.status=1</if>" +
258   - " <if test='online == false' > AND dc1.status=0</if>" +
259   - " <if test='hasSubChannel == true' > AND dc1.subCount >0</if>" +
260   - " <if test='hasSubChannel == false' > AND dc1.subCount=0</if>" +
261   - "<if test='channelIds != null'> AND dc1.channelId in <foreach item='item' index='index' collection='channelIds' open='(' separator=',' close=')'>" +
262   - "#{item} " +
263   - "</foreach> </if>" +
264   - "ORDER BY dc1.channelId ASC " +
265   - "Limit #{limit} OFFSET #{start}" +
266   - " </script>"})
267   - List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String parentChannelId, String query,
268   - Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds);
269   -
270 298 @Select("SELECT * FROM device_channel WHERE deviceId=#{deviceId} AND status=1")
271 299 List<DeviceChannel> queryOnlineChannelsByDeviceId(String deviceId);
272 300  
... ... @@ -316,10 +344,10 @@ public interface DeviceChannelMapper {
316 344 "select * " +
317 345 "from device_channel " +
318 346 "where deviceId=#{deviceId}" +
319   - " <if test='parentId != null and length != null' > and parentId = #{parentId} or left(channelId, #{parentId.length()}) = #{parentId} and length(channelId)=#{length} </if>" +
  347 + " <if test='parentId != null and length != null' > and parentId = #{parentId} or left(channelId, LENGTH(#{parentId})) = #{parentId} and length(channelId)=#{length} </if>" +
320 348 " <if test='parentId == null and length != null' > and parentId = #{parentId} or length(channelId)=#{length} </if>" +
321 349 " <if test='parentId == null and length == null' > and parentId = #{parentId} </if>" +
322   - " <if test='parentId != null and length == null' > and parentId = #{parentId} or left(channelId, #{parentId.length()}) = #{parentId} </if>" +
  350 + " <if test='parentId != null and length == null' > and parentId = #{parentId} or left(channelId, LENGTH(#{parentId})) = #{parentId} </if>" +
323 351 " </script>"})
324 352 List<DeviceChannel> getChannelsWithCivilCodeAndLength(String deviceId, String parentId, Integer length);
325 353  
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/DeviceMapper.java
... ... @@ -39,9 +39,12 @@ public interface DeviceMapper {
39 39 "mobilePositionSubmissionInterval," +
40 40 "subscribeCycleForAlarm," +
41 41 "ssrcCheck," +
  42 + "asMessageChannel," +
42 43 "geoCoordSys," +
43 44 "treeType," +
44   - "online" +
  45 + "online," +
  46 + "mediaServerId," +
  47 + "(SELECT count(0) FROM device_channel WHERE deviceId=device.deviceId) as channelCount "+
45 48 " FROM device WHERE deviceId = #{deviceId}")
46 49 Device getDeviceByDeviceId(String deviceId);
47 50  
... ... @@ -70,6 +73,7 @@ public interface DeviceMapper {
70 73 "mobilePositionSubmissionInterval," +
71 74 "subscribeCycleForAlarm," +
72 75 "ssrcCheck," +
  76 + "asMessageChannel," +
73 77 "geoCoordSys," +
74 78 "treeType," +
75 79 "online" +
... ... @@ -98,6 +102,7 @@ public interface DeviceMapper {
98 102 "#{mobilePositionSubmissionInterval}," +
99 103 "#{subscribeCycleForAlarm}," +
100 104 "#{ssrcCheck}," +
  105 + "#{asMessageChannel}," +
101 106 "#{geoCoordSys}," +
102 107 "#{treeType}," +
103 108 "#{online}" +
... ... @@ -152,9 +157,11 @@ public interface DeviceMapper {
152 157 "mobilePositionSubmissionInterval," +
153 158 "subscribeCycleForAlarm," +
154 159 "ssrcCheck," +
  160 + "asMessageChannel," +
155 161 "geoCoordSys," +
156 162 "treeType," +
157 163 "online," +
  164 + "mediaServerId," +
158 165 "(SELECT count(0) FROM device_channel WHERE deviceId=de.deviceId) as channelCount FROM device de" +
159 166 "<if test=\"online != null\"> where online=${online}</if>"+
160 167 " </script>"
... ... @@ -164,9 +171,6 @@ public interface DeviceMapper {
164 171 @Delete("DELETE FROM device WHERE deviceId=#{deviceId}")
165 172 int del(String deviceId);
166 173  
167   - @Update("UPDATE device SET online=0")
168   - int outlineForAll();
169   -
170 174 @Select("SELECT " +
171 175 "deviceId, " +
172 176 "coalesce(custom_name, name) as name, " +
... ... @@ -192,6 +196,7 @@ public interface DeviceMapper {
192 196 "mobilePositionSubmissionInterval," +
193 197 "subscribeCycleForAlarm," +
194 198 "ssrcCheck," +
  199 + "asMessageChannel," +
195 200 "geoCoordSys," +
196 201 "treeType," +
197 202 "online " +
... ... @@ -222,6 +227,7 @@ public interface DeviceMapper {
222 227 "mobilePositionSubmissionInterval," +
223 228 "subscribeCycleForAlarm," +
224 229 "ssrcCheck," +
  230 + "asMessageChannel," +
225 231 "geoCoordSys," +
226 232 "treeType," +
227 233 "online" +
... ... @@ -243,12 +249,13 @@ public interface DeviceMapper {
243 249 "<if test=\"mobilePositionSubmissionInterval != null\">, mobilePositionSubmissionInterval=#{mobilePositionSubmissionInterval}</if>" +
244 250 "<if test=\"subscribeCycleForAlarm != null\">, subscribeCycleForAlarm=#{subscribeCycleForAlarm}</if>" +
245 251 "<if test=\"ssrcCheck != null\">, ssrcCheck=#{ssrcCheck}</if>" +
  252 + "<if test=\"asMessageChannel != null\">, asMessageChannel=#{asMessageChannel}</if>" +
246 253 "<if test=\"geoCoordSys != null\">, geoCoordSys=#{geoCoordSys}</if>" +
247 254 "<if test=\"treeType != null\">, treeType=#{treeType}</if>" +
248 255 "<if test=\"mediaServerId != null\">, mediaServerId=#{mediaServerId}</if>" +
249 256 "WHERE deviceId=#{deviceId}"+
250 257 " </script>"})
251   - int updateCustom(Device device);
  258 + void updateCustom(Device device);
252 259  
253 260 @Insert("INSERT INTO device (" +
254 261 "deviceId, " +
... ... @@ -259,9 +266,11 @@ public interface DeviceMapper {
259 266 "updateTime," +
260 267 "charset," +
261 268 "ssrcCheck," +
  269 + "asMessageChannel," +
262 270 "geoCoordSys," +
263 271 "treeType," +
264   - "online" +
  272 + "online," +
  273 + "mediaServerId" +
265 274 ") VALUES (" +
266 275 "#{deviceId}," +
267 276 "#{name}," +
... ... @@ -271,9 +280,11 @@ public interface DeviceMapper {
271 280 "#{updateTime}," +
272 281 "#{charset}," +
273 282 "#{ssrcCheck}," +
  283 + "#{asMessageChannel}," +
274 284 "#{geoCoordSys}," +
275 285 "#{treeType}," +
276   - "#{online}" +
  286 + "#{online}," +
  287 + "#{mediaServerId}" +
277 288 ")")
278 289 void addCustomDevice(Device device);
279 290  
... ... @@ -282,4 +293,7 @@ public interface DeviceMapper {
282 293  
283 294 @Select("select * from device")
284 295 List<Device> getAll();
  296 +
  297 + @Select("select * from device where asMessageChannel = 1")
  298 + List<Device> queryDeviceWithAsMessageChannel();
285 299 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/dao/ParentPlatformMapper.java
... ... @@ -15,10 +15,10 @@ import java.util.List;
15 15 public interface ParentPlatformMapper {
16 16  
17 17 @Insert("INSERT INTO parent_platform (enable, name, serverGBId, serverGBDomain, serverIP, serverPort, deviceGBId, deviceIp, " +
18   - " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, " +
  18 + " devicePort, username, password, expires, keepTimeout, transport, characterSet, ptz, rtcp, asMessageChannel, " +
19 19 " status, startOfflinePush, catalogId, administrativeDivision, catalogGroup, createTime, updateTime, treeType) " +
20 20 " VALUES (#{enable}, #{name}, #{serverGBId}, #{serverGBDomain}, #{serverIP}, #{serverPort}, #{deviceGBId}, #{deviceIp}, " +
21   - " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, " +
  21 + " #{devicePort}, #{username}, #{password}, #{expires}, #{keepTimeout}, #{transport}, #{characterSet}, #{ptz}, #{rtcp}, #{asMessageChannel}, " +
22 22 " #{status}, #{startOfflinePush}, #{catalogId}, #{administrativeDivision}, #{catalogGroup}, #{createTime}, #{updateTime}, #{treeType})")
23 23 int addParentPlatform(ParentPlatform parentPlatform);
24 24  
... ... @@ -40,6 +40,7 @@ public interface ParentPlatformMapper {
40 40 "characterSet=#{characterSet}, " +
41 41 "ptz=#{ptz}, " +
42 42 "rtcp=#{rtcp}, " +
  43 + "asMessageChannel=#{asMessageChannel}, " +
43 44 "status=#{status}, " +
44 45 "startOfflinePush=#{startOfflinePush}, " +
45 46 "catalogGroup=#{catalogGroup}, " +
... ... @@ -68,9 +69,12 @@ public interface ParentPlatformMapper {
68 69 "FROM parent_platform pp ")
69 70 List<ParentPlatform> getParentPlatformList();
70 71  
71   - @Select("SELECT * FROM parent_platform WHERE enable=#{enable}")
  72 + @Select("SELECT * FROM parent_platform WHERE enable=#{enable} ")
72 73 List<ParentPlatform> getEnableParentPlatformList(boolean enable);
73 74  
  75 + @Select("SELECT * FROM parent_platform WHERE enable=1 and asMessageChannel = 1")
  76 + List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel();
  77 +
74 78 @Select("SELECT * FROM parent_platform WHERE serverGBId=#{platformGbId}")
75 79 ParentPlatform getParentPlatByServerGBId(String platformGbId);
76 80  
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/RedisCatchStorageImpl.java
... ... @@ -177,12 +177,14 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
177 177 @Override
178 178 public boolean startDownload(StreamInfo stream, String callId) {
179 179 boolean result;
  180 + String key=String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
  181 + userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId);
180 182 if (stream.getProgress() == 1) {
181   - result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
182   - userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream);
  183 + logger.debug("添加下载缓存==已完成下载=》{}",key);
  184 + result = RedisUtil.set(key, stream);
183 185 }else {
184   - result = RedisUtil.set(String.format("%S_%s_%s_%s_%s_%s_%s", VideoManagerConstants.DOWNLOAD_PREFIX,
185   - userSetting.getServerId(), stream.getMediaServerId(), stream.getDeviceID(), stream.getChannelId(), stream.getStream(), callId), stream, 60*60);
  186 + logger.debug("添加下载缓存==未完成下载=》{}",key);
  187 + result = RedisUtil.set(key, stream, 60*60);
186 188 }
187 189 return result;
188 190 }
... ... @@ -617,7 +619,7 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
617 619 stream,
618 620 callId
619 621 );
620   - List<Object> streamInfoScan = RedisUtil.scan(key);
  622 + List<Object> streamInfoScan = RedisUtil.scan2(key);
621 623 if (streamInfoScan.size() > 0) {
622 624 return (StreamInfo) RedisUtil.get((String) streamInfoScan.get(0));
623 625 }else {
... ... @@ -855,7 +857,8 @@ public class RedisCatchStorageImpl implements IRedisCatchStorage {
855 857  
856 858 @Override
857 859 public void sendAlarmMsg(AlarmChannelMessage msg) {
858   - String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM_RECEIVE;
  860 + // 此消息用于对接第三方服务下级来的消息内容
  861 + String key = VideoManagerConstants.VM_MSG_SUBSCRIBE_ALARM;
859 862 logger.info("[redis发送通知] 报警{}: {}", key, JSON.toJSON(msg));
860 863 RedisUtil.convertAndSend(key, (JSONObject)JSON.toJSON(msg));
861 864 }
... ...
src/main/java/com/genersoft/iot/vmp/storager/impl/VideoManagerStorageImpl.java
... ... @@ -13,6 +13,7 @@ import com.genersoft.iot.vmp.storager.dao.*;
13 13 import com.genersoft.iot.vmp.storager.dao.dto.ChannelSourceInfo;
14 14 import com.genersoft.iot.vmp.utils.DateUtil;
15 15 import com.genersoft.iot.vmp.vmanager.gb28181.platform.bean.ChannelReduce;
  16 +import com.genersoft.iot.vmp.web.gb28181.dto.DeviceChannelExtend;
16 17 import com.github.pagehelper.PageHelper;
17 18 import com.github.pagehelper.PageInfo;
18 19 import org.slf4j.Logger;
... ... @@ -189,7 +190,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
189 190 dataSourceTransactionManager.commit(transactionStatus); //手动提交
190 191 return true;
191 192 }catch (Exception e) {
192   - e.printStackTrace();
  193 + logger.error("未处理的异常 ", e);
193 194 dataSourceTransactionManager.rollback(transactionStatus);
194 195 return false;
195 196 }
... ... @@ -305,7 +306,7 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
305 306 }
306 307 return true;
307 308 }catch (Exception e) {
308   - e.printStackTrace();
  309 + logger.error("未处理的异常 ", e);
309 310 dataSourceTransactionManager.rollback(transactionStatus);
310 311 return false;
311 312 }
... ... @@ -359,8 +360,8 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
359 360 }
360 361  
361 362 @Override
362   - public List<DeviceChannel> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, String query, Boolean hasSubChannel, Boolean online, int start, int limit,List<String> channelIds) {
363   - return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, null, query, hasSubChannel, online, start, limit,channelIds);
  363 + public List<DeviceChannelExtend> queryChannelsByDeviceIdWithStartAndLimit(String deviceId, List<String> channelIds, String query, Boolean hasSubChannel, Boolean online, int start, int limit) {
  364 + return deviceChannelMapper.queryChannelsByDeviceIdWithStartAndLimit(deviceId, channelIds, null, query, hasSubChannel, online, start, limit);
364 365 }
365 366  
366 367  
... ... @@ -370,6 +371,11 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
370 371 }
371 372  
372 373 @Override
  374 + public List<DeviceChannelExtend> queryChannelsByDeviceId(String deviceId, List<String> channelIds, Boolean online) {
  375 + return deviceChannelMapper.queryChannelsWithDeviceInfo(deviceId, null,null, null, online,channelIds);
  376 + }
  377 +
  378 + @Override
373 379 public PageInfo<DeviceChannel> querySubChannels(String deviceId, String parentChannelId, String query, Boolean hasSubChannel, Boolean online, int page, int count) {
374 380 PageHelper.startPage(page, count);
375 381 List<DeviceChannel> all = deviceChannelMapper.queryChannels(deviceId, parentChannelId, query, hasSubChannel, online,null);
... ... @@ -512,6 +518,16 @@ public class VideoManagerStorageImpl implements IVideoManagerStorage {
512 518 }
513 519  
514 520 @Override
  521 + public List<ParentPlatform> queryEnablePlatformListWithAsMessageChannel() {
  522 + return platformMapper.queryEnablePlatformListWithAsMessageChannel();
  523 + }
  524 +
  525 + @Override
  526 + public List<Device> queryDeviceWithAsMessageChannel() {
  527 + return deviceMapper.queryDeviceWithAsMessageChannel();
  528 + }
  529 +
  530 + @Override
515 531 public void outlineForAllParentPlatform() {
516 532 platformMapper.outlineForAllParentPlatform();
517 533 }
... ...
src/main/java/com/genersoft/iot/vmp/utils/DateUtil.java
... ... @@ -45,7 +45,6 @@ public class DateUtil {
45 45  
46 46 public static String ISO8601Toyyyy_MM_dd_HH_mm_ss(String formatTime) {
47 47 return formatter.format(formatterCompatibleISO8601.parse(formatTime));
48   -
49 48 }
50 49  
51 50 /**
... ...
src/main/java/com/genersoft/iot/vmp/utils/redis/RedisUtil.java
... ... @@ -881,7 +881,13 @@ public class RedisUtil {
881 881  
882 882 return new ArrayList<>(resultKeys);
883 883 }
884   -
  884 + public static List<Object> scan2(String query) {
  885 + if (redisTemplate == null) {
  886 + redisTemplate = SpringBeanFactory.getBean("redisTemplate");
  887 + }
  888 + Set<String> keys = redisTemplate.keys(query);
  889 + return new ArrayList<>(keys);
  890 + }
885 891 // ============================== 消息发送与订阅 ==============================
886 892 public static void convertAndSend(String channel, JSONObject msg) {
887 893 if (redisTemplate == null) {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/bean/WVPResult.java
... ... @@ -28,6 +28,10 @@ public class WVPResult&lt;T&gt; implements Cloneable{
28 28 return new WVPResult<>(ErrorCode.SUCCESS.getCode(), msg, t);
29 29 }
30 30  
  31 + public static WVPResult success() {
  32 + return new WVPResult<>(ErrorCode.SUCCESS.getCode(), ErrorCode.SUCCESS.getMsg(), null);
  33 + }
  34 +
31 35 public static <T> WVPResult<T> success(T t) {
32 36 return success(t, ErrorCode.SUCCESS.getMsg());
33 37 }
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/MobilePosition/MobilePositionController.java
... ... @@ -30,7 +30,7 @@ import java.util.UUID;
30 30 * 位置信息管理
31 31 */
32 32 @Tag(name = "位置信息管理")
33   -@CrossOrigin
  33 +
34 34 @RestController
35 35 @RequestMapping("/api/position")
36 36 public class MobilePositionController {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/SseController/SseController.java
... ... @@ -17,7 +17,7 @@ import org.springframework.web.servlet.mvc.method.annotation.SseEmitter;
17 17 * @data: 2021-01-20
18 18 */
19 19 @Tag(name = "SSE推送")
20   -@CrossOrigin
  20 +
21 21 @Controller
22 22 @RequestMapping("/api")
23 23 public class SseController {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/alarm/AlarmController.java
... ... @@ -6,35 +6,28 @@ import com.genersoft.iot.vmp.gb28181.bean.DeviceAlarm;
6 6 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
7 7 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommander;
8 8 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
9   -import com.genersoft.iot.vmp.media.zlm.ZLMHttpHookListener;
10 9 import com.genersoft.iot.vmp.service.IDeviceAlarmService;
11 10 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
12 11 import com.genersoft.iot.vmp.utils.DateUtil;
13 12 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
14   -import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
15 13 import com.github.pagehelper.PageInfo;
16 14 import io.swagger.v3.oas.annotations.Operation;
17 15 import io.swagger.v3.oas.annotations.Parameter;
18   -import io.swagger.v3.oas.annotations.responses.ApiResponse;
19 16 import io.swagger.v3.oas.annotations.tags.Tag;
20 17 import org.slf4j.Logger;
21 18 import org.slf4j.LoggerFactory;
22 19 import org.springframework.beans.factory.annotation.Autowired;
23   -import org.springframework.http.HttpStatus;
24   -import org.springframework.http.ResponseEntity;
25 20 import org.springframework.util.ObjectUtils;
26   -import org.springframework.util.StringUtils;
27 21 import org.springframework.web.bind.annotation.*;
28 22  
29 23 import javax.sip.InvalidArgumentException;
30 24 import javax.sip.SipException;
31 25 import java.text.ParseException;
32   -import java.time.LocalDateTime;
33 26 import java.util.Arrays;
34 27 import java.util.List;
35 28  
36 29 @Tag(name = "报警信息管理")
37   -@CrossOrigin
  30 +
38 31 @RestController
39 32 @RequestMapping("/api/alarm")
40 33 public class AlarmController {
... ... @@ -78,11 +71,11 @@ public class AlarmController {
78 71 if (ObjectUtils.isEmpty(deviceIds)) {
79 72 deviceIds = null;
80 73 }
  74 +
81 75 if (ObjectUtils.isEmpty(time)) {
82 76 time = null;
83   - }
84   - if (!DateUtil.verification(time, DateUtil.formatter) ){
85   - return null;
  77 + }else if (!DateUtil.verification(time, DateUtil.formatter) ){
  78 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "time格式为" + DateUtil.PATTERN);
86 79 }
87 80 List<String> deviceIdList = null;
88 81 if (deviceIds != null) {
... ... @@ -110,7 +103,7 @@ public class AlarmController {
110 103 deviceAlarm.setAlarmDescription("test");
111 104 deviceAlarm.setAlarmMethod("1");
112 105 deviceAlarm.setAlarmPriority("1");
113   - deviceAlarm.setAlarmTime(DateUtil.formatterISO8601.format(LocalDateTime.now()));
  106 + deviceAlarm.setAlarmTime(DateUtil.getNow());
114 107 deviceAlarm.setAlarmType("1");
115 108 deviceAlarm.setLongitude(115.33333);
116 109 deviceAlarm.setLatitude(39.33333);
... ... @@ -177,16 +170,17 @@ public class AlarmController {
177 170 if (ObjectUtils.isEmpty(alarmType)) {
178 171 alarmType = null;
179 172 }
  173 +
180 174 if (ObjectUtils.isEmpty(startTime)) {
181 175 startTime = null;
  176 + }else if (!DateUtil.verification(startTime, DateUtil.formatter) ){
  177 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN);
182 178 }
  179 +
183 180 if (ObjectUtils.isEmpty(endTime)) {
184 181 endTime = null;
185   - }
186   -
187   -
188   - if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
189   - throw new ControllerException(ErrorCode.ERROR100.getCode(), "开始时间或结束时间格式有误");
  182 + }else if (!DateUtil.verification(endTime, DateUtil.formatter) ){
  183 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN);
190 184 }
191 185  
192 186 return deviceAlarmService.getAllAlarm(page, count, deviceId, alarmPriority, alarmMethod,
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceConfig.java
... ... @@ -14,7 +14,6 @@ import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
14 14 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
15 15 import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
16 16 import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
17   -
18 17 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
19 18 import io.swagger.v3.oas.annotations.Operation;
20 19 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -22,9 +21,7 @@ import io.swagger.v3.oas.annotations.tags.Tag;
22 21 import org.slf4j.Logger;
23 22 import org.slf4j.LoggerFactory;
24 23 import org.springframework.beans.factory.annotation.Autowired;
25   -import org.springframework.http.ResponseEntity;
26 24 import org.springframework.util.ObjectUtils;
27   -import org.springframework.util.StringUtils;
28 25 import org.springframework.web.bind.annotation.*;
29 26 import org.springframework.web.context.request.async.DeferredResult;
30 27  
... ... @@ -34,7 +31,6 @@ import java.text.ParseException;
34 31 import java.util.UUID;
35 32  
36 33 @Tag(name = "国标设备配置")
37   -@CrossOrigin
38 34 @RestController
39 35 @RequestMapping("/api/device/config")
40 36 public class DeviceConfig {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceControl.java
... ... @@ -32,7 +32,7 @@ import java.text.ParseException;
32 32 import java.util.UUID;
33 33  
34 34 @Tag(name = "国标设备控制")
35   -@CrossOrigin
  35 +
36 36 @RestController
37 37 @RequestMapping("/api/device/control")
38 38 public class DeviceControl {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/device/DeviceQuery.java
... ... @@ -24,6 +24,7 @@ import io.swagger.v3.oas.annotations.Operation;
24 24 import io.swagger.v3.oas.annotations.Parameter;
25 25 import io.swagger.v3.oas.annotations.tags.Tag;
26 26 import org.apache.commons.compress.utils.IOUtils;
  27 +import org.apache.ibatis.annotations.Options;
27 28 import org.slf4j.Logger;
28 29 import org.slf4j.LoggerFactory;
29 30 import org.springframework.beans.factory.annotation.Autowired;
... ... @@ -46,7 +47,7 @@ import java.util.*;
46 47  
47 48 @Tag(name = "国标设备查询", description = "国标设备查询")
48 49 @SuppressWarnings("rawtypes")
49   -@CrossOrigin
  50 +
50 51 @RestController
51 52 @RequestMapping("/api/device/query")
52 53 public class DeviceQuery {
... ... @@ -97,8 +98,10 @@ public class DeviceQuery {
97 98 @Parameter(name = "page", description = "当前页", required = true)
98 99 @Parameter(name = "count", description = "每页查询数量", required = true)
99 100 @GetMapping("/devices")
  101 + @Options()
100 102 public PageInfo<Device> devices(int page, int count){
101   -
  103 +// if (page == null) page = 0;
  104 +// if (count == null) count = 20;
102 105 return storager.queryVideoDeviceList(page, count,null);
103 106 }
104 107  
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/gbStream/GbStreamController.java
... ... @@ -17,7 +17,7 @@ import org.springframework.web.bind.annotation.*;
17 17 import java.util.List;
18 18  
19 19 @Tag(name = "视频流关联到级联平台")
20   -@CrossOrigin
  20 +
21 21 @RestController
22 22 @RequestMapping("/api/gbStream")
23 23 public class GbStreamController {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/media/MediaController.java
... ... @@ -24,7 +24,7 @@ import javax.servlet.http.HttpServletRequest;
24 24  
25 25 @Tag(name = "媒体流相关")
26 26 @Controller
27   -@CrossOrigin
  27 +
28 28 @RequestMapping(value = "/api/media")
29 29 public class MediaController {
30 30  
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/platform/PlatformController.java
... ... @@ -7,6 +7,7 @@ import com.genersoft.iot.vmp.conf.DynamicTask;
7 7 import com.genersoft.iot.vmp.conf.UserSetting;
8 8 import com.genersoft.iot.vmp.conf.exception.ControllerException;
9 9 import com.genersoft.iot.vmp.gb28181.bean.ParentPlatform;
  10 +import com.genersoft.iot.vmp.gb28181.bean.ParentPlatformCatch;
10 11 import com.genersoft.iot.vmp.gb28181.bean.PlatformCatalog;
11 12 import com.genersoft.iot.vmp.gb28181.bean.SubscribeHolder;
12 13 import com.genersoft.iot.vmp.gb28181.transmit.cmd.ISIPCommanderForPlatform;
... ... @@ -37,7 +38,7 @@ import java.util.List;
37 38 * 级联平台管理
38 39 */
39 40 @Tag(name = "级联平台管理")
40   -@CrossOrigin
  41 +
41 42 @RestController
42 43 @RequestMapping("/api/platform")
43 44 public class PlatformController {
... ... @@ -205,58 +206,8 @@ public class PlatformController {
205 206 ) {
206 207 throw new ControllerException(ErrorCode.ERROR400);
207 208 }
208   - parentPlatform.setCharacterSet(parentPlatform.getCharacterSet().toUpperCase());
209   - ParentPlatform parentPlatformOld = storager.queryParentPlatByServerGBId(parentPlatform.getServerGBId());
210   - parentPlatform.setUpdateTime(DateUtil.getNow());
211   - if (!parentPlatformOld.getTreeType().equals(parentPlatform.getTreeType())) {
212   - // 目录结构发生变化,清空之前的关联关系
213   - logger.info("保存平台{}时发现目录结构变化,清空关联关系", parentPlatform.getDeviceGBId());
214   - storager.cleanContentForPlatform(parentPlatform.getServerGBId());
215   -
216   - }
217   - boolean updateResult = storager.updateParentPlatform(parentPlatform);
218   -
219   - if (updateResult) {
220   - // 保存时启用就发送注册
221   - if (parentPlatform.isEnable()) {
222   - if (parentPlatformOld != null && parentPlatformOld.isStatus()) {
223   - try {
224   - commanderForPlatform.unregister(parentPlatformOld, null, null);
225   - } catch (InvalidArgumentException | ParseException | SipException e) {
226   - logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
227   - }
228   - try {
229   - Thread.sleep(500);
230   - } catch (InterruptedException e) {
231   - logger.error("[线程休眠失败] : {}", e.getMessage());
232   - }
233   - // 只要保存就发送注册
234   - try {
235   - commanderForPlatform.register(parentPlatform, null, null);
236   - } catch (InvalidArgumentException | ParseException | SipException e) {
237   - logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
238   - }
239   -
240   - } else {
241   - // 只要保存就发送注册
242   - try {
243   - commanderForPlatform.register(parentPlatform, null, null);
244   - } catch (InvalidArgumentException | ParseException | SipException e) {
245   - logger.error("[命令发送失败] 国标级联 注册: {}", e.getMessage());
246   - }
247   - }
248   - } else if (parentPlatformOld != null && parentPlatformOld.isEnable() && !parentPlatform.isEnable()) { // 关闭启用时注销
249   - try {
250   - commanderForPlatform.unregister(parentPlatformOld, null, null);
251   - } catch (InvalidArgumentException | ParseException | SipException e) {
252   - logger.error("[命令发送失败] 国标级联 注销: {}", e.getMessage());
253   - }
254   - // 停止订阅相关的定时任务
255   - subscribeHolder.removeAllSubscribe(parentPlatform.getServerGBId());
256   - }
257   - } else {
258   - throw new ControllerException(ErrorCode.ERROR100.getCode(),"写入数据库失败");
259   - }
  209 +
  210 + platformService.update(parentPlatform);
260 211 }
261 212  
262 213 /**
... ... @@ -279,12 +230,16 @@ public class PlatformController {
279 230 throw new ControllerException(ErrorCode.ERROR400);
280 231 }
281 232 ParentPlatform parentPlatform = storager.queryParentPlatByServerGBId(serverGBId);
  233 + ParentPlatformCatch parentPlatformCatch = redisCatchStorage.queryPlatformCatchInfo(serverGBId);
282 234 if (parentPlatform == null) {
283 235 throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
284 236 }
  237 + if (parentPlatformCatch == null) {
  238 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "平台不存在");
  239 + }
285 240 // 发送离线消息,无论是否成功都删除缓存
286 241 try {
287   - commanderForPlatform.unregister(parentPlatform, (event -> {
  242 + commanderForPlatform.unregister(parentPlatform, parentPlatformCatch.getSipTransactionInfo(), (event -> {
288 243 // 清空redis缓存
289 244 redisCatchStorage.delPlatformCatchInfo(parentPlatform.getServerGBId());
290 245 redisCatchStorage.delPlatformKeepalive(parentPlatform.getServerGBId());
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/play/PlayController.java
... ... @@ -41,7 +41,7 @@ import java.util.UUID;
41 41 * @author lin
42 42 */
43 43 @Tag(name = "国标设备点播")
44   -@CrossOrigin
  44 +
45 45 @RestController
46 46 @RequestMapping("/api/play")
47 47 public class PlayController {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/playback/PlaybackController.java
... ... @@ -40,7 +40,7 @@ import java.util.UUID;
40 40 * @author lin
41 41 */
42 42 @Tag(name = "视频回放")
43   -@CrossOrigin
  43 +
44 44 @RestController
45 45 @RequestMapping("/api/playback")
46 46 public class PlaybackController {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/ptz/PtzController.java
1 1 package com.genersoft.iot.vmp.vmanager.gb28181.ptz;
2 2  
3   -
  3 +
4 4 import com.genersoft.iot.vmp.conf.exception.ControllerException;
  5 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  6 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
  7 +import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
  9 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
5 10 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
6 11 import io.swagger.v3.oas.annotations.Operation;
7 12 import io.swagger.v3.oas.annotations.Parameter;
... ... @@ -10,23 +15,16 @@ import org.slf4j.Logger;
10 15 import org.slf4j.LoggerFactory;
11 16 import org.springframework.beans.factory.annotation.Autowired;
12 17 import org.springframework.util.ObjectUtils;
13   -import org.springframework.util.StringUtils;
14 18 import org.springframework.web.bind.annotation.*;
15 19 import org.springframework.web.context.request.async.DeferredResult;
16 20  
17   -import com.genersoft.iot.vmp.gb28181.bean.Device;
18   -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
19   -import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
20   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
21   -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
22   -
23 21 import javax.sip.InvalidArgumentException;
24 22 import javax.sip.SipException;
25 23 import java.text.ParseException;
26 24 import java.util.UUID;
27 25  
28 26 @Tag(name = "云台控制")
29   -@CrossOrigin
  27 +
30 28 @RestController
31 29 @RequestMapping("/api/ptz")
32 30 public class PtzController {
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/gb28181/record/GBRecordController.java
... ... @@ -3,40 +3,37 @@ package com.genersoft.iot.vmp.vmanager.gb28181.record;
3 3 import com.genersoft.iot.vmp.common.StreamInfo;
4 4 import com.genersoft.iot.vmp.conf.exception.ControllerException;
5 5 import com.genersoft.iot.vmp.conf.exception.SsrcTransactionNotFoundException;
  6 +import com.genersoft.iot.vmp.gb28181.bean.Device;
  7 +import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
  8 +import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
6 9 import com.genersoft.iot.vmp.gb28181.transmit.callback.RequestMessage;
  10 +import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
7 11 import com.genersoft.iot.vmp.service.IDeviceService;
8 12 import com.genersoft.iot.vmp.service.IPlayService;
  13 +import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
9 14 import com.genersoft.iot.vmp.utils.DateUtil;
10 15 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
11 16 import com.genersoft.iot.vmp.vmanager.bean.StreamContent;
12 17 import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
13   -
14 18 import io.swagger.v3.oas.annotations.Operation;
15 19 import io.swagger.v3.oas.annotations.Parameter;
16 20 import io.swagger.v3.oas.annotations.tags.Tag;
17 21 import org.slf4j.Logger;
18 22 import org.slf4j.LoggerFactory;
19 23 import org.springframework.beans.factory.annotation.Autowired;
20   -import org.springframework.web.bind.annotation.CrossOrigin;
21 24 import org.springframework.web.bind.annotation.GetMapping;
22 25 import org.springframework.web.bind.annotation.PathVariable;
23 26 import org.springframework.web.bind.annotation.RequestMapping;
24 27 import org.springframework.web.bind.annotation.RestController;
25 28 import org.springframework.web.context.request.async.DeferredResult;
26 29  
27   -import com.genersoft.iot.vmp.gb28181.bean.Device;
28   -import com.genersoft.iot.vmp.gb28181.bean.RecordInfo;
29   -import com.genersoft.iot.vmp.gb28181.transmit.callback.DeferredResultHolder;
30   -import com.genersoft.iot.vmp.gb28181.transmit.cmd.impl.SIPCommander;
31   -import com.genersoft.iot.vmp.storager.IVideoManagerStorage;
32   -
33 30 import javax.sip.InvalidArgumentException;
34 31 import javax.sip.SipException;
35 32 import java.text.ParseException;
36 33 import java.util.UUID;
37 34  
38 35 @Tag(name = "国标录像")
39   -@CrossOrigin
  36 +
40 37 @RestController
41 38 @RequestMapping("/api/gb_record")
42 39 public class GBRecordController {
... ... @@ -74,10 +71,10 @@ public class GBRecordController {
74 71 }
75 72 DeferredResult<WVPResult<RecordInfo>> result = new DeferredResult<>();
76 73 if (!DateUtil.verification(startTime, DateUtil.formatter)){
77   - throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime error, format is " + DateUtil.PATTERN);
  74 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "startTime格式为" + DateUtil.PATTERN);
78 75 }
79 76 if (!DateUtil.verification(endTime, DateUtil.formatter)){
80   - throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime error, format is " + DateUtil.PATTERN);
  77 + throw new ControllerException(ErrorCode.ERROR100.getCode(), "endTime格式为" + DateUtil.PATTERN);
81 78 }
82 79  
83 80 Device device = storager.queryVideoDevice(deviceId);
... ...
src/main/java/com/genersoft/iot/vmp/vmanager/log/LogController.java
... ... @@ -6,25 +6,18 @@ import com.genersoft.iot.vmp.service.ILogService;
6 6 import com.genersoft.iot.vmp.storager.dao.dto.LogDto;
7 7 import com.genersoft.iot.vmp.utils.DateUtil;
8 8 import com.genersoft.iot.vmp.vmanager.bean.ErrorCode;
9   -import com.genersoft.iot.vmp.vmanager.bean.WVPResult;
10 9 import com.github.pagehelper.PageInfo;
11   -
12 10 import io.swagger.v3.oas.annotations.Operation;
13 11 import io.swagger.v3.oas.annotations.Parameter;
14 12 import io.swagger.v3.oas.annotations.tags.Tag;
15 13 import org.slf4j.Logger;
16 14 import org.slf4j.LoggerFactory;
17 15 import org.springframework.beans.factory.annotation.Autowired;
18   -import org.springframework.http.HttpStatus;
19   -import org.springframework.http.ResponseEntity;
20 16 import org.springframework.util.ObjectUtils;
21   -import org.springframework.util.StringUtils;
22 17 import org.springframework.web.bind.annotation.*;
23 18  
24   -import java.text.ParseException;
25   -
26 19 @Tag(name = "日志管理")
27   -@CrossOrigin
  20 +
28 21 @RestController
29 22 @RequestMapping("/api/log")
30 23 public class LogController {
... ... @@ -67,18 +60,21 @@ public class LogController {
67 60 if (ObjectUtils.isEmpty(query)) {
68 61 query = null;
69 62 }
  63 +
  64 + if (!userSetting.getLogInDatebase()) {
  65 + logger.warn("自动记录日志功能已关闭,查询结果可能不完整。");
  66 + }
  67 +
70 68 if (ObjectUtils.isEmpty(startTime)) {
71 69 startTime = null;
  70 + }else if (!DateUtil.verification(startTime, DateUtil.formatter) ){
  71 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "startTime格式为" + DateUtil.PATTERN);
72 72 }
  73 +
73 74 if (ObjectUtils.isEmpty(endTime)) {
74 75 endTime = null;
75   - }
76   - if (!userSetting.getLogInDatebase()) {
77   - logger.warn("自动记录日志功能已关闭,查询结果可能不完整。");
78   - }
79   -
80   - if (!DateUtil.verification(startTime, DateUtil.formatter) || !DateUtil.verification(endTime, DateUtil.formatter)){
81   - throw new ControllerException(ErrorCode.ERROR400);
  76 + }else if (!DateUtil.verification(endTime, DateUtil.formatter) ){
  77 + throw new ControllerException(ErrorCode.ERROR400.getCode(), "endTime格式为" + DateUtil.PATTERN);
82 78 }
83 79  
84 80 return logService.getAll(page, count, query, type, startTime, endTime);
... ...